Spring注解驱动开发

一、组件注册

1、@Configuration & @Bean

@Configuration:声明这个类是一个配置类,加入ioc容器中。

@Bean:注册组件。默认的beanId是函数名,可以用value属性进行指定。

java 复制代码
@Configuration
public class MyConfig {

    @Bean
    public Person person() {
        return new Person("ZhangSan", 10);
    }
}

测试类:

java 复制代码
@Test
public void testIoc() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
	Person person = applicationContext.getBean(Person.class);
	System.out.println(person);
	String[] names = applicationContext.getBeanNamesForType(Person.class);
	Arrays.asList(names).forEach(System.out::println);
}

2、@ComponentScan & @ComponentScans

包扫描,扫描包下面的@Controller、@Service等组件类

等同于配置文件的 <context:component-scan >

@ComponentScan

  • value

要扫描的包,等同于basePackages

  • basePackages

同value

  • includeFilters

指定只包含的组件

属性值是一个@Filter数组

  • excludeFilters

指定排除的组件。

属性值是一个@Filter数组

示例1:扫描com.linwillen包下的组件,排除@Controller、@Service注解的类

java 复制代码
@Configuration
@ComponentScan(value = "com.linwillen", excludeFilters = {
    @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})
})
public class MyConfig {

    @Bean
    public Person person() {
        return new Person("ZhangSan", 10);
    }
}

示例2:只包含@Controller、@Service注解的类。(需要先禁用默认的过滤规则useDefaultFilters=false,因为默认的过滤规则是全部扫描)

java 复制代码
@Configuration
@ComponentScan(value = "com.linwillen",
        useDefaultFilters = false,
        includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})
})
public class MyConfig {

    @Bean
    public Person person() {
        return new Person("ZhangSan", 10);
    }
}

@ComponentScan.Filter

配置包扫描的过滤规则

@Filter

  • type

要排除的方式:FilterType。

  • Annotation:按照注解
  • Assignable_type:按照给定的类
  • AspectJ:使用AspectJ表达式
  • Regex:使用正则表达式
  • Custom:使用自定义规则,该规则必须是TypeFilter的实现类
  • value

要排除的类型,同classes

  • classes

同value

  • pattern

正则表达式

自定义TypeFilter规则:

MetadataReader:读取到的当前正在扫描的类的信息

MetadataReaderFactory:可以获取到其他任何类的信息

java 复制代码
public class MyTypeFilter implements TypeFilter {

    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        // 获取当前正在扫描类的注解信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        // 获取当前正在扫描的类的类信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        // 获取当前的资源信息
        metadataReader.getResource();

        String className = classMetadata.getClassName();

        return "com.linwillen.dao.UserDao".equals(className);
    }
}

3、@Scope

调整作用域。

  • value

取值:

  • ConfigurableBeanFactory.SCOPE_SINGLETON : "singleton",单实例(默认值),ioc容器启动就会调用方法创建对象到ioc容器中。
  • ConfigurableBeanFactory.SCOPE_PROTOTYPE : "prototype",多实例,ioc容器启动时并不会调用方法创建对象,每次获取时才会调用方法创建对象。
  • WebApplicationContext.SCOPE_REQUEST:"request",同一个请求创建一个实例
  • WebApplicationContext.SCOPE_SESSION:"session",同一个session创建一个实例
  • scopeName

同'value'

例如:

java 复制代码
@Bean
@Scope
public Person person() {
    return new Person("WangWu", 30);
}

4、@Lazy

针对单实例的Bean。

单实例Bean默认在容器启动时创建对象。懒加载在第一次使用Bean时才创建对象并初始化。

例如:

java 复制代码
@Bean
@Lazy
public Person person02() {
    return new Person("WangWu", 30);
}

5、@Conditional

按照条件进行判断,满足条件才给容器中注册Bean。

该注解在Spring、SpringBoot底层中经常使用。

value属性需要传入一个判断条件,该判断条件是一个实现了Condition接口的类。

自定义判断条件类:

java 复制代码
public class MyCondition implements Condition {


    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata annotatedTypeMetadata) {
        // 获取类加载器
        ClassLoader classLoader = context.getClassLoader();
        // 获取ioc使用的beanFactory
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        // 获取到bean定义的注册类
        BeanDefinitionRegistry registry = context.getRegistry();
        ResourceLoader resourceLoader = context.getResourceLoader();

        // 获取当前环境信息
        Environment environment = context.getEnvironment();
        String osName = environment.getProperty("os.name");
        return osName.startsWith("Windows");
    }
}

在bean上加上判断条件:

java 复制代码
@Conditional(MyCondition.class)
@Bean(name = "person01")
public Person person01() {
    return new Person("Lisi", 22);
}

该注解也可以用在类上,对类中所有bean统一设置:

java 复制代码
@Configuration
@Conditional(MyCondition.class)
public class MyConfig {

    @Bean("person")
    public Person person() {
        return new Person("ZhangSan", 10);
    }

    @Bean("person01")    
    public Person person01() {
        return new Person("lisi", 18);
    }
}

6、@Import

给容器中注册组件:

  • 包扫描+组件标注注解(@Controller、@Service、@Repository、@Component)[自己写的类]
  • @Bean [导入的第三方包里面的组件]
  • @Import[快速的给容器中导入一个组件]
  • 使用Spring提供的FactoryBean(工厂bean)

使用方式:

三种使用方法可以混合使用。

  1. @Import(要导入的组件类数组):容器中就会自动注册这个组件,id默认是类的全限定名。
  2. @Import(ImportSelector):ImportSelector会返回要导入的类的全限定名的数组
  3. @Import(ImportBeanDefinitionRegistrar):手动注册bean

自定义ImportSelector:

AnnotationMetadata:当前标注@Import注解的类的所有注解信息。

返回值不能是null,否则会报空指针异常。如果需要返回的内容,则需使用:return new String[0]。

java 复制代码
public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"com.linwillen.bean.Yellow", "com.linwillen.bean.Blue"};
    }
}

自定义ImportBeanDefinitionRegistrar:

AnnotationMetadata:当前类的注解信息

BeanDefinitionRegistry:BeanDefinition注册类。需要添加到容器中的Bean,可以通过BeanDefinitionRegistry.registerBeanDefinition手工注册进来。

java 复制代码
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {


    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        String[] beanDefinitionNames = registry.getBeanDefinitionNames();
        // Arrays.asList(beanDefinitionNames).forEach(System.out::println);

        // 判断ioc容器中是否注册了指定id的Bean
        boolean redDefinition = registry.containsBeanDefinition("com.linwillen.bean.Red");
        boolean blueDefinition = registry.containsBeanDefinition("com.linwillen.bean.Blue");

        if(redDefinition && blueDefinition) {
            // 调用registry的registerBeanDefinition方法手工注册Bean
            // 使用RootBeanDefinition指定要注册的Bean的类型
            RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
            // 指定注册进容器的bean的id
            registry.registerBeanDefinition("rainBow", beanDefinition);
        }
    }
}
java 复制代码
@Configuration
@Conditional(MyCondition.class)
@Import({Color.class, Red.class, MyImportSelector.class})  // 可以同时配置要导入的类名、ImportSelector、ImportBeanDefinitionRegistrar
public class MyConfig {

    @Bean("person")
    public Person person() {
        return new Person("ZhangSan", 10);
    }
}

7、FactoryBean

Spring提供的 工厂Bean接口

自定义FactoryBean,用来生成Color的Bean:

java 复制代码
public class ColorFactoryBean implements FactoryBean<Color> {
    // 返回一个color对象,这个对象会添加进容器中
    @Override
    public Color getObject() throws Exception {
        return new Color();
    }

    // 返回的对象的类型
    @Override
    public Class<?> getObjectType() {
        return Color.class;
    }

    // 是否为单例:true代表这个bean是单实例的,false代表每次获取都会创建一个新的bean
    @Override
    public boolean isSingleton() {
        return true;
    }
}

添加该FactoryBean为Bean:

java 复制代码
@Configuration
@Conditional(MyCondition.class)
public class MyConfig {

    @Bean("person")
    public Person person() {
        return new Person("ZhangSan", 10);
    }

    @Bean("person01")
    public Person person01() {
        return new Person("lisi", 18);
    }

    @Bean("colorTest")
    public ColorFactoryBean colorFactoryBean() {
        return new ColorFactoryBean();
    }
}

在获取colorTest这个Bean时,实际获取到的bean对象的类型是ColorFactoryBean::getObjectType()返回的类型,即Color类型。获取到的Bean对象是ColorFactoryBean::getObject()创建的对象。

如果想要获取ColorFactoryBean类型的bean对象,需要在BeanID前添加前缀&,即&colorTest。这个前缀是在BeanFactory.FACTORY_BEAN_PREFIX中定义的。

二、生命周期

1、@Bean中配置生命周期

使用@Bean注解的initMethod、destroyMethod两个属性配置bean的初始化和销毁方法。

