Spring注解驱动

Spring注解驱动

声明:本文是尚硅谷Spring注解驱动课程所作的笔记

尚硅谷Spring注解驱动教程(雷丰阳源码级讲解)哔哩哔哩bilibili

 

1、注册组件

(1)配置类

@Configuration注解来标注该类是一个Spring的配置类。

@Bean注解将Person类注入到Spring的IOC容器中。

1 @Configuration
2 public class MainConfig {
3 4     //给容器中注册一个Bean;类型为返回值的类型,id默认为用方法名
5     @Bean
6     public Person person(){
7         return new Person("zs",18);
8     }
9 }
1 @Test
2 public void test(){
3     AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
4     Person person = applicationContext.getBean(Person.class);
5     System.out.println(person);
6 }

 

(2)@ComponentScan

@ComponentScan开启组件扫描,使用Spring的包扫描功能对项目中的包进行扫描,凡是在指定的包或其子包中的类上标注了@Repository、@Service、@Controller、@Component注解的类都会被扫描到,并将这个类注入到Spring容器中。

  ①value指定要扫描的包名

1 @Configuration
2 @ComponentScan(value = "com.my")
3 public class MainConfig {}

 

  ②excludeFilters指定扫描的时候按照什么规则排除哪些组件

1 //excludeFilters = Filter[]
2 @ComponentScan(value = "com.my",excludeFilters = {
3         @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, Service.class})
4 })

 

  ③includeFilters指定扫描的时候只需要包含哪些组件

      必须将使用默认扫描方式置为false,否则不起效果。

1 //includeFilters = Filter[]
2 @ComponentScan(value = "com.my",includeFilters = {
3         @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, Service.class})
4 },useDefaultFilters = false)

 

  ④@ComponentScans

     一个类可以定义多个@ComponentScan,因此可以将这些@ComponentScan放进一个@ComponentScans注解中。

1 @ComponentScans(value = {
2         @ComponentScan(value = "com.my",includeFilters = {
3                 @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
4         },useDefaultFilters = false)
5 })

 

(3)过滤规则

  FilterType.ANNOTATION:按照注解

  FilterType.ASSIGNABLE_TYPE:按照给定的类型

  FilterType.ASPECTJ:使用ASPECTJ表达式

  FilterType.REGEX:使用正则表达式

  FilterType.CUSTOM:使用自定义规则

 1 public class MyTypeFilter implements TypeFilter {
 2  3     //MetadataReader:读取到的当前正在扫描的类的信息
 4     //MetadataReaderFactory:可以获取到其他任何类的信息
 5  6     @Override
 7     public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
 8         //获取当前类注解的信息
 9         AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
10         //获取当前正在扫描的类的类信息
11         ClassMetadata classMetadata = metadataReader.getClassMetadata();
12         //获取当前类资源(类的路径)
13         Resource resource = metadataReader.getResource();
14         String className = classMetadata.getClassName();
15         System.out.println("类名:" + className);
16         if(className.contains("er")){
17             return true;
18         }
19         return false;
20     }
21 }
1 @Configuration
2 @ComponentScan(value = "com.my",includeFilters = {
3     @ComponentScan.Filter(type = FilterType.CUSTOM,classes = {MyTypeFilter.class})
4 },useDefaultFilters = false)

 

(4)@Scope

设置组件作用域。

1 @Configuration
2 public class MainConfig2 {
3     @Scope("prototype")
4     @Bean
5     public Person person(){
6         return new Person("zs",18);
7     }
8 }
1 @Test
2 public void test03(){
3     AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
4     Person bean = applicationContext.getBean(Person.class);
5     Person bean2 = applicationContext.getBean(Person.class);
6     System.out.println(bean == bean2);
7 }
8 //Scope值为默认时,即单实例时,结果为true
9 //Scope值为prototype时,即多实例时,结果为false

 

(5)@Lazy

懒加载只对单实例bean有用。

单实例bean默认是在容器启动的时候创建对象。懒加载使得容器启动时不创建对象,在第一次使用(获取)Bean时创建对象,并初始化。

1 @Lazy
2 @Bean
3 public Person person(){
4     System.out.println("给容器添加Person...");
5     return new Person("zs",18);
6 }

 

(6)@Conditional

@Conditional注解可以按照一定的条件进行判断,满足条件向容器中注册bean,不满足条件就不向容器中注册bean。

@Conditional注解不仅可以添加到类上,也可以添加到方法上。

  ①编写条件类

 1 public class LinuxCondition implements Condition {
 2 
 3     //ConditionContext:判断条件能使用的上下文(环境)
 4     //AnnotatedTypeMetadata:当前标注了@Conditional注解的注释信息
 5 
 6     @Override
 7     public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
 8         // 判断操作系统是否是Linux系统
 9 
10         // 1. 获取到bean的创建工厂(能获取到IOC容器使用到的BeanFactory,它就是创建对象以及进行装配的工厂)
11         ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
12         // 2. 获取到类加载器
13         ClassLoader classLoader = conditionContext.getClassLoader();
14         // 3. 获取当前环境信息,它里面就封装了我们这个当前运行时的一些信息,包括环境变量,以及包括虚拟机的一些变量
15         Environment environment = conditionContext.getEnvironment();
16         // 4. 获取到bean定义的注册类
17         BeanDefinitionRegistry registry = conditionContext.getRegistry();
18 
19         String property = environment.getProperty("os.name");
20         if (property.contains("linux")) {
21             return true;
22         }
23 
24         return false;
25     }
26 }
 1 public class WindowsCondition implements Condition {
 2 
 3     @Override
 4     public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
 5         Environment environment = conditionContext.getEnvironment();
 6         String property = environment.getProperty("os.name");
 7         if (property.contains("Windows")) {
 8             return true;
 9         }
10         return false;
11     }
12 }

  ②在配置类中添加@Conditional注解

 1 @Conditional({WindowsCondition.class})
 2 @Bean("window")
 3 public Person person02(){
 4     return new Person("Windows",25);
 5 }
 6 @Conditional({LinuxCondition.class})
 7 @Bean("linux")
 8 public Person person03(){
 9     return new Person("Linux",55);
10 }

 

  ③测试

1 @Test
2 public void test(){
3     AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
4     String[] definitionNames = applicationContext.getBeanDefinitionNames();
5     for (String name : definitionNames) {
6         System.out.println(name);
7     }
8 }

 

 

(7)@Import

向Spring容器中注册bean通常有以下几种方式:

  • 包扫描+给组件标注注解(@Controller、@Servcie、@Repository、@Component),但这种方式比较有局限性,局限于我们自己写的类

  • @Bean注解,通常用于导入第三方包中的组件

  • @Import注解,快速向Spring容器中导入一个组件

@Import注解提供了@Bean注解的功能,@Import注解只允许放到类上面,不允许放到方法上。

简单示例:

1 public class Color {}
1 @Configuration
2 @Import(Color.class)
3 public class MainConfig2 {}

(8)@Import—使用ImportSelector

通过实现ImportSelector接口的类,可以实现批量导入的功能。返回需要导入的组件的全类名数组。

 1 //自定义逻辑,返回需要导入的组件
 2 public class MyImportSelector implements ImportSelector {
 3 
 4     //返回值就是要导入到容器中的组件全类名
 5     //AnnotationMetadata:当前标注@Import注解的类的所有注解信息
 6     @Override
 7     public String[] selectImports(AnnotationMetadata annotationMetadata) {
 8         return new String[]{"com.my.bean.Blue","com.my.bean.Green"};
 9     }
10 }
1 @Configuration
2 @Import({Color.class, MyImportSelector.class})
3 public class MainConfig2 {}

(9)@Import—使用ImportBeanDefinitionRegistrar

ImportBeanDefinitionRegistrar本质上是一个接口。在ImportBeanDefinitionRegistrar接口中,有一个registerBeanDefinitions()方法,通过该方法,我们可以向Spring容器中注册bean实例。

 1 public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
 2 
 3     //AnnotationMetadata:当前类的注解信息
 4     //BeanDefinitionRegistry:BeanDefinition注册类
 5     //把所有需要添加到容器中的bean;调用BeanDefinitionRegistry.registerBeanDefinition()手工注册进来
 6 
 7     @Override
 8     public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
 9         boolean definition = registry.containsBeanDefinition("com.my.bean.Green");
