【Spring】注解驱动开发

1. 组件注册

1.1 传统 xml 配置

  • 创建一个 spring-annotation 项目用于演示

    pom.xml 的依赖为:

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.4</version>
        </dependency>
    </dependencies>
    
  • Person 类

    package com.ice.pojo;
    
    import java.util.StringJoiner;
    
    public class Person {
        private String name;
        private Integer age;
    
        public Person() {
        }
    
        public Person(String name, Integer age) {
            this.name = name;
        this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
        this.name = name;
        }
    
        public Integer getAge() {
        return age;
        }
    
        public void setAge(Integer age) {
        this.age = age;
        }
    
        @Override
        public String toString() {
            return new StringJoiner(", ", Person.class.getSimpleName() + "[", "]")
                    .add("name='" + name + "'")
                    .add("age=" + age)
                .toString();
        }
    }
    
  • beans.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 http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="person" class="com.ice.pojo.Person">
            <property name="age" value="18"/>
            <property name="name" value="张三"/>
        </bean>
    </beans>
    
  • MainTest 类

    import com.ice.pojo.Person;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class MainTest {
        public static void main(String[] args) {
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
            Person person = applicationContext.getBean("person", Person.class);
            System.out.println(person);
        }
    }
    

运行结果为:

Person[name='张三', age=18]

1.2 Java Config 配置

我们新建一个配置类:

package com.ice.config;

import com.ice.pojo.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

// 声明该类是一个配置类
@Configuration
public class MainConfig {

    // 给容器中注册一个 bean
    // 类型为返回值类型,id 默认为方法名
    @Bean
    public Person person() {
        return new Person("亚索", 20);
    }
}

再写一个测试类:

import com.ice.config.MainConfig;
import com.ice.pojo.Person;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.util.Arrays;


public class MainTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        
        // 获取 bean
        Person bean = applicationContext.getBean(Person.class);
        System.out.println(bean);
		
        // 打印 Person 类型 bean 的 id
        String[] namesForType = applicationContext.getBeanNamesForType(Person.class);
        Arrays.stream(namesForType).forEach(System.out::println);
    }
}

输出结果为:

Person[name='亚索', age=20]
person

如果更改配置类中的方法名:

@Bean
public Person person01() {
    return new Person("亚索", 20);
}

再运行测试类的结果为:

Person[name='亚索', age=20]
person01

有没有办法不通过修改逻辑代码来改变 bean 的 id 呢? 可以给注解传递参数来指定 bean 的名字

@Bean("people")
public Person person() {
    return new Person("亚索", 20);
}

此时再运行测试类,结果为:

Person[name='亚索', age=20]
people

1.3 组件扫描

传统的组件扫描是要在 xml 配置中开启注解扫描 <context:component-scan base-package="com.ice"/>,只要类标记了 @Controller@Service@Repository@Component 注解就会被添加到 Spring 容器中.

Java Config 配置如何实现呢?可以使用 @ComponentScan 注解.

下面是 @ComponentScan 的源码(省略了不常用的):

package org.springframework.context.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.annotation.AliasFor;
import org.springframework.core.type.filter.TypeFilter;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
	
    // 基本扫描包,全限定名
	@AliasFor("basePackages")
	String[] value() default {};

	@AliasFor("value")
	String[] basePackages() default {};

	// 基本扫描包,传入类所在的 package 被识别为基本扫描包
	Class<?>[] basePackageClasses() default {};

	boolean useDefaultFilters() default true;

	// 过滤规则,在 basePackages 中符合过滤规则的才被扫描
    // 即使 useDefaultFilters 为 True,DefaultFilters 中没有,也会添到加 includeFilters 的参数作为合法的过滤规则
    // 这里注意,如果 DefaultFilters 的有你不想要过滤的类型,必须设置 useDefaultFilters=false
	Filter[] includeFilters() default {};

	// 排除规则,是一个 Filter 数组,Filter 是该注解内部定义的一个注解
	Filter[] excludeFilters() default {};

	// 是否延迟加载
	boolean lazyInit() default false;

	// 内部注解,定义过滤规则
	@Retention(RetentionPolicy.RUNTIME)
	@Target({})
	@interface Filter {

		// 设置按照什么过滤,默认是注解
		FilterType type() default FilterType.ANNOTATION;

		// 设置过滤的类(按照注解过滤就是 注解类.class,按照其他方式过滤,就是其他相关类.class)
		@AliasFor("classes")
		Class<?>[] value() default {};

		@AliasFor("value")
		Class<?>[] classes() default {};

		// 按照 AspectJ type pattern expression 或 正则表达式 过滤的 模式字符串
		String[] pattern() default {};
	}
}

下面举几个栗子:

// 声明该类是一个配置类
@Configuration
@ComponentScan(basePackages = "com.ice")
public class MainConfig {
    // ...
}

编写一写组件添加注解后,再编写一个测试方法:

@Test
public void test01(){
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
    String[] definitionNames = applicationContext.getBeanDefinitionNames();
    Arrays.stream(definitionNames).forEach(System.out::println);
}

除了 Spring 必须要加载的组件外,可以看到其他我们自己写的组件也被加载进来了:

mainConfig
bookController
bookDao
bookService
people

我们添加一些排除规则:

@Configuration
@ComponentScan(basePackages = "com.ice", excludeFilters = {
        @Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})
})
public class MainConfig {
	// ...
}

此时再运行 test01(),结果为:

mainConfig
bookDao
people

我们还可以指定过滤规则:

// 声明该类是一个配置类
@Configuration
@ComponentScan(basePackages = "com.ice", includeFilters = {
        @Filter(type = FilterType.ANNOTATION, classes = {Service.class, Controller.class})
},useDefaultFilters = false)
public class MainConfig {
	// ...
}

其结果为:

mainConfig
bookController
bookService
people