java 复制代码
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");    }
}

配置类编写:

java 复制代码
@Configuration
public class MyLifeCycleConfig {

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

2、实现InitializingBean和DisposableBean接口

InitializingBean接口的afterPropertiesSet()实现初始化逻辑;
DispoableBean接口的destroy()实现销毁逻辑;

java 复制代码
public class Car implements InitializingBean, DisposableBean {

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

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

    @Override
    public void destroy() {
        System.out.println("car...destroy");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        init();
    }
}

3、@PostConstruct & @PreDestroy

JSR250注解:

  • @PostConstrct,标注在方法上,对象执行构造行数,创建并赋值之后执行
  • @PreDestroy,标注在方法上,在容器移除对象之前执行
java 复制代码
public class Car{
    public Car() {
        System.out.println("Car constructor...");
    }
    
    @PostConstruct
    public void init() {
        System.out.println("car .... init ...");
    }

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

4、BeanPostProcessor接口

Bean的后置处理器,在bean的初始化前后进行处理。

  • postProcessBeforeInitialization:Bean构造方法执行之后、初始化方法执行之前 处理
  • postProcessAfterInitialization:Bean初始化之后处理

自定义BeanPostProcessor

java 复制代码
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("before init..." + beanName);
        return bean;  // 返回传入的原始bean,或者进行了包装之后的bean
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("after init ..." + beanName);
        return bean;// 返回传入的原始bean,或者进行了包装之后的bean
    }
}

输出结果:

java 复制代码
before init...myLifeCycleConfig
after init ...myLifeCycleConfig
Car constructor...
before init...car
car .... init ...
after init ...car
car...destroy

执行过程:

  1. AbstractAutowireCapableBeanFactory.doCreateBean中调用popolulateBean为bean填充属性,然后调用initializeBean开始进行bean的初始化以及bean初始化的前后处理。
  2. AbstractAutowireCapableBeanFactory.doCreateBean调用initializeBean方法:
  3. 该方法会先调用applyBeanPostProcessorsBeforeInitialization(warppedBean, beanName);初始化前前置处理
  4. 然后调用invokeInitMethods对bean进行初始化
  5. 最后调用applyBeanPostProcessorsAfterInitialization,初始化后置处理。


applyBeanPostProcessorsBeforeInitialization会遍历所有的BeanPostProcessor,执行每个BeanPostProcessor的postProcessBeforeInitialization方法,如果某个BeanPostProcessor::postProcessBeforeInitialization返回null,则直接退出循环,不再执行后面的BeanPostProcessor。

源码中使用的BeanPostProcessor

源码中有很多组件通过BeanPostProcessor进行实现。

以ApplicationContextAwareProcessor为例。

ApplicationContextAwareProcessor可以为Bean中注入ioc容器。利用的就是BeanPostProcessor的postProcessBeforeInitialization方法:获取bean实现的接口,然后根据实现的接口不同,调用相应的方法,把ioc容器传入对应的方法参数内。

源码:

java 复制代码
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
    AccessControlContext acc = null;

    if (System.getSecurityManager() != null &&
        (bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
         bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
         bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
        acc = this.applicationContext.getBeanFactory().getAccessControlContext();
    }

    // ....
    
    invokeAwareInterfaces(bean);
    
    return bean;
}
java 复制代码
private void invokeAwareInterfaces(Object bean) {
    if (bean instanceof Aware) {
        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 ApplicationContextAware) {
            ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
        }
    }
}

在Bean中使用示例:

Car类实现ApplicationContextWareProcesser中判断的可以注入ioc容器的接口,然后实现对应的方法

java 复制代码
public class Car implements ApplicationContextAware {

    private ApplicationContext applicationContext;

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

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("为car注入applicationContext");
        this.applicationContext = applicationContext;
    }
}

@PostConstruct、@PreDestroy注解的解析是在InitDestroyAnnotationBeanPostProcessor类中实现的,该类也是通过BeanPostProcesser进行实现的。

三、属性赋值

1、@Value

@Value赋值:

  • 基本数值
    @Value("张三")
  • SpEL
    @Value(#{1 + 2})
  • 配置文件、环境变量中的值
    @Value(${os.name})

示例:

java 复制代码
public class Person {

    @Value("张三")
    private String name;

    @Value("#{ 10 + 8} ")
    private Integer age;

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

    // getter/setter方法
}

2、@PropertySource

使用@PropertySource指定读取的配置文件路径:

@PropertySource是一个可重复使用的注解。

@PropertySource的value属性可以传多个路径。

或者使用@PropertySources指定多个@PropertySource

java 复制代码
@Configuration
@PropertySource(value = "classpath:/person.properties", encoding = "utf-8")
public class MyPropertyConfig {

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

四、自动装配

1、@Autowired

复制代码
作用:

	自动按照类型注入,只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功。

	如果IoC容器中没有任何bean的类型和要注入的变量的类型匹配,则报错。

	如果IoC容器中该类型的bean存在多个,则将要注入的变量的变量名作为bean的id进行二次匹配:

		如果根据变量名可以找到唯一的bean,则进行注入。

		如果根据变量名匹配不到,则报错。

@Autowired有一个属性required,默认为true。如果设置为false,则该对象可以在获取不到bean时默认为null。

复制代码
出现位置:可以使属性上,也可以是方法、参数上。
细节:在使用注解进行注入时,变量的setter方法就不是必须的了

(1)标注在属性上

java 复制代码
public class Boss {
    @Autowired
    private Car car;
}

(2)标注在方法上

Spring容器创建当前对象,就会调用方法,完成赋值。

方法使用的参数,自定义类型的值从ioc容器中获取。

java 复制代码
@Autowired
public void setCar(Car car) {
    this.car = car;
}

(3)标注在有参构造器上

默认加在ioc容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值等操作。

给bean移除无参构造器,添加一个有参构造器,为该有参构造器添加@Autowired注解。Spring容器启动时会调用该有参构造器,并从ioc容器中获取参数对应类型的bean对象进行注入。

java 复制代码
@Autowired
public Boss(Car car) {
    this.car = car;
}

(4)标注在构造器参数位置

java 复制代码
public Boss(@Autowired Car car) {
    this.car = car;
}

如果当前类只有一个有参构造器,Spring容器创建该bean只能调用该有参构造器,有参构造器的@Autowired可以省略,参数依然可以从ioc容器中获取:
使用构造函数进行注入时,需要标注final来表示这个注入的变量不能被改变。

java 复制代码
private final Car car;

public Boss(Car car) {
    this.car = car;
}

(5) 标注在@Bean注解的方法上

在@Configuration类中通过@Bean将Color加入容器,并使用@Autowired注入Car:

此处的@Autowired也可以省略

java 复制代码
@Configuration
public class MyConfig {
    @Bean
    public Color color(@Autowired Car car) {
        return new Color();
    }
}

2、@Qualifier

作用:

复制代码
在按照类型匹配的基础上,再按照名称匹配注入。

它在给类的成员变量注入时,不能单独使用,要和@Autowired配合使用。

它在给方法参数进行注入时,可以单独使用。

属性:

复制代码
value:用于指定要注入的bean的id。

3、@Primary

让Spring自动装配时默认使用首选的Bean。

当某个bean在容器中存在多个同类型bean,且使用@Autowired时没有明确指定@Qualifier,则默认使用@Primary首选转配。也可以继续使用@Qualifier指定装配的Bean的名字。

4、@Resource

JSR250规范。

作用:直接按照bean的id进行注入。它可以独立使用。

属性:

  • name:用于指定bean的id。
  • 不能支持@Primary 以及required=false的功能

5、@Inject

JSR330规范:@Inject

需要导入依赖:

和@Autowired功能类似,但是没有required属性。

java 复制代码
<dependency>
    <gourpId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

6、Aware接口

自定义组件想要使用Spring容器底层的一些组件(例如ApplicationContext、BeanFactory、BeanName等),实现xxxAware接口,在创建对象的时候,会调用接口规定的方法,注入相应的组件。
xxxAware的总接口为Aware接口。xxxAware的对应的解析为xxxAwarePropressor。

示例:在Bean中注入ioc容器ApplicationContext,实现ApplicationContextAware接口

java 复制代码
public class Car implements ApplicationContextAware {

    private ApplicationContext applicationContext;

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

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("为car注入applicationContext");
        this.applicationContext = applicationContext;
    }
}

String值解析器(可以解析String、SpEL表达式、环境变量值、配置文件值):

java 复制代码
public class Person implements  EmbeddedValueResolverAware {
    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        String str = resolver.resolveStringValue("test: #{ 10 * 2} ==== ${person.nickName} ==== ${os.name} .");
        System.out.println(str);
    }
}

7、@Profile

五、AOP使用和原理

1、AOP使用

java 复制代码
@Aspect
public class LogAspect {

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

