【源码Spring系列】——bean的装配方式

        之前的文章讲了FactoryBean本质也是普通的bean同样可以装配到Spring容器中,本质上也装配bean的一种方式。本文主要整理Spring中bean的装配方式以及实现的原理。

图解Spring中bean的实例化流程

【源码Spring系列】——bean的装配方式_第1张图片下面主要讲解上图中装配的部分

1、xml




    

容器加载xml

ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");

2、@ImportResource

@ImportResource("spring.xml")
public class AppConfig {

}

容器加载

 ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

上述两种方式本质都xml的解析方式,此部分原理详情见《【源码Spring系列】——IOC设计理念以及自动装配原理》

3、实现FactoryBean

public class MyFactroyBean  implements FactoryBean {
   @Override
   public Object getObject() throws Exception {
      return new User();
   }

   @Override
   public Class getObjectType() {
      return User.class;
   }
}

原理是Spring中存在两个关于FactoryBean的Map

factoryBeanObjectCache 通过getObject 获取bean

allBeanNamesByType  通过getObjectType 获取bean

此部分原理详情见《【Spring源码系列】——彻底搞懂FactoryBean》

4、@Component+@ComponentScan

@ComponentScan默认扫描:@Component,@Repository,@Service,@Controller

@ComponentScan("com.mandy")
public class AppConfig {
}

//容器加载
 ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

@ComponentScan注解扩展用法

有三种过滤方式 注解,类型,自定义过滤规则

排除用法 excludeFilters

@ComponentScan(basePackages = "com.mandy",excludeFilters = {
    @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Service.class}),
    @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = {User.class})
})

包含用法 includeFilters

@ComponentScan(basePackages = "com.mandy",includeFilters = {
    @ComponentScan.Filter(type = FilterType.CUSTOM,value = {CustomTypeFilter.class})
},useDefaultFilters = false)

FilterType.CUSTOM在定义过滤规则

public class CustomTypeFilter implements TypeFilter {
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        if (classMetadata.getClassName().contains("Service")) {
            return true;
        }
        return false;
    }
}

5、@Bean+ @Configuration

@Configuration
public class AppConfig {

    @Bean
    public User user(){
        return new User();
    }

    @Bean
    public UserService userService(){
        // 调用其他@Bean方法
        return new UserService(user());
    }
}

@Configuration的作用:

1.表明当前类是一个配置类,是方法bean的源

2.将@Configuration配置的AppConfig的BeanDefinitioin属性赋值为full类型,保证AppConfig类型可以转变为cglib类型

3.将@Configuration配置的AppConfig由普通类型转变为cglib代理类型,最后会生成cglib代理对象,通过代理对象的方法拦截器,可以解决AppConfig内部方法bean之间发生依赖调用的时候从容器中去获取,避免了多例的出现。

使用场景

零xml配置,推荐使用,spring boot自动配置中大量使用@Bean xxxAutoConfiguration

6、@Import

@Import(value = MyImportBeanDefinitionRegistrar.class)
public class AppConfig {
}

ImportSelector 

其主要作用是收集需要导入的配置类,如果该接口的实现类同时实现EnvironmentAware, BeanFactoryAware ,BeanClassLoaderAware或者ResourceLoaderAware,那么在调用其selectImports方法之前先调用上述接口中对应的方法,如果需要在所有的@Configuration处理完在导入时可以实现DeferredImportSelector接口。

//实现 ImportSelector接口
public class MyImportSelector implements ImportSelector {
   @Override
   public String[] selectImports(AnnotationMetadata importingClassMetadata) {
      return new String[]{Fox.class.getName()}; 
   }
}

ImportBeanDefinitionRegistrar

因为它含有BeanDefinitionRegistry注册器,所以利用它可以在Spring定义BeanDefinition,达到装配bean的作用

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
   @Override
   public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {
      //创建BeanDefinition
      RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Fox.class);
      // 注册到容器
      registry.registerBeanDefinition("fox",rootBeanDefinition);
   }
}

使用场景

中间件底层大量使用,和Spring集成的核心扩展技术

  • mybatis-spring.jar @MapperScan

  • spring boot @SpringBootApplication XXXAutoConfiguration

  • spring cloud @EnableEurekaServer @EnableCircuitBreaker @EnableFeignClients @EnableZuulProxy

7、@Conditional

@Conditional是Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件给容器注册bean。

@Configuration
public class AppConfig {
  @Bean
  public Cat cat(){
     return new Cat();
  }
    
  @Bean
  @Conditional(value = MyConditional.class)
  public Fox fox(){
     return new Fox()
  }
}

public class MyConditional implements Condition {
   @Override
   public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
      if(context.getBeanFactory().containsBean("cat"))
         return true;
      return false;
   }
}

应用场景:

Spring boot 自动配置实现核心技术之一: 条件装配 ,Spring Boot进行了扩展

  • @ConditionalOnWebApplication:当前项目是 Web项目的条件下

  • @ConditionalOnBean:当容器里有指定 Bean 的条件下

  • @ConditionalOnMissingBean:当容器里没有指定 Bean 的情况下

  • @ConditionalOnClass:当类路径下有指定类的条件下

  • @ConditionalOnMissingClass:当类路径下没有指定类的条件下

  • @ConditionalOnProperty:指定的属性是否有指定的值

总结

      本文主要整理Spring中bean的装配配置,其中前两种方式利用xml解析的方式完成bean的加载稍有不同。本文只讲解了bean的装配方式,后面的博客继续更新图中bean的依赖注入。

你可能感兴趣的:(【源码Spring系列】)