下面再介绍下 FilterType

  • FilterType.ANNOTATION:按照注解类型

  • FilterType.ASSIGNABLE_TYPE:按照指定类类型

  • FilterType.ASPECTJ:按照 ASPECTJ表达式

  • FilterType.REGEX:按照正则表达式

  • FilterType.CUSTOM:按照自定义规则

    其实自定义的规则就是编写 TypeFilter 的实现类,重写 match() 方法,返回 true 表示匹配成功,false 表示匹配失败

    package com.ice.config;
    
    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;
    import org.springframework.core.type.filter.TypeFilter;
    
    import java.io.IOException;
    
    public class MyTypeFilter implements TypeFilter {
        @Override
        /**
         * metadataReader:读取到的当前正在扫描的类的信息
         * metadataReaderFactory:可以获取到其他任何类的信息
         */
        public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
    
            // 获取当前类的注解的信息
            AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
    
            // 获取当前正在扫描的类的信息
            ClassMetadata classMetadata = metadataReader.getClassMetadata();
    
            String className = classMetadata.getClassName();
            
            System.out.println("--->" + className);
            
            if (className.contains("er")) {
                return true;
            }
    
            // 获取当前类的资源信息(类的路径)
            Resource resource = metadataReader.getResource();
            return false;
        }
    }
    

    我们在配置类中这样写:

    @Configuration
    @ComponentScan(basePackages = "com.ice", excludeFilters = {
            @Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
    })
    public class MainConfig {
    	// ...
    }
    

    执行测试方法:

    @Test
    public void test01(){
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        String[] definitionNames = applicationContext.getBeanDefinitionNames();
        Arrays.stream(definitionNames).forEach(System.out::println);
    }
    

    测试结果为:

    --->com.ice.config.MyTypeFilter
    --->com.ice.controller.BookController
    --->com.ice.dao.BookDao
    --->com.ice.pojo.Person
    --->com.ice.service.BookService
    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    org.springframework.context.annotation.internalCommonAnnotationProcessor
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory
    mainConfig
    bookDao
    people
    

1.4 组件作用域

重新写个配置类和测试文件:

// 声明该类是一个配置类
@Configuration
public class MainConfig {

    // 给容器中注册一个 bean
    // 类型为返回值类型,id 默认为方法名
    @Bean("person")
    public Person person() {
        return new Person("亚索", 20);
    }
}
@Test
public void test02(){
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);

    Person person1 = applicationContext.getBean("person", Person.class);
    Person person2 = applicationContext.getBean("person", Person.class);

    System.out.println(person1.hashCode());
    System.out.println(person2.hashCode());

}

运行测试方法:

1422222071
1422222071

说明返回的两个实例是一样的. 如何使它返回的不是单例呢,可以使用 @Scope 注解

他可以传如下值:

  • prototype:多实例
  • singleton:单实例(默认值)
  • request:同一次请求创建一个实例
  • session:同一个session创建一个实例
@Configuration
public class MainConfig {

    // 给容器中注册一个 bean
    // 类型为返回值类型,id 默认为方法名
    @Bean("person")
    @Scope("prototype")
    public Person person() {
        return new Person("亚索", 20);
    }
}

主要用到的是 prototypesignleton

此时再运行 test02()

1990098664
1383524016

下面再来看创建实例的时机,首先是默认的 singleton 情况下:

@Configuration
public class MainConfig {

    @Bean("person")
    @Scope
    public Person person() {
        System.out.println("给容器创建 Person 实例...");
        return new Person("亚索", 20);
    }
}

然后我们先执行如下方法:

@Test
public void test02(){
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
}

输出结果为:

给容器创建 Person 实例...

说明单实例情况下, IOC 容器启动会调用方法创建对象到 IOC 容器中,以后每次从容器中获取

下面再看 prototype 情况下:

@Configuration
public class MainConfig {

    // 给容器中注册一个 bean
    // 类型为返回值类型,id 默认为方法名
    @Bean("person")
    @Scope("prototype")
    public Person person() {
        System.out.println("给容器创建 Person 实例...");
        return new Person("亚索", 20);
    }
}

同样执行上面的测试方法,会发现什么也没输出。

下面我们获取 bean 执行一下:

@Test
public void test02() {
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);

    Person person1 = applicationContext.getBean("person", Person.class);
    Person person2 = applicationContext.getBean("person", Person.class);

}

输出结果为:

给容器创建 Person 实例...
给容器创建 Person 实例...

1.5 懒加载

这是针对单实例情况,因为单实例是在创建容器的时候就实例化 bean 了

懒加载就是容器启动不创建对象,第一次获取时候才创建对象并初始化

老样子,配置类:

@Configuration
public class MainConfig {

    // 给容器中注册一个 bean
    // 类型为返回值类型,id 默认为方法名
    @Bean("person")
    @Lazy  // 懒加载
    public Person person() {
        System.out.println("给容器创建 Person 实例...");
        return new Person("亚索", 20);
    }
}

测试方法:

@Test
public void test02() {
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
    System.out.println("IOC容器创建完成...");
}

输出结果:

IOC容器创建完成...

说明创建容器时没有创建对象

更改一下测试方法:

@Test
public void test02() {
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
    System.out.println("IOC容器创建完成...");
    Person person1 = applicationContext.getBean("person", Person.class);
    Person person2 = applicationContext.getBean("person", Person.class);
    System.out.println(person1==person2);
}

输出结果为:

IOC容器创建完成...
给容器创建 Person 实例...
true

1.6 条件注册

@Conditional:按照一定的条件进行判断,满足条件给容器中注册 bean

首先写个配置类:

@Configuration
public class MainConfig {

    @Bean("riven")
    public Person person01() {
        return new Person("瑞雯", 16);
    }

    @Bean("timor")
    public Person person02() {
        return new Person("提莫", 22);
    }
}

再来个测试方法:

@Test
public void test03() {
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
    String[] names= applicationContext.getBeanNamesForType(Person.class);
    Arrays.stream(names).forEach(System.out::println);

    Map<String, Person> persons = applicationContext.getBeansOfType(Person.class);
    System.out.println(persons);
}

测试一下,输出结果为:

riven
timor
{riven=Person[name='瑞雯', age=16], timor=Person[name='提莫', age=22]}

下面我们希望在不同系统下注册不同的 bean,那就要使用 @Conditional 注解了.

该注解源码为:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
	Class<? extends Condition>[] value();
}