    @Before("pointCut()")
    public void logStart(JoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("@Before The method "+name+" begins with "+ Arrays.toString(args));

    }

    @After("pointCut()")
    public void logEnd(JoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("@After The method "+name+" ends with "+ Arrays.toString(args));

    }

    @AfterReturning(value = "pointCut()",returning = "result")
    public void logReturning(JoinPoint joinPoint, Object result){
		String name = joinPoint.getSignature().getName();
        System.out.println("@AfterReturning 除法正常返回...返回值:" + result);
    }

    @AfterThrowing(value = "pointCut()",throwing = "exception")
    public void logException(JoinPoint joinPoint, Exception exception){
		String name = joinPoint.getSignature().getName();
        System.out.println("@AfterThrowing 除法出现异常...异常信息:" + exception.getMessage());
    }

    @Around(value = "pointCut()", argNames = "joinPoint")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        // 前置处理
        System.out.println("@Around 方法执行前: " + joinPoint.getSignature().getName());

        // 执行目标方法
        Object result = joinPoint.proceed(); // 关键点:调用目标方法

        // 后置处理
        System.out.println("@Around 方法执行后: " + joinPoint.getSignature().getName());

        return result; // 返回结果
    }

}

将切面类和要切入的类都加入ioc容器,并且使用@EnableAspectJAutoProxy在配置类中开启Aspectj自动代理:

java 复制代码
@EnableAspectJAutoProxy
@Configuration
public class AopConfig {

    @Bean
    public LogAspect logAspect() {
        return new LogAspect();
    }

    @Bean
    public MathCalculator mathCalculator() {
        return new MathCalculator();
    }

}

测试类:

java 复制代码
	@Test
    public void testAop() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class);
        MathCalculator mathCalculator = context.getBean(MathCalculator.class);
        mathCalculator.div(1, 0);
        context.close();
    }

2、@EnableAspectJAutoProxy原理

@EnableAspectJAutoProxy中使用@Import(AspectJAutoProxyRegistrar.class)加入了一个组件

AspectJAutoProxyRegistrar类实现了ImportBeanDefinitionRegistrar接口,可以手动加载组件(特别注意这里只是加了BeanDefinition(Bean的定义信息),真正的Bean还没有被创建出来)

(1)AspectJAutoProxyRegistrar的执行过程:

  • 先判断是否存在beanID为org.springframework.aop.config.internalAutoProxyCreator的bean,不存在则注册一个AnnotationAwareAspectJAutoProxyCreator类型的bean定义信息
  • 然后获取类上面的@EnableAspectJAutoProxy属性信息,判断proxyTargetClass属性、exposeProxy属性,做一些后续处理

AnnotationAwareAspectJAutoProxyCreator的继承关系如下图:AnnotationAwareAspectJAutoProxyCreator最终实现了BeanFactoryAware、BeanPostProcessor

java 复制代码
注:
BeanPostProcessor接口定义的方法是:
- postProcessBeforeInitialization()
- postProcessAfterInitialization()

InstantiationAwareBeanPostProcessor接口继承了BeanPostProcessor,另外又增加了两个方法:
- postProcessBeforeInstantiation()
- postProcessAfterInstantiation()

3、注册AnnotationAwareAspectJAutoProxyCreator

AOP的底层原理就是创建AnnotationAwareAspectJAutoProxyCreator这个Bean的实例,把这个创建过程搞懂了,AOP就能理解了

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

  2. 注册配置类,调用refresh()刷新容器。以下为refresh()方法内部的执行逻辑:

  3. registerBeanPostProcessor(beanFactory);注册bean的后置处理器

    a. 先获取ioc容器已经定义了的需要创建对象的所有BeanPostProcessor

    b. 给容器中加别的BeanPostProcessor

    c. 优先注册实现了PriorityOrdered接口的BeanProcessor

    d. 再给容器中注册实现了Ordered接口的BeanPostProcessor

    e. 注册没实现优先级接口的BeanPostProcessor

    f. 注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象,保存在容器中

    例如:创建internalAutoProxyCreator的BeanPostProcessor【AnnotationAwareAspectAutoProxyCreator】

    • 创建bean的实例
    • populateBean给bean的各种属性赋值
    • initializeBean初始化bean
      • invokeAwareMethods:处理Aware接口的方法回调
      • applyBeanPostProcessorsBeforeInitialization:应用后置处理器的beforeInitialization方法
      • invokeInitMethods:执行@Bean注解等自定义的Bean初始化方法
      • applyBeanPostProcessorsAfterInitialization:执行后置处理器的afterInitialization方法
    • BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)创建成功;-> aspectJAdvisorsBuilder

    g. 把BeanPostProcessor注册到BeanFactory中,beanFactoy.addBeanPostProcessor(postProcessor)

  4. finishBeanFactoryInitialization(beanFactory)完成bean初始化工作,创建剩下的单实例bean(此时创建的bean,是那些不属于后置处理器的普通单实例bean。后置处理器本身也是bean,后置处理器的bean在第3.f步进行创建)

    a. 遍历获取容器中所有的bean,依次创建对象:getBean(beanName)。getBean -> doGetBean -> getSingleton

    b. 创建bean;AnnotationAwareAspectJAutoProxyCreator会在所有bean创建之前进行一次拦截,调用实现的InstantiationAwareBeanPostProcessor接口的postProcessBeforeInstantiation()方法。

    • 创建好的bean都会被缓存起来
    • createBean();创建bean
      • resolveBeforeInstantiation(beanName, mdbToUse)解析beforeInstantiation。
        希望后置处理器能在此返回一个代理对象,如果能返回代理对象就使用,如果不能就下一步doCreateBean
      • 后置处理器先尝试返回对象
    • doCreateBean(beanName, mbdToUse, args) ,真正的去创建一个bean实例 ,和第3.f环节中的操作流程一样
java 复制代码
// 拿到所有BeanPostProcessor后置处理器,如果该后置处理器属于InstantiationAwareBeanPostProcessor,则执行该后置处理器的postProcessBeforeInstantiation方法。
// (注意postProcessBeforeInstantiation方法 不同于 postProcessBeforeInitialization方法)
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
    bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}

BeanPostProcessor的postProcessBeforeInitialization和postProcessAfterInitialization是在Bean对象创建完成初始化前后调用的;

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

即AnnotationAwareAspectJAutoProxyCreator实现的InstantiationAwareBeanPostProcessor的方法,会在任何bean创建之前先尝试返回bean的实例。

4、InstantiationAwareBeanPostProcessor接口

AnnotationAwareAspectJAutoProxyCreator的父类AbstractAutoProxyCreator,实现了InstantiationAwareBeanPostProcessor接口,它的作用是:

  1. 每一个bean创建之前,调用postProcessBeforeInstantiation()

    1.1. 判断当前bean是否在advisedBeans中(advisedBeans保存了所有需要增强的bean)

    1.2. 调用isInfrastructureClass()判断当前Bean是否是基础类型

    • 1.2.1. aop相关基础设施类型的bean不应该被动态代理。
    • 1.2.2. 切面的bean不应该被动态代理:带有@Aspect注解的bean

    1.3. 调用shouldSkip()判断当前bean是否需要跳过

  2. AbstractAutoProxyCreator的postProcessAfterInstantiation()直接返回true

  3. AbstractAutoProxyCreator的postProcessBeforeInitialization()直接返回传入的bean对象

  4. 创建好bean对象后,调用postProcessAfterInitialization()

    调用wrapIfNecessary()方法,如果需要的话对bean进行包装。

    a. 同postProcessBeforeInstantiation()一样进行一些校验:

    ■ 当前bean是否在advisedBeans中

    ■ 当前bean是否是基础类型

    ■ 当前bean是否需要跳过

    b. 调用getAdvicesAndAdvisorsForBean()获取当前bean的所有增强器(通知方法):Object[] specificInterceptors

    ⅰ. 找到候选的所有增强器

    ⅱ. 找到能在当前bean中使用的增强器

    ⅲ. 给增强器排序

    c. 将bean保存到advisedBeans中,表示当前bean已经被增强处理了

    d. 如果当前Bean需要增强,创建当前bean的代理对象;

    ⅰ. 获取所有增强器(通知方法)

    ⅱ. 保存到proxyFactory中

    ⅲ. 创建代理对象

    Spring在DefaultAopProxyFactory::createAopProxy中自动决定使用哪种方式创建代理对象:

    ● JdkDynamicAopProxy

    ● ObjenesisCglibAopProxy

  5. 给容器中返回cglib增强了的代理对象

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

5、MethodInterceptor方法拦截器链

