• Spring(二)-生命周期 + 自动装配(xml) +自动装配(注解)


    1、生命周期

    Spring容器的 bean的生命周期;

    1.1 默认生命周期

    1.1.1 生命周期

    1. 调用构造方法,创建实例对象;
    2. set方法,给实例对象赋值;
    3. init 初始化方法 初始化对象;(手写并配置到bean上init-method="")
    4. 使用容器中的bean对象;
    5. destroy 销毁方法 销毁对象 (手写并配置到bean上destroy-method="")

    1.1.2 bean 实体类

    Truck

    @Data
    @ToString
    public class Truck {
    
        //品牌
        private String brand;
    	//厂商
        private String factory;
    	//价格
        private Double price;
    
        public Truck() {
            //空参构造方法,观察bean什么时候实例化
            System.out.println("------ 1.调用构造方法,创建实例对象 ------\n");
        }
    
        public void setBrand(String brand) {
            //任意一个set方法,观察bean什么时候注入参数
            System.out.println("------ 2.set方法,给实例对象赋值 ------");
            this.brand = brand;
        }
    
    
        public  void  initTruck(){
            //init初始化方法,观察bean什么时候初始化
            //需要再配置bean的时候,配置init初始化方法
            System.out.println("------ 3.Truck init 初始化方法 初始化对象 ------\n");
            this.brand = "大运";
        }
    
        public  void  destroyTruck(){
            //destory方法,观察bean什么时候销毁
            //需要再配置bean的时候,配置destory销毁方法
            System.out.println("------ 5.Truck destroy 销毁方法 销毁对象 ------\n");
        }
    	//这里方法上标注的序号是测试后得来的;
    }
    

    1.1.3 bean 配置

    spring-lifecycle.xml

    
    <bean id="truck" class="com.kgc.spring.lifecycle.Truck" init-method="initTruck" destroy-method="destroyTruck">
        <property name="brand" value="江淮">property>
        <property name="factory" value="安徽">property>
        <property name="price" value="200000">property>
    bean>
    

    1.1.4 测试

    public class TestSpringLifeCycle {
    
        //定义全局容器对象,如果需要关闭容器对象,
        //必须使用ApplicationContext的子接口 ConfigurableApplicationContext
        //ApplicationContext接口主要各种属性的get方法;
        //ConfigurableApplicationContext重在对各种属性的配置;
        
    //    private ApplicationContext context;
        private ConfigurableApplicationContext context;
    
        @Before
        public void initApplicationContext(){
            context = new ClassPathXmlApplicationContext("spring-lifecycle.xml");
        }
    
        //测试spring 容器的bean的生命周期,默认和加了处理器两种场景
        @Test
        public void  testSpringBeanLifeCycle(){
    
            //从容器中,获取Truck的是实例对象
            Truck truck = context.getBean("truck", Truck.class);
    
            //使用对象
            System.out.println("------ 4.使用容器中的bean对象"+truck +" ------");
    
            //关闭容器
            context.close();
    
        }
    
    }
    

    输出结果:

    //可以得出 spring中bean的 默认生命周期
    ------ 1.调用构造方法,创建实例对象 ------
    
    ------ 2.set方法,给实例对象赋值 ------
        
    ------ 3.Truck init 初始化方法 初始化对象 ------
        
    ------ 4.使用容器中的bean对象Truck(brand=大运, factory=安徽, price=200000.0) ------
        
    ------ 5.Truck destroy 销毁方法 销毁对象 ------
    

    1.1.5 ApplicationContext 和 ConfigurableApplicationContext

    参考博客: ApplicationContext和ConfigurableApplicationContext解析

    • ApplicationContext接口主要 各种属性的get方法;

    • ConfigurableApplicationContext重在对 各种 属性的配置;

    1.2 增加后置处理器

    1.2.1 生命周期

    1.调用构造方法,创建实例对象;

    2.set方法,给实例对象赋值;

    3-1.后置处理的 before 方法;

    3.初始化方法 初始化对象;

    3+1.后置处理器的的 after 方法;

    4.使用容器中的bean对象;

    5.destroy 销毁方法 销毁对象;

    1.2.2 后置处理器

    • 要求:必须实现 BeanPostProcessor 接口

    • 自定义 bean 的 后置处理器,对容器中所有的bean统一处理(生效)

    • 要生效的话,必须将此处理器放到容器中(配置到spring的核心配置文件中,增加处理器的实例配置);

    注意:当前案例,只对容器中的一个实例处理

    MyBeanPostProcessor

    public class MyBeanPostProcessor implements BeanPostProcessor {
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            //在容器中bean的实例对象调用 初始化方法 前 自动调用(init方法可以没有,不影响)
            //模拟处理容器中的bean,接下来的写法,仅限于当前的用法案例(容器中就 只有一个 卡车实例)
            Truck truck = (Truck)bean;
    
            System.out.println("++++++ 容器中的卡车对象 "+truck+"++++++");
    
            System.out.println("++++++ 3-1,后置处理的 before 方法 ++++++");
            truck.setBrand("后置处理的before方法");
            System.out.println("++++++ 处理后的 卡车对象 "+truck+" ++++++\n");
    
            return truck;
        }
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            //bean 初始化方法 执行 后,调用此方法处理bean
            Truck truck = (Truck)bean;
    
            System.out.println("++++++ 初始化容器中的卡车对象 "+truck+"++++++");
    
            System.out.println("++++++ 3+1,后置处理器的的 after 方法 ++++++");
            truck.setBrand("after");
            System.out.println("++++++ 初始化后 处理后的 卡车对象 "+truck+" ++++++\n");
            return truck;
    
        }
    }
    

    1.2.3 bean 配置

    在配置文件中配置 MyBeanPostProcessor;

    
    <bean class="com.kgc.spring.lifecycle.MyBeanPostProcessor">bean>
    

    1.2.4 测试

    跟默认生命周期的测试代码一致;

    输出结果:

    ------ 1.调用构造方法,创建实例对象 ------
    
    ------ 2.set方法,给实例对象赋值 ------
    ++++++ 容器中的卡车对象 Truck(brand=江淮, factory=安徽, price=200000.0)++++++
        
    ++++++ 3-1,后置处理的 before 方法 ++++++ ------ 2.set方法,给实例对象赋值 ------
    ++++++ 处理后的 卡车对象 Truck(brand=后置处理的before方法, factory=安徽, price=200000.0) ++++++
    
    ------ 3.Truck init 初始化方法 初始化对象 ------
    ++++++ 初始化容器中的卡车对象 Truck(brand=大运, factory=安徽, price=200000.0)++++++
        
    ++++++ 3+1,后置处理器的的 after 方法 ++++++ ------ 2.set方法,给实例对象赋值 ------
    ++++++ 初始化后 处理后的 卡车对象 Truck(brand=after, factory=安徽, price=200000.0) ++++++
    
    ------ 4.使用容器中的bean对象Truck(brand=after, factory=安徽, 
                               
    ------ 5.Truck destroy 销毁方法 销毁对象 ------
    

    1.2.3 BeanPostProcesso

    参考博客:BeanPostProcessor简介

    BeanPostProcessor官方定义为工厂钩子,我们也俗称后置处理器。它允许自定义修改新的bean实例,例如检查标记接口或用代理包装它们。应用程序上下文可以在其bean定义中自动检测BeanPostProcessor bean,并将它们应用于随后创建的任何bean。

    BeanPostProcessor 的 前置处理后置处理

    ![image-20220826150821502](Spring-02 生命周期 + 自动装配(xml) +自动装配(注解).assets/image-20220826150821502.png)

    2、自动装配(xml)

    2.1 bean 实体类

    Person

    @Data
    public class Person {
        //昵称
        private String nickName;
        //车子
        private Car car;
        //房子
        private House house;
    }
    

    Car

    @Data
    public class Car {
        //品牌
        private String brand;
    	//厂商
        private String factory;
    	//价格
        private Double price;
    }
    

    House

    @Data
    public class House {
        //户型
        private  String type;
        //面积
        private double area;
        //价格
        private  Integer price;
    }
    

    2.2 bean 配置 (byType)

    autowire="byType":根据属性 的 类型自动装配;

    spring-auto.xml

    
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        
        
        <bean id="car" class="com.kgc.spring.auto.Car">
            <property name="brand" value="Bnw520">property>
            <property name="factory" value="华晨">property>
            <property name="price" value="450000">property>
        bean>
    
        
        <bean id="house" class="com.kgc.spring.auto.House">
            <property name="type" value="三室一厅">property>
            <property name="area" value="96">property>
            <property name="price" value="2800000">property>
        bean>
    
        
        <bean id="person" class="com.kgc.spring.auto.Person" autowire="byType">
            <property name="nickName" value="huayu">property>
        bean>
    
    beans>
    

    2.3 测试

    public class TestSpringAutoUserXml {
        private ApplicationContext context;
    
        @Before
        public void initApplicationContext(){
            context = new ClassPathXmlApplicationContext("spring-auto.xml");
        }
    
        @Test
        public  void testSpringAuto(){
            Person person = context.getBean("person", Person.class);
            
            //使用对象
            System.out.println("容器中的person对象:"+person);
    
        }
    
    }
    

    输出结果:

    容器中的person对象:Person(
        					nickName=huayu, 
                            car=Car(brand=Bnw520, factory=华晨, price=450000.0), 
                            house=House(type=三室一厅, area=96.0, price=2800000)
    						)
    

    2.4 bean 配置 (多个同类型bean)

    bean 配置:

    其他不变,多增加一个Car类型的实例bean;

    <bean id="car" class="com.kgc.spring.auto.Car">
        <property name="brand" value="Bnw520">property>
        <property name="factory" value="华晨">property>
        <property name="price" value="450000">property>
    bean>
    
    <bean id="carNew" class="com.kgc.spring.auto.Car">
        <property name="brand" value="AudiA6">property>
        <property name="factory" value="一汽">property>
        <property name="price" value="450000">property>
    bean>
    

    测试,报错信息:

    No qualifying bean of type 'com.kgc.spring.auto.Car' available: expected single matching bean but found 2: car,carNew

    总结:autowire="byType" 当有多个相同类型的bean时无法确定要装配的 bean;

    2.5 bean 配置(byName)

    其他配置信息不变,设置 autowire="byName" ,根据 属性 的 名字 自动装配;

    <bean id="person" class="com.kgc.spring.auto.Person" autowire="byName">
        <property name="nickName" value="hauyu">property>
    bean>
    

    测试输出结果:

    容器中的person对象:Person(
        					nickName=huayu, 
                            car=Car(brand=Bnw520, factory=华晨, price=450000.0), 
                            house=House(type=三室一厅, area=96.0, price=2800000)
    						)
    

    总结

    • byType:根据类型自动装配:
      • 根据实体属性的  类型,到容器中,根据  bean类型  进行唯一匹配,如果可以匹配到对应类型的bean的实例,就会执行自动装配, 如果不能唯一匹配(同类型的bean有多个),会报错;
    • byName: 根据名称自动装配:
      • 根据属性属性名,到容器中,根据 bean的id 属性值,进行唯一匹配,如果能够成功匹配,执行自动装配, 如果匹配不到,不执行自动装配,实体属性为null;

    3、自动装配 (注解)

    3.1 注解

    • @Component 普通组件注解;
    • @Repository 持久层注解
    • @Service 业务层注解
    • @Controller 控制层注解

    3.3.1 注解的原理

    默认情况下:spring自动将分层组件(@Controller,@Service,@Repository,@component)标识的类(不是接口),自动创建实例对象放入容器中,使用bean的标识id值为 对应类名首字母小写 就相当于,帮我们手动添加了配置 :

    ...

    3.1.2 自定义id 属性

    如果不想使用默认的类名首字母小写,我们可以使用注解的value属性执行一个自定义的id值;
    比如:@Service(value="自定义的id值"),注解只有value属性,可以省略value执行,简化为@Service("自定义的id值")

    3.1.3 分层组件的目的

    分层组件的目的,就仅仅是为了方便开发人员明确当前注解所在的类所对应的角色,在使用上,建议使用,按照官方定义的使用,防止模糊不清;在springMVC框架中@Controller有特殊含义;

    3.2 配置文件

    spring创建容器对象时,如果解析到 component-scan 组件扫描配置,会将base-package指定的基包(父包)及其子包所有增加了分层组件的类,自动创建实例,放进容器中;

    配置文件

    
    <beans
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns="http://www.springframework.org/schema/beans"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
    ">
      
     <context:component-scan base-package="com.kgc.spring.acnocation" >    context:component-scan>
    
    beans>
    

    3.3 默认id属性 测试

    3.3.1 实体

    @Component
    public class User {
        //用户名
        @Value("huayu") //@Value()  自动装配参数
        private  String userName;
        //用户密码
        @Value("123")
        private String userPwd;
    }
    

    3.3.2 测试

    @Test
    public  void testSpringAutoUserAnnotation(){
    	
        User user = context.getBean("user", User.class);
       
        System.out.println(user);
        //User(userName=huayu, userPwd=123)
    
    }
    

    3.4 自定义id属性 测试

    3.4.1 实体(自定义id属性)

    @Component(value = "myUser")
    public class User {
        //用户名
        private  String userName;
        //用户密码
        private String userPwd;
    }
    

    3.4.2 测试

    @Test
    public  void testSpringAutoUserAnnotation(){
    
        //User user = context.getBean("user", User.class);
        //自定义id后,默认id不能使用: No bean named 'user' available
        
        //必须使用自定义id
        User user = context.getBean("myUser", User.class);
        
        System.out.println(user);
        //User(userName=huayu, userPwd=123)
    
    }
    

    3.5 自动装配

    3.5.1 @Autowired

    • 组件自动装配,可以实现实体属性类型的自动装配,自动到spring的容器中,根据当前属性的类型或者名称进行注入,如果容器中能匹配到,就直接将实例对象注入到当前实体属性上,无序手动指定;
    • @Autowired自动装配原理:首先会根据byType方式,进行自动装配,
      • 如果不能唯一匹配(存在同类型多个实例对象),会再次尝试使用byName方式,根据当前实体属性名,到容器中进行匹配(容器中bean的id值),如果能唯一匹配,直接执行自动装配,
    • 默认情况下,@Autowired注解标识的实体属性,必须被装配
      • 如果装配失败,就直接抛出异常;
      • 如果不需要校验必须被装配(项目启动,如果装配失败,项目是起不来);
      • 通过指定required = false,去除必须执行自动装配的校验(即便容器中找不到装配的实例,也不会抛出异常);
    • 如果自动装配,容器中存在多个同类型的bean对象,可以使用注解@Qualifier("容器中同类型多个bean的某个id值"),实现指定到容器中,找对应的bean实例对象,进行自动装配;
    • 底层是如何做的:在指定要扫描的包时, 元素会自动注册一个bean的后置处理器:AutowiredAnnotationBeanPostProcessor的实例。该后置处理器可以自动装配标记了@Autowired、@Resource或@Inject注解的属性

    3.5.2 实体

    People

    @Data
    @Component("myPeople")
    public class People {
        //昵称
        @Value("huayu")
        private String name;
        //玩具
        @Autowired
        private Toy toy;
    
    }
    

    Toy接口

    public interface Toy {
        //得到玩具
        public void getToy();
    
    }
    

    ToyImpl1

    @Component("toy1")
    public class ToyImpl1 implements Toy {
    
        @Value("玩具车")
        private String toyName;
    
        @Override
        public void getToy() {
            System.out.println(this.toyName);
        }
    }
    

    3.5.3 测试

    注意:可以通过接口类型获取实现类(推荐使用);

    @Test
    public  void testAutowired  (){
        People people = context.getBean("myPeople", People.class);
    
        people.getToy().getToy(); //玩具车
    
    }
    

    3.5.4 存在多个相同类型的bean

    当存在多个相同类型的bean不能唯一匹配,会自动装配错误

    在写一个Toy实现类,ToyImpl2

    @Component("toy2")
    public class ToyImpl2 implements Toy {
    
        @Value("尤克里里")
        private String toyName;
    
        @Override
        public void getToy() {
            System.out.println(this.toyName);
        }
    
    }
    
    3.5.4.1 测试

    报错信息(项目无法启动):

    No qualifying bean of type 'com.kgc.spring.acnocation.bean.Toy' available: expected single matching bean but found 2: toy1,toy2

    主要信息:类型无法唯一匹配;

    3.5.4.2 required = false 允许不装配

    People

    @Data
    @Component("myPeople")
    public class People {
    
        //昵称
        @Value("huayu")
        private String name;
    
        //玩具
        @Autowired (required = false)
        private Toy toy;
    
    }
    

    项目可以启动但是还是报错(一般项目中不会有两个相同类型的实现类;)

    3.5.4.3 @Quailfy

    People

    @Data
    @Component("myPeople")
    public class People {
    
        //昵称
        @Value("huayu")
        private String name;
    
        //玩具
        @Autowired 
        @Qualifier("toy2") //指定bean的id值
        private Toy toy;
    
    }
    
    3.5.4.4 测试
    @Test
    public  void testAutowired  (){
        People people = context.getBean("myPeople", People.class);
    
        System.out.println(people.getToy());
        //com.kgc.spring.acnocation.bean.ToyImpl2@15d9bc04
        
        people.getToy().getToy();
        //尤克里里
    
    }
    

    3.6 指定扫描 排除扫描

    3.6.1 指定扫描

    include-filter

    指定扫描(包含扫描):

    • 只会扫描指定的或者某类组件(使用分组扫描),加入到容器中;
    • 但是必须配合父标签的user-default-filter使用,默认值是true,就是全部扫描;
    • 指定扫描,如果要生效必须改为false
    • 指定扫描某类组件,type="annotation" expression="某类组件注解的全类名";
    • 指定扫描某个类,type="assignable" expression="某个类的全类名";
    3.6.1.1 指定扫描某类组件

    type="annotation"

    • org.springframework.stereotype.Component
    • org.springframework.stereotype.Repository
    • org.springframework.stereotype.Service
    • org.springframework.stereotype.Controller
    
    <context:component-scan base-package="com.kgc.spring.acnocation" use-default-filters="false" >
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
    context:component-scan>
    
    3.6.1.2 指定扫描某个类

    type="assignable"

    
    <context:component-scan base-package="com.kgc.spring.acnocation" use-default-filters="false" >
        <context:include-filter type="assignable" expression="com.kgc.spring.acnocation.bean.ToyImpl1"/>
    context:component-scan>
    

    3.6.2 排除扫描

    exclude-filter

    • 排除扫描(剔除扫描):排除指定的某类组件不加入到容器中,处理排除外的其他组件,仍然会被添加到容器中;
    • 不需要配合父标签,use-default-filters="true" 因为,默认就是在全部扫描的基础上剔除;
    • 排除扫描某类组件,type="annotation" expression="某类组件注解的全类名";
    • 排除扫描某个类,type="assignable" expression="某个类的全类名";
    3.6.2.1 排除扫描某类组件

    type="annotation"

    
    
    <context:component-scan base-package="com.kgc.spring.acnocation" use-default-filters="true" >
    	<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
    context:component-scan>
    
    3.6.2.2 排除扫描个类

    type="assignable"

    
    <context:component-scan base-package="com.kgc.spring.acnocation" >
    	<context:exclude-filter type="assignable" expression="com.kgc.spring.acnocation.bean.ToyImpl1"/>
    context:component-scan>
    
  • 相关阅读:
    力扣(LeetCode)216. 组合总和 III(2022.08.04)
    19 04-读取DTC快照信息
    【CSS】解决对齐的小问题
    基于python+django+vue+MySQL的酒店推荐系统
    【20220628】【信号处理】自相关函数在信号处理中的应用——提取被噪声干扰的周期信号的周期
    CUDA 安装
    leetcode刷题日记:205. Isomorphic Strings(同构字符串)
    第一个微信小程序的初始化过程、小程序微信开发平台的下载、如何注册一个微信小程序的账号
    【自然语言处理】理解词向量、CBOW与Skip-Gram模型
    Intel Memory-Management Registers
  • 原文地址:https://www.cnblogs.com/xiaoqigui/p/16632269.html