10         boolean definition2 = registry.containsBeanDefinition("com.my.bean.Blue");
11         if(definition && definition2){
12             //指定Bean的定义信息
13             RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Color.class);
14             //注册Bean,指定bean名
15             registry.registerBeanDefinition("color",rootBeanDefinition);
16         }
17     }
18 }
1 @Configuration
2 @Import({MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
3 public class MainConfig2 {}

(10)FactoryBean

用户可以通过实现FactoryBean接口定制实例化bean的逻辑。

 1 //创建一个Spring定义的FactoryBean
 2 public class ColorFactoryBean implements FactoryBean<Color> {
 3     //返回一个Color对象,这个对象会添加到容器中
 4     @Override
 5     public Color getObject() throws Exception {
 6         return new Color();
 7     }
 8 
 9     @Override
10     public Class<?> getObjectType() {
11         return Color.class;
12     }
13     //是否为单例
14     @Override
15     public boolean isSingleton() {
16         return true;
17     }
18 }
1 //配置类
2 @Bean
3 public ColorFactoryBean colorFactoryBean(){
4     return new ColorFactoryBean();
5 }
 1 @Test
 2 public void test05(){
 3     AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
 4     Object bean = applicationContext.getBean("colorFactoryBean");
 5     Object bean2 = applicationContext.getBean("&colorFactoryBean");
 6     System.out.println("bean类型:" + bean.getClass());
 7     System.out.println("bean2类型:" + bean2.getClass());
 8 }
 9 //输出结果:
10 //bean类型:class com.my.bean.Color
11 //bean2类型:class com.my.bean.ColorFactoryBean

注意:

当配置文件中标签的class属性配置的实现类是FactoryBean时,通过 getBean()方法返回的不是FactoryBean本身,而是FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()代理了getBean()方法。

 

2、Bean生命周期

  通常意义上讲的bean的生命周期,指的是bean从创建到初始化,经过一系列的流程,最终销毁的过程。只不过,在Spring中,bean的生命周期是由Spring容器来管理的。在Spring中,我们可以自己来指定bean的初始化和销毁的方法。我们指定了bean的初始化和销毁方法之后,当容器在bean进行到当前生命周期的阶段时,会自动调用我们自定义的初始化和销毁方法。

(1)指定初始方法和销毁方法

@Bean注解中的initMethod指定初始化方法,destroyMethod指定销毁方法。

对象创建完成,如果对象中存在一些属性,并且这些属性也都赋好值之后,那么就会调用bean的初始化方法。

  ①构造(对象创建)

    单实例:在容器启动的时候创建对象

    多实例:在每次获取的时候创建对象

  ②销毁

    单实例:容器关闭的时候

    多实例:容器不会管理这个bean,容器不会调用销毁方法

 1 public class Car {
 2     public Car() {
 3         System.out.println("car constructor...");
 4     }
 5 
 6     public void init() {
 7         System.out.println("car ... init...");
 8     }
 9 
10     public void destroy() {
11         System.out.println("car ... destroy...");
12     }
13 }
1 @Bean(initMethod = "init",destroyMethod = "destroy")
2 public Car car(){
3     return new Car();
4 }

 

(2)InitializingBean和DisposableBean接口

  Spring中提供了一个InitializingBean接口,该接口为bean提供了属性初始化后的处理方法,它只包括afterPropertiesSet方法,凡是继承该接口的类,在bean的属性初始化后都会执行该方法。

  Spring为bean提供了两种初始化的方式,第一种方式是实现InitializingBean接口(也就是要实现该接口中的afterPropertiesSet方法),第二种方式是在配置文件或@Bean注解中通过init-method来指定,这两种方式可以同时使用,同时使用先调用afterPropertiesSet方法,后执行init-method指定的方法。

  实现了DisposableBean接口的bean在销毁前,Spring将会调用DisposableBean接口的destroy()方法。

 1 public class Car implements InitializingBean, DisposableBean {
 2     public Car() {
 3         System.out.println("car constructor...");
 4     }
 5 
 6     @Override
 7     public void afterPropertiesSet() throws Exception {
 8         System.out.println("car ... afterPropertiesSet...");
 9     }
10 
11     @Override
12     public void destroy() throws Exception {
13         System.out.println("car ... destroy...");
14     }
15 }
1 @Bean
2 public Car car(){
3     return new Car();
4 }

(3)@PostConstruct和@PreDestroy注解

@PostConstruct和@PreDestroy注解是Java中的注解,并不是Spring提供的注解。

 1 @Component
 2 public class Dog {
 3     public Dog() {
 4         System.out.println("dog constructor...");
 5     }
 6 
 7     // 在对象创建完成并且属性赋值完成之后调用
 8     @PostConstruct
 9     public void init() {
10         System.out.println("dog...@PostConstruct...");
11     }
12 
13     // 在容器销毁(移除)对象之前调用
14     @PreDestroy
15     public void destory() {
16         System.out.println("dog...@PreDestroy...");
17     }
18 }

 

(4)BeanPostProcessor后置处理器

  BeanPostProcessor是一个接口,其中有两个方法,即postProcessBeforeInitialization和postProcessAfterInitialization这两个方法,这两个方法分别是在Spring容器中的bean初始化前后执行。

  postProcessBeforeInitialization方法会在bean实例化和属性设置之后,自定义初始化方法之前被调用,而postProcessAfterInitialization方法会在自定义初始化方法之后被调用。当容器中存在多个BeanPostProcessor的实现类时,会按照它们在容器中注册的顺序执行。对于自定义的BeanPostProcessor实现类,还可以让其实现Ordered接口自定义排序。

 1 @Component
 2 public class MyBeanPostProcessor implements BeanPostProcessor {
 3 
 4     @Override
 5     public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
 6         System.out.println("postProcessBeforeInitialization..." + s + "=>" + o);
 7         return o;
 8     }
 9 
10     @Override
11     public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
12         System.out.println("postProcessAfterInitialization..." + s + "=>" + o);
13         return o;
14     }
15 }

 

(5)BeanPostProcessor原理

  可以将关键代码的调用过程使用如下伪代码表述出来。

1 populateBean(beanName, mbd, instanceWrapper); // 给bean进行属性赋值
2 initializeBean(beanName, exposedObject, mbd)
3 {
4     applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
5     invokeInitMethods(beanName, wrappedBean, mbd); // 执行自定义初始化
6     applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
7 }

 

  在Spring中,调用initializeBean()方法之前,调用了populateBean()方法为bean的属性赋值,为bean的属性赋好值之后,再调用initializeBean()方法进行初始化。

  在initializeBean()中,调用自定义的初始化方法(即invokeInitMethods())之前,调用了applyBeanPostProcessorsBeforeInitialization()方法,而在调用自定义的初始化方法之后,又调用了applyBeanPostProcessorsAfterInitialization()方法。至此,整个bean的初始化过程就这样结束了。

Spring注解驱动开发第16讲——面试官再问你BeanPostProcessor的执行流程,就把这篇文章甩给他!

 

(6)BeanPostProcessor在Spring底层的使用

①ApplicationContextAwareProcessor类

  org.springframework.context.support.ApplicationContextAwareProcessor是BeanPostProcessor接口的一个实现类,这个类的作用是可以向组件中注入IOC容器。

②BeanValidationPostProcessor类

  org.springframework.validation.beanvalidation.BeanValidationPostProcessor类主要是用来为bean进行校验操作的,当我们创建bean,并为bean赋值后,我们可以通过BeanValidationPostProcessor类为bean进行校验操作。

③InitDestroyAnnotationBeanPostProcessor类

  org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor类主要用来处理@PostConstruct注解和@PreDestroy注解。

④AutowiredAnnotationBeanPostProcessor类

  org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor类主要是用于处理标注了@Autowired注解的变量或方法。

Spring注解驱动开发第17讲——BeanPostProcessor在Spring底层是如何使用的?

 

3、属性赋值

(1)@Value

Spring中的@Value注解可以为bean中的属性赋值。

@Value注解可以标注在字段、方法、参数以及注解上,而且在程序运行期间生效。

 