需要在使用时传递条件值,是一个 Condition 数组,进入 Condition 接口中查看,可以发现是个函数式接口:

@FunctionalInterface
public interface Condition {
    /**
     * ConditionContext:判断条件能使用的上下文环境(根据你注解的对象来决定是什么时候的环境)
     * AnnotatedTypeMetadata:当前标注了 @Conditional 注解的类或方法的注释信息
     */
	boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

我们只要实现 matches() 就完成一个条件.

下面我们来实现根据不同操作系统来注册不同的 bean

public class LinuxCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 1. 能获取到 IOC 使用的 BeanFactory
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        // 2. 获取该类的加载器
        ClassLoader classLoader = context.getClassLoader();
        // 3. 获取当前的环境信息
        Environment environment = context.getEnvironment();
        // 4. 获取到 bean 定义的注册类
        BeanDefinitionRegistry registry = context.getRegistry();

        String property = environment.getProperty("os.name");
        if (property.contains("Linux")){
            return true;
        }

        return false;
    }
}
public class WindowsCondition implements Condition {
    @Override
    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;
    }
}

然后更改配置类:

@Configuration
public class MainConfig {

    @Bean("riven")
    @Conditional({WindowsCondition.class})
    public Person person01() {
        return new Person("瑞雯", 16);
    }

    @Bean("timor")
    @Conditional({LinuxCondition.class})
    public Person person02() {
        return new Person("提莫", 22);
    }
}

测试方法为:

@Test
public void test03() {
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);

    Environment environment = applicationContext.getEnvironment();
    String property = environment.getProperty("os.name");
    System.out.println(property);

    String[] names = applicationContext.getBeanNamesForType(Person.class);
    Arrays.stream(names).forEach(System.out::println);

    Map<String, Person> persons = applicationContext.getBeansOfType(Person.class);
    System.out.println(persons);
}

结果为:

Windows 10
riven
{riven=Person[name='瑞雯', age=16]}

1.7 @Import 注册

给容器中注册组件一般有以下几种方式:

  • 包扫描 + 组件标注注解(@Controller@Repository@Service@Component
  • @Bean(自定义配置类中利用构造器创建对象实例)
  • @Import(快速给容器中导入一个组件)

第一种方式:直接导入

配置类:

@Configuration
@Import(Color.class)  // 可以传数组,如:{Color.class, Shape.class}
public class MainConfig {
  
}

测试方法:

@Test
public void test04() {
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);

    String[] names = applicationContext.getBeanDefinitionNames();
    Arrays.stream(names).forEach(System.out::println);
}

测试结果:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
com.ice.pojo.Color
riven

可以发现,Color 已经作为 bean 导入了,bean 的名字默认是全限定类名

第二种方式:ImportSelector

配置类:

@Configuration
@Import(MyImportSelector.class)
public class MainConfig {

}

MyImportSelector

public class MyImportSelector implements ImportSelector {
    @Override
    // 返回值就是要导入到容器中的全限定类名
    // AnnotationMetadata 当前配置类的所有注解信息,不仅仅是 @Import
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.ice.pojo.Color", "com.ice.pojo.Shape"};
    }
}

测试方法:

@Test
public void test04() {
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);

    String[] names = applicationContext.getBeanDefinitionNames();
    Arrays.stream(names).forEach(System.out::println);
}

测试结果:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
com.ice.pojo.Color
com.ice.pojo.Shape

可以看到两个类也被注册了

第三种方式:ImportBeanDefinitionRegistrar

配置类

// 声明该类是一个配置类
@Configuration
@Import({Color.class, Shape.class, MyImportBeanDefinitionRegistrar.class})
public class MainConfig {

}

MyImportBeanDefinitionRegistrar

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    /**
     * AnnotationMetadata:当前类的注解信息
     * BeanDefinitionRegistry:BeanDefinition 注册类
     *
     * 把所有需要添加到容器中的 bean,调用 BeanDefinitionRegistry.registerBeanDefinition 手工注册进来
     */
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        boolean shape = registry.containsBeanDefinition("com.ice.pojo.Shape");
        boolean color = registry.containsBeanDefinition("com.ice.pojo.Color");
        if (shape && color) {
            // 指定 bean 定义信息(bean 的类型、作用域等等)
            RootBeanDefinition rainbow = new RootBeanDefinition(Rainbow.class);
            // 注册一个 bean 的定义信息,并指定 bean 名
            registry.registerBeanDefinition("rainbow", rainbow);
        }
    }
}

测试方法:

@Test
public void test04() {
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);

    String[] names = applicationContext.getBeanDefinitionNames();
    Arrays.stream(names).forEach(System.out::println);
}

测试结果:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
com.ice.pojo.Color
com.ice.pojo.Shape
rainbow

1.8 工厂 Bean 注册

配置类

@Configuration
public class MainConfig {
    @Bean()
    public ColorFactoryBean colorFactoryBean(){
        return new ColorFactoryBean();
    }
}

ColorFactoryBean

// 创建一个 Spring 对的 FactoryBean
public class ColorFactoryBean implements FactoryBean<Color> {

    @Override
    // 返回一个 Color 对象,这个对象会添加到容器中
    public Color getObject() throws Exception {
        System.out.println("ColorFactoryBean 的 getObject() 方法");
        return new Color();
    }

    @Override
    public Class<?> getObjectType() {
        return Color.class;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }
}

测试方法:

@Test
public void test04() {
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);

    String[] names = applicationContext.getBeanDefinitionNames();
    Arrays.stream(names).forEach(System.out::println);

    System.out.println("===========================");

    Object colorBean = applicationContext.getBean("colorFactoryBean"); // 默认返回的是工厂方法注册的 bean
    System.out.println(colorBean.getClass());

    Object colorFactoryBean = applicationContext.getBean("&colorFactoryBean"); // 如果要获取工厂 bean 本身在开头加 &
    System.out.println(colorFactoryBean.getClass());
}

测试结果:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
colorFactoryBean
===========================
ColorFactoryBean 的 getObject() 方法
class com.ice.pojo.Color
class com.ice.bean.ColorFactoryBean