目标方法执行:

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

  1. 进入CglibAopProxy的intercept()方法,拦截目标方法的执行
    a. 根据ProxyFactory类型的advised对象,获取到将要执行的目标方法的拦截器链
    拦截器链:每一个通知方法被包装成MethodInterceptor方法拦截器,后续利用MethodInterceptor机制执行
    List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    ⅰ. List interceptorList保存所有拦截器
    一个默认的ExposeInvocationInterceptor + 自定义的增强通知方法
    ⅱ. 遍历所有的增强器,将其转换为Interceptor:registry.getInterceptors(advisor);
    将Advisor转换为MethodInterceptor(MethodInterceptor继承于Interceptor):
    判断如果Advisor属于MethodInterceptor类型,则直接加入interceptors数组;
    通过遍历AdvisorAdapter适配器(前置通知适配器、返回结果通知适配器、异常通知适配器),尝试将Advisor转换为MethodInterceptor,加入interceptors数组

@AfterThrowing注解的增强器转换为AspectJAfterThrowingAdvice

@AfterReturning注解的增强器转换为AfterReturningAdviceInterceptor

@After注解的增强器转换为AspectJAfterAdvice

@Before注解的增强器转换为MethodBeforeAdviceInterceptor

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

c. 如果有拦截器链,把需要执行的目标对象、目标方法、拦截器链等信息传入创建一个CglibMethodInvocation对象,并调用该对象的proceed()方法。

java 复制代码
Object retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();

拦截器链的调用过程:proceed()的执行过程。

CglibMethodInvocation继承于ReflectiveMethodInvocation,实际执行的是ReflectiveMethodInvocation::proceed()

  1. 如果没有拦截器执行目标方法,或者拦截器的索引和(拦截器数组-1)大小一样(即到了最后一个拦截器),执行目标方法。
    因为拦截器的链式执行是类似入栈操作,是后入先出,所以拦截器链数组中的拦截器顺序与执行顺序相反
  2. 否则获取到拦截器链中(当前索引 += 1)的拦截器,执行其invoke()方法
  3. 拦截器的invoke()方法,会在进行一定操作后,递归链式调用proceed()方法。
    例如@Before增强器对应的MethodBeforeAdviceInterceptor拦截器,其invoke()方法为:
    @AfterReturning增强器对应的AfterReturningAdviceInterceptor拦截器,其invoke()方法为:
java 复制代码
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
    this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
    return mi.proceed();  // 链式调用,继续进行后面的链的执行
}
java 复制代码
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
    Object retVal = mi.proceed(); // 链式调用,继续进行后面的链的执行
    this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
    return retVal;
}

6、AOP增强器注解执行顺序

根据注解进行排序

spring4顺序依次为:@Around、@Before、@After、@AfterReturning/@AfterThrowing、@Around

spring5顺序依次为:@Around、@Before、@AfterReturning/@AfterThrowing、@After、@Around

AbstractAutoProxyCreator执行postProcessBeforeInstantiation(),其中进行shouldSkip()等判断时,会调用findCandidateAdvisors()获取所有候选的增强器。

AnnotationAwareAspectJAutoProxyCreator中实现了findCandidateAdvisors()方法。AnnotationAwareAspectJAutoProxyCreator通过调用this.aspectJAdvisorsBuilder.buildAspectJAdvisors()获取所有的增强器,aspectJAdvisorsBuilder.buildAspectJAdvisors()通过advisorFactory.getAdvisors(factory)获取增强器。

aspectJAdvisorsBuilder、advisorFactory都是在AnnotationAwareAspectJAutoProxyCreator的initBeanFactory()方法中实例化出来

java 复制代码
@Override
protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
	if (this.aspectJAdvisorFactory == null) {
            this.aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory);
        }
        this.aspectJAdvisorsBuilder =
                new BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, 
                                                             this.aspectJAdvisorFactory);
}

即最后调用Collections.sort(增强器, METHOD_COMPARATOR)对所有增强器进行排序,排序算法为METHOD_COMPARATOR对象。

METHOD_COMPARATOR对象的定义赋值源码:

java 复制代码
private static final Comparator<Method> METHOD_COMPARATOR;

static {
    CompoundComparator<Method> comparator = new CompoundComparator<Method>();
    // 首选排序方式:根据注解进行排序,顺序依次为:@Around、@Before、@After、@AfterReturning、@AfterThrowing
    comparator.addComparator(new ConvertingComparator<Method, Annotation>(
        new InstanceComparator<Annotation>(
            Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class),
        new Converter<Method, Annotation>() {
            @Override
            public Annotation convert(Method method) {
                AspectJAnnotation<?> annotation =
                    AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method);
                return (annotation != null ? annotation.getAnnotation() : null);
            }
        }));
    // 当两个增强器的注解相同时,继续用第二种排序方式进行比较:以字符串方式比较增强器的方法名称
    comparator.addComparator(new ConvertingComparator<Method, String>(
        new Converter<Method, String>() {
            @Override
            public String convert(Method method) {
                return method.getName();
            }
        }));
    METHOD_COMPARATOR = comparator;
}

所以,如果想自定义一个类似ReflectiveAspectJAdvisorFactory的类MyReflectiveAspectJAdvisorFactory,更改AOP注解排序方式,则需:

  1. 自定义一个类似EnableAspectJAutoProxy的注解MyEnableAspectJAutoProxy,令其@Import注解加载的组件为自定义的组件MyAspectJAutoProxyRegistrar
  2. 修改MyAspectJAutoProxyRegistrar中的AopConfigUtiles为自定义的MyAopConfigUtiles,因为AopConfigUtiles会将AnnotationAwareAspectJAutoProxyCreator加载为beanId为internalAutoProxyCreator的bean。以及修改判断的使能AspectJ注解为MyEnableAspectJAutoProxy
  3. 修改MyAopConfigUtiles中registerAspectJAnnotationAutoProxyCreatorIfNecessary()方法加载的bean类为MyAnnotationAwareAspectJAutoProxyCreator
  4. 修改MyAnnotationAwareAspectJAutoProxyCreator的initBeanFactory()方法中创建的aspectJAdvisorFactory对象为new MyReflectiveAspectJAdvisorFactory(beanFactory)
  5. 在MyReflectiveAspectJAdvisorFactory中的静态代码块中修改成自己需要的排序方式

7、总结

1、Spring 启动,执行invokeBeanFactoryPostProcessors(beanFactory);创建实现了【BeanFactoryProcessor、BeanDefinitionRegistryPostProcessor】接口的Bean。并执行它们的【postProcessBeanFactory()、postProcessBeanDefinitionRegistry()】方法。

2、通过1步骤,向容器中添加了ConfigurationClassPostProcessor【BeanDefinitionRegistryPostProcessor】对象,并调用它的BeanDefinitionRegistryPostProcessor()方法,解析了@Configuration、@EnableAspectJAutoProxy、@Import等注解,又向容器中添加了其他BeanDefinition。比如AnnotationAwareAspectJAutoProxyCreator【SmartInstantiationAwareBeanPostProcessor、BeanFactoryAware】

3、执行registerBeanPostProcessors(beanFactory);创建实现了【BeanPostProcessor、DestructionAwareBeanPostProcessor、InstantiationAwareBeanPostProcessor、SmartInstantiationAwareBeanPostProcessor、MergedBeanDefinitionPostProcessor】接口的Bean。比如就会创建AnnotationAwareAspectJAutoProxyCreator的Bean对象

4、在创建AnnotationAwareAspectJAutoProxyCreator对象的过程中,会调用AbstractAutoProxyCreator.postProcessBeforeInstantiation() ,扫描带有 @Aspect 注解的类,生成 AspectMetadata 对象,并将每个通知方法(@Before、@Around 等)会被封装为 Advisor 对象,包含 Pointcut(切点表达式)和 Advice(通知逻辑)。

5、会在finishBeanFactoryInitialization(beanFactory);方法中通过AbstractAutoProxyCreator.postProcessAfterInitialization()方法,该方法通过 shouldProxyTargetClass 判断是否需要创建代理,如果需要创建代理,根据目标对象实现接口,判断生成JDK代理还是CGLIB代理对象

6、当调用代理对象的方法时,会触发 InvocationHandler.invoke(JDK 代理)或 MethodInterceptor.intercept(CGLIB 代理),进入通知链执行

六、声明式事务使用和原理

1、声明式事务使用

java 复制代码
@EnableTransactionManagement
@Configuration
@ComponentScan({"com.linwillen.dao", "com.linwillen.service"})
public class TxConfig {

	@Bean
	public DataSource dataSource() throws Exception{
		ComboPooledDataSource dataSource = new ComboPooledDataSource();
		dataSource.setUser("root");
		dataSource.setPassword("root");
		dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
		dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/ry-seata");
		return dataSource;
	}

	@Bean
	public JdbcTemplate jdbcTemplate() throws Exception {
		// @Configuration类中的@Bean方法在Spring中被特殊执行,
		// 此处虽然传入dataSource(),但是Spring不会执行dataSource()方法重新new一个对象,而是会直接从ioc容器中获取
		return new JdbcTemplate(dataSource());
	}

	@Bean
	public PlatformTransactionManager transactionManager() throws Exception{
		return new DataSourceTransactionManager(dataSource());
	}
}
java 复制代码
@Repository
public class UserDao {

