Spring注解(生命周期)
对于上面的知识图解,需要一点一点的研究。
首先核心容器:
控制反转 和 依赖注入
创建工程:
maven仓库搜索 spring context :
引入后
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.1.RELEASE</version>
</dependency>
以前是通过 application.xml 进行配置设置
配置类 等同于以前的配置文件:
package com.toov5.bean; public class Person { int age; String name; public Person() { } public Person(int age, String name) { super(); this.age = age; this.name = name; } @Override public String toString() { return "Person [age=" + age + ", name=" + name + "]"; } }
config:
package com.toov5.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.toov5.bean.Person; @Configuration public class config { @Bean //给容器注册一个Bean,类型为返回值的类型。xml中的id是用方法作为id public Person person() { return new Person(1, "toov5"); } }
测试类:
package com.toov5.test; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import com.toov5.bean.Person; import com.toov5.config.config; public class test { public static void main(String[] args) { @SuppressWarnings("resource") ApplicationContext applicationContex = new AnnotationConfigApplicationContext(config.class); // 之前是传递配置文件的位置 // 现在是我们设计的配置类的位置 Person bean = applicationContex.getBean(Person.class); // 通过类型去获取 System.out.println(bean); String[] beanNamesForType = applicationContex.getBeanNamesForType(Person.class); // 根据类型找到bean的名字 for (String name : beanNamesForType) { System.out.println(name); // 返回bean的名字 我们可以在cofig中配置@Bean的名字 } } }
在xml配置的时候,我们使用的是包扫描的方式
<context: component-scan base-package= "com.toov5"> </context: conponent-scan>
包扫描: 只要标注了 @Controller @Service @Repository @Component
用注解搞定: @ComponentScan(value="com.toov5.bean")
可以指定要扫描的包
排除的包,包括可以按照 名字 按照类型等去排除 excludeFilters
指定的包,指定扫描的时候只需要包含哪些组件 includeFilters
还可以通过自定义规则 CUSTOM 通过 implements TypeFilter 重写match方法
metadataReader : 读取到的当前正在扫描的类的信息
metadataReaderFactory: 可以获取到其他任何类
config:
package com.toov5.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.stereotype.Controller; import org.springframework.stereotype.Service; @Configuration //标记@Service 和 @Controller的 @ComponentScan(value="com.toov5",excludeFilters = { @Filter(type=FilterType.ANNOTATION,classes= { Controller.class,Service.class })}) //@ComponentScan(value="com.toov5") public class config { }
controller
package com.toov5.controller; import org.springframework.stereotype.Controller; @Controller public class BookController { }
Service
package com.toov5.service; import org.springframework.stereotype.Service; @Service public class BookService { }
Dao
package com.toov5.dao; import org.springframework.stereotype.Repository; @Repository public class BookDao { }
测试:
package com.toov5.test; import org.junit.jupiter.api.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import com.toov5.config.config; public class test { @SuppressWarnings("resource") @Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(config.class); String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); for(String name : beanDefinitionNames) { System.out.println(name); } } }
config类:
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.stereotype.Controller; import org.springframework.stereotype.Service; @Configuration //标记@Service 和 @Controller的 @ComponentScan(value="com.toov5",excludeFilters = { // @Filter(type=FilterType.ANNOTATION,classes= { Controller.class,Service.class }), @Filter(type=FilterType.CUSTOM, classes= {MyTypeFilter.class}) }) //@ComponentScan(value="com.toov5") public class config { }
自定义的:
package com.toov5.config; import java.io.IOException; import org.springframework.core.io.Resource; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.ClassMetadata; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; public class MyTypeFilter implements org.springframework.core.type.filter.TypeFilter{ public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { //获取当前类注解的信息 AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); //获取当前正在扫描的类的类信息 ClassMetadata classMetadata = metadataReader.getClassMetadata(); //获取当前资源(类路径) Resource resource = metadataReader.getResource(); // 获取到类名 String className = classMetadata.getClassName(); System.out.println("---->"+className); return false; } }
运行结果:
可以继续往下玩儿:
package com.toov5.config; import java.io.IOException; import org.springframework.core.io.Resource; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.ClassMetadata; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; public class MyTypeFilter implements org.springframework.core.type.filter.TypeFilter{ public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { //获取当前类注解的信息 AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); //获取当前正在扫描的类的类信息 ClassMetadata classMetadata = metadataReader.getClassMetadata(); //获取当前资源(类路径) Resource resource = metadataReader.getResource(); // 获取到类名 String className = classMetadata.getClassName(); System.out.println("---->"+className); //加入校验逻辑 if (className.contains("er")) { return true; //匹配成功 } return false; } }
@Scope 设置作用域
config:
package com.toov5.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import com.toov5.Bean.Person; @Configuration //标记@Service 和 @Controller的 @ComponentScan(value="com.toov5") public class config { @Bean public Person person() { return new Person(12,"toov5"); } }
Test:
package com.toov5.test; import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import com.toov5.config.config; public class test { @SuppressWarnings("resource") @Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(config.class); String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); for(String name : beanDefinitionNames) { System.out.println(name); } //comfig 的@Bean 默认是单例的 Object p1 = applicationContext.getBean("person"); Object p2 = applicationContext.getBean("person"); System.out.println( p1==p2); } }
prototype: 多实例的
singleton: 单实例的(默认的)
request: 同一次请求创建一个实例
session: 同一个session创建一个实例
@Configuration //标记@Service 和 @Controller的 @ComponentScan(value="com.toov5") public class config { @Bean @Scope("prototype") public Person person() { return new Person(12,"toov5"); } }
单例情况下:
public class config { @Bean(name="people") @Scope() public Person person() { System.out.println("容器创建...."); return new Person(12,"toov5"); } }
测试类启动ioc容器:
public class test { @SuppressWarnings("resource") @Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(config.class); } }
单例模式,ioc容器启动后会调用方法创建对象放到IOC容器总 以后每次获取就是直接从容器中获取. 类似于从map获取 map.get()
如果是多例模式:
ioc容器启动是不会创建对象的!
只有调用时候,并且调用一次,获取一次 调用方法创建对象
config:
@Configuration //标记@Service 和 @Controller的 @ComponentScan(value="com.toov5") public class config { @Bean(name="people") @Scope("prototype") public Person person() { System.out.println("容器创建...."); return new Person(12,"toov5"); } }
public class test { @SuppressWarnings("resource") @Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(config.class); System.out.println("IOC容器创建完成"); Object p1 = applicationContext.getBean("people"); System.out.println(p1); Object p2 = applicationContext.getBean("people"); System.out.println(p2); } }
@Lazy
懒加载: 单实例bean,默认在容器启动的时候创建对象,懒加载容器启动时候先不创建。第一次在使用获取Bean时候才创建对象,并且进行初始化。
config:
@Configuration //标记@Service 和 @Controller的 @ComponentScan(value="com.toov5") public class config { @Bean(name="people") @Lazy public Person person() { System.out.println("容器创建...."); return new Person(12,"toov5"); } }
测试:
public class test { @SuppressWarnings("resource") @Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(config.class); System.out.println("IOC容器创建完成"); Object p1 = applicationContext.getBean("people"); System.out.println(p1); Object p2 = applicationContext.getBean("people"); System.out.println(p2); } }
第一次获取时候加载。只创建一次。
@Condition 按照条件注册bean
也是springboot 底层大量使用的。 按照一定条件进行判断,满足条件给容器注册Bean
原来情况:
@Configuration //标记@Service 和 @Controller的 @ComponentScan(value="com.toov5") public class config { @Bean(name="Jack") public Person person1() { System.out.println("容器创建...."); return new Person(50,"马云"); } @Bean(name="Linux") public Person person2() { System.out.println("容器创建...."); return new Person(51,"Linux"); } }
测试:
import com.toov5.Bean.Person; import com.toov5.config.config; public class test { @SuppressWarnings("resource") @Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(config.class); String[] beanNamesForType = applicationContext.getBeanNamesForType(Person.class); for(String name : beanNamesForType) { System.out.println(name); } } }
打印:
要求:
如果操作系统是win10 被容器注册 linux
如果是linux 给容器注册 马云
代码:
public class test { @SuppressWarnings("resource") @Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(config.class); ConfigurableEnvironment environment = applicationContext.getEnvironment(); String osName = environment.getProperty("os.name"); System.out.println(osName); } }
打印:
动态获取环境变量的值
context的巧用:
// 能获取到ioc使用的beanfactory ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); //获取到类加载器 ClassLoader classLoader = context.getClassLoader(); //获取到bean定义的注册类 能创建 获取 查询 bean 的定义 BeanDefinitionRegistry registry = context.getRegistry();
config:
@Configuration public class config { @Conditional({WindowsCondition.class}) @Bean(name="Windows") public Person person1() { System.out.println("容器创建...."); return new Person(50,"Windows"); } @Conditional({LinuxCondition.class}) @Bean(name="Linux") public Person person2() { System.out.println("容器创建...."); return new Person(51,"Linux"); } }
条件:
public class LinuxCondition implements Condition{ // ConditionContext 判断条件能使用的上下文 // AnnotatedTypeMetadata 注释信息 public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { //判断是否linux 系统 //运行时环境信息 Environment environment = context.getEnvironment(); String property = environment.getProperty("os.name"); if (property.contains("Linux")) { return true; } return false; } }
条件:
public class WindowsCondition implements Condition { public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Environment environment = context.getEnvironment(); String property = environment.getProperty("os.name"); if (property.contains("Windows")) { return true; } return false; } }
测试:
public class test { @SuppressWarnings("resource") @Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(config.class); String[] beanNamesForType = applicationContext.getBeanNamesForType(Person.class); for(String name : beanNamesForType) { System.out.println(name); } } }
可以做更多的判断,更多的条件
public class WindowsCondition implements Condition { public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Environment environment = context.getEnvironment(); String property = environment.getProperty("os.name"); if (property.contains("Windows")) { return true; } BeanDefinitionRegistry registry = context.getRegistry(); boolean result = registry.containsBeanDefinition("Windows"); //容器中是否包含Windows // registry 判断没有 可以自己注册一个 非常多的判断条件 也可以给容器中注册bean return false; } }
@Condition还可以标注在类上面, 满足当前条件 这个类 中配置的所有Bean注册才能生效
@Configuration @Conditional({WindowsCondition.class}) public class config { @Bean(name="Windows") public Person person1() { System.out.println("容器创建...."); return new Person(50,"Windows"); } @Bean(name="Linux") public Person person2() { System.out.println("容器创建...."); return new Person(51,"Linux"); } }
打印:
给容器中注册组件:
1、 包+组件标注-@Controller @Service @Repository @Component
如果导入第三方包呢?
2、 别人写类: @Bean 导入的第三方包里面的组件
3、 @Import 快速给容器中导入一个组件 id默认全类名
@ImportSelector 返回需要导入的组件的全类名数组
4、Spring提供的FactoryBean
测试:
public class test { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(config.class); private void printBeans(AnnotationConfigApplicationContext applicationContext) { String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); for(String name : beanDefinitionNames) { System.out.println(name); } } @Test public void test01(){ printBeans(applicationContext); } }
查看Ioc中的bean组件:
除了ioc自己的,还有那几个正常的
这么玩儿:
package com.toov5.Bean; public class Animal { String color ; String name; public Animal() { // TODO Auto-generated constructor stub } public Animal(String color, String name) { super(); this.color = color; this.name = name; } }
config:
public class test { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(config.class); private void printBeans(AnnotationConfigApplicationContext applicationContext) { String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); for(String name : beanDefinitionNames) { System.out.println(name); } } @Test public void test01(){ printBeans(applicationContext); } }
结果:
如果导入多个 ,可以用数组形式@Import({A.class, B.class})
id 默认是全类名
config:
@Configuration @Conditional({WindowsCondition.class}) @Import( {Animal.class , MyImportSelector.class}) public class config { @Bean(name="Windows") public Person person1() { System.out.println("容器创建...."); return new Person(50,"Windows"); } @Bean(name="Linux") public Person person2() { System.out.println("容器创建...."); return new Person(51,"Linux"); } }
类:
//自定义逻辑 返回需要导入的组件 public class MyImportSelector implements ImportSelector{ //返回值,就是要导入到容器中的组件全类名 // AnnotationMetadata: 当前标注@Import注解的类的所有注解信息 public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[] {"com.toov5.Bean.Fruit"} ; } }
结果:
从容器中获取之:
public class test { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(config.class); private void printBeans(AnnotationConfigApplicationContext applicationContext) { String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); for(String name : beanDefinitionNames) { System.out.println(name); } } @Test public void test01(){ printBeans(applicationContext); Object bean = applicationContext.getBean(Fruit.class); System.out.println(bean); } }
结果:
业务判断:
public class MyImportBeanDefinitionRegister implements ImportBeanDefinitionRegistrar{ //AnnotationMetadata 当前类的注解信息 和 其他信息 // BeanDefinitionRegistry: BeanDefinition注册类 //可以调用BeanDefinitionRegistry方法,自定义来注册Bean组件到容器中 public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //写逻辑 Boolean result =registry.containsBeanDefinition("com.toov5.Bean.Fruit"); //容器中是否有电脑 if (result) { //判断逻辑 //指定bean的定义信息 包括scope等等 RootBeanDefinition beanDefinition = new RootBeanDefinition(Pen.class); //向容器中注册bean的名字 registry.registerBeanDefinition("pen", beanDefinition); } } }
配置:
@Configuration @Conditional({WindowsCondition.class}) @Import( {Animal.class , MyImportSelector.class, MyImportBeanDefinitionRegister.class}) public class config { @Bean(name="Windows") public Person person1() { System.out.println("容器创建...."); return new Person(50,"Windows"); } @Bean(name="Linux") public Person person2() { System.out.println("容器创建...."); return new Person(51,"Linux"); } }
测试:
public class test { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(config.class); private void printBeans(AnnotationConfigApplicationContext applicationContext) { String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); for(String name : beanDefinitionNames) { System.out.println(name); } } @Test public void test01(){ printBeans(applicationContext); Object bean = applicationContext.getBean(Fruit.class); System.out.println(bean); } }
FactoryBean 工厂Bean 区别普通的Bean,导入到容器中,容器会调用无参构造器,然后创建一个对象注册到的容器中
工厂Bean 是个工厂, 是个接口
容器调用:
返回对象给容器
还有两个方法:
创建一个 Pen 的工厂Beran
import org.springframework.beans.factory.FactoryBean; import com.toov5.Bean.Pen; //创建一个spring 定义的工厂bean public class PenFactoryBean implements FactoryBean<Pen>{ //返回的对象会添加到容器中 public Pen getObject() throws Exception { // TODO Auto-generated method stub System.out.println("PenFactoryBean---getBean---"); return new Pen(); } public Class<?> getObjectType() { // TODO Auto-generated method stub return Pen.class; } //如果是单例(true) 则在容器中只会保存一份 public boolean isSingleton() { // TODO Auto-generated method stub return false; } //如果是false 则每次获取都会创建新的 即调用 getObject 方法 }
然后将工厂bean 加入到容器
@Configuration @Import( {Animal.class , MyImportSelector.class, MyImportBeanDefinitionRegister.class}) public class config { @Bean public PenFactoryBean penFactoryBean() { return new PenFactoryBean(); } }
工厂Bean 获取的是调用getObject创建的对象
public class test { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(config.class); private void printBeans(AnnotationConfigApplicationContext applicationContext) { String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); for(String name : beanDefinitionNames) { System.out.println(name); } } @Test public void test01(){ printBeans(applicationContext); Object bean1 = applicationContext.getBean("penFactoryBean"); Object bean2 = applicationContext.getBean("penFactoryBean"); System.out.println(bean1==bean2); Object bean3 = applicationContext.getBean("&penFactoryBean"); System.out.println(bean3); //加&获取到工厂bean调用getObject创建的对象 //要获取工厂Bean本身,需要id前面加& 否则就是工厂bean的本身了 } }
打印:
在于其他第三方框架整合时候 FactoryBean用的很多
方法一: Bean生命周期,Bean创建到初始化,到销毁的过程。由容器管理。
初始化 和 销毁的方法可以自定义
xml配置中 通过 init-method destory-method 配置方法指定
方法二: 注解中:
构造(对象创建)
单例: 在容器启动的时候创建
多例:在每次获取的时候创建
对于生命周期的方法声明,用注解:
Bean: @Bean(initMethod="init", destroyMethod="destory")
public class Car { public Car() { System.out.println("无参构造初始化"); } public void init() { System.out.println("CarBean---init"); } public void destory() { System.out.println("CarBean---destory"); } }
config:
@Configuration public class BeanLife { @Bean(initMethod="init", destroyMethod="destory") public Car car() { return new Car(); } }
测试方法:
@Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanLife.class); System.out.println("容器创建完成"); }
结果:
关闭容器:
@Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanLife.class); System.out.println("容器创建完成"); //关闭容器 applicationContext.close(); }
在数据源的配置过程中 有很多属性的配置需要用到 销毁时候 关闭连接等等
总结:
初始化, 对象创建完成,并赋值,调用初始化方法
销毁, 容器关闭时候。 但是如果是多例情况,创建对象是获取时候才会执行。销毁时候,是不进行的。容器不管的。
方法三: Spring 还提供了两个接口: Bean通过实现InitializingBean (定义初始化逻辑)
DisposableBean (定义销毁逻辑)
Bean:
@Component public class Cat implements InitializingBean, DisposableBean{ public Cat() { System.out.println("cat 构造函数..."); } //销毁方法 public void destroy() throws Exception { System.out.println("cat destory"); } //初始化方法 public void afterPropertiesSet() throws Exception { System.out.println("cat afterPropertiesSet"); } }
配置:
@ComponentScan("com.toov5.Bean") @Configuration public class BeanLife { @Bean(initMethod="init", destroyMethod="destory") public Car car() { return new Car(); } }
测试:
@Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanLife.class); System.out.println("容器创建完成"); //关闭容器 applicationContext.close(); }
还可以使用: JSP250规范的 @PostConstruct 在bean创建完成 并且属性赋值完成执行初始化
@Predestory 当bean从容器中销毁Bean之前 通知清理工作
Bean:
@Component public class Cat implements InitializingBean, DisposableBean{ public Cat() { System.out.println("cat 构造函数..."); } //销毁方法 public void destroy() throws Exception { System.out.println("cat destory"); } //初始化方法 public void afterPropertiesSet() throws Exception { System.out.println("cat afterPropertiesSet"); } }
config:
@ComponentScan("com.toov5.Bean") @Configuration public class BeanLife { @Bean(initMethod="init", destroyMethod="destory") public Car car() { return new Car(); } }
test:
@Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanLife.class); System.out.println("容器创建完成"); //关闭容器 applicationContext.close(); }
结果:
还可以使用interface BeanPostProcessor: 在bean初始化前后进行一些处理工作
postProcessBeforeInitialization( ) 初始化之前工作
postProcessAfterInitialization( ) 初始化之后工作
后置处理器: 初始化前后进行处理工作
每个bean在初始化init之前调用 postProcessBeforeInitialization
初始化init 之后调用 postProcessAfterInitialization
对于Bean 的生命周期:
构造器初始化, 初始化(可以自定义指定),初始化前后可以使用(BeanPostProcessor)进行拦截。 销毁(自定义执行)
关于BeanPostProcessor 在Spring 底层的使用
@Component public class Dog implements ApplicationContextAware{ private ApplicationContext applicationContext; public Dog() { System.out.println("dog 构造函数初始化"); } @PostConstruct public void init() { System.out.println("对象创建 并且赋值之后 调用了 @PostConstruct"); } @PreDestroy public void destory() { System.out.println("容器关闭 移除bean,调用了 @PreDestroy"); } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { //dog对象被创建后 ioc容器会被传递进来 this.applicationContext=applicationContext; } }
BeanValidationPostProcessor
可以做数据校验, 当对象创建完 给Bean赋值以后。做数据校验
还有
InitDestoryAnnottionBeanPostProcessor
AutowiredAnnotationBeanPostProcessor 对象创建完了之后 处理所有 @Autowried标注的属性
小结: BeanPostProcessor 接口 : bean赋值,注入其他组件,@Autowried,生命周期注解,@Async 等等