2. 生命周期

bean 的生命周期就是指 bean 从创建、初始化到销毁的过程.

下面介绍如何指定 bean 的初始化和销毁方法.

2.1 @Bean

传统方式在 xml 配置文件中指定 bean 的 init-method 和 destroy-method,注解使用如下所示:

Car 类:

public class Car {
    public Car() {
        System.out.println("Car constructor...");
    }

    public void init() {
        System.out.println("Car init...");
    }

    public void destroy() {
        System.out.println("Car destroy...");
    }
}

配置类:

@Configuration
public class LifeCycleConfig {

    @Bean(initMethod = "init", destroyMethod = "destroy")
    public Car car() {
        return new Car();
    }
}

测试方法:

@Test
public void test01() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(LifeCycleConfig.class);
    System.out.println("容器创建完成...");
    applicationContext.close();
}

输出结果:

Car constructor...
Car init...
容器创建完成...
Car destroy...

注意多实例的情况,容器在多实例情况下不会管理这个 bean,在第一次使用时创建实例,调用 init() 方法,容器不会调用 destroy() 方法.

2.2 InitializingBean 和 DisposableBean 接口

Dog 类:

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;


@Component
public class Dog implements InitializingBean, DisposableBean {
    public Dog() {
        System.out.println("Dog constructor...");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("Dog destroy...");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("Dog afterPropertiesSet...");
    }
}

配置类:

@Configuration
@ComponentScan("com.ice.pojo")
public class LifeCycleConfig {

}

测试方法:

@Test
public void test02() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(LifeCycleConfig.class);
    System.out.println("容器创建完成...");
    applicationContext.close();
}

输出结果:

Dog constructor...
Dog afterPropertiesSet...
容器创建完成...
Dog destroy...

2.3 @PostConstruct 和 @PreDestroy

这是 JSR250 提供的注解

Tiger 类:

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;


public class Tiger {
    public Tiger() {
        System.out.println("Tiger Constructor...");
    }

    @PostConstruct
    public void init(){
        System.out.println("Tiger @PostConstruct...");
    }

    @PreDestroy
    public void destroy(){
        System.out.println("Tiger @PreDestroy...");
    }
}

配置类:

@Configuration
@Import(Tiger.class)
public class LifeCycleConfig {

}

测试方法:

@Test
public void test03() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(LifeCycleConfig.class);
    System.out.println("容器创建完成...");
    applicationContext.close();
}

输出结果:

Tiger Constructor...
Tiger @PostConstruct...
容器创建完成...
Tiger @PreDestroy...

2.4 BeanPostProcessor 后置处理器

该接口源码为:

public interface BeanPostProcessor {

	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

}

显然,在创建 bean 之后,一个方法在初始化方法之前执行,一个方法在初始化方法之后执行.

MyBeanPostProcessor 类:

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization...");
        System.out.println("    " + beanName + "=>" + bean);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization...");
        System.out.println("    " + beanName + "=>" + bean);
        return bean;
    }
}

配置类:

@Configuration
@ComponentScan({"com.ice.pojo", "com.ice.bean"})
public class LifeCycleConfig {

}

测试方法:

@Test
public void test04() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(LifeCycleConfig.class);
    System.out.println("容器创建完成...");
    applicationContext.close();
}

测试结果:

postProcessBeforeInitialization...
    lifeCycleConfig=>com.ice.config.LifeCycleConfig$$EnhancerBySpringCGLIB$$293187cc@61dd025
postProcessAfterInitialization...
    lifeCycleConfig=>com.ice.config.LifeCycleConfig$$EnhancerBySpringCGLIB$$293187cc@61dd025
Dog constructor...
postProcessBeforeInitialization...
    dog=>com.ice.pojo.Dog@77167fb7
Dog afterPropertiesSet...
postProcessAfterInitialization...
    dog=>com.ice.pojo.Dog@77167fb7
容器创建完成...
Dog destroy...

Spring 底层大量使用 BeanPostProcessor,如 @Autowired@Async 等等注解的解析使用.

3. @Value 属性赋值

@Value 该注解可以传下面几种值:

  • 基本数值
  • #{}:SpEL
  • ${}:取出配置文件中的值(在运行环境变量里面的值)

Person 类:

public class Person {

    @Value("张三")
    private String name;
    @Value("#{20-2}")
    private Integer age;
    // ...
}

输出结果:

Person[name='张三', age=18]

下面测试 ${} 非方法赋值:

person.properties 文件:

person.nickname=riven

Person 类:

public class Person {

    @Value("张三")
    private String name;
    @Value("#{20-2}")
    private Integer age;
    @Value("${person.nickName}")
    private String nickName;
    // ...
}

配置类:

@Configuration
@PropertySource(value = {"classpath:/person.properties"})
public class PropertiesValuesConfig {

    @Bean
    public Person person() {
        return new Person();
    }
}

可以用 @PropertySource 来加载外部配置文件.

测试结果:

Person[name='张三', age=18, nickName='riven']

4. 自动装配

自动装配就是 Spring 利用依赖注入,完成对 IOC 容器中各个组件的依赖关系赋值.

4.1 @Autowired

@Autowired 默认按照类型匹配,其有一个参数 required,赋值为 false 时,允许将 null 值注入

