之前的文章讲了FactoryBean本质也是普通的bean同样可以装配到Spring容器中,本质上也装配bean的一种方式。本文主要整理Spring中bean的装配方式以及实现的原理。
图解Spring中bean的实例化流程
下面主要讲解上图中装配的部分
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的依赖注入。