	@Autowired
	private JdbcTemplate jdbcTemplate;

	public void insert() {
		String sql = "insert into undo_log (branch_id, xid, context, rollback_info, log_status, log_created, log_modified) values(?, ?, ?, ?, ?, ?, ?)";
		jdbcTemplate.update(sql, "2", "Ind01", "0001", "张三", 1, new Date(), new Date());
	}
}
java 复制代码
@Service
public class UserService {

    @Autowired
    private UserDao userDao;

	@Transactional
	public void insert() {
		userDao.insert();
	}
}

2、@EnableTransactionManagement

@EnableTransactionManagement使用@Import引入了TransactionManagementConfigurationSelector

AdivceModelmportSelect中获取@EnableTransactionManagement注解的mode属性,传递给selectImports(adviceMode)方法。

TransactionManagementConfigurationSelector根据传入的mode值,选择引入不同的组件。

@EnableTransactionManagement的mode属性默认值为AdviceMode.PROXY,所以默认注册的组件为:

● AutoProxyRegistrar

● ProxyTransactionManagementConfiguration

3、AutoProxyRegistrar

七、扩展原理

观察者模式

1、ApplicationListener

监听容器中发布的事件,完成事件驱动模型开发。

java 复制代码
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
	void onApplicationEvent(E event);
}

接口的泛型为监听的事件类型,即只能监听ApplicationEvent及其下面的子类

开发步骤:

  1. 编写一个监听器(实现ApplicationListener接口),来监听某个事件(ApplicationEvent及其子类)
    例如:
java 复制代码
@Component
public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {

	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		System.out.println("收到事件: " + event);
	}
}
  1. 把监听器加入到容器中(使用@Compenent)
  2. 只要容器中有相关事件的发布,就能监听到这个事件。
    例如:
    ○ ContextRefreshedEvent:容器刷新完成事件(所有bean都完成创建时,Spring会自动发布这个事件)
    ○ ContextClosedEvent:容器关闭事件(Spring自动发布的事件)
  3. 发布一个事件
    使用ioc容器进行发布:
java 复制代码
applicationContext.publishEvent(new ApplicationEvent("我的事件") {});

调用过程:

  1. 容器创建中的refresh()方法;
  2. 在容器刷新方法的最后一步,调用了finishRefresh()方法完成容器刷新
  3. 容器完成刷新的方法内发布容器刷新事件:
java 复制代码
publishEvent(new ContextRefreshedEvent(this));

publishEvent()方法的事件发布的流程:

a. 获取事件的多播器/派发器:getApplicationEventMulticaster()

b. multicastEvent()派发事件

c. 派发事件方法中获取到所有支持当前事件类型的ApplicationListener

java 复制代码
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
    // 如果有Executor,可以使用多线程异步派发
    Executor executor = getTaskExecutor();
    if (executor != null) {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                invokeListener(listener, event);  // 执行listener里面的onApplicationEvent方法
            }
        });
    }
    else {  // 否则,同步方式直接执行listener里面的onApplicationEvent方法
        invokeListener(listener, event);
    }
}

事件多播器(派发器)的注册:

  1. 容器创建对象:refresh()
  2. refresh()方法中有一步调用initApplicationEventMulticaster():初始化ApplicationEventMulticaster;
    先到容器中尝试获取beanId为applicationEventMulticaster的ApplicationEventMulticaster类型组件。
    如果可以获取到,则直接使用;
    如果获取不到,则new SimpleApplicationEventMulticaster(beanFactory);,然后注册到ioc容器中;

监听器的注册:

  1. 容器创建对象:refresh()
  2. refresh()方法中有一步调用registerListeners():注册监听器
    从ioc容器中拿到所有的ApplicationListener类型监听器bean,把它们注册添加到applicationEventMulticaster派发器中;

2、@EventListener & SmartInitializingSingleton

@EventListener

使用@EventListener注解监听事件。

标记在方法上,使用classes属性声明要监听的事件类型。ApplicationEvent类型的方法参数可以获得到该事件。

示例:

java 复制代码
@Service
public class UserService {

    @EventListener(classes = {ApplicationEvent.class})
    public void listener(ApplicationEvent event) {
        System.out.println("得到事件:" + event);
    }
}

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

EventListenerMethodProcessor实现了SmartInitializingSingleton接口。

SmartInitializingSingleton

在所有单实例bean都创建完成之后调用,调用的时机类似ContextRefreshedEvent。

接口方法:

● afterSingletonsInstantiated()

调用过程:

  1. ioc创建对象并刷新容器:refresh()
  2. refresh()调用finishBeanFactoryInitialization()
  3. finishBeanFactoryInitialization()初始化剩下的单实例bean
    a. 遍历所有待创建的单实例bean,调用getBean()创建所有的单实例bean
    b. 获取所有创建好的单实例bean,判断是否是SmartInitializingSingleton类型。
    如果是该类型,就调用其afterSingletonsInstantiated()方法

八、容器创建源码

Spring的refresh()方法进行容器的创建和刷新,进入AbstractApplicationcontext类的refresh()方法中。

