• Java框架(三)--Spring IoC容器与Bean管理(7)--基于注解配置IoC容器


    基于注解配置IoC容器

    基于注解的优势

    摆脱繁琐的XML形式的bean与依赖注入配置。
    基于“声明式”的原则,更适合轻量级的现代企业应用。
    让代码可读性变得更好,研发人员拥有更好的开发体验。

    三类注解

    组件类型注解 - 声明当前类的功能与职责。
    自动装配注解 - 根据属性特征自动注入对象。
    元数据注解 - 更细化的辅助IoC容器管理对象的注解。

    四种组件类型注解

    在这里插入图片描述

    开启组件扫描

    在这里插入图片描述

    代码演示

    打开IDEA创建新的maven工程,在pom.xml引入Spring依赖

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.ql.spring</groupId>
        <artifactId>ioc_annotation</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <properties>
            <maven.compiler.source>8</maven.compiler.source>
            <maven.compiler.target>8</maven.compiler.target>
        </properties>
        <repositories>
            <repository>
                <id>aliyun</id>
                <name>aliyun</name>
                <url>https://maven.aliyun.com/repository/public</url>
            </repository>
        </repositories>
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.2.6.RELEASE</version>
            </dependency>
        </dependencies>
    </project>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    在resources目录下创建Spring配置文件applicationContext.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            https://www.springframework.org/schema/context/spring-context.xsd">
        <!-- 在IoC容器初始化时自动扫描四种组件类型注解并完成实例化
             @Repository
             @Service
             @Controller
             @Component
        -->
        <context:component-scan base-package="com.ql.spring"/>
    </beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在com.ql.spring.ioc.dao包下创建UserDao类

    package com.ql.spring.ioc.dao;
    
    import org.springframework.stereotype.Repository;
    //组件类型注解默认beanId为类名首字母小写
    //beanId = userDao
    @Repository
    public class UserDao {
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在com.ql.spring.ioc.service包下创建UserService类

    package com.ql.spring.ioc.service;
    
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserService {
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在com.ql.spring.ioc.controller包下创建UserController类

    package com.ql.spring.ioc.controller;
    
    import org.springframework.stereotype.Controller;
    
    @Controller
    public class UserController {
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在com.ql.spring.ioc.utils包下创建StringUtils类

    package com.ql.spring.ioc.utils;
    
    import org.springframework.stereotype.Component;
    
    @Component
    public class StringUtils {
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    然后com.ql.spring.ioc包下创建入口类SpringApplication执行

    package com.ql.spring.ioc;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class SpringApplication {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
            String[] ids = context.getBeanDefinitionNames();
            for (String id: ids) {
                System.out.println(id + ":" + context.getBean(id));
            }
        }
    }
    /*
    userController:com.ql.spring.ioc.controller.UserController@6483f5ae
    userDao:com.ql.spring.ioc.dao.UserDao@b9afc07
    userService:com.ql.spring.ioc.service.UserService@382db087
    stringUtils:com.ql.spring.ioc.utils.StringUtils@73d4cc9e
    org.springframework.context.annotation.internalConfigurationAnnotationProcessor:org.springframework.context.annotation.ConfigurationClassPostProcessor@80169cf
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@5427c60c
    org.springframework.context.annotation.internalCommonAnnotationProcessor:org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@15bfd87
    org.springframework.context.event.internalEventListenerProcessor:org.springframework.context.event.EventListenerMethodProcessor@543e710e
    org.springframework.context.event.internalEventListenerFactory:org.springframework.context.event.DefaultEventListenerFactory@57f23557
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    两类自动装配注解

    在这里插入图片描述

    按类型装配@Autowired

    修改com.ql.spring.ioc.dao包下的UserDao

    package com.ql.spring.ioc.dao;
    
    import org.springframework.stereotype.Repository;
    //组件类型注解默认beanId为类名首字母小写
    //beanId = userDao
    @Repository
    public class UserDao {
        public UserDao() {
            System.out.println("正在创建UserDao:"+this);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    修改com.ql.spring.ioc.service包下的UserService

    package com.ql.spring.ioc.service;
    
    import com.ql.spring.ioc.dao.UserDao;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserService {
        private UserService(){
            System.out.println("正在创建UserService:"+this);
        }
        @Autowired
        private UserDao udao;
    
        public UserDao getUdao() {
            return udao;
        }
    
        public void setUdao(UserDao udao) {
            System.out.println("setUdao:"+udao);
            this.udao = udao;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    然后修改入口类SpringApplication重新执行

    package com.ql.spring.ioc;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class SpringApplication {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
    		UserService userService = context.getBean("userService", UserService.class);
            System.out.println(userService.getUdao());
        }
    }
    /*
    正在创建UserDao:com.ql.spring.ioc.dao.UserDao@149494d8
    正在创建UserService:com.ql.spring.ioc.service.UserService@710726a3
    com.ql.spring.ioc.dao.UserDao@149494d8
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    从运行结果得知,装配注解放在属性上,不执行set方法。其原理为:Spring IoC容器会自动通过反射技术将属性private修饰符自动修改改为public,直接进行赋值。所以日常开发时如果用注解注入时不会生成set方法。

    如果把@Autowired注解放在set方法上再执行,如下:

    package com.ql.spring.ioc.service;
    
    import com.ql.spring.ioc.dao.UserDao;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserService {
        private UserService(){
            System.out.println("正在创建UserService:"+this);
        }
        //@Autowired
        private UserDao udao;
    
        public UserDao getUdao() {
            return udao;
        }
    
        @Autowired
        public void setUdao(UserDao udao) {
            System.out.println("setUdao:"+udao);
            this.udao = udao;
        }
    }
    /*
    正在创建UserDao:com.ql.spring.ioc.dao.UserDao@306279ee
    正在创建UserService:com.ql.spring.ioc.service.UserService@77846d2c
    setUdao:com.ql.spring.ioc.dao.UserDao@306279ee
    com.ql.spring.ioc.dao.UserDao@306279ee
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    得知:如果装配注解放在set方法上,则自动按类型/名称对set方法参数进行注入。

    下面再分析@Autowired注解:
    在com.ql.spring.ioc.dao包下创建IUserDao接口

    package com.ql.spring.ioc.dao;
    
    public interface IUserDao {
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    然后UserDao实现这个接口

    package com.ql.spring.ioc.dao;
    
    import org.springframework.stereotype.Repository;
    
    @Repository
    public class UserDao implements IUserDao{
        public UserDao() {
            System.out.println("正在创建UserDao:"+this);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在com.ql.spring.ioc.dao包下再创建一个UserOracleDao类也实现IUserDao接口

    package com.ql.spring.ioc.dao;
    
    import org.springframework.stereotype.Repository;
    
    @Repository
    public class UserOracleDao implements IUserDao{
        public UserOracleDao() {
            System.out.println("正在创建UserOracleDao:"+this);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    此时在UserService中属性注解注入形式,然后运行代码就会报错

    package com.ql.spring.ioc.service;
    
    import com.ql.spring.ioc.dao.IUserDao;
    import com.ql.spring.ioc.dao.UserDao;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserService {
        private UserService(){
            System.out.println("正在创建UserService:"+this);
        }
        @Autowired
        private IUserDao udao;
    
        public IUserDao getUdao() {
            return  udao;
        }
    }
    //NoUniqueBeanDefinitionException: No qualifying bean of type 'com.ql.spring.ioc.dao.IUserDao' available: expected single matching bean but found 2: userDao,userOracleDao
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    报错的原因为按类型注入时,有两个对象都实现了IUserDao接口,所以不知道该注入哪个bean。
    解决方案有几种:
    1、去掉两个实现类其中一个类的注解,如去掉UserDao类的@Repository注解。
    2、两个实现类其中一个类再加个注解,如UserOracleDao类再加个注解为@Primary,@Primary代表主要的意思,其含义是如果IoC容器中出现了多个相同类型的对象的话,默认采用@Primary所描述的对象完成注入。
    3、注入注解选择@Resource按名称装配。

    按名称装配@Resource

    1、@Resource设置name属性,则按name在IoC容器中将bean注入。
    2、@Resource未设置name属性。
    2.1、以属性名作为bean name在IoC容器中匹配bean,如有匹配则注入。
    2.2、按属性名未匹配,则按类型进行匹配,同@Autowired,需加入@Primary解决类型冲突。
    使用建议:在使用@Resource对象时推荐设置name或保证属性名与bean名称一致。

    在com.ql.spring.ioc.service包下创建DepartmentService

    package com.ql.spring.ioc.service;
    
    import com.ql.spring.ioc.dao.IUserDao;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    
    @Service
    public class DepartmentService {
        //@Resource(name="userOracleDao")
        //private IUserDao udao;
        @Resource
        private IUserDao userOracleDao;
            public void joinDepartment(){
            System.out.println(userOracleDao);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    然后在入口类SpringApplication改为如下再执行

    package com.ql.spring.ioc;
    
    import com.ql.spring.ioc.service.DepartmentService;
    import com.ql.spring.ioc.service.UserService;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class SpringApplication {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
            UserService userService = context.getBean("userService", UserService.class);
            System.out.println(userService.getUdao());
    
            DepartmentService departmentService = context.getBean("departmentService", DepartmentService.class);
            departmentService.joinDepartment();
            /*        String[] ids = context.getBeanDefinitionNames();
            for (String id: ids) {
                System.out.println(id + ":" + context.getBean(id));
            }*/
        }
    }
    /*
    正在创建UserDao:com.ql.spring.ioc.dao.UserDao@548ad73b
    正在创建UserOracleDao:com.ql.spring.ioc.dao.UserOracleDao@4c762604
    正在创建UserService:com.ql.spring.ioc.service.UserService@10d59286
    com.ql.spring.ioc.dao.UserOracleDao@4c762604
    com.ql.spring.ioc.dao.UserOracleDao@4c762604
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    如果在DepartmentService中将属性名再改为userDao再运行

    package com.ql.spring.ioc.service;
    
    import com.ql.spring.ioc.dao.IUserDao;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    
    @Service
    public class DepartmentService {
        //@Resource(name="userOracleDao")
        //private IUserDao udao;
        @Resource
        private IUserDao userDao;
        public void joinDepartment(){
            System.out.println(userDao);
        }
    }
    /*
    正在创建UserDao:com.ql.spring.ioc.dao.UserDao@548ad73b
    正在创建UserOracleDao:com.ql.spring.ioc.dao.UserOracleDao@4c762604
    正在创建UserService:com.ql.spring.ioc.service.UserService@10d59286
    com.ql.spring.ioc.dao.UserOracleDao@4c762604
    com.ql.spring.ioc.dao.UserDao@548ad73b
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    其他元数据注解

    在这里插入图片描述
    在resources目录添加属性文件config.properties添加内容为

    metaData = ql.com
    
    • 1

    在applicationContext.xml中添加配置

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            https://www.springframework.org/schema/context/spring-context.xsd">
        <!--通过Spring IoC容器初始化时加载属性文件-->
        <context:property-placeholder location="classpath:config.properties"/>
        <!-- 在IoC容器初始化时自动扫描四种组件类型注解并完成实例化
             @Repository
             @Service
             @Controller
             @Component
        -->
        <context:component-scan base-package="com.ql.spring"/>
    </beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    修改UserService类,添加相关注解

    package com.ql.spring.ioc.service;
    
    import com.ql.spring.ioc.dao.IUserDao;
    import com.ql.spring.ioc.dao.UserDao;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Scope;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.PostConstruct;
    
    @Service
    @Scope("prototype")//设置单例/多例,在XML中bean scope完全相同
    public class UserService {
        @Value("${metaData}")
        private String metaData;
    
        private UserService(){
            System.out.println("正在创建UserService:"+this);
        }
    
        @PostConstruct//XML中bean init-method完全相同
        public void init(){
            System.out.println("初始化UserService对象,metaData="+metaData);
        }
        @Autowired
        private IUserDao udao;
    
        public IUserDao getUdao() {
            return  udao;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    运行入口类SpringApplication.java

    package com.ql.spring.ioc;
    
    import com.ql.spring.ioc.service.DepartmentService;
    import com.ql.spring.ioc.service.UserService;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class SpringApplication {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
            UserService userService = context.getBean("userService", UserService.class);
            System.out.println(userService.getUdao());
        }
    }
    /*
    正在创建UserDao:com.ql.spring.ioc.dao.UserDao@1efee8e7
    正在创建UserOracleDao:com.ql.spring.ioc.dao.UserOracleDao@1ee807c6
    正在创建UserService:com.ql.spring.ioc.service.UserService@3c0a50da
    初始化UserService对象,metaData=ql.com
    com.ql.spring.ioc.dao.UserOracleDao@1ee807c6
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
  • 相关阅读:
    Codeforces Round #750 (Div. 2)(A,B,C,D,F1)
    OSS文件上传
    8ARM-PEG-Cl 8ARM-PEG-Chloride 八臂聚乙二醇氯 八臂PEG氯
    ESP32-IDF MQTT连接aws亚马逊云
    基于主动视觉机制的深度学习--一个综合池化框架
    Go语言知识查漏补缺|基本数据类型
    【JAVA高级】——玩转JDBC中的三层架构
    HarmonyOS分布式协同演奏技术实现路线(Java)
    leetcode 944. 删列造序
    css样式重置
  • 原文地址:https://blog.csdn.net/qq_32091929/article/details/125571292