(2)不通过配置文件注入属性的情况

  • 注入普通字符串

    @Value("zs")
    private String name; // 注入普通字符串
  • 注入SpEL表达式结果

    @Value("#{20-2}")
    private Integer age;
  • 注入操作系统属性

    @Value("#{systemProperties['os.name']}")
    private String systemPropertiesName; // 注入操作系统属性
  • 注入其他bean中属性的值

    @Value("#{person.name}")
    private String username; // 注入其他bean中属性的值,即注入person对象的name属性中的值
  • 注入文件资源

    @Value("classpath:/config.properties")
    private Resource resourceFile; // 注入文件资源
  • 注入URL资源

    @Value("http://www.baidu.com")
    private Resource url; // 注入URL资源

 

(3)通过配置文件注入属性的情况

  ①在项目src/main/resources目录下新建一个属性文件,例如person.properties。

person.nickName=zhangsan

 

  ②在配置类中引入properties文件

1 //使用@PropertySource读取外部配置文件中的k/v保存到运行的环境变量中,加载完外部配置文件,通过${}取出配置文件中的值
2 @PropertySource(value = {"classpath:/person.properties"})
3 @Configuration
4 public class MainConfigOfPropertyValue {
5     @Bean
6     public Person person(){
7         return new Person();
8     }
9 }

 

  ③加载完外部的配置文件以后,接着我们就可以使用${key}取出配置文件中key所对应的值,并将其注入到bean的属性中

1 @Value("${person.nickName}")
2 private String nickName;

 

(4)#{}和${}区别

  • #{···}:用于执行SpEl表达式,并将内容赋值给属性

  • ${···}:主要用于加载外部属性文件中的值

  • ${···}和#{···}可以混合使用,但是必须#{}在外面,${}在里面

 

4、自动装配

(1)@Autowired

@Autowired注解可以对类成员变量、方法和构造函数进行标注,完成自动装配的工作。@Autowired注解可以放在类、接口以及方法上。

  • @Autowired注解默认是优先按照类型去容器中找对应的组件,相当于是调用了如下这个方法:

    applicationContext.getBean(类名.class);

    若找到则就赋值。

  • 如果找到多个相同类型的组件,那么是将属性名称作为组件的id,到IOC容器中进行查找,这时就相当于是调用了如下这个方法:

    applicationContext.getBean("组件的id");

如果要自动装配的类没有进行注册,默认情况下会报异常。如果希望其找不到时置为null,可以设置@Autowired注解的属性required为false。

1 @Autowired(required = false)
2 private BookDao bookDao;

 

(2)@Qualifier

@Autowired是先根据类型进行自动装配的,存在多个再通过属性名进行匹配。如果需要按指定的名称进行装配,则可以通过@Qualifier注解来实现。

1 @Qualifier("bookDao2")
2 @Autowired
3 private BookDao bookDao;
4 //注册了两个BookDao:bookDao、bookDao2
5 //默认情况下是匹配bookDao
6 //由于@Qualifier注解进行了指定,所以匹配bookDao2

 

(3)@Primary

  在Spring中使用注解时,常常会使用到@Autowired这个注解,它默认是根据类型Type来自动注入的。但有些特殊情况,对同一个接口而言,可能会有几种不同的实现类,而在默认只会采取其中一种实现的情况下,就可以使用@Primary注解来标注优先使用哪一个实现类。

  如果使用了@Qualifier注解,无论是否使用了@Primary注解,都会装配@Qualifier注解标注的对象。

 

(4)@Resource

  @Resource注解是Java规范里面的,也可以说它是JSR250规范里面定义的一个注解。该注解默认按照名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,那么默认取字段名将其作为组件的名称在IOC容器中进行查找,如果注解写在setter方法上,那么默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。

  • 不支持@Primary注解的功能

  • 不支持@Autowired(required = false)功能

1 @Resource(name = "bookDao2")
2 private BookDao bookDao;

 

(5)@Inject

@Inject注解也是Java规范里面的,也可以说它是JSR330规范里面定义的一个注解。该注解默认是根据参数名去寻找bean注入。

  ①导入javax.inject包

<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

 

  ②使用

1 @Inject
2 private BookDao bookDao;
  • 支持@Primary注解优先注入

  • 不支持@Autowired(required = false)功能

 

(6)实现方法、构造器位置的自动装配

  ①标注在实例方法上

    当@Autowired注解标注在方法上时,Spring容器在创建当前对象的时候,就会调用相应的方法为对象赋值。如果标注的方法存在参数时,那么方法使用的参数和自定义类型的值,需要从IOC容器中获取。

1 @Autowired
2 //标注在方法,Spring容器创建当前对象,就会调用方法,完成赋值
3 //方法使用的参数,自定义类型的值从IOC容器中获取
4 public void setCar(Car car) {
5     this.car = car;
6 }
7 public void setCar(@Autowired Car car) {
8     this.car = car;
9 }
1 //@Bean+参数:参数从容器中获取,可以省略@Autowired
2 @Bean
3 public Boss boss(Car car){
4     Boss boss = new Boss();
5     boss.setCar(car);
6     return boss;
7 }

  ②标注在构造方法上

    使用@Autowired注解标注在构造方法上时,构造方法中的参数对象也是从IOC容器中获取的。

    使用@Autowired注解标注在构造方法上时,如果组件中只有一个有参构造方法,那么这个有参构造方法上的@Autowired注解可以省略,并且参数位置的组件还是可以自动从IOC容器中获取。

 1 public Car getCar() {
 2     return car;
 3 }
 4 
 5 @Autowired
 6 public Boss(Car car) {
 7     this.car = car;
 8     System.out.println("Boss...有参构造器");
 9 }
10 public Boss(Car car) {
11     this.car = car;
12     System.out.println("Boss...有参构造器");
13 }

 

(7)注入Spring底层的组件

  自定义的组件要想使用Spring容器底层的一些组件,比如ApplicationContext(IOC容器)、底层的BeanFactory等等,那么只需要让自定义组件实现XxxAware接口即可。此时,Spring在创建对象的时候,会调用XxxAware接口中定义的方法注入相关的组件。

  XxxAware接口的底层原理是由XxxAwareProcessor实现类实现的,也就是说每一个XxxAware接口都有它自己对应的XxxAwareProcessor实现类。

 1 @Component
 2 public class Color implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware {
 3 
 4     @Override
 5     public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
 6         System.out.println("传入的IOC:" + applicationContext);
 7     }
 8 
 9 
10     @Override
11     public void setBeanName(String s) {
12         System.out.println("当前bean的名字:" + s);
13     }
14 
15     @Override
16     public void setEmbeddedValueResolver(StringValueResolver stringValueResolver) {
17         String resolveStringValue = stringValueResolver.resolveStringValue("你好,${os.name},我的年龄是#{20*18}");
18         System.out.println("解析的字符串:" + resolveStringValue);
19     }
20 }

 

(8)@Profile

  在容器中如果存在同一类型的多个组件,那么可以使用@Profile注解标识要获取的是哪一个bean。也可以说@Profile注解是Spring为我们提供的可以根据当前环境,动态地激活和切换一系列组件的功能。这个功能在不同的环境使用不同的变量的情景下特别有用,例如,在开发环境、测试环境、生产环境中使用不同的数据源,在不改变代码的情况下,可以使用这个注解来动态地切换要连接的数据库。

  • @Profile注解不仅可以标注在方法上,也可以标注在配置类上

  • 写了环境标识的bean,只有在这个环境被激活的时候才能注册到容器中。默认是default环境

  • 如果@Profile注解标注在配置类上,那么只有是在指定的环境的时候,整个配置类里面的所有配置才会生效

  • 如果一个bean上没有使用@Profile注解进行标注,那么这个bean在任何环境下都会被注册到IOC容器中,当然了,前提是在整个配置类生效的情况下

 1 @Configuration
 2 public class MainConfigOfProfile {
 3 
 4     @Profile("test")
 5     @Bean
 6     public Blue blue(){
 7         return new Blue();
 8     }
 9 
10     @Profile("dev")
11     @Bean
12     public Green green(){
13         return new Green();
14     }
15 
16     @Bean
17     public Red red(){
18         return new Red();
19     }
20 }
 1 @Test
 2 public void test(){
 3     // 1. 使用无参构造器创建一个IOC容器
 4     AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
 5     // 2. 在我们容器还没启动创建其他bean之前,先设置需要激活的环境(可以设置激活多个环境哟)
 6     applicationContext.getEnvironment().setActiveProfiles("dev");
 7     // 3. 注册主配置类
 8     applicationContext.register(MainConfigOfProfile.class);
 9     // 4. 启动刷新容器
10     applicationContext.refresh();
11 
12     String[] definitionNames = applicationContext.getBeanDefinitionNames();
13     for (String name : definitionNames) {
14         System.out.println(name);
15     }
16 
17     // 关闭容器
18     applicationContext.close();
19 }

 

