写点什么

bean 的一生

  • 2024-01-25
    北京
  • 本文字数:7952 字

    阅读完需:约 26 分钟

你曾读 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")@Slf4jpublic 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")@Slf4jpublic 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 生命周期及扩展点代码模拟, 希望对大家有所帮助。🤝🤝🤝


作者:京东保险 胡忠


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

用户头像

拥抱技术,与开发者携手创造未来! 2018-11-20 加入

我们将持续为人工智能、大数据、云计算、物联网等相关领域的开发者,提供技术干货、行业技术内容、技术落地实践等文章内容。京东云开发者社区官方网站【https://developer.jdcloud.com/】,欢迎大家来玩

评论

发布
暂无评论
bean的一生_京东科技开发者_InfoQ写作社区