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

图解Spring中bean的实例化流程

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

下面主要讲解上图中装配的部分

1、xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="user" class="com.mandy.bean.User"/>

</beans>

容器加载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的依赖注入。