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;}@Overridepublic 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 默认为方法名@Beanpublic 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);// 获取 beanPerson bean = applicationContext.getBean(Person.class);System.out.println(bean);// 打印 Person 类型 bean 的 idString[] 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=falseFilter[] 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")@Scopepublic 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 {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {// 1. 能获取到 IOC 使用的 BeanFactoryConfigurableListableBeanFactory 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 {@Overridepublic 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 当前配置类的所有注解信息,不仅仅是 @Importpublic 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();}@Overridepublic Class<?> getObjectType() {return Color.class;}@Overridepublic 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"); // 默认返回的是工厂方法注册的 beanSystem.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...");}@Overridepublic void destroy() throws Exception {System.out.println("Dog destroy...");}@Overridepublic 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...");}@PostConstructpublic void init(){System.out.println("Tiger @PostConstruct...");}@PreDestroypublic 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 {@Nullabledefault Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean;}@Nullabledefault Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {return bean;}}

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

MyBeanPostProcessor 类:

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("postProcessBeforeInitialization...");System.out.println("    " + beanName + "=>" + bean);return bean;}@Overridepublic 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 {@Beanpublic 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 接口,则不处理,直接返回 beanif (!(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;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {System.out.println("传入的 IOC:" + applicationContext);this.applicationContext = applicationContext;}@Overridepublic void setBeanName(String name) {System.out.println("当前 bean 的名字:" + name);}@Overridepublic 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 {@Beanpublic Calculator calculator() {return new Calculator();}@Beanpublic 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 {@Overridepublic 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 类型,是则返回 trueif (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();
    

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

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

    • 正常运行结束

    • 抛出异常

【Spring】注解驱动开发相关推荐

  1. 0、Spring 注解驱动开发

    0.Spring注解驱动开发 0.1 简介 <Spring注解驱动开发>是一套帮助我们深入了解Spring原理机制的教程: 现今SpringBoot.SpringCloud技术非常火热,作 ...

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

    @EnableAspectJAutoProxy注解 在配置类上添加@EnableAspectJAutoProxy注解,便能够开启注解版的AOP功能.也就是说,如果要使注解版的AOP功能起作用的话,那么 ...

  3. Spring注解驱动开发学习总结8:自动装配注解@Autowire、@Resource、@Inject

    Spring注解驱动开发学习总结8:自动装配注解@Autowire.@Resource.@Inject 1.自动装配@Autowire.@Resource.@Inject 1.1 构建bookDao ...

  4. 【Spring注解驱动开发】二狗子让我给他讲讲@EnableAspectJAutoProxy注解

    写在前面 最近,二狗子入职了新公司,新入职的那几天确实有点飘.不过慢慢的,他发现他身边的人各个身怀绝技啊,有Spring源码的贡献者,有Dubbo源码的贡献者,有MyBatis源码的贡献者,还有研究A ...

  5. SPRING注解驱动开发-雷神课程超详细笔记

    SPRING注解驱动开发-雷神课程超详细笔记 时间:2021-03-21 2022-04-06更新:最近翻起一年多前写的笔记复习,还是收获颇多,很多当时无法理解的知识现在慢慢能理解了,可能是工作一年的 ...

  6. spring注解驱动开发-5 Spring AOP实现

    Spring AOP实现 前言 AOP案例实现 1.编写目标类 2.编写切面类 3.编写配置类 4.编写测试类 end... 前言 AOP为Aspect Oriented Programming的缩写 ...

  7. spring注解驱动开发-10 Servlet3.0

    Spring AOP实现 前言 servlet3.0简介 ServletContainerInitializer shared libraries(共享库) / runtimes pluggabili ...

  8. spring注解驱动开发-8 Spring 扩展原理

    Spring 扩展原理 前言 BeanFactoryPostProcessor 测试实例编写 ExtConfig MyBeanFactoryPostProcessor ExtTest 源码分析 Bea ...

  9. spring注解驱动开发-4 Spring 自动装配

    Spring 自动装配 前言 Spring 自动装配的几种方式 1.@Autowired @Qualifier("组件id") @Primary 2.@Resource方式 3.@ ...

  10. spring注解驱动开发-7 Spring声明式事务

    Spring 声明式事务 前言 @EnableTransactionManagement AutoProxyRegistrar InfrastructureAdvisorAutoProxyCreato ...

最新文章

  1. 卡内基梅隆大学提出基于学习的动作捕捉模型,用自监督学习实现人类3D动作追踪
  2. java actor_Akka笔记之Actor简介
  3. UA OPTI512R 傅立叶光学导论8 多元脉冲函数
  4. wireshark抓取https并解密方法一
  5. 使用BootStrap编写网页,如何设置全屏页面背景?
  6. C语言 FileStreaming(文件流)
  7. 自定义注解完成数据库切库
  8. Gym - 101194F(后缀数组)
  9. 315页 A Tutorial on Graph Neural Networks for NLP
  10. 每日小记2012.5.1
  11. 整理OpenResty+Mysql+Tomcat+JFinal+Cannal+HUI
  12. python-网易云简单爬虫
  13. 综合型集团该如何利用数字化转型支撑磅礴多元的服务?
  14. 浅谈XPS文件格式。
  15. 各种类型相机rtsp取流格式大汇总
  16. 国产操作系统统信UOS的简单故障维护,系统崩溃小妙招
  17. java综合技术分享
  18. 我的世界神奇宝贝服务器注册指令,《我的世界》神奇宝贝MOD召唤指令大全
  19. 考研英语|传统文化英语高频词汇
  20. 东北大学5月校赛c题

热门文章

  1. 穷举法(枚举法)实例解析
  2. [编译链接装载系列]之聊聊目标文件与ELF格式
  3. 天风掌财社新股认购如何操作?
  4. 分时线的9代表什么_一位从亏损到稳赚的老股民告诉你:为什么要打板?
  5. 王垠:完全用Linux工作及其后续
  6. Wifi文件传输项目总结
  7. web错误代码ERR_BLOCKED_BY_RESPONSE
  8. 基于javaweb+jsp的晚会抽奖系统(java+Jdbc+Servlet+Ajax+mysql)
  9. android开发,动态图标,Android动态加载很难?带你快速实现App图标热更新
  10. 算法实现: 在二叉树中找到两个节点的最近公共祖先