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);
}
}
主要用到的是
prototype
和signleton
此时再运行 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;
}
}
测试方法:
-
使用命令行动态传递参数
@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
-
代码方式激活:
@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 的注解模式.
-
首先在如下图示位置打断点,执行上面的 AOP 测试方法:
-
进入
AopConfigUtils
的registerAspectJAnnotationAutoProxyCreatorIfNecessary()
方法:
这里进入的是一个参数的方法,然后其内部调用重载的registerAspectJAnnotationAutoProxyCreatorIfNecessary()
方法,将source=null
传入到该重载方法中:
此处调用了registerOrEscalateApcAsRequired()
方法,可以发现传入了AnnotationAwareAspectJAutoProxyCreator.class
,我们要创建的组件就是这个类的实例! -
进入
AopConfigUtils
的registerOrEscalateApcAsRequired()
方法: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; }
-
创建好组件
org.springframework.aop.config.internalAutoProxyCreator
的定义信息后,方法返回到第一步的方法中:至此,组件
AnnotationAwareAspectJAutoProxyCreator
定义信息注册成功.
后面见到
@EnableXXX
注解理解原理也是同样的方法,看看它注册了什么组件,执行了什么方法
5.2.2 AnnotationAwareAspectJAutoProxyCreator 组件
首先,我们先看看继承关系:
我们需要关注的主要是后置处理器(bean 初始化完成前后所做的处理)和 BeanFactory(自动装配)
下面我们看看需要在哪些方法上打断点:
AbstractAutoProxyCreator
的setBeanFactory()
方法AbstractAutoProxyCreator
的postProcessBeforeInstantiation()
方法AbstractAutoProxyCreator
的postProcessAfterInitialization()
方法AbstractAdvisorAutoProxyCreator
的setBeanFactory()
方法AnnotationAwareAspectJAutoProxyCreator
的initBeanFactory()
方法
另外再给配置类中的 bean 方法打上断点.
下面开始令人激动的 debug 环节,记录下调用的方法流程:
-
传入配置类,创建 IOC 容器:
-
注册配置类,调用
refresh()
刷新容器 -
在
refresh()
方法中,有一步registerBeanPostProcessors(beanFactory)
,用来注册拦截 bean 创建的BeanPostProcessors
-
进入
registerBeanPostProcessors()
方法 -
进入
PostProcessorRegistrationDelegate
的registerBeanPostProcessors()
方法: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
中的已有的后置处理器的定义 -
我们看看上面代码如何注册
org.springframework.aop.config.internalAutoProxyCreator
组件的-
首先根据名字从 BeanFactory 中获取该类的对象:
为啥是这个地方呢,因为
AnnotationAwareAspectJAutoProxyCreater
类实现了Ordered
接口. -
进入
beanFactory.getBean()
方法: -
然后在
doGetBean()
方法中:我们看到,其实是调用
getSingleton()
方法来获取这个 bean 的 -
我们再进入
getSingleton()
方法中:首先看看能不能获取已有的 bean:
显然,这里是
null
,接下来可以看到在尝试通过getObject()
方法获取新创建的 bean -
getObject()
方法没有固定的方法体,这里它的实现是通过 lambda 表达式传过去的,它的方法体长这样:-
singletonFactory
的类型是ObjectFactory
类型,它是一个函数式接口,只有getObject()
一个方法 -
可以看到里面的确有创建 bean 的方法
createBean()
-
-
至此可以总结出,注册
BeanPostProcessor
,实际上就是创建BeanPostProcessor
对象,保存在容器中. -
下面看看具体如何创建名字为
internalAutoProxyCreator
的BeanPostProcessor
——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()
源码-
这里就调用
AbstractAdvisorAutoProxyCreator
的setBeanFactory()
方法: -
再然后调用的是
AnnotationAwareAspectJAutoProxyCreator
的initBeanFactory()
方法:
这里将之前的
BeanFactory
包装了一下.
[2]
invokeInitMethods()
源码 -
-
-
上面执行完一圈,又回到了
PostProcessorRegistrationDelegate
的registerBeanPostProcessors()
方法:先排序,然后调用
registerBeanPostProcessors()
方法注册到beanFactory
中:
以上创建和注册
AnnotationAwareAspectJAutoProxyCreator
的过程
-
继续 debug,回到
AbstractApplicationContext
的refresh()
方法然后进入到
finishBeanFactoryInitialization()
方法中:这里将完成 BeanFactory 初始化工作,创建剩下的单实例 bean.
-
遍历获取容器中所有的 bean,依次创建对象
-
进入
getBean()
方法: -
再进入
doGetBean()
方法:该方法首先检查该实例是否已经注册,先从缓存中获取当前 bean,如果能获取到,说明 bean 是之前创建过的,直接使用,否则再创建
只要创建好的 bean 都会被缓存起来
-
如果没有创建过实例,
doGetBean()
接下来将调用createBean()
方法之前看注册后置处理器组件的时候略过了创建的过程,只看了初始化的过程,这里展开看具体的创建过程
-
下面详细看看
resolveBeforeInstantiation()
方法:这里要区分两种后置处理器:
BeanPostProcessor
的postProcessBeforeInitialization()
方法是在 bean 初始化之前调用InstantiationAwareBeanPostProcessor
的postProcessBeforeInstantiation()
方法是在 bean 实例化之前调用
-
5.2.3 创建 AOP 代理
看看 InstantiationAwareBeanPostProcessor
的 postProcessBeforeInstantiation()
方法是如何创建 AOP 代理的
我们需要关注要创建 AOP 代理的过程,所以 debug 到 Caculator
bean 的地方
-
判断当前 bean 是否在 advisedBean 中(保存了需要增强的 bean)
-
判断当前 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,进入如下方法: -
-
执行
new
方法创建对象 -
下面 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.2.4 目标方法的执行
容器中保存组建的代理对象(增强后的对象),这个对象里面保存了详细信息(如增强器,目标对象,…)
我们在测试方法中打断点:
Step into 后,可以进入 CglibAopProxy
的 intercept()
方法:
用于拦截目标方法的执行,intercept()
方法整体流程如下:
下面看看如何获取拦截器链的,首先进入 getInterceptorsAndDynamicInterceptionAdvice()
:
其核心是调用 advisorChainFactory
的 getInterceptorsAndDynamicInterceptionAdvice()
方法:
-
新建
interceptorList
用于存储拦截器,其长度是确定的,包含一个默认的org.springframework.aop.interceptor.ExposeInvocationInterceptor.ADVISOR
和 4 个增强器(因为写了 4 个切面方法) -
遍历所有增强器,将其转为
Interceptor
并返回可以看到,不管哪种情况,都要调用
registry.getInterceptors(advisor)
方法:
每一个通知方法包装为拦截器,利用
MethodInterceptor
机制 ,形成拦截器链
-
获取拦截器链后,创建
CglibMethodInvocation
对象并调用其proceed()
方法:retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
就在这里开始反射调用通知方法和目标方法,下面看看他的链式调用过程
该测试代码的详细的调用流程图如下图所示:
-
正常运行结束
-
抛出异常
-