bean的一生

你曾读spring源码 "不知所云"、"绞尽脑汁"、"不知所措"嘛🤣🤣🤣

那这篇文章可能会对你有所帮助,小编尝试用简单、易懂的例子来模拟spring经典代码👉Spring Bean生命周期及扩展点,

让你能够****轻松的读懂Spring Bean的生命周期,更加深入的理解Spring。


那好,下面小编将从如下几个步骤来介绍✍️✍️✍️

1》回顾Spring Bean相关知识点

1.1什么是Bean

1.2什么是 Spring Bean 的生命周期

1.3Spring Bean 的生命周期的扩展点

2模拟 Spring Bean 生命周期及扩展点

2.1流程图

2.2代码功能介绍

2.3代码实现

2.3.1指定扫描路径

2.3.2扫描、生成classList

2.3.3bean定义、建立beanName映射关系、保存beanPostProcessor对象

2.3.4实例化bean、对象填充属性、执行award方法、BeanPostProcessor干预、初始化、放入单例池、获取bean

2.3.5业务类实现

2.3.6运行结果

3总结


1 》回顾Spring Bean相关知识点

1. 1什么是Bean

对于普通的 Java 对象,当 new 的时候创建对象,然后该对象就能够使用了。一旦该对象不再被使用,则由 Java 自动进行垃圾回收。

而 Spring 中的对象是 bean,bean 和普通的 Java 对象没啥大的区别,只不过 Spring 不再自己去 new 对象了,而是由 IoC 容器去帮助我们实例化对象并且管理它,我们需要哪个对象,去问 IoC 容器要即可。IoC 其实就是解决对象之间的耦合问题,Spring Bean 的生命周期完全由容器控制。

简而言之,bean 是由 Spring IoC 容器实例化、组装和管理的对象。

1.2什么是 Spring Bean 的生命周期

普通的Java对象生命周期:

实例化

该对象不再被使用时通过垃圾回收机制进行回收

Spring Bean 的生命周期:

实例化 Instantiation

属性赋值 Populate

初始化 Initialization

销毁 Destruction

1.3Spring Bean 的生命周期的扩展点

Bean 自身的方法

实例化 -> 属性赋值 -> 初始化 -> 销毁

容器级的方法(BeanPostProcessor 一系列接口)

主要是后处理器方法,比如InstantiationAwareBeanPostProcessor、BeanPostProcessor 接口方法。

这些接口的实现类是独立于 Bean 的,并且会注册到 Spring 容器中。

在 Spring 容器创建任何 Bean 的时候,这些后处理器都会发生作用。

Bean 级生命周期方法

可以理解为 Bean 类直接实现接口的方法,比如 BeanNameAware、BeanFactoryAware、ApplicationContextAware、InitializingBean、DisposableBean 等方法,这些方法只对当前 Bean 生效。


如上为Spring Bean知识的回顾👏👏👏,如果忘记了,没关系,让咱们在代码中寻找记忆。下面咱们开始模拟Spring Bean 生命周期。

2 》 模拟 Spring Bean 生命周期及扩展点

在看代码之前,首先,先给大家展示一下流程图、代码功能介绍,方便大家理解。

2.1 》 流程图

2.2 》 代码功能介绍

Application.java 启动类,实例化spring容器

AnnotationConfig.java 配置类,指定扫描路径

AnnotationApplicationContext.java 核心类,bean创建、获取

BeanDefinition.java BeanDefinition定义

SqBeanPostProcessor.java 后置处理器,初始化前后对bean进行干预

User.java 业务类,用户对象,用户信息设置

UserService.java 业务类,用户service,实现BeanNameAware、InitializingBean

spring注解模拟

Autowired.java

Component.java

Lazy.java

Scope.java

ComponentScan.java

spring接口模拟

BeanNameAware.java

BeanPostProcessor.java

InitializingBean.java


现在咱们开始看代码、看代码、看代码✌️✌️✌️

2.3》 代码实现

2.3.1》 AnnotationConfig.java 指定扫描路径

复制代码
@ComponentScan("com.hz.spring.demo.service")
public class AnnotationConfig { 
 
}

2.3.2 》 AnnotationApplicationContext.java 扫描、生成classList

根据配置类config,获取扫描路径

通过类加载器获取target class 路径

扫描文件路径,加载类文件,放入classList中,为类操作做准备

