Learn more about spring in small details

Learn more about spring in small details

通过小细节了解Spring

  1. @Configuration注解与其proxyBeanMethods属性的理解
  2. Spring 内置的扫描器的理解记录

  1. @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 修饰了;

  1. 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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值