5、AOP

(1)AOP简介

  AOP(Aspect Orient Programming),直译过来就是面向切面编程。AOP是指在程序的运行期间动态地将某段代码切入到指定方法、指定位置进行运行的编程方式。AOP的底层是使用动态代理实现的。

案例:

  ①导入AOP依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>4.3.12.RELEASE</version>
</dependency>

 

  ②定义目标类(业务逻辑类)

1 public class MathCalculator {
2 
3     public int div(int i, int j) {
4         System.out.println("MathCalculator...div...");
5         return i / j;
6     }
7 
8 }

 

  ③定义切面类

通过@Aspect注解进行标识。

AOP中的通知方法及其对应的注解与含义如下:

  • 前置通知(对应的注解是@Before):在目标方法运行之前运行

  • 后置通知(对应的注解是@After):在目标方法运行结束之后运行,无论目标方法是正常结束还是异常结束都会执行

  • 返回通知(对应的注解是@AfterReturning):在目标方法正常返回之后运行

  • 异常通知(对应的注解是@AfterThrowing):在目标方法运行出现异常之后运行

  • 环绕通知(对应的注解是@Around):动态代理,我们可以直接手动推进目标方法运行(joinPoint.procced())

注意:

可以在参数中添加上JoinPoint,通过该参数获取目标方法信息

JoinPoint参数一定要放在参数列表的第一位,否则Spring是无法识别的

 1 //切面类
 2 @Aspect
 3 public class LogAspects {
 4 
 5     // 如果切入点表达式都一样的情况下,那么我们可以抽取出一个公共的切入点表达式
 6     @Pointcut("execution(public int com.my.aop.MathCalculator.*(..))")
 7     public void pointCut() {}
 8 
 9     @Before("pointCut()")
10     public void logStart(JoinPoint joinPoint) {
11         Object[] args = joinPoint.getArgs();
12         System.out.println(joinPoint.getSignature().getName() + "运行......@Before,参数列表是:{"+ Arrays.asList(args) +"}");
13     }
14 
15     //如果切入点表达式在外部类,则需要写上包路径
16     @After("com.my.aop.LogAspects.pointCut()")
17     public void logEnd(JoinPoint joinPoint) {
18         System.out.println(joinPoint.getSignature().getName() + "结束......@After");
19     }
20 
21     @AfterReturning(value = "pointCut()",returning = "returns")
22     public void logReturn(JoinPoint joinPoint,Object returns) {
23         System.out.println(joinPoint.getSignature().getName() + "正常返回......@AfterReturning,运行结果是:{"+ returns +"}");
24     }
25 
26     @AfterThrowing(value = "pointCut()",throwing = "e")
27     public void logException(JoinPoint joinPoint,Exception e) {
28         System.out.println(joinPoint.getSignature().getName() + "出现异常......异常信息:{"+ e +"}");
29     }
30 
31 }

 

  ④将目标类和切面类加入到IOC容器

    使用@EnableAspectJAutoProxy注解开启基于注解的AOP模式。

 1 @EnableAspectJAutoProxy
 2 @Configuration
 3 public class MainConfigOfAop {
 4 
 5     @Bean
 6     public MathCalculator calculator(){
 7         return new MathCalculator();
 8     }
 9 
10     @Bean
11     public LogAspects logAspects(){
12         return new LogAspects();
13     }
14 }

 

  ⑤测试

1 @Test
2 public void test(){
3     AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAop.class);
4     MathCalculator bean = applicationContext.getBean(MathCalculator.class);
5     bean.div(1,1);
6     applicationContext.close();
7 }

 

(2)@EnableAspectJAutoProxy分析

  向Spring的配置类上添加@EnableAspectJAutoProxy注解之后,会向IOC容器中注册AnnotationAwareAspectJAutoProxyCreator,翻译过来就叫注解装配模式的AspectJ切面自动代理创建器。

AnnotationAwareAspectJAutoProxyCreator
    ->AspectJAwareAdvisorAutoProxyCreator(父类)
        ->AbstractAdvisorAutoProxyCreator(父类)
            ->AbstractAutoProxyCreator(父类)
                implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware(两个接口)

AnnotationAwareAspectJAutoProxyCreator类的结构图:

Spring注解驱动开发第26讲——总有人让我给他讲讲@EnableAspectJAutoProxy注解

 

(3)分析创建和注册AnnotationAwareAspectJAutoProxyCreator的过程

①传入配置类

②注册配置类,调用refresh()刷新容器

③registerBeanPostProcessors(beanFactory);注册bean的后置处理器来方便拦截bean的创建

  1)、先获取IOC容器已经定义了的需要创建对象的所有BeanPostProcessor

  2)、给容器中加别的BeanPostProcessor

  3)、优先注册实现了PriorityOrdered接口的BeanPostProcessor

  4)、再给容器中注册实现了Ordered接口的BeanPostProcessor

  5)、注册实现优先级接口的BeanPostProcessor

  6)、注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象,保存在容器中

    创建internalAutoProxyCreator的BeanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】

      1、创建Bean的实例

      2、populateBean;给bean的各种属性赋值

      3、initializeBean;初始化bean

        (1)invokeAwareMethods();处理Aware接口的方法回调

        (2)applyBeanPostProcessorsBeforeInitialization();应用后置处理器的

      PostProcessorsBeforeInitialization()

        (3)invokeInitMethods();执行自定义的初始化方法

        (4)applyBeanPostProcessorsAfterInitialization();执行后置处理器的PostProcessorsAfterInitialization()

      4、BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator);创建成功—>aspectJAdvisorsBuilder

  7)、把BeanPostProcessor注册到BeanFactory中

beanFactory.addBeanPostProcessor(postProcessor);

Spring注解驱动开发第28讲——创建和注册AnnotationAwareAspectJAutoProxyCreator的过程

 

(4)完成BeanFactory的初始化工作

④finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作,创建剩下的单实例bean

  1)、遍历获取容器中所有的Bean,依次创建对象getBean(beanName);

       getBean() —> doGetBean() —> getSingleton()

  2)、创建bean

    【AnnotationAwareAspectJAutoProxyCreator 在所有bean创建之前会有一个拦截,因为它是InstantiationAwareBeanPostProcessor类型,会先调用postProcessorBeforeInstantiation()】

    1、先从缓存中获取当前bean,如果能获取到,说明bean是之前被创建过的,直接去使用,否则再创建

      只要创建好的Bean都会被缓存起来

    2、createBean();创建bean

      【BeanPostProcessor是在Bean对象创建完成初始化前后调用的】

      【InstantiationAwareBeanPostProcessor是在创建Bean实例之前先尝试用后置处理器返回对象的】

      (1)、resolveBeforeInstantiation(beanName,mbdToUse);解析BeforeInstantiation()

          希望后置处理器在此能返回一个代理对象;如果能返回对象就使用,如果不能就继续往下执行

          一、后置处理器先尝试返回对象;

            bean=applyBeanPostProcessorsBeforeInstantiation();拿到所有后置处理器,如果是InstantiationAwareBeanPostProcessor,就执行                                                     postProcessorBeforeInstantiation()

            if(bean!=null){

              bean=applyBeanPostProcessoresAfterInitialization()

            }

      (2)、doCreateBean(beanName,mbdToUser,args);真正的去创建一个bean实例;和3.6流程一样

Spring注解驱动开发第29讲——注册完AnnotationAwareAspectJAutoProxyCreator后置处理器之后,就得完成BeanFactory的初始化工作了

 

(5)创建AOP代理