复制代码
public AnnotationApplicationContext(Class<AnnotationConfig> config) {  
  this.config = config;   
   try {        
   // 扫描        
   ComponentScan componentScan = config.getAnnotation(ComponentScan.class);       
    // com.hz.spring.demo.service        
    String path = componentScan.value();        
    path = path.replace(".", "/");        
    // 通过类加载器 获取target class 路径        
    ClassLoader classLoader = AnnotationApplicationContext.class.getClassLoader();        
    URL url = classLoader.getResource(path);        
    if (url != null) {            
        // 获取classList            
        File file = new File(url.getFile());           
        List<Class<?>> classList = this.scanAndGetClassList(classLoader, file);            
        // bean定义、建立beanName映射关系、保存beanPostProcessor对象           
         this.compBdMap(classList);            
        // 实例化。创建bean。放入单例池            
        // 核心、核心、核心            
         this.instanceBean();        
         }   
     } catch (Exception e) {        
         log.error("AnnotationApplicationContext error:", e);   
     }
}

扫描、加载class

复制代码
private List<Class<?>> scanAndGetClassList(ClassLoader classLoader, File file) {    
List<Class<?>> classList = Lists.newArrayList();    
if (file.isDirectory()) {       
     File[] files = file.listFiles();        
     if (files != null) {            
         for (File f : files) {                
             try {                    
                 String absolutePath = f.getAbsolutePath();                    
                 // Window target文件路径                    
                 // D:\company\comp\company-rest\target\test-classes\com\hz\spring2\service\User.class                    
                 // D:\company\comp\company-rest\target\test-classes\com\hz\spring2\service\UserService.class                    
                 // absolutePath = absolutePath.substring(absolutePath.indexOf("com\\"), absolutePath.indexOf(".class"));                    
                 // absolutePath = absolutePath.replace("\\", ".");                    
                 // Mac target文件路径                    
                 // /Users/huzhong5/hz/IdeaProjects/yb-claim/psc-invoicing/target/test-classes/com/hz/spring/demo/service/UserService.class                    
                 absolutePath = absolutePath.substring(absolutePath.indexOf("com/"), absolutePath.indexOf(".class"));                    
                 absolutePath = absolutePath.replace("/", ".");                   
                 // absolutePath: com.hz.spring.demo.service.UserService                    
                 Class<?> clazz = classLoader.loadClass(absolutePath);                    
                 classList.add(clazz);                
               } catch (Exception e) {                    
                   log.error("scanAndGetClassList error e:", e);               
                }           
          }        
     }    
 }    
     return classList;
 }

2.3.3 》 bean定义、建立beanName映射关系、保存beanPostProcessor对象

遍历classList

创建BeanDefinition、BeanDefinition赋值属性、建立beanName与BeanDefinition映射关系

保存beanPostProcessor对象

复制代码
private void compBdMap(List<Class<?>> classList) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException
 {    
 for (Class<?> clazz : classList) {        
 // 遍历 @Component("") 类。设置bean属性        
 if (clazz.isAnnotationPresent(Component.class)) {            
 Component clazzAnnotation = clazz.getAnnotation(Component.class);           
  String beanName = clazzAnnotation.value();           
   BeanDefinition beanDefinition = new BeanDefinition();           
    beanDefinition.setBeanClass(clazz);           
     if (clazz.isAnnotationPresent(Scope.class)) {                
        Scope scopeAnnotation = clazz.getAnnotation(Scope.class);               
        String scope = scopeAnnotation.value();                
        beanDefinition.setScope(scope);           
     } else {                
       // 默认单例               
        beanDefinition.setScope(SCOPE_SINGLETON);           
      }           
       // userService:beanDefinition            
       beanDefinitionMap.put(beanName, beanDefinition);            
       // 保存beanPostProcessor对象            
       // 通过反射获取对象            
       // clazz : SQYCBeanPostProcessor            
       if (BeanPostProcessor.class.isAssignableFrom(clazz)) {                
          BeanPostProcessor instance = (BeanPostProcessor) clazz.getConstructor().newInstance();               
          beanPostProcessorList.add(instance);           
       }       
    }    
   }
 }

遍历beanDefinitionMap,判断beanDefinition作用域

作用域为单例Bean,放入单例池

作用域为多例Bean,多次创建