如果一个类型有多个 bean,此时可以搭配 @Qualifier 注解指定 bean 的名称 (@Qualifier("beanName")

这里是先按照类型匹配,再按照名称匹配

4.2 @Primary

除了每次用 @Qualifier 注解,指定类型相同时匹配的 bean 的名字,我们也可以使用 @Primary 注解指定默认 bean,当有多个相同类型的 bean 时,首先用它

不能和 @Qualifiler 同时使用!

4.3 @Resource

默认按照组件名称进行装配,如果找不到则按照类型匹配

@Resource(name="beanName")

@Resource 不支持注入 null 值,不支持 @Primary 的功能

4.4 @Inject

需要导入:

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

@Autowired 功能一样,但是 @Inject 没有参数

4.5 Aware 注入 Spring 底层组件

自定义组件想要使用 Spring 底层的一些组件(ApplicationContext,BeanFactory…)可以实现 XXXXAware 接口,其顶级接口为 Aware.

在创建对象的时候,会调用接口规定的方法注入相关组件

XXXXAware其实都是由 ApplicationContextAwareProcessor 后置处理器处理的,以回调方式传入底层组件.

class ApplicationContextAwareProcessor implements BeanPostProcessor{
    // ...
}

它重写了 BeanPostProcessor 后置处理器的 postProcessBeforeInitialization() 方法:

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

    // 这里就判断了,如果没有实现那些个 XXXAware 接口,则不处理,直接返回 bean
    if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
          bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
          bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware ||
          bean instanceof ApplicationStartupAware)) {
        return bean;
    }

    AccessControlContext acc = null;

    if (System.getSecurityManager() != null) {
        acc = this.applicationContext.getBeanFactory().getAccessControlContext();
    }

    // invokeAwareInterfaces() 方法就是处理那些个 XXXAware 接口重写方法
    if (acc != null) {
        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
            invokeAwareInterfaces(bean);
            return null;
        }, acc);
    }
    else {
        invokeAwareInterfaces(bean);
    }

    return bean;
}

下面是 invokeAwareInterfaces() 的源码:

private void invokeAwareInterfaces(Object bean) {
    if (bean instanceof EnvironmentAware) {
        ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
    }
    if (bean instanceof EmbeddedValueResolverAware) {
        ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
    }
    if (bean instanceof ResourceLoaderAware) {
        ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
    }
    if (bean instanceof ApplicationEventPublisherAware) {
        ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
    }
    if (bean instanceof MessageSourceAware) {
        ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
    }
    if (bean instanceof ApplicationStartupAware) {
        ((ApplicationStartupAware) bean).setApplicationStartup(this.applicationContext.getApplicationStartup());
    }
    if (bean instanceof ApplicationContextAware) {
        ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
    }
}

下面我们自己写一个栗子:

自定义组件:

public class Red implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("传入的 IOC:" + applicationContext);
        this.applicationContext = applicationContext;
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("当前 bean 的名字:" + name);
    }

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        String s = resolver.resolveStringValue("你好${os.name},我是#{4*5}");
        System.out.println("解析的字符串:" + s);
    }
}

配置类:

@Configuration
@Import(Red.class)
public class AwareConfig {
    //...
}

测试方法:

public static void main(String[] args) {
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AwareConfig.class);
}

测试结果:

当前 bean 的名字:com.ice.pojo.Red
解析的字符串:你好Windows 10,我是20
传入的 IOC:org.springframework.context.annotation.AnnotationConfigApplicationContext@27973e9b, started on Fri Mar 12 20:42:50 CST 2021

4.6 Profile

Profile 是 Spring 为我们提供的,可以根据当前环境,动态的激活和切换一系列组件的功能.

@Profile 指定组件在哪个环境情况下才能被注册到容器中,不指定,等价于 @Profile("default"),任何环境下都能注册这个组件.

@Profile 注解可以写在上和方法

一个配置文件中有的标注 @Profile 注解,有的没有,那么没有标注的 bean 任何时候都会被加载.

配置类:

import com.ice.pojo.Rainbow;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

import javax.sql.DataSource;
import java.beans.PropertyVetoException;

@Configuration
public class ProfileConfig {

    @Bean
    @Profile("test")
    public Rainbow rainbow() {
        return new Rainbow();
    }

    @Bean("TestDataSource")
    @Profile("test")
    public DataSource dataSourceTest() throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser("root");
        dataSource.setUser("root");
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/ssm");
        dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
        return dataSource;
    }

    @Bean("DevDataSource")
    @Profile("dev")
    public DataSource dataSourceDev() throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser("root");
        dataSource.setUser("root");
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/mybatis");
        dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
        return dataSource;
    }

    @Bean("ProdDataSource")
    @Profile("prod")
    public DataSource dataSourceProd() throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser("root");
        dataSource.setUser("root");
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/jdbc");
        dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
        return dataSource;
    }
}

测试方法:

  1. 使用命令行动态传递参数

    @Test
    public void test01() {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ProfileConfig.class);
    
        String[] names = applicationContext.getBeanDefinitionNames();
        Arrays.stream(names).forEach(System.out::println);
    
    }
    

    执行前要设置虚拟机参数:

    在这里插入图片描述

    接着:

    在这里插入图片描述

    输出结果为:

    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    org.springframework.context.annotation.internalCommonAnnotationProcessor
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory
    profileConfig
    DevDataSource
    
  2. 代码方式激活:

    @Test
    public void test01() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.getEnvironment().setActiveProfiles("test");
        applicationContext.register(ProfileConfig.class);
        applicationContext.refresh();
    
        String[] names = applicationContext.getBeanDefinitionNames();
        Arrays.stream(names).forEach(System.out::println);
    
    
    }
    

    输出结果:

    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    org.springframework.context.annotation.internalCommonAnnotationProcessor
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory
    profileConfig
    rainbow
    TestDataSource
    

5. AOP

5.1 AOP 功能测试

【pom.xml】

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.ice</groupId>
    <artifactId>spring-aop</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.4</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.3.4</version>
        </dependency>
    </dependencies>
</project>

【业务逻辑类】

public class Calculator {
    public int div(int i, int j) {
        System.out.println("Calculator div ...");
        return i / j;
    }
}

【日志切面类】

切面类里的方法需要动态感知 Calculator.div() 运行到哪里,然后执行

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;

import java.util.Arrays;

@Aspect
public class LogAspects {

    @Pointcut("execution(public int com.ice.aop.Calculator.*(..))")
    public void pointCut() {
    }

    @Before("pointCut()")
    public void logStart(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        System.out.println("@Before:" + joinPoint.getSignature().getName() + "运行...参数列表是:{" + Arrays.toString(args) + "}");
    }

    @After("pointCut()")
    public void logEnd(JoinPoint joinPoint) {
        System.out.println("@After:" + joinPoint.getSignature().getName() + "结束...");
    }