AnnotationAwareAspectJAutoProxyCreator 【InstantiationAwareBeanPostProcessor】作用:

  ①每一个bean创建之前,调用postProcessorBeforeInstantiation();

    1)、判断当前bean是否在advisedBean中(保存了所有需要增强的bean)

    2)、判断当前bean是否是基础类型的Advice、Pointcut、Advisor、AopInfrastructureBean,或者是否是切面(@Aspect)

    3)、判断是否需要跳过

      1、获取候选的增强器(切面里面的通知方法)【List<Advisor> candidateAdivisors】

        每一个封装的通知方法的增强器是InstantiationModelAwarePointcutAdvisor;

        判断每一个增强器是否是AspectJPointcutAdvisor类型的,是则返回为true

      2、返回false

  ②创建对象

    调用postProcessorBeforeInstantiation() —>

        return wrapIfNecessary(bean,beanName,cacheKey);//如果有必要则包装

    1)、获取当前bean的所有增强器(通知方法)

      1、找到候选的所有的增强器

      2、获取到能在bean使用的增强器(找哪些通知方法是需要切入到当前bean方法的)

      3、给增强器排序

      4、封装成Object[] specificInterceptors

    2)、保存当前bean在advisedBeans中

    3)、如果当前bean需要增强,创建当前bean的代理对象

      1、获取所有增强器(通知方法)

      2、保存到proxyFactory

      3、创建代理对象:Spring自行决定

        JdkDynamicAopProxy(config);jdk代理

        ObjenesisCglibAopProxy(config);cglib代理

    4)、给容器中返回当前组件使用cglib增强了的代理对象

    5)、以后容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程

Spring注解驱动开发第30讲——AnnotationAwareAspectJAutoProxyCreator作为后置处理器,你知道它都做了些什么吗?

 

(6)目标方法执行

容器中保存了组件的代理对象(cglib增强后的对象),这个对象里面保存了详细信息(比如增强器,目标对象等)。

①CglibAopProxy.intercept();拦截目标方法的执行

②根据ProxyFactory对象获取将要执行的目标方法拦截器链;

List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method,targetClass);

  1)、List<Object> interceptorList保存所有拦截器(5)

    一个默认的ExposeInvocationInterceptor和4个增强器

  2)、遍历所有的增强器,将其转为Interceptor;

    registry.getInterceptors(advisor);

  3)、将增强器转为List<MethodInterceptor>;

    如果是MethodInterceptor,直接加入集合中

    如果不是,使用AdvisorAdapter将增强器转为MethodInterceptor

    转换完成返回MethodInterceptor数组

③如果没有拦截器链,直接执行目标方法

  拦截器链(每一个通知方法又被包装为方法拦截器,利用MethodInterceptor机制)

④如果有拦截器链,把需要执行的目标对象,目标方法,拦截器链等信息传入并创建一个CglibMethodInvocation对象,并调用Object retval = mi.proceed();

Spring注解驱动开发第31讲——目标方法的拦截逻辑

 

(7)拦截器链的执行过程

①如果没有拦截器执行目标方法,或者拦截器的索引和拦截器数组-1大小一样(指定到了最后一个拦截器)执行目标方法

②链式获取每一个拦截器,拦截器执行invoke()方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行

  拦截器链的机制,保证通知方法与目标方法的执行顺序

Spring注解驱动开发第32讲——拦截器链的执行过程

 

(8)AOP原理总结

Spring注解驱动开发第33讲——AOP原理总结

 

6、声明式事务

(1)搭建环境

  ①导入依赖

<!--c3p0数据源-->
<dependency>
    <groupId>c3p0</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.1.2</version>
</dependency>
<!--MySQL数据库驱动-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.44</version>
</dependency>
<!--spring-jdbc模块-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>4.3.12.RELEASE</version>
</dependency>

 

  ②配置类

 1 @ComponentScan("com.my.tx")
 2 @Configuration
 3 public class TxConfig {
 4 
 5     // 注册c3p0数据源
 6     @Bean
 7     public DataSource dataSource() throws Exception {
 8         ComboPooledDataSource dataSource = new ComboPooledDataSource();
 9         dataSource.setUser("root");
10         dataSource.setPassword("12345678");
11         dataSource.setDriverClass("com.mysql.jdbc.Driver");
12         dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/mybatis");
13         return dataSource;
14     }
15 
16     @Bean
17     public JdbcTemplate jdbcTemplate() throws Exception {
18         //Spring对@Configuration的文件会进行特殊处理,配置类中多次调用同一方法,并不是重新执行方法,而是从容器中找组件
19         JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());
20         return jdbcTemplate;
21     }
22 
23 }

 

  ③开发Dao层

 1 @Repository
 2 public class UserDao {
 3 
 4     @Autowired
 5     private JdbcTemplate jdbcTemplate;
 6 
 7     public void insert() {
 8         String sql = "insert into `users`(username, pwd) values(?, ?)";
 9         String username = UUID.randomUUID().toString().substring(0, 5);
10         jdbcTemplate.update(sql, username, "123456"); // 增删改都来调用这个方法
11     }
12 }

 

  ④开发Service层

 1 @Service
 2 public class UserService {
 3 
 4     @Autowired
 5     private UserDao userDao;
 6 
 7     public void insertUser() {
 8         userDao.insert();
 9         System.out.println("插入完成...");
10     }
11 }

 

(2)开启事务

  ①给相应方法标注@Transactional,表示当前方法是事务方法

1 @Transactional
2 public void insertUser() {}

 

  ②开启基于注解的事务管理功能

1 @EnableTransactionManagement
2 @ComponentScan("com.my.tx")
3 @Configuration
4 public class TxConfig {}

 

  ③配置事务管理器来控制事务

1 @Bean
2 public PlatformTransactionManager transactionManager() throws Exception {
3     return new DataSourceTransactionManager(dataSource());
4 }

 

(3)声明式事务原理的源码分析

①@EnableTransactionManagement

  利用TransactionManagementConfigurationSelector向容器中导入组件

  导入两个组件:

    i.AutoProxyRegistrar

    ii.ProxyTransactionManagementConfiguration

②AutoProxyRegistrar:给容器中注册一个InfrastructureAdvisorAutoProxyCreator组件

  InfrastructureAdvisorAutoProxyCreator:利用后置处理器机制在对象创建以后,包装对象,返回一个代理对象(增强器),代理对象执行方法利用拦截器链进行执行

③ProxyTransactionManagementConfiguration

  1)、给容器中注册事务增强器

    (1)、事务增强器要用到事务注解的信息,通过AnnotationTransactionAttributeSource解析事务注解

    (2)、事务拦截器:TransactionInterceptor:保存了事务属性信息,事务管理器,是一个MethodInterceptor

        在目标方法执行的时候,执行拦截器链

        事务拦截器执行流程:

          1、先获取事务相关属性

          2、再获取PlatformTransactionManager,如果事先没有添加指定任何transactionManager,最终会从容器中按照类型获取一个TransactionManager

          3、执行目标方法

            如果异常,获取到事务管理器,利用事务管理回滚操作

            如果正常,利用事务管理器,提交事务

Spring注解驱动开发第35讲——声明式事务原理的源码分析

 

7、扩展原理

(1)BeanFactoryPostProcessor

  BeanFactoryPostProcessor其实就是BeanFactory(创建bean的工厂)的后置处理器。

  BeanFactoryPostProcessor的调用时机是在BeanFactory标准初始化之后,这样一来,我们就可以来定制和修改BeanFactory里面的一些内容了,此时,所有的bean定义已经保存加载到BeanFactory中了,但是bean的实例还未创建。

  调用步骤:

    ①IOC容器创建对象

    ②invokeBeanFactoryPostProcessors(beanFactory);执行BeanFactoryPostProcessor

      如何找到所有的BeanFactoryPostProcessor并执行他们的方法:

        1)、直接在BeanFactory中找到所有类型是BeanFactoryPostProcessor的组件,并执行他们的方法

        2)、在初始化创建其他组件前面执行

Spring注解驱动开发第36讲——或许,这是你以前没看过的从源码角度理解BeanFactoryPostProcessor的原理

 