java 复制代码
public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {

			// Prepare this context for refreshing.
			/**
			 * 做容器刷新前的准备工作
			 * 1、设置容器的启动时间
			 * 2、设置活跃状态为true
			 * 3、设置关闭状态为false
			 * 4、获取Environment对象,并加载当前系统的属性值到Environment对象中
			 * 5、准备监听器和事件的集合对象,默认为空的集合,留给子类做扩展
			 */
			prepareRefresh();//1

			// Tell the subclass to refresh the internal bean factory.
			// 创建容器对象:DefaultListableBeanFactory
			// 加载xml配置文件的属性值到当前工厂中,最重要的就是BeanDefinition
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();//2

			// Prepare the bean factory for use in this context.
			// beanFactory的准备工作,对各种属性进行填充
			prepareBeanFactory(beanFactory);//3

			try {
				// Allows post-processing of the bean factory in context subclasses.
				// 子类覆盖方法做额外的处理,此处我们自己一般不做任何扩展工作,但是可以查看web中的代码,是有具体实现的
				postProcessBeanFactory(beanFactory);//4

				// Invoke factory processors registered as beans in the context.
				// *****调用各种beanFactory处理器
				invokeBeanFactoryPostProcessors(beanFactory);//5
				// Register bean processors that intercept bean creation.
				// 注册bean处理器,这里只是注册功能,真正调用的是getBean方法
				registerBeanPostProcessors(beanFactory);//6

				// Initialize message source for this context.
				// 为上下文初始化message源,即不同语言的消息体,国际化处理,在springmvc的时候通过国际化的代码重点讲
				initMessageSource();//7

				// Initialize event multicaster for this context.
				// 初始化事件监听多路广播器
				initApplicationEventMulticaster();//8

				// Initialize other special beans in specific context subclasses.
				// 留给子类来初始化其他的bean
				onRefresh();//9

				// Check for listener beans and register them.
				// 在所有注册的bean中查找listener bean,注册到消息广播器中
				registerListeners();//10

				// Instantiate all remaining (non-lazy-init) singletons.
				// 初始化剩下的单实例(非懒加载的)
				finishBeanFactoryInitialization(beanFactory);//11

				// Last step: publish corresponding event.
				// 完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人
				finishRefresh();//12
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

1、prepareRefresh()

调用prepareRefresh() 进行容器的预处理

(1)调用initPropertySources() 初始化属性设置

复制代码
AbstractApplicationContext 类中的该方法是空的,留给子类进行实现。子类自定义个性化的属性设置方法

(2)如果在(1)步有自定义的属性配置,则调用 getEnvironment().validateRequiredProperties();对属性进行一些必输等校验。

(3)创建List集合,用来保存容器中的一些早期事件

复制代码
this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();

2、obtainFreshBeanFactory()

调用 obtainFreshBeanFactory() 获取 beanFactory

(1)调用 refreshBeanFactory();刷新beanFactory(修改标志位refreshed为true)。然后设置了序列号ID

(2)进入GenericApplicationContext类中,构造方法中创建了 beanFactory

复制代码
public GenericApplicationContext() {
	this.beanFactory = new DefaultListableBeanFactory();
}

3、prepareBeanFactory(beanFactory)

进行beanFactory的预准备工作,对beanFactory进行一些设置

(1)设置beanFactory的类加载器、支持的表达式解析器等等

(2)添加部分的后置处理器

复制代码
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

(3) 设置要忽略自动装配的接口,很多同学理解不了为什么此处要对这些接口进行忽略,原因非常简单,这些接口的实现是由容器通过set方法进行注入的,所以在使用autowire进行注入的时候需要将这些接口进行忽略

复制代码
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

(4)注册可以解析的自动装配。我们可以在任何组件中使用 @Autowired等注解自动注入以下组件

复制代码
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);

(5)继续添加后置处理器

复制代码
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

(6)添加编译时的AspectJ支持

在java中织入分为三种方式,分为编译器织入,类加载器织入,运行期织入。

  1. 编译器织入是指在java编译器,采用特殊的编译器,将切面织入到java类中
  2. 类加载期织入则指通过特殊的类加载器,在类字节码加载到JVM时,织入切面
  3. 运行期织入则是采用cglib和jdk进行切面的织入

aspectj提供了两种织入方式

  1. 第一种是通过特殊编译器,在编译器,将aspectj语言编写的切面类织入到java类中
  2. 第二种是类加载期织入,就是下面的load time weaving
复制代码
if (!NativeDetector.inNativeImage() && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
	beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
	// Set a temporary ClassLoader for type matching.
	beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}

(7)给beanFactory中注册一些环境组件

复制代码
// 注册默认的系统环境bean到一级缓存中
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
	beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
	beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
	beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
if (!beanFactory.containsLocalBean(APPLICATION_STARTUP_BEAN_NAME)) {
	beanFactory.registerSingleton(APPLICATION_STARTUP_BEAN_NAME, getApplicationStartup());
}

4、postProcessBeanFactory(beanFactory)

AbstractApplicationContext中这个方法为空方法,子类可以重写这个方法,在beanFactory创建并预准备完成之后的后置处理工作。比如:可以查看web中的代码,是有具体实现的
1~4是BeanFactory的创建以及预准备工作

5、invokeBeanFactoryPostProcessors(beanFactory)

执行 BeanFactoryPostProcessors。BeanFactoryPostProcessor 是 bean工厂的后置处理器。在BeanFactory标准初始化之后执行的。

它有两大接口:

  • BeanFactoryProcessor
  • BeanFactoryProcessor下的BeanDefinitionRegistryPostProcessor
复制代码
// 此处传入的 AbstractApplicationContext的 getBeanFactoryPostProcessors() 默认是空的
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

(1) 定义 regularPostProcessors 集合用来存放普通的 BeanFactoryProcessors

(2)定义 registryProcessors 集合用来存放 BeanDefinitionRegistryPostProcessor 类型的 BeanFactoryProcessors

(3)如果传入的 AbstractApplicationContext.getBeanFactoryPostProcessors() 有内容,则先执行其中的 BeanDefinitionRegistryPostProcessor类型后置处理器的 postProcessBeanDefinitionRegistry 方法。并把 beanFactoryPostProcessors 存入对应的集合中

(4)获取ioc容器中的 BeanDefinitionRegistryPostProcessors 并执行:

  1. 获取所有 PriorityOrdered类型的后置处理器,执行其 postProcessBeanDefinitionRegistry方法,并加入 registryProcessors 数组

  2. 获取所有 Ordered 类型的后置处理器,执行其 postProcessBeanDefinitionRegistry方法,并加入 registryProcessors 数组

  3. 获取所有无排序的后置处理器,执行其 postProcessBeanDefinitionRegistry方法,并加入 registryProcessors 数组

    此处使用了循环获取,在获取一遍后会接着再获取下一遍,直到获取不到。
    猜测可能有些后置处理器的postProcessBeanDefinitionRegistry方法会再向容器添加BeanDefinitionRegistryPostProcessors

  4. 执行 registryProcessors 集合中的所有后置处理器的 postProcessBeanFactory方法

  5. 执行 regularPostProcessors 集合中的所有BeanFactoryPostProcessors 的 postProcessBeanFactory方法

(5)获取 ioc 容器中的普通BeanFactoryPostProcessors并执行

  1. 获取容器中所有 BeanFactoryPostProcessors,并按照是否属于PriorityOrdered、是否属于Ordered、是否是无排序进行归类
  2. 执行所有PriorityOrdered类型的BeanFactoryPostProcessors 的postProcessBeanFactory方法
  3. 执行所有Ordered类型的BeanFactoryPostProcessors 的postProcessBeanFactory方法
  4. 执行所有无排序的BeanFactoryPostProcessors 的postProcessBeanFactory方法

6、registerBeanPostProcessors(beanFactory)

调用registerBeanPostProcessors(beanFactory) ,注册 Bean后置处理器,拦截bean的创建过程

BeanPostProcessor接口的子接口:

  • BeanPostProcessor
  • DestructionAwareBeanPostProcessor
  • InstantiationAwareBeanPostProcessor
  • SmartInstantiationAwareBeanPostProcessor
  • MergedBeanDefinitionPostProcessor

(1)获取所有的BeanPostProcessor。后置处理器也可以通过 PriorityOrdered、Ordered接口设置优先级

(2)注册PriorityOrdered优先级接口的后置处理器,把后置处理器添加进BeanFactory中

(3)再注册Ordered接口的后置处理器

(4)再注册没有实现优先级接口的后置处理器

(5)注册MergedBeanDefinitionPostProcessor

(6)注册ApplicationListenerDetector

bash 复制代码
ApplicationListenerDetector用来在Bean创建完成后检查是否为ApplicationListener,如果是则添加进容器的监听器中

7、initMessageSource()

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

(1)获取BeanFactory

(2)判断容器中是否存在beanID为messageSource、类型为MessageSource的bean

如果存在,则赋值给容器的messageSource属性;

如果没有,则创建一个DelegatingMessageSource对象,注册到容器中。

bash 复制代码
可以通过MessageSource对象的getMessage方法获取到国际化资源文件中对应的key值

8、initApplicationEventMulticaster()

初始化事件派发器

(1) 获取BeanFactory

(2) 从BeanFactory中获取beanID为applicationEventMulticaster、ApplicationEventMulticaster类型的事件派发器,如果从容器中获取不到,就会创建一个SimpleApplicationEventMulticaster类型的事件派发器,并添加进容器中

9、onRefresh()

初始化其他的一些特殊Bean,默认为空实现,留给子类进行自定义重写

10、registerListener()

给容器中注册所有的ApplicationListener

(1)从容器中获取所有的ApplicationListener

(2)将每个监听器添加进事件派发器中

bash 复制代码
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);

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

11、finishBeanFactoryInitialization(beanFactory)

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

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

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

(2)获取Bean的定义信息:RootBeanDefinition

(3)Bean不是抽象的,是单实例的,不是懒加载的

3.1 判断如果是FactoryBean(是否是实现FactoryBean接口的工厂Bean),利用工厂方法创建对象

3.2 如果不是工厂Bean,调用getBean(beanName)创建对象

3.3 getBean(beanName)调用doGetBean()创建对象

3.3.1 先从缓存中保存的单实例bean尝试获取,如果能获取到说明这个Bean之前被创建过(所有创建过的单实例bean都会被缓存起来)

java 复制代码
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

3.3.2 缓存中获取不到时,准备开始Bean的创建对象流程

3.3.3 markBeanAsCreated()标记当前Bean已经被创建,防止多线程运行时同一个单实例bean被多次创建

3.3.4 获取bean的定义信息RootBeanDefinition

3.3.5 getDependsOn()获取当前bean依赖的其他bean,如果有,按照getBean()的方式把依赖的bean创建出来

3.3.6 启动单实例bean的创建流程

java 复制代码
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
    @Override
    public Object getObject() throws BeansException {
        try {
            return createBean(beanName, mbd, args);
        }
        catch (BeansException ex) {
            // Explicitly remove instance from singleton cache: It might have been put there
            // eagerly by the creation process, to allow for circular reference resolution.
            // Also remove any beans that received a temporary reference to the bean.
            destroySingleton(beanName);
            throw ex;
        }
    }
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);

3.3.7 createBean()的内部实现

3.3.8 在创建Bean对象之前,提前执行InstantiationAwareBeanPostProcessor类型的BeanPostProcessor,给后置处理器一个机会返回一个代理对象

java 复制代码
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);

调用applyBeanPostProcessorsBeforeInstantiation()触发InstantiationAwareBeanPostPRocessor的postProcessBeforeInstantiation()方法;

如果前一步有返回值(即要返回代理对象,不继续后面的bean创建和初始化),则继续调用applyBeanPostProcessorAfterInitialization()触发postProcessAfterInitialization()方法

3.3.9 如果上一步没有返回代理对象,则调用doCreateBean()创建bean

3.3.9.1 创建bean实例:createBeanInstance();利用工厂方法或者对象的构造器创建出Bean实例,此时的bean对象被构造出来,但是还未进行属性的赋值

3.3.9.2 调用applyMergedBeanDefinitionPostProcessors(),执行MergedBeanDefinitionPostProcessors的postProcessMergedBeanDefinition()方法

3.3.9.3 调用populateBean()为bean的属性进行赋值

3.3.9.3.1 获取InstantiationAwareBeanPostProcessor后置处理器,执行后置处理器的postProcessAfterInstantiation()方法

3.3.9.3.2 执行InstantiationAwareBeanPostProcessor后置处理器的postProcessPropertyValue()方法

3.3.9.3.3 调用applyPropertyValues(),反射bean的setter方法进行赋值

