Spring进阶学习 03、Bean的生命周期

举报
长路 发表于 2022/11/28 19:24:53 2022/11/28
【摘要】 文章目录前言一、认识BeanPostProcessor(后置处理器)1.1、介绍BeanPostProcessor1.2、后置处理器小实战(实现自定义注解赋值)二、正式进入Bean的生命周期世界脑图预览2.1、实例化前2.2、实例化和推断构造方法2.3、实例化后与填充属性阶段2.4、初始化前2.5、初始化2.6、初始化后2.6.1、基本使用2.6.2、初始化后来进行AOP 前言 本篇博客是在学习

@[toc]

前言

本篇博客是在学习 图灵学院-SPRING源码教程 笔记,若文章中出现相关问题,请指出!

所有博客文件目录索引:博客目录索引(持续更新)

一、认识BeanPostProcessor(后置处理器)

1.1、介绍BeanPostProcessor

原理介绍

BeanPostProcessor能够让我们程序员来对Bean的生成进行干涉,其能够处理当前的bean实例,我们可以对其进行一些操作。

image-20210821220730596

  • 确切的来说该接口能够实现实例化前后的一些操作,只针对于该接口不涉及其扩展的接口,提供了两个方法分别是初始化前与初始化后!

核心

  1. 若是你自己实现了BeanPostProcessor接口并且交由Spring容器管理,那么其就会生效,此时你可以对生成的实例bean来进行一些后置处理操作。
  2. 实现的BeanPostProcessor接口作用与所有的Spring的bean实例,并不仅仅针对于某一个!

源码

其默认实现了两个方法,两个方法的参数都是一致的都可以拿到对应spring生成的bean对象以及bean的名称,默认都是返回的bean对象即Spring生成的bean实例

public interface BeanPostProcessor {

    //初始化前
	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

    //初始化后
	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

}

测试demo

image-20210821222834243

@Component
public class ChangluBeanPostProcessor implements BeanPostProcessor {

    //初始化前
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization=》Bean:"+bean+",beanName:"+beanName);
        return bean;
    }

    //初始化后
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization=》Bean:"+bean+",beanName:"+beanName);
        return bean;
    }
}

@ComponentScan("xyz.changlu")
public class Config {
}

@Component
public class User {
}

@Component
public class UserService {
}

//测试类
public class Main{
    public static void main(String[] args) throws IOException {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class);
        System.out.println(applicationContext.getBean("userService", UserService.class));
    }
}

image-20210821222750855



1.2、后置处理器小实战(实现自定义注解赋值)

案例目的:通过使用后置处理器来对某个属性进行注入值。

image-20210821223740359

既然Spring给我们提供的后置处理器能够得到Bean对象以及BeanName,我们就可以对其来进行一些操作:

//该注解用于注入值
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ChangLu {
    String value();
}

//实体类
@Component
public class User {
    @ChangLu("changlu")
    private String username;

    public void test(){
        System.out.println(username);
    }
}

@ComponentScan("xyz.changlu")
public class Config {
}

//后置处理器:来通过反射实现注入操作
@Component
public class ChangluBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        //对于@ChangLu注解中的值注入到对应属性中
        Class<?> aClass = bean.getClass();
        for (Field field : aClass.getDeclaredFields()) {
            if (field.isAnnotationPresent(ChangLu.class)) { //检测其是否包含该注解
                ChangLu annotation = field.getAnnotation(ChangLu.class);
                String value = annotation.value();
                field.setAccessible(true);
                try {
                    field.set(bean, value);
                    System.out.println("成功注入值:" + value);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return bean;
    }
}

测试方法:

public class Main{
    public static void main(String[] args) throws IOException {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class);
        applicationContext.getBean("user", User.class).test();
    }
}

image-20210821224012196



二、正式进入Bean的生命周期世界

脑图预览

单个Bean的生命周期全貌如下图,在没有了解Bean的生命周期前,我们对于Bean的基本概念仅仅停留在一个实例化阶段,当大致学习好之后我们对于Spring的全貌就有了一大部分的了解了!

Spring源码分析

在Spring的实际源码中就继承实现了BeanPostProcessor以及其他相关的接口,上图中没有标注红的就是Spring自己实现的一些抽象类,它们来为我们提供了一些的现成方法,例如AOP、参数注入等等,这些都是基于Bean的生命周期方法来实现的!

下面我们就根据这些接口来自己实现一些关于指定Bean的一些功能。



2.1、实例化前

对于声明@Component的实体类,Spring首先会生成BeanDefinition,其想要实例化为一个对象前需要经历实例化前的这个过程。

方式:通过自定义一个后置处理其来实现InstantiationAwareBeanPostProcessor接口并重写postProcessBeforeInstantiation方法即可定义bean的实例化前方法,你可以注意到其中参数仅仅只有class以及beanName名称。

image-20210822103951945