    @AfterReturning(value = "pointCut()", returning = "result")
    public void logReturn(JoinPoint joinPoint, Object result) {
        System.out.println("@AfterReturning:" + joinPoint.getSignature().getName() + "正常返回...运行结果:{" + result + "}");
    }

    @AfterThrowing(value = "pointCut()", throwing = "exception")
    public void logException(JoinPoint joinPoint, Exception exception) {
        System.out.println("@AfterThrowing:" + joinPoint.getSignature().getName() + "异常...异常信息:{" + exception + "}");
    }
}
  • JoinPoint 如需要,必须写在第一个形参位置
  • 如需返回值或异常信息需要在注解里指定接收的形参

【配置类】

import com.ice.aop.Calculator;
import com.ice.aop.LogAspects;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy  // 开启基于注解的 AOP 模式
public class AOPConfig {

    @Bean
    public Calculator calculator() {
        return new Calculator();
    }

    @Bean
    public LogAspects logAspects() {
        return new LogAspects();
    }
}

【测试方法】

@Test
public void test01() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AOPConfig.class);
    Calculator calculator = applicationContext.getBean(Calculator.class);
    calculator.div(1, 1);
    System.out.println("==================================");
    calculator.div(1, 0);
}

【输出结果】

@Before:div运行...参数列表是:{[1, 1]}
Calculator div ...
@AfterReturning:div正常返回...运行结果:{1}
@After:div结束...
==================================
@Before:div运行...参数列表是:{[1, 0]}
Calculator div ...
@AfterThrowing:div异常...异常信息:{java.lang.ArithmeticException: / by zero}
@After:div结束...

5.2 AOP 原理

5.2.1 @EnableAspectJAutoProxy 注解

配置类加上这个注解开启 AOP 模式,其源码为:

![06](annotation-img/06.png)![06](annotation-img/06.png)@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

	boolean proxyTargetClass() default false;

	boolean exposeProxy() default false;

}

它导入了组件 AspectJAutoProxyRegistrar,然后再进去这个类查看,可以发现其实现了 ImportBeanDefinitionRegistrar 接口,重写的 registerBeanDefinitions() 方法用于自定义注册 bean(详见 1.7):

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

	@Override
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // ...
	}
}

下面看看它注册什么 bean 来开启 AOP 的注解模式.

  1. 首先在如下图示位置打断点,执行上面的 AOP 测试方法:

    在这里插入图片描述

  2. 进入 AopConfigUtilsregisterAspectJAnnotationAutoProxyCreatorIfNecessary() 方法:

    在这里插入图片描述
    这里进入的是一个参数的方法,然后其内部调用重载的 registerAspectJAnnotationAutoProxyCreatorIfNecessary() 方法,将 source=null 传入到该重载方法中:
    在这里插入图片描述
    此处调用了 registerOrEscalateApcAsRequired() 方法,可以发现传入了 AnnotationAwareAspectJAutoProxyCreator.class,我们要创建的组件就是这个类的实例!

  3. 进入 AopConfigUtilsregisterOrEscalateApcAsRequired() 方法:

    private static BeanDefinition registerOrEscalateApcAsRequired(
        Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
    
        Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
        
        // 进入该方法时 cls = AnnotationAwareAspectJAutoProxyCreator.class
    	
        // 判断组件 AUTO_PROXY_CREATOR_BEAN_NAME 是否存在如果这个组件已经有了,就做一些工作,并且返回 null
        // AUTO_PROXY_CREATOR_BEAN_NAME = "org.springframework.aop.config.internalAutoProxyCreator"
        if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
            BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
            if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
                int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
                int requiredPriority = findPriorityForClass(cls);
                if (currentPriority < requiredPriority) {
                    apcDefinition.setBeanClassName(cls.getName());
                }
            }
            return null;
        }
    	
        // 我们第一次执行到这应该没有这个组件信息,会直接跳到这里执行
        // 这里已经比较熟悉了,就是前面说的 @Import 注册的第三种方式
        
        // 指定 bean 定义信息(bean 的类型、作用域等等)
        RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
        beanDefinition.setSource(source);
        beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
        beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        
        // 这里创建组件定义信息,并指定组件名为 org.springframework.aop.config.internalAutoProxyCreator
        // AUTO_PROXY_CREATOR_BEAN_NAME = "org.springframework.aop.config.internalAutoProxyCreator"
        registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
        return beanDefinition;
    }
    
  4. 创建好组件 org.springframework.aop.config.internalAutoProxyCreator定义信息后,方法返回到第一步的方法中:

    在这里插入图片描述

    至此,组件 AnnotationAwareAspectJAutoProxyCreator 定义信息注册成功.

后面见到 @EnableXXX 注解理解原理也是同样的方法,看看它注册了什么组件,执行了什么方法

5.2.2 AnnotationAwareAspectJAutoProxyCreator 组件

首先,我们先看看继承关系:

在这里插入图片描述

我们需要关注的主要是后置处理器(bean 初始化完成前后所做的处理)和 BeanFactory(自动装配)

下面我们看看需要在哪些方法上打断点:

  • AbstractAutoProxyCreatorsetBeanFactory() 方法
  • AbstractAutoProxyCreatorpostProcessBeforeInstantiation() 方法
  • AbstractAutoProxyCreatorpostProcessAfterInitialization() 方法
  • AbstractAdvisorAutoProxyCreatorsetBeanFactory() 方法
  • AnnotationAwareAspectJAutoProxyCreatorinitBeanFactory() 方法

另外再给配置类中的 bean 方法打上断点.