3.3.9.4 调用initializeBean()进行bean的初始化

3.3.9.4.1 调用invokeAwareMethods()执行xxxxAware接口的方法:BeanNameAware、BeanClassLoaderAware、BeanFactoryAware

3.3.9.4.2 调用applyBeanPostProcessorsBeforeInitialization(),执行BeanPostProcessor的postProcessBeforeInitialization()方法

12、finishRefresh()

完成BeanFactory的创建工作,IOC容器创建完成

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

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

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

void onRefresh();

void onclose();

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

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

总结

1、在spring容器启动后,向容器中添加配置类的BeanDefinition,此时这个配置类加了什么注解也会被记录

2、invokeBeanFactoryPostProcessors(beanFactory)

2.1 处理BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor接口,优先处理BeanDefinitionRegistryPostProcessor,如果都是BeanDefinitionRegistryPostProcessor接口会根据是否有PriorityOrdered、Ordered接口进行排序处理

2.2 ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry 向容器中注入配置类里声明的BeanDefinition信息。比如会解析@Configuration、@Component、@Bean、@ComponentScan、@Import、@ImportResource、@Order等等注解,调用BeanDefinitionRegistry.registerBeanDefinition向容器中注入Bean定义信息,同时也会调用@Import注解实现了ImportBeanDefinitionRegistrar接口的方法向容器中注入Bean定义信息

3、registerBeanPostProcessors(beanFactory):创建实现了BeanPostProcessor接口的类,调用getBean()方法获取对象并加入到容器中,注意这里只是注册,真正调用BeanPostProcessor是在getBean方法

4、finishBeanFactoryInitialization(beanFactory);实例化并初始化剩下的Bean

九、Servlet3.0

1、Servlet3.0概述

JSR 315规范。不再需要 web.xml,可以直接通过 @WebServlet等相关注解配置Servlet

java 复制代码
@WebServlet("/hello")
public class HelloServlet extends HttpServlet{

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		System.out.println("hello");
		resp.getWriter().write("hello...");
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doGet(req, resp);
	}
	
}

2、共享库/运行时插件能力

ServletContainerInitializer

Servlet容器启动会扫描当前应用里面每一个jar包的 ServletContainerInitializer接口 的实现类。

该实现类必须绑定在 META-INF/services/javax.servlet.ServletContainerInitializer文件中,该文件的内容就是ServletContainerInitializer实现类的全类名。

总结:Servlet容器启动时,会扫描当前应用每一个jar包(或者类路径下)的 META-INF/services/javax.servlet.ServletContainerInitializer文件中指定的实现类,启动并运行这个实现类的方法

示例:

编写ServletContainerInitializer的实现类,实现其onStartup()方法

java 复制代码
public class MyServletInitializer implements ServletContainerInitializer{

	@Override
	public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
		System.out.println("MyServletContainerInitializer启动了......");
	}
}

在src下创建文件夹:META-INF/services,在该文件夹下创建javax.servlet.ServletContainerInitializer文件,并写入ServletContainerInitializer接口实现类的全类名:

java 复制代码
com.study.servlet.init.MyServletInitializer

启动tomcat,MyServletInitializer的onStartup()方法也会自动跟着启动

@HandlesTypes

ServletContainerInitializer实现类可以添加@HandlesTypes注解,并传入注解指定的类型数组。onStartup()方法会接收到@HandlesTypes指定的类型的所有子类/子接口/实现类/子类的子类等,但是不接收指定的类型本身

示例:

编写接口HelloService

编写接口HelloServiceSub,继承与HelloService

编写抽象类AbstractHelloService,实现HelloService接口

编写类HelloServiceImpl,实现HelloService接口

编写类AbstractHelloServiceImpl,实现抽象类AbstractHelloService

java 复制代码
@HandlesTypes({HelloService.class})
public class MyServletInitializer implements ServletContainerInitializer{

	
	@Override
	/**
	 * 应用启动的时候,会运行onStartup方法
	 * @param c 接收HandlesType指定的类型下面的子类/实现类/子接口(HandleTypes指定的类型本身接收不到)
	 * @param ctx 代表当前Web应用的ServletContext,一个Web应用对应一个ServletContext
	 */
	public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
		c.forEach(handleClass -> System.out.println(handleClass.getSimpleName()));
		System.out.println("MyServletContainerInitializer启动了......");
	}
}

tomcat启动后,控制台输出结果:

java 复制代码
AbstractHelloServiceImpl
HelloServiceSub
HelloServiceImpl
AbstractHelloService
MyServletContainerInitializer启动了......

ServletContext

有时候引入的第三方的jar包会包含一些Filter、Servlet,但是无法修改jar包内容为其添加@WebServlet注解,此时可以通过onStartup()方法的ServletContext参数为Servlet容器添加组件

示例:

编写Servlet类:

java 复制代码
public class HelloServlet extends HttpServlet{

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		System.out.println("hello");
		resp.getWriter().write("hello...");
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doGet(req, resp);
	}
}

在ServletContainerInitializer实现类的onStartup()方法中,将该Servlet添加进Servlet容器:

java 复制代码
public class MyServletInitializer implements ServletContainerInitializer{
	
	@Override
	public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
		Dynamic helloServlet = ctx.addServlet("helloServlet", new HelloServlet());
		helloServlet.addMapping("/hello");
		System.out.println("MyServletContainerInitializer启动了......");
	}
}

注意:

使用编码的方式,在项目启动的时候给ServletContext里面添加组件:

  • 并非所有获取到的ServletContext都能添加组件,必须在项目启动的时候才能添加
  • 可以在ServletContainerInitializer实现类的onStartup()方法里得到的ServletContext进行添加
  • 可以在监听器ServletContextListener的contextInitialized(ServletContextEvent arg)方法的ServletContextEvent进行获取添加
java 复制代码
@Override
public void contextInitialized(ServletContextEvent arg) {
    ServletContext sc = arg.getServletContext();
    // TODO 添加组件
}

3、Spring中使用Servlet3.0

SpringMVC中的ServletContainerInitializer

SpringMVC的 spring-web-xxxx.jar 包中带有 META-INF/services/javax.servlet.ServletContainerInitializer文件,文件内容 为:

java 复制代码
org.springframework.web.SpringServletContainerInitializer

SpringServletContainerInitializer类实现了ServletContainerInitializer接口,并使用@HandlesTypes注解引入了WebApplicationInitializer接口

源码如下:

java 复制代码
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
    @Override
	public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
			throws ServletException {

		List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();

        // 遍历WebApplicationInitializer接口的实现类,添加进initializers数组
		if (webAppInitializerClasses != null) {
			for (Class<?> waiClass : webAppInitializerClasses) {
				if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
						WebApplicationInitializer.class.isAssignableFrom(waiClass)) {

                    // ....
						initializers.add((WebApplicationInitializer) waiClass.newInstance());
                }
			}
		}

		// ......
        
        // 遍历执行每个WebApplicationInitializer实现类的onStartup()方法
		AnnotationAwareOrderComparator.sort(initializers);
		for (WebApplicationInitializer initializer : initializers) {
			initializer.onStartup(servletContext);
		}
	}
}

WebApplicationInitializer接口源码如下:

java 复制代码
public interface WebApplicationInitializer {
	void onStartup(ServletContext servletContext) throws ServletException;
}

SpringMVC默认的WebApplicationInitializer抽象实现

WebApplicationInitializer接口的抽象实现类:

  • AbstractContextLoaderInitializer
  • AbstractDispatcherServletInitializer
  • AbstractAnnotationConfigDispatcherServletInitializer
  • AbstractReactiveWebInitializer

(1)AbstractContextLoaderInitializer

创建根容器

(2)AbstractDispatcherServletInitializer

创建一个Web的IOC容器:createServletApplicationContext()

创建前端控制器:createDispatcherServlet(servletAppContext)

将创建的DispatcherServlet添加进ServletContext中,并通过实现getServletMapping()设置Mapping等配置:

java 复制代码
FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());

ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
registration.setLoadOnStartup(1);
registration.addMapping(getServletMappings());
registration.setAsyncSupported(isAsyncSupported());

(3)AbstractAnnotationConfigDispatcherServletInitializer

注解方式配置的初始化器。

创建根容器:createRootApplicationContext()方法中,通过实现getRootConfigClasses()传入配置类,根据配置类注册根容器

创建web的ioc容器:createServletApplicationContext()方法中,通过实现getServletConfigClasses()传入配置类,根据配置类创建web的ioc容器

如果以注解方式启动SpringMVC,继承AbstractAnnotationConfigDispatcherServletInitializer,实现对应的抽象方法指定DispatcherServlet的配置信息。

根容器(Root WebApplicationContext)用来扫描Services、Repositories。

Web的ioc容器(Servlet WebApplicationContext)用来扫描 Controllers、ViewResolver、HandlerMapping。

示例配置