(2)BeanDefinitionRegistryPostProcessor

  继承了BeanFactoryPostProcessor。

  调用时机是在所有bean定义信息将要被加载,bean实例还未创建。

  在BeanFactoryPostProcessor前面执行的。

  利用BeanDefinitionRegistryPostProcessor给容器再额外添加一些组件。

  调用过程:

    ①IOC创建对象

    ②refresh() —> invokeBeanFactoryPostProcessors(beanFactory);

    ③从容器中获取到所有的BeanDefinitionRegistryPostProcessor组件

      1)、依次触发所有的postProcessBeanDefinitionRegistry()

      2)、再来触发postProcessBeanFactory()方法

    ④再从容器中找到BeanFactoryPostProcessor组件,然后依次触发postProcessBeanFactory()方法

Spring注解驱动开发第37讲——你知道Spring中BeanDefinitionRegistryPostProcessor是如何执行的吗?

 

(3)ApplicationListener

  ApplicationListener的作用主要是来监听IOC容器中发布的一些事件(ApplicationEvent及其子类),只要事件发生便会来触发该监听器的回调,从而来完成事件驱动模型的开发。

用法:

  首先编写监听器,把监听器加入到容器中。

1 @Component
2 public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {
3     
4     // 当容器中发布此事件以后,下面这个方法就会被触发
5     @Override
6     public void onApplicationEvent(ApplicationEvent applicationEvent) {
7         System.out.println("收到事件:" + applicationEvent);
8     }
9 }

 

  其次,可以在测试时发布一个事件。

 1 @Test
 2 public void test2(){
 3     AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ExtConfig.class);
 4 
 5     applicationContext.publishEvent(new ApplicationEvent(new String("发布的事件")) {
 6     });
 7 
 8     // 关闭容器
 9     applicationContext.close();
10 }
11 /*
12 收到三个事件:
13 ContextRefreshedEvent:容器刷新完成事件。
14 IOCTest_Ext$1[source=发布的事件]:自己发布的事件。
15 ContextClosedEvent:容器关闭事件。
16 */

 

源码分析:

分析ContextRefreshedEvent事件:

①容器创建对象;调用refresh();

②refresh()中调用finishRefresh();容器刷新完成

③finishRefresh()中调用以下语句,发布事件

publishEvent((ApplicationEvent)(new ContextRefreshedEvent(this)))

④事件发布流程

  分析ContextClosedEvent事件:

    ①容器关闭时,applicationContext.close();

    ②close()方法中调用doClose();

    ③doClose()中调用以下语句,发布事件

publishEvent((ApplicationEvent)(new ContextClosedEvent(this)));

 

【事件发布流程】:

  1)、获取事件多播器(派发器):getApplicationEventMulticaster()

  2)、multicastEvent()派发事件

    1、获取到所有的ApplicationListener,并进行遍历

    2、如果有Executor,可以支持使用Executor进行异步派发

      Executor executor = this.getTaskExecutor();

    3、否则,同步的方式执行listener方法:invokeListener(listener, event);

      拿到listener回调onApplicationEvent方法

 

【事件多播器(派发器)】:

  ①容器创建对象,refres();

  ②refres()中调用initApplicationEventMulticaster();初始化ApplicationEventMulticaster

    1)、先去容器中找有没有“id=applicationEventMulticaster”组件

    2)、有则直接获取;如果没有则创建

 this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);

      并且加入到容器中,我们就可以在其他组件要派发事件时,自动注入这个applicationEventMulticaster

 

【容器中有哪些监听器】

  ①容器创建对象,refres();

  ②refres()中调用registerListeners();注册监听器

    1)、从容器中拿到所有的监听器,把他们注册到applicationEventMulticaster中

String[] listenerBeanNames = this.getBeanNamesForType(ApplicationListener.class, true, false);

    2)、将listener注册到ApplicationEventMulticaster中

this.getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
 

Spring注解驱动开发第39讲——你不知道的ApplicationListener的原理

 

(4)@EventListener

  我们只需要简单地给该方法上标注一个@EventListener注解,就可以让它来监听事件了。可以通过@EventListener注解中的classes属性来指定要监听哪些事件。

1 @Service
2 public class UserService {
3 
4     @EventListener(classes= ApplicationEvent.class)
5     public void listen() {
6         System.out.println("UserService...");
7     }
8 }

 

原理:

  使用EventListenerMethodProcessor处理器来解析方法上的@EventListener。

Spring注解驱动开发第40讲——你晓得@EventListener这个注解的原理吗?

 

8、Spring容器创建过程

(1)BeanFactory预准备

Spring容器的refresh()【创建刷新】;

①prepareRefresh( )刷新前的预处理;

  1)、initPropertySources( )初始化一些属性设置;子类自定义个性化的属性设置方法

  2)、getEnvironment( ). validateRequiredProperties( ) ;获取其环境变量,然后校验属性的合法性

  3)、earlyApplicationEvents= new LinkedHashSet < ApplicationEvent >() ;保存容器中的一些早期的事件;【允许收集早期的容器事件,等待事件派发器可用之后,即可进行发布

②obtainFreshBeanFactory() ;获取BeanFactory;

  1)、refreshBeanFactory() ;刷新【创建】 BeanFactory;

      创建了一个this.beanFactory = new DefaultListableBeanFactory();

      设置id;

  2)、getBeanFactory( ) ;返回刚才GenericApplicationContext创建的BeanFactory对象;

  3)、将创建的BeanFactory【DefaultListableBeanFactory】返回;

③prepareBeanF actory(beanFactory) ; BeanFactory的预准备工作(BeanFactory进行一些设置) ;

  1)、设置BeanFactory的类加载器、支持表达式解析器...

  2)、添加部分BeanPostProcessor 【Applicat ionContextAwareProcessor】

  3)、设置忽略的自动装配的接口【作用就是,这些接口的实现类不能通过接口类型来自动注入】EnvironmentAware、EmbeddedValueResolverAware、XXX;

  4)、注册可以解析的自动装配;我们能直接在任何组件中自动注入:

      BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext

  5)、添加BeanPostProcessor 【ApplicationListenerDetector】

  6)、添加编译时的AspectJ;

  7)、给BeanFactory中注册一些能用的组件;

      environment【ConfigurableEnvironment】、

      systemProperties【Map<String, Object>】、

      systemEnvironment【Map<String,Object>】

④postProcessBeanFactory( beanFactory ); BeanFactory准备工作完成后进行的后置处理工作;

  1)、子类通过重写这个方法来在BeanFactory创建并预准备完成以后做进一步的设置

Spring注解驱动开发第41讲——Spring IOC容器创建源码解析(一)之BeanFactory的创建以及预准备工作

 

(2)执行BeanFactoryPostProcessor

⑤invokeBeanFactoryPostProcessors( beanFactory ) ;执行BeanFactoryPostProcessor;

  BeanFactoryPostProcessor: BeanFactory的后置处理器。在BeanF actory标准初始化之后执行的;

  两个接口: BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor

  1)、执行BeanFactoryPostProcessor的方法;

    先执行BeanDefinitionRegistryPostProcessor的方法

      1、获取所有的BeanDefinitionRegistryPostProcessor;

      2、先执行实现了PriorityOrdered优先级接口的BeanDefinitionRegistryPostProcessor.

        postProcessor . postProcessBeanDefinitionRegistry(registry);

      3、再执行实现了Ordered顺序接口的BeanDefinitionRegi stryPostProcessor;

        postProcessor . postProcessBeanDefinitionRegistry(registry);

      4、最后执行没有实现任何优先级或者是顺序接口的BeanDefinitionRegistryPostProcessors;

        postProcessor . postProcessBeanDefinitionRegistry(registry);

 

    再执行BeanFactoryPostProcessor的方法

      1、获取所有的BeanFactoryPostProcessor;

      2、先执行实现了PriorityOrdered优先级接口的BeanFactoryPostProcessor.

        postProcessor. postProcessBeanFactory( )

      3、再执行实现了Ordered顺序接口的BeanFactoryPostProcessor;

        postProcessor. postProcessBeanFactory( )

      4、最后执行没有实现任何优先级或者是顺序接口的BeanFactoryPostProcessor;

        postProcessor. postProcessBeanFactory( )

Spring注解驱动开发第42讲——Spring IOC容器创建源码解析(二)之执行BeanFactoryPostProcessor

 

(3)注册BeanPostProcessor

⑥registerBeanPostProcessors (beanFactory) ;注册BeanPostProcessor (Bean的后置处理器)【作用是拦截bean的创建过程】

