图书网站建设论文,永州企业网站建设价格,网站建设合同副本,经营性 网站备案文章目录 前言功能第一种#xff1a;ApplicationContext第二种方式#xff1a;ApplicationContextAware第三种#xff1a;BeanFactoryPostProcessor 源码第一种第二种第三种 前言
本篇是针对如何写一个比较好的spring工具的一个探讨。
功能
下面三种方式#xff0c;你觉… 文章目录 前言功能第一种ApplicationContext第二种方式ApplicationContextAware第三种BeanFactoryPostProcessor 源码第一种第二种第三种 前言
本篇是针对如何写一个比较好的spring工具的一个探讨。
功能
下面三种方式你觉得哪种最好
第一种直接注入ApplicationContext第二种实现ApplicationContextAware接口第三种实现BeanFactoryPostProcessor接口
第一种ApplicationContext
它的功能如下它有国际化功能beanFactory功能事件发布功能以及资源加载功能作为上下文他这个功能已经很强大了。
它发生的时机是在bean实例化后的依赖注入。 示例
Component
public class CustomConfig9 {Autowiredprivate ApplicationContext applicationContext;PostConstructpublic void init() {CustomConfig3 bean applicationContext.getBean(CustomConfig3.class);System.out.println(customConfig9 获取了 customConfig3 bean);}
} 优点 这种方式也比较简单在需要用的bean中直接注入就行
缺点 它的局限就是在bean中才能使用如果你要给工具类或一个静态方法中使用你就不太好这样做你得控制你业务的执行时机
第二种方式ApplicationContextAware
这种方式是通过Bean初始化后执行Aware接口回调方式实现我见过很多项目他们都是这样做的
Component
public class SpringUtil implements ApplicationContextAware {private static ApplicationContext applicationContext;Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {SpringUtil.applicationContext applicationContext;}public static T T getBean(ClassT clazz) {return applicationContext.getBean(clazz);}
}
这写法没毛病可是它存在一个bug 不是任何地方都能使用。
为何这么说
那么我再增加一个类
Component
public class CustomConfig6 implements InitializingBean {private CustomConfig config;Overridepublic void afterPropertiesSet() throws Exception {config SpringUtil.getBean(CustomConfig.class);// 这里示例比较简单一般业务场景可能是jdbc检索redis缓存这些逻辑}
}
在我增加了这样的一个类后你觉得你的项目会是正常的吗
请思考3秒钟…
.
.
.
那么答案是有可能是异常的为何
大家还记得Aware接口是在哪个时机调用的它是在bean初始化后调用的spring bean的生命周期是单线程的如果说Spring先实例化了CustomConfig6那么它会先调用afterPropertiesSet里的SpringUtil.getBean而这时SpringUtil还没有被实例化SpringUtil里的applicationContext必然是null
为何会有先后顺序
我们先复习一下Spring怎么扫描bean的Spring是先扫描的当前包下的class顺序扫描扫描到的class在经过一些了的校验后会放到一个容器里实例化时再根据bean的名称它是一个list进行遍历实例化到这大概的一个原因应该明了了吧如果SpringUtil所在的文件位置考前在其他类之前扫描到就能先实例化那么就是正常的如果它靠后就会出现其他业务bean回调时通过SpringUtil使用ApplicationContext功能而出现空指针异常。
优点 使用时直接静态方法调用方便
缺点 可能存在bug
第三种BeanFactoryPostProcessor
在第二种的方式上进行优化我们需要考虑它的一个初始化时机bean实例化都是统一进行的所以我们要打破这个规则提前进行对SpringUtil中的applicationContext进行赋值所以我们可以使用BeanFactoryPostProcessor这个后置处理器是在beanFactory准备完成后端一个回调操作我们的bean配置类等等这些都是在这里被扫描出来的是bean生命周期开始的开端。
Component
public class SpringUtil2 implements BeanFactoryPostProcessor {private static ConfigurableListableBeanFactory applicationContext;Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {SpringUtil2.applicationContext beanFactory;}public static T T getBean(ClassT clazz) {return applicationContext.getBean(clazz);}
}
优点 可用在任何地方法
源码
第一种
ApplicationContext它是通过依赖注入进行注入的我们直接看创建bean的方法
位置org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean 那我们的ApplicationContext就是在populateBean方法中被注入点但是在此之前它需要查找注入点然后在注入时可以直接通过注入点进行属性注入。图片没有写全注入点包含Value, Inject, 依赖注入的也包含Value这写 详细的看spring源码篇四依赖注入控制反转
Autowired注入是由AutowiredAnnotationBeanPostProcessor后置处理器进行处理而Resource由CommonAnnotationBeanPostProcessor处理
第二种
第二种是Aware方法调用也是在bean初始化时调用的如上面图片描述的他会在initializeBean方法中调用位置org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition) invokeAwareMethods它提供了3个Aware
BeanNameAware回调setBeanName实际上调用有我们控制你想做什么就做什么BeanClassLoaderAwarebean容器的类加载器通过它你可以加载到classpath这个包含多种路径下的所有classBeanFactoryAwarebean工厂bean容器回调
其他的在ApplicationContextAwareProcessor 如上只要你实现ResourceLoaderAware, ApplicationEventPublisherAware, MessageSourceAware, ApplicationContextAware他都会吧applicationContext给你设置上。
第三种
这第三种Spring启动时执行的一个方法就是进行bean容器的初始化
这就是我们main方法里的SpringApplication.run 这部分就是执行我们自定义的beanFactoryPostProcessor它分排序的和没有排序的这个方法已经来会先进行BeanDefinitionRegistryPostProcessor这个处理器的执行这里就是进行扫描解析配置类和beanComponent, Configuation, Bean....的地方。所以我们在后面才能获取的我们自定义的bean并提前实例化。