编写根容器配置类:根容器不扫描 Controller

java 复制代码
@ComponentScan(value="com.study", excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = { Controller.class })
})
public class RootConfig {
}

编写Web的ioc容器配置类:web的ioc容器只扫描Controller

java 复制代码
// 必须使用 userDefaultFilters=false 禁用默认的过滤规则,否则includeFilters不能生效
@ComponentScan(value = "com.study", includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
}, useDefaultFilters = false)
public class AppConfig {
}

编写启动类:AbstractAnnotationConfigDispatcherServletInitializer实现了 WebApplicationInitializer,只需继承该类即可。

java 复制代码
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    /**
     * 获取根容器的配置类:(对应以前的Spring的配置文件)
     */
    protected Class<?>[] getRootConfigClasses() {
        return new Class[] { RootConfig.class };
    }

    @Override
    /**
     * 获取Web容器的配置类:(对应以前的SpringMVC配置文件)
     */
    protected Class<?>[] getServletConfigClasses() {
        return new Class[] { AppConfig.class };
    }

    @Override
    /**
     * 获取DispatcherServlet的映射信息
     */
    protected String[] getServletMappings() {
        /*
            "/" : 拦截所有请求(包括静态资源 *.js、*.png等),但是不拦截 *.jsp
            "/*" : 拦截所有请求,会拦截 *.jsp
         */
        return new String[] {"/"};
    }
}

定制SpringMVC

  1. 使用@EnableMVC注解开启SpringMVC的定制配置功能
    等同于xml配置中的 mvc:annotation-driven/
  2. 实现WebMvcConfigrer接口,或者继承WebMvcConfigrerAdapter,重写想要配置的方法即可
java 复制代码
@EnableWebMvc
public class AppConfig extends WebMvcConfigurerAdapter {
    @Override
    /**
     * 配置视图解析器
     */
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.jsp("/WEB-INF/views/", ".jsp");
    }

    @Override
    /**
     * 配置静态资源访问
     */
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

4、异步请求处理

  1. 在 Servlet3.0之前,Servlet采用Thread-Per-Request的方式处理请求,即每一次Http请求都由某一个线程从头到尾负责处理。
  2. 如果一个请求需要进行IO操作,比如访问数据库、调用第三方服务接口等,那么其所对应的线程将同步地等待IO操作完成,而IO操作是非常慢的,所以此时的线程并不能及时地释放回收线程池以供后续使用,在并发量越来越大的情况下,这将带来严重的性能问题。即便是像Spring、Structs这样的高层框架也脱离不了这样的桎梏,因为他们都是建立在Servlet之上的。为了解决这样的问题,Servlet3.0引入了异步处理,然后在 Servlet 3.1 中又引入了非阻塞IO来进一步增强异步处理的性能。

Servlet3.0原生异步处理

  1. 在@WebServlet注解中设置asyncSupported=true
  2. 在doGet()/doPost()方法中,通过req.startAsync()获取到AsyncContext对象
  3. 调用AsyncContext对象的start()方法,传入一个Thread线程对象
  4. 在线程的run()方法中,使用AsyncContext对象的complete()方法完成操作
  5. AsyncContext对象可以获取request、response进行后续操作

示例程序:

java 复制代码
@WebServlet(value = "/helloAsync", asyncSupported = true)
public class HelloAsyncServlet extends HttpServlet{

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		System.out.println("主线程开始: " + Thread.currentThread());
		AsyncContext startAsync = req.startAsync();
		startAsync.start(() -> {
			try {
				System.out.println("子线程开始:" + Thread.currentThread());
				sayHello();
				startAsync.complete();
//				AsyncContext asyncContext = req.getAsyncContext();
				ServletResponse response = startAsync.getResponse();
				response.getWriter().write("hello Async");
				System.out.println("子线程结束开始:" + Thread.currentThread());
			} catch (Exception e) {
				e.printStackTrace();
			}
		});
		
		System.out.println("主线程结束: " + Thread.currentThread());
	}
	
	public void sayHello() throws Exception {
		Thread.sleep(3000);
	}

}

SpringMVC的Callable

返回一个Callable类型的返回值。

示例程序:

java 复制代码
@Controller
public class AsyncController {

    @RequestMapping("/asyncTest01")
    public Callable<String> asyncTest01() {
        /*Callable<String> callable = new Callable<String>() {
            @Override
            public String call() throws Exception {
                Thread.sleep(2000);
                return "asyncTest01 exec......";
            }
        };
        return callable;*/

        return () -> {
            Thread.sleep(2000);
            return "success";
        };
    }
}

如果加了拦截器,拦截在这里插入代码片器的preHandle()方法会执行两次,SpringMVC执行过程如下:

java 复制代码
拦截器preHandle.......
主线程开始....
主线程结束....
-----------至此,DispatcherServlet及所有Filter退出线程--------

----------Callable开始执行----------
Callable子线程开始....
Callable子线程结束....
----------Callable执行结束-----------

----------SpringMVC将请求重新派发给容器--------------
拦截器preHandle...
拦截器posthandle.....(Callable的返回值就是目标方法返回值,此时不再执行目标方法)
拦截器afterCompletion.....

原理:

  1. 控制器返回Callable
  2. Spring异步处理:将Callable提交到TaskExecutor,使用一个隔离的线程进行执行
  3. DispatcherServlet和所有的Filter退出Web容器的线程,但是Response保持打开状态
  4. Callable返回结果,SpringMVC将请求重新派发给容器,恢复之前的处理
  5. 根据Callable返回的结果,SpringMVC继续进行视图渲染等(从收请求-视图渲染 重新进行)

SpringMVC的异步拦截器

SpringMVC的普通拦截器并不能拦截到真正的Callable请求。如果需要拦截异步请求,则需要使用异步拦截器:

  • 使用原生API的AsyncListener
  • 使用SpringMVC的AsyncHandlerInterceptor接口

SpringMVC的DeferredResult

使用步骤:

  1. 在Controller中创建一个DeferredResult对象,并保存进消息队列或其他位置中
  2. 将该DeferredResult对象进行返回
  3. 编写一个监听,消费消息队列里面的DeferredResult对象
  4. 将结果通过DeferredResult对象的setResult()方法设置给DeferredResult对象

示例程序:

java 复制代码
@Controller
public class AsyncController {

    /**
     * 创建订单请求
     */
    @RequestMapping("/createOrder")
    @ResponseBody
    public DeferredResult<Object> createOrder() {
        // 创建一个deferredResult对象,保存进消息队列中
        // 并不直接进行订单的创建。等其他线程消费该消息队列的deferredResult对象,并通过setResult()给deferredResult对象设置结果之后,将结果响应给浏览器
        DeferredResult<Object> deferredResult = new DeferredResult<>(3000L, "create failed ...");
        DeferredResultQueue.save(deferredResult);
        return deferredResult;
    }

    
    /**
     * 模拟监听器,执行消息队列中的DeferredResult对象
     */
    @RequestMapping("/create")
    @ResponseBody
    public String create() {
        // 从消息队列中取出deferredResult对象,进行订单的创建
        String orderNo = UUID.randomUUID().toString();
        DeferredResult<Object> deferredResult = DeferredResultQueue.get();
        // 设置要响应给浏览器的内容
        deferredResult.setResult("orderNo=" + orderNo);
        return "success: " + orderNo;
    }
}

模拟消息队列的程序:

java 复制代码
// 模拟消息队列
public class DeferredResultQueue {

    private static Queue<DeferredResult<Object>> queue = new ConcurrentLinkedDeque<>();

    // 向队列中添加内容
    public static void save(DeferredResult<Object> deferredResult) {
        queue.add(deferredResult);
    }

    // 消费队列中的对象
    public static DeferredResult<Object> get() {
        return queue.poll();
    }
}
相关推荐
邓不利东2 小时前
Spring中过滤器和拦截器的区别及具体实现
java·后端·spring
努力的小郑3 小时前
Spring三级缓存硬核解密:二级缓存行不行?一级缓存差在哪?
java·spring·面试
hello早上好4 小时前
Spring AOP:从代理创建到切点匹配
java·后端·spring
chanalbert4 小时前
从单体到微服务:Spring Cloud 开篇与微服务设计
spring boot·spring·spring cloud
netyeaxi15 小时前
Java:使用spring-boot + mybatis如何打印SQL日志?
java·spring·mybatis
小七mod16 小时前
【MyBatis】MyBatis与Spring和Spring Boot整合原理
spring boot·spring·mybatis
程序猿小D18 小时前
[附源码+数据库+毕业论文]基于Spring+MyBatis+MySQL+Maven+jsp实现的个人财务管理系统,推荐!
java·数据库·mysql·spring·毕业论文·ssm框架·个人财务管理系统
永日4567020 小时前
学习日记-spring-day42-7.7
java·学习·spring
二十雨辰20 小时前
[尚庭公寓]07-Knife快速入门
java·开发语言·spring