不同接口类型的BeanPostProcessor;在Bean创建前后的执行时机是不一样的

  • BeanPostProcessor

  • DestructionAwareBeanPostProcessor

  • Instantiat ionAwareBeanPostProcessor

  • SmartInstantiationAwareBeanPostProcessor

  • MergedBeanDefinitionPostProcessor【internalPostProcessors】

 

  1)、获取所有的BeanPostProcessor ;

    后置处理器都默认可以通过PriorityOrdered、ordered接口来执行优先级

  2)、先注册PriorityOrdered优先级接口的BeanPostProcessor;

    把每一个BeanPostProcessor;添加到BeanFactory中

    beanFactory.addBeanPostProcesson( postProcessor);

  3)、再注册Ordered接口的

  4)、最后注册没有实现任何优先级接口的

  5)、最终注册MergedBeanDefinitionPostProcessor;

  6)、注册一个ApplicationListenerDetector; 来在Bean创建完成后检查是否是ApplicationListener,如果是

    applicationContext.addApplicationListener( (ApplicationListener<?>) bean);

Spring注解驱动开发第43讲——Spring IOC容器创建源码解析(三)之注册BeanPostProcessor

 

(4)初始化MessageSource组件

⑦initMessageSource();初始化MessageSource组件(做国际化功能;消息绑定,消息解析) ;

  1)、获取BeanFactory

  2)、看容器中是否有id为messageSource的,类型是MessageSource的组件

    如果有赋值给messageSource,如果没有自己创建一个DelegatingMessageSource;

    MessageSource:取出国际化配置文件中的某个key的值;能按照区域信息获取;

  3)、把创建好的MessageSource注册在容器中,以后获取国际化配置文件的值的时候,可以自动注入MessageSource,通过调用它的getMessage方法获取

    beanFactory. registersingleton(MESSAGE_ SOURCE_ BEAN NAME, this . messageSource);

Spring注解驱动开发第44讲——Spring IOC容器创建源码解析(四)之初始化MessageSource组件

 

(5)初始化事件派发器、监听器等

⑧initApplicationEventMulticaster( ) ;初始化事件派发器;

  1)、获取BeanFactory

  2)、从BeanFactory中获取applicationEventMulticaster的ApplicationEventMulticaster;

  3)、如果上一步没有配置;创建一个SimpleApplicationEventMulticaster

  4)、将创建的ApplicationEventMulticaster添加到BeanFactory中,以后其他组件直接自动注入

 

⑨onRefresh();留给子容器(子类)

  1)、子类重写这个方法,在容器刷新的时候可以自定义逻辑;

 

⑩registerListeners() ;将所有项目里面的ApplicationListener注册进容器中来;

  1)、从容器中拿到所有的ApplicationListener

  2)、将每个监听器添加到事件派发器中;

    getApplicationEventMulticaster(). addApplicationListenerBean(listenerBeanName)

  3)、派发之前步骤产生的事件;

Spring注解驱动开发第45讲——Spring IOC容器创建源码解析(五)之初始化事件派发器

 

(6)初始化所有剩下的单实例bean

⑪finishBeanFactoryInitialization( beanFactory) ;初始化所有剩下的单实例bean;

  1)、beanFactory.preInstantiateSingletons( ) ;初始化后剩下的单实例bean

    1、获取容器中的所有Bean,依次进行初始化和创建对象

    2、获取Bean的定义信息; RootBeanDefinition

    3、Bean不是抽象的,是单实例的,不是懒加载;

      (1)、判断是否是FactoryBean;是否是实现FactoryBean接口的Bean;

      (2)、不是工厂Bean。利用getBean(beanName) ;创建对象

        0.getBean( beanName); ioc. getBean();

        1.doGetBean(name, null, null, false) ;

        2.先获取缓存中保存的单实例Bean。如果能获取到说明这个Bean之前被创建过(所有创建过的单实例Bean都会被缓存起来

private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);

        3.缓存中获取不到,开始Bean的创建对象流程;

        4.标记当前bean已经被创建

        5.获取Bean的定义信息;

        6.【获取当前Bean依赖的其他Bean ;如果有按照getBean( )把依赖的Bean先创建出来; 】(getDependsOn())

        7.启动单实例Bean的创建流程;

          1).createBean( beanName, mbd, args) ;

          2).object bean = resolveBeforeInstantiation( beanName, mbdToUse) ;让BeanProcessor先拦截返回代理对象

            【InstantiationAwareBeanPostProcessor 】提前执行;

            先触发: postProcessBeforeInstantiation();

            如果有返回值:触发postProcessAfterInitialization();

          3).如果前面的InstantiationAwareBeanPostProcessor没有返回代理对象;调用4)

          4).Object beanInstance = doCreateBean( beanName, mbdToUse, args) ;创建Bean

            1创建Bean实例; createBeanInstance( beanName, mbd, args);

              利用工厂方法或者对象的构造器创建出Bean实例;

            2applyMergedBeanDefinitionPostProcessors ( mbd, beanType, beanName);

              调用MergedBeanDefinitionPostProcessor

              bdp.postProcessMergedBeanDefinition( mbd, beanType, beanName) ;

      (3)、【Bean属性赋值】populateBean( beanName, mbd, instanceWrapper);

        赋值之前:

          1.拿到InstantiationAwareBeanPostProcessor后置处理器;

            postProcessAfterInstantiation( );

          2.拿到InstantiationAwareBeanPostProcessor后置处理器;

            postProcessPropertyValues ( );

        ==============

          3.应用Bean属性的值;为属性利用setter方法等进行赋值;

            applyPropertyValues ( beanName, mbd, bw, pvs);

      (4)、【Bean初始化】initializeBean( beanName, exposed0bject, mbd);

          1.【执行Aware接口方法】invokeAwareMethods ( beanName, bean) ;执行xxxAware接口

            BeanNameAware \ BeanClassLoaderAware \BeanFactoryAware

          2.【执行后置处理器初始化之前】 applyBeanPostProcessorsBeforeInitialization()

            BeanPostProcessor. postProcessBeforeInitialization () ;

          3.【执行初始化方法】invokeInitMethods ( beanName, wrappedBean, mbd);

            1是否是InitializingBean接口的实现;执行接口规定的初始化;

            2是否自定义初始化方法;

          4.【执行后置处理器初始化之后】applyBeanPostProcessorsAfterInitialization

            BeanPostProcessor. postProcessAfterInitialization( );

          5.注册Bean的销毁方法;

      (5)、将创建的Bean添加到缓存中singletonObjects;

          ioc容器就是这些Map;很多的Map里面保存了单实例Bean,环境信息。。。。

          所有Bean都利用getBean创建完成以后;

          检查所有的Bean是否是SmartInitializingsingleton接口的;如果是;就执行afterSingletonsInstantiated()

Spring注解驱动开发第46讲——Spring IOC容器创建源码解析(六)之初始化所有剩下的单实例bean(上)

Spring注解驱动开发第47讲——Spring IOC容器创建源码解析(七)之初始化所有剩下的单实例bean(下)

 

(7)容器创建完成

⑫finishRefresh() ;完成BeanFactory的初始化创建工作; IOC容器就创建完成;

  1)、initLifecycleProcessor( ) ;初始化和生命周期有关的后置处理器; LifecycleProcessor

    默认从容器中找是否有lifecycleProcessor的组件【LifecycleProcessor】 ;如果没有new DefaultlifecycleProcessor();

    加入到容器;

    写一个LifecycleProcessor的实现类,可以在BeanFactory

    void onRefresh(); void onClose();

  2)、getLifecycleProcessor().onRefresh();

    拿到前面定义的生命周期处理器(BeanFactory) ;回调onRefresh();

  3)、publishEvent (new ContextRefreshedEvent (this)) ;发布容器刷新完成事件;

  4)、LiveBeansView.registerApplicationContext(this);

Spring注解驱动开发第48讲——Spring IOC容器创建源码解析(八)之完成BeanFactory的初始化创建工作,最终完成容器创建

 

(8)总结

Spring注解驱动开发第49讲——Spring IOC容器创建源码解析(九)之Spring IOC容器创建源码总结

 

9、Servlet3.0

(1)ServletContainerInitializer