复制代码
private void instanceBean() {   
    Set<Map.Entry<String, BeanDefinition>> entries = beanDefinitionMap.entrySet();   
    for (Map.Entry<String, BeanDefinition> entry : entries) {       
        BeanDefinition beanDefinition1 = entry.getValue();        
        String beanName = entry.getKey();        
        if (SCOPE_SINGLETON.equals(beanDefinition1.getScope())) {            
           if (!singletonObjects.containsKey(beanName)) {                
            // create                
            Object instance = this.doCreateBean(beanName, beanDefinition1);                
            singletonObjects.put(beanName, instance);            
            }        
        } else {            
           this.doCreateBean(beanName, beanDefinition1);       
        }    
    }
 }

2.3.4 》 实例化bean、对象填充属性、执行award 方法、BeanPostProcessor干预、初始化、放入单例池、获取bean

实例化bean

对象填充属性

BeanNameAware 设置beanName

BeanPostProcessor,初始化前后进行bean干预

InitializingBean 初始化,设置属性

bean创建完成,返回bean,放入单例池

复制代码
private Object doCreateBean(String beanName, BeanDefinition beanDefinition1) {    
    // com.hz.spring.demo.service.UserService    
    Class<?> beanClass = beanDefinition1.getBeanClass();    
    try {        
    // 实例化bean        
    Object instance = beanClass.getConstructor().newInstance();        
    // 填充属性。为bean添加属性。如:userService 添加属性 user        
    // 解析Autowired注解的属性        
    Field[] declaredFields = beanClass.getDeclaredFields();        
    for (Field declaredField : declaredFields) {            
        if (declaredField.isAnnotationPresent(Autowired.class)) {               
        // user 他也是一个bean。执行从单例池获取就可以                
        // 根据beanName获取对象                
        Object bean = this.getBean(declaredField.getName());                
        declaredField.setAccessible(true);               
        // 将属性declaredField 赋值给 instance               
        declaredField.set(instance, bean);            
        }        
    }        
    // award.通过beanNameAward实现获取beanName        
    if (instance instanceof BeanNameAware) {            
        ((BeanNameAware) instance).setBeanName(beanName);        
    }       
    // 初始化之前。BeanPostProcessor干预。应用场景:AOP 、属性注入、资源回收        
    for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {            
        instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);        
    }        
    // 初始化。在属性注入完成后调用。可以对属性进行一些改动       
    if (instance instanceof InitializingBean) {            
        try {                
            ((InitializingBean) instance).afterPropertiesSet();            
        } catch (Exception e) {                
            log.error("doCreateBean InitializingBean error:", e);            
        }        
    }        
    
    // 初始化之后。BeanPostProcessor干预      
    for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {            
        instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName);        
    }       
    return instance;    
} catch (Exception e) {        
   log.error("doCreateBean error:", e);    
}    
return null;
}

根据beanName获取bean

复制代码
public Object getBean(String beanName) {    
    BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);    
    String scope = beanDefinition.getScope();    
    if (SCOPE_SINGLETON.equals(scope)) {        
        Object bean = singletonObjects.get(beanName);        
        if (bean == null) {            
            bean = this.doCreateBean(beanName, beanDefinition);        
        }        
        return bean;    
    } else if (SCOPE_PROTOTYPE.equals(scope)) {        
        return this.doCreateBean(beanName, beanDefinition);    
    }    
    return null;
}

BeanPostProcessor实现类定义

复制代码
@Component("sqBeanPostProcessor")
@Slf4j
public class SqBeanPostProcessor implements BeanPostProcessor {    
    @Override    
    public Object postProcessBeforeInitialization(Object bean, String beanName) {        
        log.info("SqBeanPostProcessor {} 初始化之前", beanName);        
        return bean;   
    }    
    
    @Override    
    public Object postProcessAfterInitialization(Object bean, String beanName) {        
        log.info("SqBeanPostProcessor {} 初始化之后", beanName);        
        // 可以改变对象。很强大    
        return bean;   
    }
}

如上,bean创建流程就完成啦✌️✌️✌️

2.3.5 》 业务类实现

下面,咱们看看业务类怎样对Spring Bean 进行扩展,

UserService业务核心类定义,实现BeanNameAware、InitializingBean,对bean进行干预;

test() 方法,获取用户属性、beanName

setBeanName() 通过beanNameAward实现获取beanName

afterPropertiesSet() 在属性注入完成后调用,可以对属性进行一些改动