//指定类实例化
@Component
public class User {
    public User(){
        System.out.println("实例化");
    }
}

//自定义后置处理器
@Component
public class ChangluBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
    /**
     * 在实例化前执行的方法,此时还没有实例化
     * @param beanClass 实例化的class类
     * @param beanName 此次产生bean实例对应的名称
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if("user".equals(beanName)){
            System.out.println("实例化前");
        }
        //若是中途这里直接返回对象的话,后面的实例化以及实例化后的步骤会直接被过滤掉。返回的对象就会直接作为bean实例来存储
        return null;
    }
}

其他配合辅助方法:只在这部分来进行展示,其他部分就不作演示了

@ComponentScan("xyz.changlu") //进行自动扫描
public class Config {
}

public class Main{
    public static void main(String[] args) throws IOException {
        //这里我们仅需引入该类即可实现User的实例化以及实例化前方法执行
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class);
    }
}

image-20210822160621691

注意:若是实例化前就返回指定一个对象,其就会作为对应beanName的bean实例,此时实例化后包含实例化这些步骤都不会执行了!



2.2、实例化和推断构造方法

实例化前方法结束之后,就要进行bean的实例化了,在真正进行bean的实例化时还要进行推断构造方法选择指定构造器来进行实例化的产生!

推断构造方法:分为下面多种情况,简而言之Spring会根据你的配置来选择一个构造器来进行bean对象的初始化

①若是你仅仅有一个有参构造方法(没有无参构造),其带有一个参数,spring对其实例化时就会默认使用该有参构造,对于参数spring会去容器中寻找是否有对应的bean实例(先type,后name)有的话就注入。

@Component
public class UserService {
    public UserService(User user){
        System.out.println(user);
        System.out.println("1个有参构造器");
    }
}

②若是你类中有多个有参构造但是没有定义无参构造就会报错,那时因为只有多个有参构造方法时,Spring默认去使用无参的构造方法的原因!

//此时执行一定会报错,因为有多个有参构造器时并且这些构造器没有标注特定的注解,Spring会默认去找无参构造器来进行实例化
@Component
public class UserService {
    public UserService(User user){
        System.out.println(user);
        System.out.println("1个有参构造器");
    }

    public UserService(User user,Person person){
        System.out.println(user);
        System.out.println(person);
        System.out.println("2个有参构造器");
    }
}

③若是有多个有参构造方法以及无参构造方法时,我想要使用指定的有参构造,就需要配合@Autowired到对应的构造器方法上即可推断该构造方法是要进行实例化的

@Component
public class UserService {
    public UserService() {
    }

    @Autowired  //表示一定使用该构造器来进行实例化
    public UserService(User user){
        System.out.println(user);
        System.out.println("1个有参构造器");
    }

    public UserService(User user,Person person){  //注意:若是@Autowired加在这个构造器上时,若是person没有在Sprng容器中找到就会报错!
        System.out.println(user);
        System.out.println(person);
        System.out.println("2个有参构造器");
    }
}
  • 若是在两个构造方法上都加入@Autowired也会进行报错!除非你在@Autowired注解中添加required属性,默认为true表示一定要进行依赖注入在该属性或方法时,这也就导致为什么都加入会报错的情况,若是设置某个required=false就不会报错啦!
  • 若是多个有参构造上都设置了@Autowired(required=false),那么Spring会自动推断去使用参数最多的有参构造器来先进行实例化。需要额外提下的是,若是最多参数的构造器其参数在Spring容器中找不到,那么就不会采用其构造器进行实例化,进而再去尝试使用参数少的构造器来进行实例化!
  • 若是多个有参构造器上设置了@Autowired(required=false),他们都有相同的多个参数属性,并且这些参数属性都有够在Spring容器中找到,此时选择从上到下第一个构造器来进行实例化!


2.3、实例化后与填充属性阶段

当实例化对象好了之后,我们就可以进入到实例化后中了!

顺序为:实例化—实例化后填充属性

方式:自定义后置处理器继承InstantiationAwareBeanPostProcessor接口重写postProcessAfterInstantiation方法,由于执行该方法前bean已经进行了实例化,所以这里的参数就有bean以及beanName了。

@Component
public class UserService {

    private User user;
	//实例化
    public UserService() {
        System.out.println("实例化");
    }
	//填充属性阶段
    @Autowired
    public void setUser(User user) {
        this.user = user;
        System.out.println("填充属性");
    }
}

//同样该实例化后方法在Spring生成Bean对象时自动会执行
@Component
public class ChangluBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
    /**
     * 实例化后
     * @param bean bean对象
     * @param beanName 对应的beanName
     * @return 返回true表示继续执行之后的一系列生命周期;false表示不执行之后的生命周期
     * @throws BeansException
     */
    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if ("userService".equals(beanName)) {
            System.out.println("实例化后");
        }
        return true;
    }
}

其他相关类:

@Component
public class User {
    public User(){
    }
}

@ComponentScan("xyz.changlu")
public class Config {
}

image-20210822161757489



2.4、初始化前

BeanPostProcessor接口中提供的两个方法一个是初始化前,另一个是初始化后。我们也可以通过继承BeanPostProcessor接口InstantiationAwareBeanPostProcessor接口来实现初始化前的方法,见如下:

下面通过实现InstantiationAwareBeanPostProcessor接口来实现初始化前的方法执行,与实现BeanPostProcessor其实性质都是相同的:

@Component
public class ChushihuaBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
    //初始化前方法中的小应用:来手动执行bean对象中的某个方法
    //也就是说再执行实例化后方法时,会去额外对该实例化对象中的方法进行遍历查看是否有指定注解,有的话执行该方法
    if("userService".equals(beanName)){
        System.out.println("初始化前");
        Class<?> aClass = bean.getClass();
        Method[] methods = aClass.getDeclaredMethods();
        for (Method method : methods) {
            //若是有@PostConstruct注解就就执行该方法
            if(method.isAnnotationPresent(ChangLu.class)){
                try {
                    method.invoke(bean,null);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    return bean;
}

其他辅助方法:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ChangLu {
    String value();
}

@Component
public class UserService {
    @ChangLu  //其实该注解表示初始化时的方法
    public void test(){
       System.out.println("初始化前中通过反射执行的方法!");
    }
}

image-20210822162849861



2.5、初始化

初始化在生命周期阶段位置为:填充属性—初始化前—初始化—初始化后,一旦初始化前方法执行好后就会执行初始化方法!

初始化方法可以通过两种方式实现:

//方式一:通过实现InitializingBean接口
@Component
public class UserService implements InitializingBean{ //1、实现InitializingBean接口
    //2、重写afterPropertiesSet方法
	@Override
    public void afterPropertiesSet() throws Exception {
        System.out.println(this);//可以通过this来得到当前实例对象
        System.out.println("初始化");
    }
}

//方式二:在对应实体类中编写方法,添加注解@PostConstruct即表示该方法为初始化方法
@Component
public class UserService implements InitializingBean{
    @PostConstruct
    public void test(){
        System.out.println("初始化...");
    }
}

注意:若是同时使用这两种方式,使用@PostConstruct注解的会优先执行。并且初始前的方法返回的bean若是为null,@PostConstruct注解声明的方法不会执行,而是执行后面的afterPropertiesSet方法及之后!



2.6、初始化后

2.6.1、基本使用

同样我们通过实现BeanPostProcessor接口或继承BeanPostProcessor的相关接口来进行方法重写即可实现初始化后方法:

@Component
public class ChushihuaBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if("userService".equals(beanName)){
            System.out.println("初始化后方法");
        }
        return bean;
    }
}

其他相关类:

@Component
public class UserService implements InitializingBean{

    private User user;

    //实例化
    public UserService() {
        System.out.println("实例化");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println(this);//可以通过this来得到当前实例对象
        System.out.println("初始化");
    }
}

image-20210822163538310



2.6.2、初始化后来进行AOP

Spring中自带的实现AOP的类

image-20210822103047542

  • 你可以看到该抽象类实现了InstantiationAwareBeanPostProcessor接口,其内部实际上就是通过重写初始化后方法来实现的AOP!

一般流程:UserService->UserService对象->AOP->代理对象,也就是在实例好对象之后来进行的AOP生成代理对象并返回。

在Spring本身的逻辑里面,会看你这个类有没有实现自己所定义的接口,若是你实现了就会用JDK的动态代理;若是没有实现自己所定义的接口,那么就会使用CGLIB动态代理。

默认情况下,Spring会基于你的类来生成一个对象然后来进行AOP,产生一个代理对象


手写后置处理器来实现AOP

手动实现AOP功能:

public interface MyInterface {
    void handle();
}

@Component
public class UserService MyInterface {
    @Override
    public void handle() {
        System.out.println("AOP=>handle业务主方法");
    }
}

//自定义后置处理器
@Component
public class ChushihuaBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

    //初始化后
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if ("userService".equals(beanName)) {
            System.out.println("实例化后");
            //进行AOP,返回的是代理对象
            return Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("AOP前置方法");
                    //核心主方法
                    method.invoke(bean,args);
                    System.out.println("AOP后置方法");
                    return null;
                }
            });
        }
        return bean;
    }
}

测试方法:

@ComponentScan("xyz.changlu")
public class Config {
}

public class Main{
    public static void main(String[] args) throws IOException {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class);
        //由于后置处理器最终返回的是Proxy代理对象,所以这里第二个参数就传入对应动态代理的接口
        MyInterface userService = applicationContext.getBean("userService", MyInterface.class);
        //需要调用指定业务方法才能够模拟实现AOP操作
        userService.handle();
    }
}

image-20210822164546798

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。