下面开始令人激动的 debug 环节,记录下调用的方法流程:

  1. 传入配置类,创建 IOC 容器:

    在这里插入图片描述

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

    在这里插入图片描述

  3. refresh() 方法中,有一步 registerBeanPostProcessors(beanFactory),用来注册拦截 bean 创建的 BeanPostProcessors

    在这里插入图片描述

  4. 进入 registerBeanPostProcessors() 方法

    在这里插入图片描述

  5. 进入 PostProcessorRegistrationDelegateregisterBeanPostProcessors() 方法:

    public static void registerBeanPostProcessors(
        ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
    	
        // 先获取 IOC 容器中已经定义了的需要创建对象的所有 BeanPostProcessor 的定义信息[1]
        String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
    
        // 将已有的后置处理器的定义信息添加到容器中
        int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
        beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
    
        // Separate BeanPostProcessors that implement PriorityOrdered, Ordered, and the rest.
        // 这里注意,分离的后置处理器是 postProcessorNames 里面的
        List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
        List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
        List<String> orderedPostProcessorNames = new ArrayList<>();
        List<String> nonOrderedPostProcessorNames = new ArrayList<>();
        for (String ppName : postProcessorNames) {
            if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
                priorityOrderedPostProcessors.add(pp);
                if (pp instanceof MergedBeanDefinitionPostProcessor) {
                    internalPostProcessors.add(pp);
                }
            }
            else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
                orderedPostProcessorNames.add(ppName);
            }
            else {
                nonOrderedPostProcessorNames.add(ppName);
            }
        }
    
        // First, register the BeanPostProcessors that implement PriorityOrdered.
        sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
        registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
    
        // Next, register the BeanPostProcessors that implement Ordered.
        // 注意,AnnotationAwareAspectJAutoProxyCreater 就实现了 Ordered 的接口,所以在这里处理
        List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
        for (String ppName : orderedPostProcessorNames) {
            BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
            orderedPostProcessors.add(pp);
            if (pp instanceof MergedBeanDefinitionPostProcessor) {
                internalPostProcessors.add(pp);
            }
        }
        sortPostProcessors(orderedPostProcessors, beanFactory);
        registerBeanPostProcessors(beanFactory, orderedPostProcessors);
    
        // Now, register all regular BeanPostProcessors.
        List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
        for (String ppName : nonOrderedPostProcessorNames) {
            BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
            nonOrderedPostProcessors.add(pp);
            if (pp instanceof MergedBeanDefinitionPostProcessor) {
                internalPostProcessors.add(pp);
            }
        }
        registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);
    
        // Finally, re-register all internal BeanPostProcessors.
        sortPostProcessors(internalPostProcessors, beanFactory);
        registerBeanPostProcessors(beanFactory, internalPostProcessors);
    
        // Re-register post-processor for detecting inner beans as ApplicationListeners,
        // moving it to the end of the processor chain (for picking up proxies etc).
        beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
    }
    

    [1] postProcessorNames 中的已有的后置处理器的定义

    在这里插入图片描述

  6. 我们看看上面代码如何注册 org.springframework.aop.config.internalAutoProxyCreator 组件的

    1. 首先根据名字从 BeanFactory 中获取该类的对象:

      在这里插入图片描述

      为啥是这个地方呢,因为 AnnotationAwareAspectJAutoProxyCreater 类实现了Ordered 接口.

    2. 进入 beanFactory.getBean() 方法:

      在这里插入图片描述

    3. 然后在 doGetBean() 方法中:

      在这里插入图片描述

      我们看到,其实是调用 getSingleton() 方法来获取这个 bean 的

    4. 我们再进入 getSingleton() 方法中:

      首先看看能不能获取已有的 bean:

      在这里插入图片描述

      显然,这里是 null,接下来可以看到在尝试通过 getObject() 方法获取新创建的 bean

      在这里插入图片描述

    5. getObject() 方法没有固定的方法体,这里它的实现是通过 lambda 表达式传过去的,它的方法体长这样:

      在这里插入图片描述

      • singletonFactory 的类型是 ObjectFactory 类型,它是一个函数式接口,只有 getObject() 一个方法

      • 可以看到里面的确有创建 bean 的方法 createBean()

    6. 至此可以总结出,注册 BeanPostProcessor,实际上就是创建 BeanPostProcessor 对象,保存在容器中.

    7. 下面看看具体如何创建名字为 internalAutoProxyCreatorBeanPostProcessor——AnnotationAwareAspectJAutoProxyCreater

      1)、createBean() 方法中,调用 doCreateBean() 方法创建:

      在这里插入图片描述

      2)、在 doCreateBean() 方法中,首先创建 bean(这部分在后面有,这里略过), 之后再进行初始化,下面是初始化的部分:

      在这里插入图片描述

      3)、下面看看初始化的详细过程:

      protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
          
          if (System.getSecurityManager() != null) {
              AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                  // 如果这个 bean 实现了 Aware 接口,就执行对应接口的方法[1]
                  invokeAwareMethods(beanName, bean);
                  return null;
              }, getAccessControlContext());
          }
          else {
              // 如果这个 bean 实现了 Aware 接口,就执行对应接口的方法[1]
              invokeAwareMethods(beanName, bean);
          }
      
          Object wrappedBean = bean;
          if (mbd == null || !mbd.isSynthetic()) {
              // 执行后置处理器的 PostProcessorsBeforeInitialization() 方法
              // 这里不用怕自己调自己玩完了,其实默认实现方法体是将传入的 bean 直接返回不作处理
              wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
          }
      
          try {
              // 执行自定义的初始化方法[2]
              // 就是实现了 InitializingBean 接口重写的 afterPropertiesSet() 方法
              invokeInitMethods(beanName, wrappedBean, mbd);
          }
          catch (Throwable ex) {
              throw new BeanCreationException(
                  (mbd != null ? mbd.getResourceDescription() : null),
                  beanName, "Invocation of init method failed", ex);
          }
          if (mbd == null || !mbd.isSynthetic()) {
              // 执行后置处理器的 PostProcessorsAfterInitialization() 方法
              wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
          }
      
          return wrappedBean;
      }
      

      [1] invokeAwareMethods() 源码

      在这里插入图片描述

      • 这里就调用 AbstractAdvisorAutoProxyCreatorsetBeanFactory() 方法:

        在这里插入图片描述

      • 再然后调用的是 AnnotationAwareAspectJAutoProxyCreatorinitBeanFactory() 方法:

        在这里插入图片描述

      这里将之前的 BeanFactory 包装了一下.

      [2] invokeInitMethods() 源码

      在这里插入图片描述

  7. 上面执行完一圈,又回到了 PostProcessorRegistrationDelegateregisterBeanPostProcessors() 方法:

    在这里插入图片描述

    先排序,然后调用 registerBeanPostProcessors() 方法注册到 beanFactory 中:

    在这里插入图片描述

