版权声明:本文为博主原创文章,转载请注明出处!!!!!!!!!! https://blog.csdn.net/qq_21434959/article/details/83374488
1. 说明
现在开发大多数使用springboot 配置,自spring4.x后推荐使用@bean 注解式进行容器组件的注入 ,这篇文章简要对比了注解开发和xml开发,并介绍了在注解开发中bean的生命周期。
2. IOC容器创建对比
xml方式
bean.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd ">
<context:property-placeholder location="classpath:person.properties"/>
<!-- 包扫描、只要标注了@Controller、@Service、@Repository,@Component -->
<!-- <context:component-scan base-package="com.atguigu" use-default-filters="false"></context:component-scan> -->
<bean id="person" class="com.uu.anhusky.bean.Person" scope="prototype">
<property name="age" value="1"/>
<property name="name" value="zhangsan"/>
</bean>
<!-- 开启基于注解版的切面功能 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!-- <tx:annotation-driven/> -->
</beans>
@Test
public void test01() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Person person = (Person) applicationContext.getBean("person");
System.out.println(person.toString());
printBeanName(applicationContext);
}
注解bean方式
Config配置文件
@Configuration // 告诉spring这是一个配置类
public class MainConfig {
//给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
@Bean()
public Person person() {
return new Person("张三", 22);
}
}
@Test
public void test01() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
Person person = (Person) applicationContext.getBean("person");
System.out.println(person.toString());
printBeanName(applicationContext);
}
3. 常用注解
3.1 加载bean的注解
1 ). @Configuration 告诉spring这是一个配置类 2 ). @ComponentScan 扫描包配置 value: 扫描包的相对路径 includeFilters: 过滤器配置,使用这个,要就将useDefaultFilters=false,手动操作 @Filter :定义过滤规则 FilterType.ANNOTATION:按照注解 FilterType.ASSIGNABLE_TYPE:按照给定的类型; FilterType.ASPECTJ:使用ASPECTJ表达式 FilterType.REGEX:使用正则指定 FilterType.CUSTOM:使用自定义规则 excludeFilters :同上 3 ). @Bean 加载
配置类示例:
@Configuration // 告诉spring这是一个配置类
@ComponentScan(value = "com.uu.anhusky",
includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class}),
// @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BookService.class}),
// @ComponentScan.Filter(type= FilterType.CUSTOM,classes={MyTypeFilter.class})
},
useDefaultFilters = false
)
public class MainConfig {
//给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
@Bean()
public Person person() {
return new Person("张三", 22);
}
}
自定义Filter 示例:
public class MyTypeFilter implements TypeFilter {
/**
* @param metadataReader
* @param metadataReaderFactory
* @return
* @throws IOException
*/
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
// 获取当前类注解信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
// 获取当前类路径信息
Resource resource = metadataReader.getResource();
// 获取当前类信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
String className = classMetadata.getClassName();
System.out.println("--->" + className);
if (className.contains("er")) {
return true;
}
return false;
}
}
4 ). @Conditional【type】【method】按条件加载 使用方式: 定义实现Condition的类,配置bean
condition实现类如下:
// 根据系统判断是否加载bean
public class LinuxCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 获取系统名称
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
if(property.contains("Linux")){
return true;
}
return false;
}
}
Configuration配置
@Conditional(value = {LinuxCondition.class})
@Configuration
public class MainConfig2 {
/**
* @Conditional({Condition}) : 按照一定的条件进行判断,满足条件给容器中注册bean
* <p>
* 如果系统是linux,给容器中注册("linux")
* 如果是mac系统,给容器中注册("Mac")
*/
@Bean("bill")
@Conditional(MacCondition.class)
public Person person01() {
return new Person("Mac", 62);
}
@Conditional(LinuxCondition.class)
@Bean("linus")
public Person person02() {
return new Person("linus", 48);
}
}
5 ). @Import: 参数是一个数组,可以直接写bean的class, 或者ImportSelector,ImportBeanDefinitionRegistrar的实现类 ImportSelector: 返回需要导入的组件的全类名数组 ImportBeanDefinitionRegistrar:手动注册bean到容器中 注意:使用@Import注册到容器的bean,默认【Id是全类名】
ImportSelector 实现类示例:
public class MyImportSecletor implements ImportSelector {
/**
* 返回值,就是到导入到容器中的组件全类名
*
* @param importingClassMetadata 当前标注@Import注解的类的所有注解信息
* @return
*/
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//方法不要返回null值
return new String[]{"com.uu.anhusky.bean.Green"};
}
ImportBeanDefinitionRegistrar的实现类
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* AnnotationMetadata:当前类的注解信息
* BeanDefinitionRegistry:BeanDefinition注册类;
* 把所有需要添加到容器中的bean;调用
* BeanDefinitionRegistry.registerBeanDefinition手工注册进来
*/
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean definitionRed = registry.containsBeanDefinition("com.uu.anhusky.bean.Red");
if(definitionRed){
//容器中有red ,就加入蓝色
RootBeanDefinition beanDefinition = new RootBeanDefinition(Blue.class);
registry.registerBeanDefinition("blue",beanDefinition);
}
}
}
Configuration配置如下
@Configuration
@Import({Color.class, Red.class, MyImportSecletor.class, MyImportBeanDefinitionRegistrar.class})
public class MainConfig2 {
}
6 ). FactoryBean(工厂Bean) 默认获取到的是工厂bean调用getObject创建的对象 要获取工厂Bean本身,我们需要给id前面加一个& &rainBowFactoryBean
FactoryBean实现类如下:
public class RainBowFactoryBean implements FactoryBean<RainBow> {
public RainBow getObject() throws Exception {
return new RainBow();
}
public Class<?> getObjectType() {
System.out.println("RainBowFactoryBean...getObject...");
return RainBow.class;
}
//是单例?
//true:这个bean是单实例,在容器中保存一份
//false:多实例,每次获取都会创建一个新的bean;
public boolean isSingleton() {
return true;
}
}
Configuration配置如下:
@Configuration
public class MainConfig2 {
@Bean
public RainBowFactoryBean rainBowFactoryBean() {
return new RainBowFactoryBean();
}
}
3.2 生命周期类的注解
生命周期简介
1 ). bean生命周期介绍: bean创建---初始化----销毁的过程 容器管理bean的生命周期; 我们可以自定义初始化和销毁方法;容器在bean进行到当前生命周期的时候来调用我们自定义的初始化和销毁方法 2 ). 构造(对象创建) 单实例:在容器启动的时候创建对象 多实例:在每次获取的时候创建对象 BeanPostProcessor.postProcessBeforeInitialization 初始化: 对象创建完成,并赋值好,调用初始化方法。。。 BeanPostProcessor.postProcessAfterInitialization 3 ). 销毁: 单实例:容器关闭的时候 多实例:容器不会管理这个bean;容器不会调用销毁方法;
注解简介
1 ). @Bean注解中定义
init和destory方法
public class MainConfigOfLifeCycle {
@Bean(initMethod = "init", destroyMethod = "destroy")
@Lazy
public Car car() {
return new Car();
}
}
2 ). Bean 实现
InitializingBean(定义初始化逻辑),DisposableBean(定义销毁逻辑)
public class AirPlane implements InitializingBean, DisposableBean {
public AirPlane(){
System.out.println("AirPlane --> construct");
}
public void destroy() throws Exception {
System.out.println("AirPlane ----> destroy");
}
public void afterPropertiesSet() throws Exception {
System.out.println("AirPlane ----> afterPropertiesSet");
}
}
3 ). bean的方法中加入注解 @PostConstruct
和@PreDestroy
public class Dog {
public Dog() {
System.out.println("Dog constructor...");
}
//对象创建并赋值之后调用
@PostConstruct
public void init() {
System.out.println("Dog....@PostConstruct...");
}
//容器移除对象之前
@PreDestroy
public void detory() {
System.out.println("Dog....@PreDestroy...");
}
}
4 ). BeanPostProcessor
【interface】: bean的后置处理器,在bean初始化前后进行一些处理:
postProcessBeforeInitialization
【method】:在初始化之前
工作postProcessAfterInitialization
【method】:在初始化之后
工作
注:BeanPostProcessor:作用bean赋值,注入其他组件,@Autowired,生命周期注解功能,@Async,xxx BeanPostProcessor;spring底层大量使用此注解
加载顺序如下:
BeanPostProcessor.postProcessBeforeInitialization
初始化:
对象创建完成,并赋值好,调用初始化方法。。。
BeanPostProcessor.postProcessAfterInitialization
销毁:
单实例:容器关闭的时候
多实例:容器不会管理这个bean;容器不会调用销毁方法;
遍历得到容器中所有的BeanPostProcessor;挨个执行beforeInitialization,
一但返回null,跳出for循环,不会执行后面的BeanPostProcessor.postProcessorsBeforeInitialization
BeanPostProcessor原理
populateBean(beanName, mbd, instanceWrapper);给bean进行属性赋值
initializeBean
{
applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
invokeInitMethods(beanName, wrappedBean, mbd);执行自定义初始化
applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
源码截图如下(可以通过打断点,查看堆栈调用信息)
AbstractAutoWireBeanFacotry 中的 doCreateBean 中的方法
初始化方法