①Servlet容器在启动我们的应用的时候,它会来扫描jar包里面的ServletContainerInitializer的实现类

②用法:首先要提供ServletContainerInitializer的一个实现类,还必须得把它绑定在META-INF/services/目录下面的名字叫javax.servlet.ServletContainerInitializer的文件里面

③Servlet容器在启动应用的时候,会扫描当前应用每一个jar包里面的META-INF/services/javax.servlet.ServletContainerInitializer文件中指定的实现类,然后,再运行该实现类中的方法

 1 //@HandlesTypes注解里面指定上我们感兴趣的类型,那么Servlet容器在启动的时候就会自动地将该类型(即HelloService接口)下面的子类,包括实现类或者子接口等全部都传递过来
 2 
 3 @HandlesTypes(value={HelloService.class})
 4 public class MyServletContainerInitializer implements ServletContainerInitializer {
 5 
 6     /*
 7      * 参数:
 8      *    ServletContext arg1:代表当前web应用。一个web应用就对应着一个ServletContext对象,此外,它也是我们常说的四大域对象之一,
 9      *    我们给它里面存个东西,只要应用在不关闭之前,我们都可以在任何位置获取到
10      *    
11      *    Set<Class<?>> arg0:我们感兴趣的类型的所有后代类型
12      *    
13      */
14     @Override
15     public void onStartup(Set<Class<?>> arg0, ServletContext arg1) throws ServletException {
16         System.out.println("我们感兴趣的所有类型:");
17         // 好,我们把这些类型来遍历一下
18         for (Class<?> clz : arg0) {
19             System.out.println(clz);
20         }
21     }
22 }
23 //javax.servlet.ServletContainerInitializer
24 com.my.servlet.MyServletContainerInitializer

 

(2)注册web三大组件

  利用基于运行时插件的ServletContainerInitializer机制得到ServletContext对象,然后再往其里面注册组件。在ServletContainerInitializer的onStartup()方法中注册Servlet、Listener和Filter。

 1 @Override
 2 public void onStartup(Set<Class<?>> arg0, ServletContext sc) throws ServletException {
 3     // TODO Auto-generated method stub
 4     System.out.println("我们感兴趣的所有类型:");
 5     // 好,我们把这些类型来遍历一下
 6     for (Class<?> clz : arg0) {
 7         System.out.println(clz);
 8     }
 9 
10     // 注册Servlet组件
11     ServletRegistration.Dynamic servlet = sc.addServlet("userServlet", new UserServlet());
12     // 配置Servlet的映射信息
13     servlet.addMapping("/user");
14 
15     // 注册Listener组件
16     sc.addListener(UserListener.class);
17 
18     // 注册Filter组件
19     FilterRegistration.Dynamic filter = sc.addFilter("userFilter", UserFilter.class);
20     // 配置Filter的映射信息
21     //第一个参数是Filter拦截的请求类型,第三个参数是Filter要拦截的路径
22     filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
23 }

 

(3)Servlet3.0与SpringMVC整合分析

①web容器在启动的时候,会扫描每个jar包下的META-INF /services/javax. servlet . ServletContainerInitializer

②加载这个文件指定的类SpringServletContainerInitializer

③Spring的应用一启动会加载感兴趣的WebApplicationInitializer接口下的所有组件;

④并且为WebApplicat ionInitializer组件创建对象(组件不是接口,不是抽象类)

  1)、AbstractContextLoaderInitializer:创建根容器; createRootApplicationContext();

  2)、AbstractDispatcherServletInitializer:

      创建一个web的ioc容器:createServletApplicationContext();

      创建了DispatcherServlet:createDispatcherServlet();

      将创建的DispatcherServlet添加到ServletContext中;

  3)、AbstractAnnotationConfigDispatcherServletInitializer:注解方式配置的DispatcherServlet初始化器

      创建根容器:createRootApplicationContext( )

      getRootConfigClasses();传入一个配置类

      创建web的ioc容器:createServletApplicationContext();

      获取配置类; getServletConfigClasses();

总结:

以注解方式来启动SpringMVC;继承AbstractAnnotatienConfigDispatcherServletInitializer;

实现抽象方法指定DispatcherServlet的配置信息;

Spring注解驱动开发第54讲——Servlet 3.0整合Spring MVC

 

(4)定制与接管SpringMVC

Spring注解驱动开发第55讲——定制与接管Spring MVC

 

(5)异步请求

 1 // @WebServlet注解表明该Servlet应该处理哪个请求
 2 @WebServlet(value="/async", asyncSupported=true) 
 3 public class HelloAsyncServlet extends HttpServlet {
 4 
 5     @Override
 6     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
 7         // 1. 先来让该Servlet支持异步处理,即asyncSupported=true
 8         // 2. 开启异步模式
 9         // 我们可以来打印一下主线程究竟是谁?
10         System.out.println("主线程开始..." + Thread.currentThread());
11         AsyncContext startAsync = req.startAsync();
12         // 3. 调用业务逻辑,进行异步处理,这儿是开始异步处理
13         startAsync.start(new Runnable() {
14             
15             @Override
16             public void run() {
17                 // TODO Auto-generated method stub
18                 try {
19                     System.out.println("副线程开始..." + Thread.currentThread());
20                     sayHello();
21                     
22                     startAsync.complete();
23                     /*
24                      * 通过下面这种方式来获取响应对象是不可行的哟!否则,会报如下异常:
25                      * java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
26                      */
27                     // 获取到异步上下文
28                     //AsyncContext asyncContext = req.getAsyncContext();
29                     // ServletResponse response = asyncContext.getResponse();
30                     
31                     // 4. 获取响应
32                     ServletResponse response = startAsync.getResponse();
33                     // 然后,我们还是利用这个响应往客户端来写数据
34                     response.getWriter().write("hello async...");
35                     System.out.println("副线程结束..." + Thread.currentThread());
36                 } catch (Exception e) {
37                     // TODO Auto-generated catch block
38                     e.printStackTrace();
39                 }
40             }
41         });
42         System.out.println("主线程结束..." + Thread.currentThread());
43     }
44     
45     public void sayHello() throws Exception {
46         // 我们可以来打印一下究竟是哪些线程在工作
47         System.out.println(Thread.currentThread() + " processing...");
48         Thread.sleep(3000); // 睡上3秒
49     }
50     
51 }

 

(6)异步请求处理(返回Callable)

 1 /*
 2 第一步,控制器返回Callable。其实,这里要说的是控制器中方法的返回值要写成Callable了,而再也不能是以前普通的字符串对象了。
 3 第二步,控制器返回Callable以后,Spring MVC就会异步地启动一个处理方法(即Spring MVC异步处理),也即将Callable提交到TaskExecutor(任务执行器)里面,并使用一个隔离的线程进行处理。
 4 第三步,与此同时,DispatcherServlet和所有的Filter将会退出Servlet容器的线程(即主线程),但是response仍然保持打开的状态。既然response依旧保持打开状态,那就表明还没有给浏览器以响应,因此我们还能给response里面写数据。
 5 第四步,最终,Callable返回一个结果,并且Spring MVC会将请求重新派发给Servlet容器,恢复之前的处理。也就是说,之前的response依旧还保存着打开的状态,仍然还可以往其里面写数据。
 6 第五步,如果还是把上一次的请求再发过来,假设上一次的请求是async01,那么DispatcherServlet依旧还是能接收到该请求,收到以后,DispatcherServlet便会再次执行,来恢复之前的处理。
 7 */
 8 @Controller
 9 public class AsyncController {
10 
11     @ResponseBody
12     @RequestMapping("/async01")
13     public Callable<String> async01() {
14         Callable<String> callable = new Callable<String>() {
15 
16             @Override
17             public String call() throws Exception {
18                 // 响应给客户端一串字符串,即"Callable<String> async01()"
19                 return "Callable<String> async01()";
20             }
21             
22         };
23         return callable;
24     }
25 }

 

Spring注解驱动开发第57讲——体验一把Spring MVC中的异步请求处理(返回Callable)

 

(7)异步请求处理(返回DeferredResult)

Spring注解驱动开发第58讲——体验一把Spring MVC中的异步请求处理(返回DeferredResult)

 

posted @ 2021-09-11 11:05  sumAll  阅读(225)  评论(0编辑  收藏  举报