复制代码
@Component("userService")
@Scope(value = "singleton")
@Slf4j
public class UserService implements BeanNameAware, InitializingBean {   
     @Autowired    
     private User user;    
     /**     
     * 可以通过spring award回调方法实现     
     * beanName应用场景:     
     * spring + dubbo。dubbo暴露服务,单个服务的地址可能是beanName的名称组成     
     */   
      private String beanName;    
      /**     
      * 所有属性填充后。获得     
      */    
      private String userName;    
      
      public void test() {        
          log.info("UserService test() user.age:{},beanName:{},userName:{}", user.getAge(), beanName, userName);    
      }    
      
      /**  
      * BeanNameAward    
      *     
      * @param beanName     
      */   
      @Override    
      public void setBeanName(String beanName) {        
          this.beanName = beanName;        
          log.info("UserService setBeanName() beanName:{}", beanName);    
      }   
       
      /**    
       * InitializingBean     
       * 所有属性填充后获得     
       *     
       * @throws Exception     
       */    
       @Override    
       public void afterPropertiesSet() throws Exception {        
           this.userName = "w";        
           this.user.setAge("24");        
           log.info("UserService afterPropertiesSet() userName:{},age:{}", userName, user.getAge());    
       }
}

2.3.6 》 运行结果

Application.java 启动类,实例化spring容器,获取userService对象,调用test方法。

复制代码
@Slf4j 
public class Application { 
    public static void main(String[] args) { 
        AnnotationApplicationContext configWebApplicationContext = new AnnotationApplicationContext(AnnotationConfig.class); 
        UserService userService = (UserService) configWebApplicationContext.getBean("userService"); 
        userService.test(); 
    } 
}

下面我们来运行下,结果如下:

17:41:39.466 [main] INFO com.hz.spring.demo.service.SqBeanPostProcessor - SqBeanPostProcessor sqBeanPostProcessor 初始化之前 17:41:39.471 [main] INFO com.hz.spring.demo.service.SqBeanPostProcessor - SqBeanPostProcessor sqBeanPostProcessor 初始化之后 17:41:39.471 [main] INFO com.hz.spring.demo.service.SqBeanPostProcessor - SqBeanPostProcessor user 初始化之前 17:41:39.472 [main] INFO com.hz.spring.demo.service.SqBeanPostProcessor - SqBeanPostProcessor user 初始化之后 17:41:39.474 [main] INFO com.hz.spring.demo.service.UserService - UserService setBeanName() beanName:userService 17:41:39.474 [main] INFO com.hz.spring.demo.service.SqBeanPostProcessor - SqBeanPostProcessor userService 初始化之前 17:41:39.474 [main] INFO com.hz.spring.demo.service.UserService - UserService afterPropertiesSet() userName:w,age:24 17:41:39.474 [main] INFO com.hz.spring.demo.service.SqBeanPostProcessor - SqBeanPostProcessor userService 初始化之后 17:41:39.474 [main] INFO com.hz.spring.demo.service.UserService - UserService test() user.age:24,beanName:userService,userName:w


3 》 总结

如上为Spring Bean生命周期及扩展点代码模拟, 希望对大家有所帮助。🤝🤝🤝

作者:京东保险 胡忠

来源:京东云开发者社区 转载请注明来源

相关推荐
BillKu2 分钟前
Java + Spring Boot + Mybatis 插入数据后,获取自增 id 的方法
java·tomcat·mybatis
全栈凯哥3 分钟前
Java详解LeetCode 热题 100(26):LeetCode 142. 环形链表 II(Linked List Cycle II)详解
java·算法·leetcode·链表
chxii4 分钟前
12.7Swing控件6 JList
java
全栈凯哥6 分钟前
Java详解LeetCode 热题 100(27):LeetCode 21. 合并两个有序链表(Merge Two Sorted Lists)详解
java·算法·leetcode·链表
YuTaoShao6 分钟前
Java八股文——集合「List篇」
java·开发语言·list
PypYCCcccCc11 分钟前
支付系统架构图
java·网络·金融·系统架构
华科云商xiao徐32 分钟前
Java HttpClient实现简单网络爬虫
java·爬虫
扎瓦1 小时前
ThreadLocal 线程变量
java·后端
BillKu1 小时前
Java后端检查空条件查询
java·开发语言
jackson凌1 小时前
【Java学习笔记】String类(重点)
java·笔记·学习