前言
一个案例引发的思考
由于安全问题,需要对项目配置文件中的明文数据库账号、密码进行脱敏处理,这里引入
Jasypt加密库对配置文件做加密处理,处理流程如下:
1、加入pom依赖
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
2、启动类中引入自定义加密策略
@Bean("jasyptStringEncryptor")
public StringEncryptor stringEncryptor()
{
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
config.setPassword(password);
config.setAlgorithm("PBEWithMD5AndDES");
config.setKeyObtentionIterations("1000");
config.setPoolSize("1");
config.setProviderName("SunJCE");
config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
config.setStringOutputType("base64");
encryptor.setConfig(config);
return encryptor;
}
大部分系统引入后能够正常运行,但是个别系统引入缺出现了问题。
如下:系统能够正常启动,登录出现异常,分析原因是因为引入了新的注入对象后影响了类的加载顺序导致自动装配置依赖对象为null,导致调用时空指针异常
由此引发了spring容器生命周期的思考,知其然,知其所以然
Bean的生命周期
容器管理bean的生命周期,以自定义初始化和销毁方法;容器在bean进行到当前生命周期的时候来调用我们自定义的初始化和销毁方法
bean创建---初始化----销毁的过程
增加初始化和销毁方法的几种方式:
1、指定初始化和销毁方法; 通过@Bean指定init-method和destroy-method;
2、通过让Bean实现InitializingBean(定义初始化逻辑), DisposableBean(定义销毁逻辑);
3、使用JSR250标签(Java Specification Requests—java 规范要求)
@PostConstruct:在bean创建完成并且属性赋值完成;来执行初始化方法
@PreDestroy:在容器销毁bean之前通知我们进行清理工作
4、BeanPostProcessor【interface】
bean的后置处理器; 在bean初始化前后进行一些处理工作;
postProcessBeforeInitialization:在初始化之前工作
postProcessAfterInitialization:在初始化之后工作
BeanPostProcessor源码分析
createBeanInstance(beanName, mbd, args);//创建实例
populateBean(beanName, mbd, instanceWrapper); //给bean进行属性赋值
initializeBean {
applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); invokeInitMethods(beanName, wrappedBean, mbd);执行自定义初始化 applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
Bean的生命周期
1、构造(对象创建) 单实例:在容器启动的时候创建对象
多实例:在每次获取的时候创建对象
2、 BeanPostProcessor.postProcessBeforeInitialization
3、 初始化: 对象创建完成,并赋值好,调用初始化方法
4、BeanPostProcessor.postProcessAfterInitialization
5、销毁:
单实例:容器关闭的时候
多实例:容器不会管理这个bean;容器不会调用销毁方法;
注入spring底层组件
如果想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory,xxx);
自定义组件实现xxxAware;在创建对象的时候,会调用接口规定的方法注入相关组件;Aware; 把Spring底层一些组件注入到自定义的Bean中;
ApplicationContextAware==》ApplicationContextAwareProcessor;
InitDestroyAnnotationBeanPostProcessor:注入@PostConstruct注解;
AutowiredAnnotationBeanPostProcessor:注入@Autowired注解
Spring底层对 BeanPostProcessor 的使用;
bean赋值,注入其他组件applicationContextAware,BeanvalidationPostProcessor,@Autowired,@Async都使用BeanPostProcessor进行处理
自动装配
自动装配逻辑
1、@Autowired:自动注入:默认优先按照类型去容器中找对应的组件:applicationContext.getBean(BookDao.class);找到就赋值
2、如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找 applicationContext.getBean("bookDao")
3、@Qualifier("bookDao"):使用@Qualifier指定需要装配的组件的id,而不是使用属性名
4、@Primary:让Spring进行自动装配的时候,默认使用首选的bean; 也可以继续使用@Qualifier指定需要装配的bean的名字 BookService{ @Autowired BookDao bookDao; }
Spring源码解析
BeanFactoryPostProcessor
1、BeanFactoryPostProcessor:beanFactory的后置处理器;
在BeanFactory标准初始化之后调用,来定制和修改BeanFactory的内容;
所有的bean定义已经保存加载到beanFactory,但是bean的实例还未创建
BeanFactoryPostProcessor原理:
1)、ioc容器创建对象
2)、invokeBeanFactoryPostProcessors(beanFactory);
如何找到所有的BeanFactoryPostProcessor并执行他们的方法;
1)、直接在BeanFactory中找到所有类型是BeanFactoryPostProcessor的组件,并执行他们的方法
2)、在初始化创建其他组件前面执行
2、BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor postProcessBeanDefinitionRegistry();
在所有bean定义信息将要被加载,bean实例还未创建的时候;
优先于BeanFactoryPostProcessor执行;
利用BeanDefinitionRegistryPostProcessor给容器中再额外添加一些组件;
原理: 1)、ioc创建对象
2)、refresh()-》invokeBeanFactoryPostProcessors(beanFactory);
3)、从容器中获取到所有的BeanDefinitionRegistryPostProcessor组件。
1、依次触发所有的postProcessBeanDefinitionRegistry()方法
2、再来触发postProcessBeanFactory()方法BeanFactoryPostProcessor;
4)、再来从容器中找到BeanFactoryPostProcessor组件;然后依次触发postProcessBeanFactory()方法
前言的问题
处理方法详见:
https://blog.csdn.net/yuanlaijike/article/details/79627836