以上创建和注册 AnnotationAwareAspectJAutoProxyCreator 的过程

  1. 继续 debug,回到 AbstractApplicationContextrefresh() 方法

    在这里插入图片描述

    然后进入到 finishBeanFactoryInitialization() 方法中:

    在这里插入图片描述

    这里将完成 BeanFactory 初始化工作,创建剩下的单实例 bean.

    1. 遍历获取容器中所有的 bean,依次创建对象

      在这里插入图片描述

    2. 进入 getBean() 方法:

      在这里插入图片描述

    3. 再进入 doGetBean() 方法:

      在这里插入图片描述

      该方法首先检查该实例是否已经注册,先从缓存中获取当前 bean,如果能获取到,说明 bean 是之前创建过的,直接使用,否则再创建

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

    4. 如果没有创建过实例,doGetBean() 接下来将调用 createBean() 方法

      之前看注册后置处理器组件的时候略过了创建的过程,只看了初始化的过程,这里展开看具体的创建过程

      在这里插入图片描述

    5. 下面详细看看 resolveBeforeInstantiation() 方法:

      在这里插入图片描述

      这里要区分两种后置处理器:

      • BeanPostProcessorpostProcessBeforeInitialization() 方法是在 bean 初始化之前调用
      • InstantiationAwareBeanPostProcessorpostProcessBeforeInstantiation() 方法是在 bean 实例化之前调用

5.2.3 创建 AOP 代理

看看 InstantiationAwareBeanPostProcessorpostProcessBeforeInstantiation() 方法是如何创建 AOP 代理的

在这里插入图片描述

我们需要关注要创建 AOP 代理的过程,所以 debug 到 Caculator bean 的地方

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

    在这里插入图片描述

  2. 判断当前 bean 是否是基础类型(Advice、Pointcut、AdvisorAopInfrastructureBean)或切面 Aspect,以及是否需要跳过

    在这里插入图片描述

    • 第一个判断:

      protected boolean isInfrastructureClass(Class<?> beanClass) {
          return (super.isInfrastructureClass(beanClass) ||
                  (this.aspectJAdvisorFactory != null && this.aspectJAdvisorFactory.isAspect(beanClass)));
      }
      

      其中,调用的父类函数为:

      protected boolean isInfrastructureClass(Class<?> beanClass) {
          boolean retVal = Advice.class.isAssignableFrom(beanClass) ||
              Pointcut.class.isAssignableFrom(beanClass) ||
                  Advisor.class.isAssignableFrom(beanClass) ||
                      AopInfrastructureBean.class.isAssignableFrom(beanClass);
          if (retVal && logger.isTraceEnabled()) {
              logger.trace("Did not attempt to auto-proxy infrastructure class [" + beanClass.getName() + "]");
          }
          return retVal;
      }
      
    • 第二个判断:

      protected boolean shouldSkip(Class<?> beanClass, String beanName) {
          // 寻找候选的增强器,其实就是通知方法
          List<Advisor> candidateAdvisors = findCandidateAdvisors();
          for (Advisor advisor : candidateAdvisors) {
              // 判断每一个增强器是否是 AspectJPointcutAdvisor 类型,是则返回 true
              if (advisor instanceof AspectJPointcutAdvisor &&
                  ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
                  return true;
              }
          }
          return super.shouldSkip(beanClass, beanName);
      }
      

    当前是 Caculator bean ,所以返回 null,继续 debug,进入如下方法:

    在这里插入图片描述

  3. 执行 new 方法创建对象

  4. 下面 debug 进入 postProcessAfterInitialization() 方法

    在这里插入图片描述

    主要就一个方法,在需要的情况下包装 bean,下面看看如何包装:

    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }
    
        // 获取候选增强器,然后得到当前 bean 的所有可用的排过序的增强器(通知方法)
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            // 保存当前 bean 在 advisedBeans 中
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            // 创建当前 bean 的代理对象[1]
            Object proxy = createProxy(
                bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }
    
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }
    

    [1] 创建代理对象的过程

    • 调用 createProxy() 方法:

      在这里插入图片描述

    • 利用代理工厂创建代理对象

      在这里插入图片描述

    • 代理工厂如何创建呢? 首先创建 AOP 代理

      在这里插入图片描述

    • 创建 AOP 代理,可以看到是首先获取AOP代理工厂,然后再创建

      在这里插入图片描述

    • 这里的 createAopProxy() 方法就是创建 AOP 代理的地方了:

      在这里插入图片描述
      Spring 自动决定动态代理的实现方法!

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

5.2.4 目标方法的执行

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

我们在测试方法中打断点:

在这里插入图片描述

Step into 后,可以进入 CglibAopProxyintercept() 方法:

在这里插入图片描述

用于拦截目标方法的执行,intercept() 方法整体流程如下:

在这里插入图片描述

下面看看如何获取拦截器链的,首先进入 getInterceptorsAndDynamicInterceptionAdvice()

在这里插入图片描述

其核心是调用 advisorChainFactorygetInterceptorsAndDynamicInterceptionAdvice() 方法:

  1. 新建 interceptorList 用于存储拦截器,其长度是确定的,包含一个默认的 org.springframework.aop.interceptor.ExposeInvocationInterceptor.ADVISOR 和 4 个增强器(因为写了 4 个切面方法)

    在这里插入图片描述

  2. 遍历所有增强器,将其转为 Interceptor 并返回

    在这里插入图片描述

    可以看到,不管哪种情况,都要调用 registry.getInterceptors(advisor) 方法:

    在这里插入图片描述

每一个通知方法包装为拦截器,利用 MethodInterceptor 机制 ,形成拦截器链

  1. 获取拦截器链后,创建 CglibMethodInvocation 对象并调用其 proceed() 方法:

    retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
    

    就在这里开始反射调用通知方法和目标方法,下面看看他的链式调用过程

    在这里插入图片描述

    该测试代码的详细的调用流程图如下图所示:

    • 正常运行结束

      在这里插入图片描述

    • 抛出异常

      在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值