工厂Bean后置处理器,主要是用来向容器中添加一些Bean的定义信息
下面给出的是演示过程中,可能会用到的依赖:
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.17</version>
</dependency>
例子:
@Component
public class Bean2 {
private static final Logger log = LoggerFactory.getLogger(Bean2.class);
public Bean2() {
log.debug("我被 Spring 管理啦");
}
}
@Controller
public class Bean3 {
private static final Logger log = LoggerFactory.getLogger(Bean3.class);
public Bean3() {
log.debug("我被 Spring 管理啦");
}
}
public class Bean4 {
private static final Logger log = LoggerFactory.getLogger(Bean4.class);
public Bean4() {
log.debug("我被 Spring 管理啦");
}
}
public class Bean1 {
private static final Logger log = LoggerFactory.getLogger(Bean1.class);
public Bean1() {
log.debug("我被 Spring 管理啦");
}
}
@Mapper
public interface Mapper1 {
}
@Mapper
public interface Mapper2 {
}
public class Mapper3 {
}
@Configuration
@ComponentScan("com.two")
public class Config {
@Bean
public Bean1 bean1() {
return new Bean1();
}
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
@Bean(initMethod = "init")
public DruidDataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
}
/*
BeanFactory 后处理器的作用
*/
public class A05 {
private static final Logger log = LoggerFactory.getLogger(A05.class);
public static void main(String[] args) throws IOException {
// ⬇️GenericApplicationContext 是一个【干净】的容器
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
// ⬇️初始化容器
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// ⬇️销毁容器
context.close();
}
}
此时容器中只有一个Bean,显然config配置类的注解都没有被解析,此时就需要添加工厂后置处理器来解析这些注解了,从而可以往容器中添加更多的bean的定义信息
public class A05 {
private static final Logger log = LoggerFactory.getLogger(A05.class);
public static void main(String[] args) throws IOException {
// ⬇️GenericApplicationContext 是一个【干净】的容器
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
// @ComponentScan @Bean @Import @ImportResource
context.registerBean(ConfigurationClassPostProcessor.class);
// ⬇️初始化容器
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// ⬇️销毁容器
context.close();
}
}
可以看出ConfigurationClassPostProcessor还能把@Component以及其衍生注解标注的类,如果被扫描到后,都会被放入容器中
/*
BeanFactory 后处理器的作用
*/
public class A05 {
private static final Logger log = LoggerFactory.getLogger(A05.class);
public static void main(String[] args) throws IOException {
// ⬇️GenericApplicationContext 是一个【干净】的容器
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
// @ComponentScan @Bean @Import @ImportResource
context.registerBean(ConfigurationClassPostProcessor.class);
// @MapperScan注解
context.registerBean(MapperScannerConfigurer.class, bd -> {
//指定扫描的包名
bd.getPropertyValues().add("basePackage", "com.two.mapper");
});
// ⬇️初始化容器
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// ⬇️销毁容器
context.close();
}
}
MapperScannerConfigurer还会额外干些事情
ConfigurationClassPostProcessor 可以解析
@ComponentScan
@Bean
@Import
@ImportResource
MapperScannerConfigurer 可以解析
Mapper 接口
这里不会去直接翻阅其源码,而是通过引用spring源码中一些类的方法,来基本还原大致的解析过程
public class ComponentScanPostProcessor implements BeanDefinitionRegistryPostProcessor {
/**
* 该方法会在context.refresh中被调用
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
try {
//这里按理来说,应该从context中或取到外部传入的config配置类对象才对
//AnnotationUtils获取Config配置类上的ComponentScan注解,如果没有标注范围null
ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
//如果配置类标注了该注解
if (componentScan != null) {
//获取注解中标注的basePackages属性标注扫描的包路径
for (String p : componentScan.basePackages()) {
//输出需要扫描的包路径
System.out.println("需要扫描的包路径为: "+p);
// com.dhy.component -> classpath*:com/dhy/component/**/*.class
// /**/*.class表示当前包和子包下面所有的类
String path = "classpath*:" + p.replace(".", "/") + "/**/*.class";
//输出该转换后的资源路径
System.out.println("转换后的资源路径: "+path);
//读取类的原信息工厂---可以从二进制字节码文件中读取出相关信息---底层使用了asm字节码重组技术
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
//通过PathMatchingResourcePatternResolver去定位符合这些路径的资源集合
Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
//负责解析@Component注解中标注的beanName
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
for (Resource resource : resources) {
//System.out.println(resource);
//当前resource是一个class文件
//通过原信息工厂获取到原信息读取器
MetadataReader reader = factory.getMetadataReader(resource);
System.out.println("类名:" + reader.getClassMetadata().getClassName());
//从二进制class文件中读取出当前类上的注解信息
AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
System.out.println("是否加了 @Component:" + annotationMetadata.hasAnnotation(Component.class.getName()));
//例如:@Controller注解上加了@Component注解
System.out.println("是否加了 @Component 派生:" + annotationMetadata.hasMetaAnnotation(Component.class.getName()));
//只选择加了Component或者其派生注解
if (annotationMetadata.hasAnnotation(Component.class.getName())
|| annotationMetadata.hasMetaAnnotation(Component.class.getName())) {
//将扫描到的符合条件的class包装成一个BeanDefinition
AbstractBeanDefinition bd = BeanDefinitionBuilder
//传入class类名
.genericBeanDefinition(reader.getClassMetadata().getClassName())
.getBeanDefinition();
//通过传入的BeanDefinition生成一个beanName
String name = generator.generateBeanName(bd, beanFactory);
//注册BeanDefinition进BeanFactory--传入BeanName,BeanDefinition
beanFactory.registerBeanDefinition(name, bd);
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
将我们自定义的ComponentScanPostProcessor注册到Bean工厂中:
public class A05 {
private static final Logger log = LoggerFactory.getLogger(A05.class);
public static void main(String[] args) throws IOException {
// ⬇️GenericApplicationContext 是一个【干净】的容器
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
// 解析 @ComponentScan
//自定义的ComponentScanPostProcessor,专门解析ComponentScan注解
context.registerBean(ComponentScanPostProcessor.class);
// ⬇️初始化容器
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// ⬇️销毁容器
context.close();
}
}
输出:
13:06:16.413 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Refreshing org.springframework.context.support.GenericApplicationContext@2d363fb3
13:06:16.442 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'com.two.ComponentScanPostProcessor'
需要扫描的包路径为: com.two
转换后的资源路径: classpath*:com/two/**/*.class
类名:com.two.A05
是否加了 @Component:false
是否加了 @Component 派生:false
类名:com.two.AtBeanPostProcessor
是否加了 @Component:false
是否加了 @Component 派生:false
类名:com.two.Bean1
是否加了 @Component:false
是否加了 @Component 派生:false
类名:com.two.ComponentScanPostProcessor
是否加了 @Component:false
是否加了 @Component 派生:false
类名:com.two.Config
是否加了 @Component:false
是否加了 @Component 派生:true
13:06:16.586 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Overriding bean definition for bean 'config' with a different definition: replacing [Root bean: class [com.two.Config]; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null] with [Generic bean: class [com.two.Config]; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null]
类名:com.two.MapperPostProcessor
是否加了 @Component:false
是否加了 @Component 派生:false
类名:com.two.component.Bean2
是否加了 @Component:true
是否加了 @Component 派生:false
类名:com.two.component.Bean3
是否加了 @Component:false
是否加了 @Component 派生:true
类名:com.two.component.Bean4
是否加了 @Component:false
是否加了 @Component 派生:false
类名:com.two.mapper.Mapper1
是否加了 @Component:false
是否加了 @Component 派生:false
类名:com.two.mapper.Mapper2
是否加了 @Component:false
是否加了 @Component 派生:false
类名:com.two.mapper.Mapper3
是否加了 @Component:false
是否加了 @Component 派生:false
13:06:16.607 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'config'
13:06:16.607 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean2'
13:06:16.607 [main] DEBUG com.two.component.Bean2 - 我被 Spring 管理啦
13:06:16.608 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean3'
13:06:16.608 [main] DEBUG com.two.component.Bean3 - 我被 Spring 管理啦
config
com.two.ComponentScanPostProcessor
//这里看出我们自定义的ComponentScanPostProcessor生效了
bean2
bean3
13:06:16.634 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Closing org.springframework.context.support.GenericApplicationContext@2d363fb3, started on Sun Mar 27 13:06:16 CST 2022
Process finished with exit code 0
工厂Bean后置处理器是在refresh方法中,每个节点被调用的,而Bean的后置处理器是在getBean方法执行Bean生命周期过程中被调用的,前者主要是为了向容器中增加Bean的定义信息,后者是为了干预Bean生命周期中各个阶段的行为。
public class AtBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
try {
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
//推荐使用这种方式读取,因为他是直接通过asm读取字节码,不会走反射过程和类加载过程
//这里直接写死配置类的路径了
MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/two/Config.class"));
//获取当前配置类中被@Bean注解标注的方法
Set<MethodMetadata> methods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
//遍历这些方法
for (MethodMetadata method : methods) {
System.out.println("当前被@Bean注解标注的方法名为: "+method);
//获取@Bean注解中的属性
Map<String, Object> annotationAttributes = method.getAnnotationAttributes(Bean.class.getName());
//找到initMethod属性对应的值
String initMethod = annotationAttributes.get("initMethod").toString();
//下面针对每个一个方法生成一个BeanDefinition
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
//我们需要把这些被@Bean标注的方法变成工厂方法---工厂方法负责生产产品
//设置工厂方法名和当前工厂方法属于的对象
builder.setFactoryMethodOnBean(method.getMethodName(), "config");
//设置自动装配模式,默认为AbstractBeanDefinition.AUTOWIRE_NO
//如果工厂方法上有参数,那么参数值是否需要自动注入? ---这就是自动装配模式需要管的事情
//对于工厂方法的参数而言,自动装配模式应该是构造方法的自动注入
builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
//设置初始化方法名称
if (initMethod.length() > 0) {
builder.setInitMethodName(initMethod);
}
//生成BeanDefinition
AbstractBeanDefinition bd = builder.getBeanDefinition();
//注册BeanDefinition到BeanFactory
beanFactory.registerBeanDefinition(method.getMethodName(), bd);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
将自定义的AtBeanPostProcessor注册到BeanFactory中
public class A05 {
private static final Logger log = LoggerFactory.getLogger(A05.class);
public static void main(String[] args) throws IOException {
// ⬇️GenericApplicationContext 是一个【干净】的容器
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
//注册自定义的工厂Bean后置处理器
context.registerBean(AtBeanPostProcessor.class);
// ⬇️初始化容器
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// ⬇️销毁容器
context.close();
}
}
输出:
13:27:15.227 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Refreshing org.springframework.context.support.GenericApplicationContext@2d363fb3
13:27:15.257 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'com.two.AtBeanPostProcessor'
当前被@Bean注解标注的方法名为: com.two.Config.bean1()
当前被@Bean注解标注的方法名为: com.two.Config.sqlSessionFactoryBean(javax.sql.DataSource)
当前被@Bean注解标注的方法名为: com.two.Config.dataSource()
13:27:15.365 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'config'
13:27:15.365 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean1'
13:27:15.366 [main] DEBUG com.two.Bean1 - 我被 Spring 管理啦
13:27:15.366 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'sqlSessionFactoryBean'
13:27:15.372 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'dataSource'
13:27:15.488 [main] INFO com.alibaba.druid.pool.DruidDataSource - {dataSource-1} inited
13:27:15.491 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'sqlSessionFactoryBean' via factory method to bean named 'dataSource'
13:27:15.497 [main] DEBUG org.apache.ibatis.logging.LogFactory - Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter.
13:27:15.502 [main] DEBUG org.mybatis.spring.SqlSessionFactoryBean - Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration
13:27:15.545 [main] DEBUG org.mybatis.spring.SqlSessionFactoryBean - Property 'mapperLocations' was not specified.
//自定义的工厂Bean后置处理器生效了
config
com.two.AtBeanPostProcessor
bean1
sqlSessionFactoryBean
dataSource
13:27:15.571 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Closing org.springframework.context.support.GenericApplicationContext@2d363fb3, started on Sun Mar 27 13:27:15 CST 2022
13:27:15.572 [main] INFO com.alibaba.druid.pool.DruidDataSource - {dataSource-1} closing ...
13:27:15.572 [main] INFO com.alibaba.druid.pool.DruidDataSource - {dataSource-1} closed
我们先看一下mybaits是如何处理这个mapper接口的
@Configuration
@ComponentScan(basePackages = "com.two")
public class Config {
@Bean
public Bean1 bean1() {
return new Bean1();
}
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
@Bean(initMethod = "init")
public DruidDataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
@Bean
public MapperFactoryBean<Mapper1> mapper1(SqlSessionFactory sqlSessionFactory) {
MapperFactoryBean<Mapper1> factory = new MapperFactoryBean<>(Mapper1.class);
factory.setSqlSessionFactory(sqlSessionFactory);
return factory;
}
@Bean
public MapperFactoryBean<Mapper2> mapper2(SqlSessionFactory sqlSessionFactory) {
MapperFactoryBean<Mapper2> factory = new MapperFactoryBean<>(Mapper2.class);
factory.setSqlSessionFactory(sqlSessionFactory);
return factory;
}
}
Mybaits对于每个mapper接口,会使用一个MapperFactoryBean来包装该mapper接口,其实我们可以猜到以后要注入该mapper接口时,就是从MapperFactoryBean获取mapper接口对应的实现类,其实MapperFactoryBean就是一个工厂类
@Bean
public MapperFactoryBean<Mapper1> mapper1(SqlSessionFactory sqlSessionFactory) {
MapperFactoryBean<Mapper1> factory = new MapperFactoryBean<>(Mapper1.class);
factory.setSqlSessionFactory(sqlSessionFactory);
return factory;
}
@Bean
public MapperFactoryBean<Mapper2> mapper2(SqlSessionFactory sqlSessionFactory) {
MapperFactoryBean<Mapper2> factory = new MapperFactoryBean<>(Mapper2.class);
factory.setSqlSessionFactory(sqlSessionFactory);
return factory;
}
这里是手动通过@Bean完成的关联注入功能,现在我们要实现通过@Mapper或者@MapperScan接口扫描自动完成这样的功能
public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
try {
//定位资源,PathMatchingResourcePatternResolver可以通过模糊匹配定位资源,并且可以一次性定位多个资源
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("classpath:com/two/mapper/**/*.class");
//beanName生成器
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
//缓存元数据读取器工厂---生产读取并解析class文件的reader
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
//遍历资源
for (Resource resource : resources) {
//读取并解析class文件的reader
MetadataReader reader = factory.getMetadataReader(resource);
//class文件解析完后得到的类元数据信息
ClassMetadata classMetadata = reader.getClassMetadata();
//当前class上注解的元数据信息
AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
//是否是一个接口并且标注了@Mapper注解
if (classMetadata.isInterface()&&annotationMetadata.hasAnnotation(Mapper.class.getName())) {
//生成一个BeanDefinition
AbstractBeanDefinition bd = BeanDefinitionBuilder
//Bean的类型是MapperFactoryBean
.genericBeanDefinition(MapperFactoryBean.class)
//联系这@Bean原生注入中的这一行代码: MapperFactoryBean<Mapper1> factory = new MapperFactoryBean<>(Mapper1.class);
//添加构造方法参数
.addConstructorArgValue(classMetadata.getClassName())
//设置自动注入模式---按照类型注入
//联系这一行: factory.setSqlSessionFactory(sqlSessionFactory);
.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
.getBeanDefinition();
//AnnotationMetadata默认会根据BeanDefinition来中的传入的当前BeanDefinition中对应的Bean.class类名首字母小写即bean作为beanName
//但是这里因为所有接口都要被MapperFactoryBean接管,因此这会导致所生成的BeanName都是一样的
//对于一样的BeanName,IOC的选择是新的覆盖旧的
String name = generator.generateBeanName(bd, beanFactory);
//注册Bean
beanFactory.registerBeanDefinition(name, bd);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
针对这里说的问题,我来演示一下:
public class A05 {
private static final Logger log = LoggerFactory.getLogger(A05.class);
public static void main(String[] args) throws IOException {
// ⬇️GenericApplicationContext 是一个【干净】的容器
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
//注册一个ConfigurationClassPostProcessor负责解析@ComponentScan @Bean @Import @ImportResource
context.registerBean(ConfigurationClassPostProcessor.class);
//注册我们写的Bean工厂后置处理器
context.registerBean(MapperPostProcessor.class);
// ⬇️初始化容器
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// ⬇️销毁容器
context.close();
}
}
mapper包下面有这三个类:
@Mapper
public interface Mapper1 {
}
@Mapper
public interface Mapper2 {
}
public class Mapper3 {
}
Spring团队的解决思路类似下面这样:
public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
try {
//定位资源,PathMatchingResourcePatternResolver可以通过模糊匹配定位资源,并且可以一次性定位多个资源
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("classpath:com/two/mapper/**/*.class");
//beanName生成器
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
//缓存元数据读取器工厂---生产读取并解析class文件的reader
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
//遍历资源
for (Resource resource : resources) {
//读取并解析class文件的reader
MetadataReader reader = factory.getMetadataReader(resource);
//class文件解析完后得到的类元数据信息
ClassMetadata classMetadata = reader.getClassMetadata();
//当前class上注解的元数据信息
AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
//是否是一个接口并且标注了@Mapper注解
if (classMetadata.isInterface()&&annotationMetadata.hasAnnotation(Mapper.class.getName())) {
//生成一个BeanDefinition
AbstractBeanDefinition bd = BeanDefinitionBuilder
//Bean的类型是MapperFactoryBean
.genericBeanDefinition(MapperFactoryBean.class)
//联系这@Bean原生注入中的这一行代码: MapperFactoryBean<Mapper1> factory = new MapperFactoryBean<>(Mapper1.class);
//添加构造方法参数
.addConstructorArgValue(classMetadata.getClassName())
//设置自动注入模式---按照类型注入
//联系这一行: factory.setSqlSessionFactory(sqlSessionFactory);
.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
.getBeanDefinition();
//为每个接口生成一个BeanDefinition---但是该BeanDefinition作用仅仅是来生成一个BeanName
AbstractBeanDefinition bd2 = BeanDefinitionBuilder.genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();
//这样每次通过bd2生成的BeanName就会不同了
String name = generator.generateBeanName(bd2, beanFactory);
//注册Bean
beanFactory.registerBeanDefinition(name, bd);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
再测试:
举例:
public class MyBean implements BeanNameAware, ApplicationContextAware, InitializingBean {
private static final Logger log = LoggerFactory.getLogger(MyBean.class);
@Override
public void setBeanName(String name) {
log.debug("当前bean " + this + " 名字叫:" + name);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.debug("当前bean " + this + " 容器是:" + applicationContext);
}
@Override
public void afterPropertiesSet() throws Exception {
log.debug("当前bean " + this + " 初始化");
}
}
主启动类:
/*
Aware 接口及 InitializingBean 接口
*/
public class A06 {
private static final Logger log = LoggerFactory.getLogger(A06.class);
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("myBean", MyBean.class);
context.refresh();
context.close();
}
}
结果:
Aware 接口提供了一种【内置】 的注入手段,例如
BeanNameAware 注入 bean 的名字
BeanFactoryAware 注入 BeanFactory 容器
ApplicationContextAware 注入 ApplicationContext 容器
EmbeddedValueResolverAware 注入 ${} 解析器
InitializingBean 接口提供了一种【内置】的初始化手段
对比
内置的注入和初始化不受扩展功能的影响,总会被执行
而扩展功能受某些情况影响可能会失效
因此 Spring 框架内部的类常用内置注入和初始化
2. 有同学说: b、c、d 的功能用 @Autowired 就能实现啊, 为啥还要用 Aware 接口呢
简单地说:
a. @Autowired 的解析需要用到 bean 后处理器, 属于扩展功能
b. 而 Aware 接口属于内置功能, 不加任何扩展, Spring 就能识别
某些情况下, 扩展功能会失效, 而内置功能不会失效
例1: 你会发现用 Aware 注入 ApplicationContext 成功, 而 @Autowired 注入 ApplicationContext 失败
例子1:
public class MyBean implements BeanNameAware, ApplicationContextAware, InitializingBean {
private static final Logger log = LoggerFactory.getLogger(MyBean.class);
@Override
public void setBeanName(String name) {
log.debug("当前bean " + this + " 名字叫:" + name);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.debug("当前bean " + this + " 容器是:" + applicationContext);
}
@Override
public void afterPropertiesSet() throws Exception {
log.debug("当前bean " + this + " 初始化");
}
@Autowired
public void aaa(ApplicationContext applicationContext) {
log.debug("当前bean " + this + " 使用@Autowired 容器是:" + applicationContext);
}
@PostConstruct
public void init() {
log.debug("当前bean " + this + " 使用@PostConstruct 初始化");
}
}
再次执行程序,查看@Autowired注解和@PostConstruct注解是否生效了
显然并没有生效,我们只有加入相应的工厂Bean后置处理器,才能生效,下面验证:
public class A06 {
private static final Logger log = LoggerFactory.getLogger(A06.class);
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("myBean", MyBean.class);
//解析Autowire注解的
context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
//解析Resource,PostConstrcut和PreDestory注解的
context.registerBean(CommonAnnotationBeanPostProcessor.class);
context.refresh();
context.close();
}
}
我们增加一个配置类Config1
@Configuration
public class MyConfig1 {
private static final Logger log = LoggerFactory.getLogger(MyConfig1.class);
@Autowired
public void setApplicationContext(ApplicationContext applicationContext) {
log.debug("注入 ApplicationContext");
}
@PostConstruct
public void init() {
log.debug("初始化");
}
@Bean // beanFactory 后处理器------->关键点
public BeanFactoryPostProcessor processor1() {
return beanFactory -> {
log.debug("执行 processor1");
};
}
}
我们需要增加ConfigurationClassPostProcessor工厂Bean后置处理器来处理@Configuration和@Bean注解
/*
Aware 接口及 InitializingBean 接口
*/
public class A06 {
private static final Logger log = LoggerFactory.getLogger(A06.class);
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("myConfig1", MyConfig1.class);
context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
context.registerBean(CommonAnnotationBeanPostProcessor.class);
context.registerBean(ConfigurationClassPostProcessor.class);
context.refresh();
context.close();
}
}
问题来了,@Autowired注解和@PostConstruct注解凉凉了
我们先来看一下Bean的生命周期流程图:
下面再来进一步分析一下@Autowired注解失效的原因:Java 配置类不包含 BeanFactoryPostProcessor 的情况
Java 配置类包含 BeanFactoryPostProcessor 的情况,因此要创建其中的 BeanFactoryPostProcessor 必须提前创建 Java 配置类,而此时的 BeanPostProcessor 还未准备好,导致 @Autowired 等注解失效
对应代码
@Configuration
public class MyConfig1 {
private static final Logger log = LoggerFactory.getLogger(MyConfig1.class);
@Autowired
public void setApplicationContext(ApplicationContext applicationContext) {
log.debug("注入 ApplicationContext");
}
@PostConstruct
public void init() {
log.debug("初始化");
}
@Bean // ⬅️ 注释或添加 beanFactory 后处理器对应上方两种情况
public BeanFactoryPostProcessor processor1() {
return beanFactory -> {
log.debug("执行 processor1");
};
}
}
注意
解决方法:
@Configuration
public class MyConfig2 implements InitializingBean, ApplicationContextAware {
private static final Logger log = LoggerFactory.getLogger(MyConfig2.class);
@Override
public void afterPropertiesSet() throws Exception {
log.debug("初始化");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.debug("注入 ApplicationContext");
}
@Bean // beanFactory 后处理器
public BeanFactoryPostProcessor processor2() {
return beanFactory -> {
log.debug("执行 processor2");
};
}
}
a. Aware 接口提供了一种【内置】 的注入手段, 可以注入 BeanFactory, ApplicationContext
b. InitializingBean 接口提供了一种【内置】的初始化手段
c. 内置的注入和初始化不受扩展功能的影响, 总会被执行, 因此 Spring 框架内部的类常用它们
演示:
public class Bean1 implements InitializingBean {
private static final Logger log = LoggerFactory.getLogger(Bean1.class);
@PostConstruct
public void init1() {
log.debug("初始化1");
}
@Override
public void afterPropertiesSet() throws Exception {
log.debug("初始化2");
}
public void init3() {
log.debug("初始化3");
}
}
public class Bean2 implements DisposableBean {
private static final Logger log = LoggerFactory.getLogger(Bean2.class);
@PreDestroy
public void destroy1() {
log.debug("销毁1");
}
@Override
public void destroy() throws Exception {
log.debug("销毁2");
}
public void destroy3() {
log.debug("销毁3");
}
}
@SpringBootApplication
public class A07_1 {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(A07_1.class, args);
context.close();
}
@Bean(initMethod = "init3")
public Bean1 bean1() {
return new Bean1();
}
@Bean(destroyMethod = "destroy3")
public Bean2 bean2() {
return new Bean2();
}
}
测试:
测试2:
public class A07_2 {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerBeanDefinition(
"myBean",
BeanDefinitionBuilder.genericBeanDefinition(MyBean.class)
.setDestroyMethodName("destroy")
.getBeanDefinition()
);
System.out.println(beanFactory.getBean(MyBean.class));
beanFactory.destroySingletons(); // 销毁之后, 仍可创建新的单例
System.out.println(beanFactory.getBean(MyBean.class));
}
static class MyBean {
public MyBean() {
System.out.println("MyBean()");
}
public void destroy() {
System.out.println("destroy()");
}
}
}
可以看出,对于beanFactory 来说,只有手动调用destroySingletons方法,才会去销毁这些单例Bean,销毁完后,可创建新的Bean
Spring 提供了多种初始化手段,除了 @PostConstruct,@Bean(initMethod) 之外,还可以实现 InitializingBean 接口来进行初始化,如果同一个 bean 用了以上手段声明了 3 个初始化方法,那么它们的执行顺序是
与初始化类似,Spring 也提供了多种销毁手段,执行顺序为
在当前版本的 Spring 和 Spring Boot 程序中,支持五种 Scope
有些文章提到有 globalSession 这一 Scope,也是陈旧的说法,目前 Spring 中已废弃
但要注意,如果在 singleton 注入其它 scope 都会有问题,解决方法有
@Scope("session")
@Component
public class BeanForSession {
private static final Logger log = LoggerFactory.getLogger(BeanForSession.class);
@PreDestroy
public void destroy() {
log.info("destroy");
}
}
@Scope("request")
@Component
public class BeanForRequest {
private static final Logger log = LoggerFactory.getLogger(BeanForRequest.class);
@PreDestroy
public void destroy() {
log.info("destroy");
}
}
@Scope("application")
@Component
public class BeanForApplication {
private static final Logger log = LoggerFactory.getLogger(BeanForApplication.class);
@PreDestroy
public void destroy() {
log.info("destroy");
}
}
测试:
@RestController
public class MyController {
@Lazy
@Autowired
private BeanForRequest beanForRequest;
@Lazy
@Autowired
private BeanForSession beanForSession;
@Lazy
@Autowired
private BeanForApplication beanForApplication;
@GetMapping(value = "/test", produces = "text/html")
public String test(HttpServletRequest request, HttpSession session) {
ServletContext sc = request.getServletContext();
String sb = "<ul>" +
"<li>" + "request scope:" + beanForRequest + "</li>" +
"<li>" + "session scope:" + beanForSession + "</li>" +
"<li>" + "application scope:" + beanForApplication + "</li>" +
"</ul>";
return sb;
}
}
/*
singleton, prototype, request, session, application
jdk >= 9 如果反射调用 jdk 中方法,会报访问异常
jdk <= 8 不会有问题
演示 request, session, application 作用域
打开不同的浏览器, 刷新 http://localhost:8080/test 即可查看效果
如果 jdk > 8, 运行时请添加 --add-opens java.base/java.lang=ALL-UNNAMED
*/
@SpringBootApplication
public class A08 {
public static void main(String[] args) {
SpringApplication.run(A08.class, args);
/*
学到了什么
a. 有几种 scope
b. 在 singleton 中使用其它几种 scope 的方法
c. 其它 scope 的销毁
1. 可以将通过 server.servlet.session.timeout=10s 观察 session bean 的销毁---默认30分钟失效
2. ServletContextScope 销毁机制疑似实现有误
*/
}
}
Tomcat的Session实现原理,这里说明了为什么至少需要
其它 scope 的销毁时机
可以将通过 server.servlet.session.timeout=30s 观察 session bean 的销毁
ServletContextScope 销毁机制疑似实现有误,,待定
以单例注入多例为例
@Scope("prototype")
@Component
public class F1 {
}
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Component
public class F2 {
}
@Scope("prototype")
@Component
public class F3 {
}
@Scope("prototype")
@Component
public class F4 {
}
有一个单例对象 E
@Component
public class E {
private static final Logger log = LoggerFactory.getLogger(E.class);
private F f;
public E() {
log.info("E()");
}
@Autowired
public void setF(F f) {
this.f = f;
log.info("setF(F f) {}", f.getClass());
}
public F getF() {
return f;
}
}
测试
E e = context.getBean(E.class);
F f1 = e.getF();
F f2 = e.getF();
System.out.println(f1);
System.out.println(f2);
输出
com.dhy.demo.cycle.F@6622fc65
com.dhy.demo.cycle.F@6622fc65
发现它们是同一个对象,而不是期望的多例对象
对于单例对象来讲,依赖注入仅发生了一次,后续再没有用到多例的 F,因此 E 用的始终是第一次依赖注入的 F
@Component
public class E {
@Autowired
@Lazy
public void setF(F f) {
this.f = f;
log.info("setF(F f) {}", f.getClass());
}
// ...
}
注意
输出
E: setF(F f) class com.itheima.demo.cycle.F$$EnhancerBySpringCGLIB$$8b54f2bc
F: F()
com.dhy.demo.cycle.F@3a6f2de3
F: F()
com.dhy.demo.cycle.F@56303b57
从输出日志可以看到调用 setF 方法时,f 对象的类型是代理类型
4种解决方法如果 jdk > 8, 运行时请添加 --add-opens java.base/java.lang=ALL-UNNAMED,否则spring可能会反射调用jdk内部方法,造成访问权限异常被拒
@Component
public class E {
@Lazy
@Autowired
private F1 f1;
@Autowired
private F2 f2;
@Autowired
private ObjectFactory<F3> f3;
@Autowired
private ApplicationContext context;
public F1 getF1() {
return f1;
}
public F2 getF2() {
return f2;
}
public F3 getF3() {
return f3.getObject();
}
public F4 getF4() {
return context.getBean(F4.class);
}
}
对于f2的设置如下:
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Component
public class F2 {
}
本质也是生成一个代理对象,每次使用代理对象的任意方法时,由代理创建新的 F2 对象
剩余两种方法,都是从IOC容器中获取bean对象
出现上面scope失效的原因在于在依赖注入阶段就去获取了对应scope的值,然后后续所有使用到该scope类型的对象都是直接返回一开始注入好的值,而不是每次都向容器去获取一下。
因此想要解决这个办法,就必须要推迟socpe bean的获取,方法有上面说的四种
更多细节可以参考本文
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://cjdhy.blog.csdn.net/article/details/123770070
内容来源于网络,如有侵权,请联系作者删除!