Learn more about spring in small details
通过小细节了解Spring
- @Configuration注解与其proxyBeanMethods属性的理解
Spring启动的时候扫描所能扫描路径下的@component注解(@Configuration是包含@component注解的),然后将其bean对应的bend定义类beanDefinition放到容器中(BeanDefinition是对bean的描述,里边存有bean的名称,Class等基本信息);
在获取所有的beanDefenition执行器后置处理器PostProcess,其中包含的后置处理器ConfigurationClassPostProcessor,而对@Configuration注解的,会对这个类进行CGLIB代码,生成一个代理的类,并且把这个类设置到BeanDefenition的Class属性中。当需要拿到这个bean的实例的时候,会从这个class属性中拿到的Class对象进行反射,那么最终反射出来的是代理增强后的类
- proxyBeanMethods属性的理解
roxyBeanMethods默认为true,表示cglib会为@Configuration生成一个代理类,而如果我们在项目中,声明创建多个相同的bean时,并且声明的bean存在引用关系的话(见下面代码),则会只创建一次,因为另一次会从IOC容器中直接获取;
如果将roxyBeanMethods设置为false,这时我们创建多个相同的bean,并且bean存在引用的关系,这时候不会使用CGLIB产生代理,而是会创建多个bean
@Configuration(proxyBeanMethods = false)
public class SimpleConfig {
@org.springframework.context.annotation.Bean
public Bean bean1() {
final Bean bean1 = new Bean("bean1");
System.out.println("bean1:" + bean2.getClass());
bean2();
return bean1;
}
@org.springframework.context.annotation.Bean
public Bean bean2() {
final Bean bean2 = new Bean("bean2");
System.out.println("bean2:" + bean2.getClass());
return bean2;
}
@AllArgsConstructor
public class Bean {
private String name;
}
}
- 分析
- proxyBeanMethods = true时:
> 输出bean2:class com.pkk.spring.cloud.gateway.runner.config.SimpleConfig$Bean【一】次
> 因为另一次会从IOC容器中直接获取,不会再创建了;而且会产生且只会产生一个代理类
- proxyBeanMethods = false时:
> 输出bean2:class com.pkk.spring.cloud.gateway.runner.config.SimpleConfig$Bean【两】次
> 此时会创建两次bean2,因为proxyBeanMethods为false,认为其是不进行代理,每次创建都是一个新的bean
- 扩展【如果此时bean1不调用bean2,也就是删除调用方法 bean2();】
- 分析
- proxyBeanMethods = true时:
> 输出bean1和bean2:class com.pkk.spring.cloud.gateway.runner.config.SimpleConfig$Bean各【一】次
> 走CGLIB代理,创建两个bean,一个bean名称为bean1的Bean对象,一个bean名称为bean2的Bean对象;
- proxyBeanMethods = false时:
> 输出bean1和bean2:class com.pkk.spring.cloud.gateway.runner.config.SimpleConfig$Bean各【一】次
> 不走CGLIB代理,创建两个bean,一个bean名称为bean1的Bean对象,一个bean名称为bean2的Bean对象;
- 总结
proxyBeanMethods默认为true,表示cglib会为@Configuration生成一个代理类,为false时,则不会产生代理类,
当多个方法调用获取同一个@Bean注解的方法时,proxyBeanMethods为true则会只创建一次,因为走IOC容器获取,
当为false时,则会创建多次不同的对象;这样当我们确定我们创建的bean没有方法调用给的时候,我们可以设置proxyBeanMethods为false,让其不走代理,以提高创建Bean效率;
proxyBeanMethods为false之后不会被代理了,所以配置类和方法也可以使用 final 修饰了;
- Spring 内置的扫描器的理解记录
使用XML 配置 Spring 组件扫描使用的是 component-scan 标签,其底层使用 ClassPathBeanDefinitionScanner 这个类完成扫描工作的。
到了 SpringBoot 这里,可以使用注解 @ComponentScan 注解配合 @Configuration、@Service、@Repository 和 @Controller 等注解使用,底层则使用了 ComponentScanAnnotationParser 解析器完成解析工作。
- 2.1 实现原理
ComponentScanAnnotationParser 解析器内部使用了 ClassPathBeanDefinitionScanner 扫描器.
ClassPathBeanDefinitionScanner扫描器内部的处理过程整理如下:
遍历 basePackages,找出这个包下的所有的 class。找出之后封装成 Resource 接口集合,这个 Resource 接口是 Spring 对资源的封装,有 FileSystemResource、ClassPathResource、UrlResource 等实现。遍历找到的 Resource 集合,通过 includeFilters 和 excludeFilters 判断是否解析。这里的 includeFilters 和 excludeFilters 是TypeFilter 接口类型的集合,是 ClassPathBeanDefinitionScanner 内部的属性。
TypeFilter 接口是一个用于判断类型是否满足要求的类型过滤器。excludeFilters 中只要有一个TypeFilter满足条件,这个Resource就会被过滤。includeFilters 中只要有一个TypeFilter满足条件,这个Resource就会被保留,最后把保留的 Resource 封装成 ScannedGenericBeanDefinition 添加到 BeanDefinition 结果集中。
TypeFilter接口的定义:
public interface TypeFilter {
boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
throws IOException;
}
- TypeFilter 常用实现:
AnnotationTypeFilter:类是否有注解修饰
RegexPatternTypeFilter:类名是否满足正则表达式。
ClassPathBeanDefinitionScanner 继承 ClassPathScanningCandidateComponentProvider类
ClassPathScanningCandidateComponentProvider 内部的构造函数提供了一个 useDefaultFilters 参数,这个参数表示是否使用默认的TypeFilter,如果设置为true,会添加默认的TypeFilter:
public ClassPathScanningCandidateComponentProvider(boolean useDefaultFilters) {
this(useDefaultFilters, new StandardEnvironment());
}
// 默认 TypeFilter
protected void registerDefaultFilters() {
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
try {
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
}catch (ClassNotFoundException ex) {
// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
}
try {
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
}catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
可以看到这里 includeFilters 加上了 AnnotationTypeFilter,并且对应的注解是 @Component。@Service、@Controller或@Repository。(这几个注解内部都是被@Component注解所修饰)。
文章引用
zombres https://blog.csdn.net/zombres/article/details/107229854