进阶学习(源码级) -Spring注解驱动开发

文章目录

  • 1、Spring
    • 1.1、容器
    • 1.2、扩展原理
    • 1.3、Web
  • 2、配置文件开发简单案例
  • 3、注解开发简单案例
    • 3.1、创建配置类
    • 3.2、测试
  • 4、包扫描
    • 4.1、配置文件扫描
    • 4.2、注解扫描
    • 4.3、排除扫描
    • 4.4、指定扫描
    • 4.5、按类型扫描
    • 4.6、自定义扫描
      • 4.6.1、编写TypeFilter的实现类(理解)
      • 4.6.2、修改配置类
  • 5、@scope注解-设置组件作用域
    • 5.1、@scope(默认单实例)
      • 5.1.1、编写配置类
      • 5.1.2、编写测试
    • 5.2、@scope参数
    • 5.3、多实例 prototype 测试
      • 5.3.1、修改配置类
      • 5.3.2、测试
    • 5.4、懒加载
  • 6、@Conditional注解(重点)
    • 6.1、业务场景
    • 6.2、编写WindowsConditional
    • 6.3、编写LinuxConditional
    • 6.4、编写配置类
    • 6.5、测试
  • 7、组件注册方法
    • 7.1、@Import(重点)
      • 7.1.1、编写配置类
      • 7.1.2、编写测试
    • 7.2、ImportSelector
      • 7.2.1、编写MyImportSelector
      • 7.2.2、修改配置类
      • 7.2.3、测试
    • 7.3、ImportBeanDefinitionRegistrar
      • 7.3.1、编写MyImportBeanDefinitionRegistrar
      • 7.3.2、修改配置类
      • 7.3.3、测试
    • 7.4、FactoryBean
      • 7.4.1、编写ColorFactoryBean
      • 7.4.2、编写配置类
      • 7.4.3、测试
  • 8、Bean生命周期
    • 8.1、自定义初始化和销毁方法
      • 8.1.1、创建Car类
      • 8.1.2、创建配置文件
      • 8.1.3、测试
    • 8.2、InitializingBean和DisposableBean 接口
      • 8.2.1、创建Cat类
      • 8.2.2、创建配置类
      • 8.2.3、测试
    • 8.3、@PostConstruct和@PreDestroy
      • 8.3.1、创建Dog类
      • 8.3.2、配置类
      • 8.3.3、测试
    • 8.4、BeanPostProcessor
      • 8.4.1、编写MyBeanPostProcessor
      • 8.4.2、配置类
      • 8.4.3、测试
      • 8.4.4、BeanPostProcessor原理
  • 9、@Value注解
    • 9.1、修改Person类
    • 9.2、配置类
    • 9.3、测试类
  • 10、自动装配
    • 10.1、@AutoWired
      • 10.1.1、required属性
      • 10.1.2、标注在方法上
      • 10.1.3、标注在构造器上
      • 10.1.3、标注在参数上
    • 10.2、@Qualifier
    • 10.3、@Primary
    • 10.4、@Resource
    • 10.5、@Inject
  • 11、使用Spring容器底层组件
    • 11.1、编写Red类
    • 11.2、配置类
    • 11.3、测试
  • 12、@Profile注解
    • 12.1、编写db.properties
    • 12.2、编写配置类
    • 12.3、环境激活方式
    • 12.4、测试
  • 13、AOP
    • 13.1、AOP测试
      • 13.1.1、编写业务逻辑类MathCalculator
      • 13.1.2、编写日志切面类LogAspects
      • 13.1.3、编写配置类
      • 13.1.5、测试
      • 13.1.6、AOP使用总结
    • 13.2、AOP原理
      • 13.2.1、@EnableAspectJAutoProxy
      • 13.2.2、AnnotationAwareAspectJAutoProxyCreator分析
      • 13.2.3、注册AnnotationAwareAspectJAutoProxyCreator流程
      • 13.2.4、AnnotationAwareAspectJAutoProxyCreator执行时机
      • 13.2.5、AnnotationAwareAspectJAutoProxyCreator作用
      • 13.2.6、拦截器链分析
      • 13.2.7、总结
  • 14、声明式事务
    • 14.1、环境搭建
      • 14.1.1、创建数据库
      • 14.1.2、编写UserDao
      • 14.1.3、编写UserService
      • 14.1.4、编写配置类
      • 14.1.5、测试
    • 14.2、事务测试
      • 14.2.1、给方法标注@Transactional
      • 14.2.2、开启注解事务管理功能
      • 14.2.3、配置事务管理器
      • 14.2.4、测试
    • 14.3、事务源码分析
  • 15、扩展原理
    • 15.1、BeanFactoryPostProcessor
      • 15.1.1、编写Dog类
      • 15.1.2、编写MyBeanFactoryPostProcessor
      • 15.1.3、编写配置类
      • 15.1.4、测试
    • 15.2、BeanDefinitionRegistryPostProcessor
      • 15.2.1、编写MyBeanDefinitionRegistryPostProcessor
      • 15.2.2、编写Cat类
      • 15.2.3、测试
  • 16、事件监听
    • 16.1、编写监听器
    • 16.2、测试
    • 16.3、源码分析
    • 16.4、@EventListener
      • 16.4.1、编写UserService类
      • 16.4.2、测试
      • 16.4.3、源码分析
  • 17、Spring容器创建过程
    • 17.1、BeanFactory的创建及预准备工作
    • 17.2、执行BeanFactory后置处理器流程
    • 17.3、注册Bean后置处理器
    • 17.4、国际化功能
    • 17.5、初始化事件派发器
    • 17.6、初始化子类中的bean
    • 17.7、注册监听器
    • 17.8、完成bean工厂初始化,实例化bean
    • 17.9、Spring容器创建流程图
    • 17.10、总结
  • 18、Servlet3.0
    • 18.1、测试
    • 18.2、ServletContainerInitializer扫描包
    • 18.3、ServletContainerInitializer注册servlet、Listener、filter
      • 18.3.1、编写MyServlet
      • 18.3.2、编写MyListener
      • 18.3.3、编写MyFilter
      • 18.3.4、编写MyServletContainerInitializer
    • 18.4、整合SpringMVC分析
    • 18.5、整合SpringMVC测试
      • 18.5.1、依赖导入
      • 18.5.1、编写HelloService
      • 18.5.2、编写HelloController
      • 18.5.3、编写RootConfig
      • 18.5.4、编写AppConfig
      • 18.5.5、编写MyWebAppInitializer
    • 18.6、配置springMVC
      • 18.6.1、编写success.jsp
      • 18.6.2、编写拦截器
      • 18.6.3、修改HelloController
      • 18.6.4、修改AppConfig
    • 18.7、spring3.0异步请求
    • 18.8、springMVC异步请求
      • 18.8.1、方法一:callable
      • 18.8.2、方法二:DeferredResult
      • 18.8.2、方法二:DeferredResult

1、Spring

1.1、容器

  • AnnotationConfigApplicationContext
  • 组件添加
  • 组件赋值
  • 组件注入
  • AOP
  • 声明式事务

1.2、扩展原理

  • BeanFactoryPostProcessor
  • BeanDefinitionRegistryPostProcessor
  • ApplicationListener
  • Spring容器创建过程

1.3、Web

  • servlet3.0
  • 异步请求

2、配置文件开发简单案例

1、导入依赖 spring-context


<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-contextartifactId>
    <version>5.2.5.RELEASEversion>
dependency>

2、编写Spring配置文件 beans.xml


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">


beans>

3、编写一个实体类 Person

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Person {
    private String name;
    private int age;

4、在Spring配置文件中配置Bean


<context:component-scan base-package="com.hh">context:component-scan>
<bean id="person" class="com.hh.bean.Person">
    <property name="name" value="haohao">property>
    <property name="age" value="18">property>
bean>

5、编写测试类

public class MainTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Person person = context.getBean("person", Person.class);
        System.out.println(person);
    }
}

6、测试

Person{name='haohao', age=18}

3、注解开发简单案例

3.1、创建配置类

  • 在方法上面加上@Bean注解后,Spring会以方法返回类型作为组件的类型方法名作为组件的 id

  • 当向@Bean中添加参数时,默认添加的第一个参数是value,强制给组件赋值id

//配置类==配置文件beans.xml
@Configuration //告诉Spring这是一个配置类
@ComponentScan(value = "com.hh",includeFilters={
//        @ComponentScan.Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
//        @ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}),
        @ComponentScan.Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})

},useDefaultFilters=false)
//Filter[] excludeFilters()  指定扫描的时候按照什么规则排除那些组件
//ComponentScan.Filter[] includeFilters() default {}; 指定扫描的时候只需要包含那些组件
//FilterType.ANNOTATION  按照注解  常用
//FilterType.ASSIGNABLE_TYPE  按照类型  常用
//FilterType.ASPECTJ   按照ASPECTJ表达式,不太常用
//FilterType.REGEX   按照正则指定
//FilterType.CUSTOM   使用自定义规则  常用


public class MainConfig {
    //给容器中注册一个Bean,类型就是返回值的类型,id默认方法名
    @Bean
    public Person person(){
        return new Person("haohao",5);
    }
}

3.2、测试

public static void main(String[] args) {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
    Person bean = applicationContext.getBean(Person.class);
    System.out.println(bean);
}

结果:

Person{name='haohao', age=5}

4、包扫描

只要标注了@Controller、@Service、@Repository、@Component的,都会被扫描加入到容器里

注意:配置类自身也会被扫描到容器中,如果存在多个配置类,则多个配置类里的所有bean对象都会被扫描进容器中

4.1、配置文件扫描


<context:component-scan base-package="com.hh">context:component-scan>

4.2、注解扫描

在配置类上添加注解@ComponentScan(value="")

//配置类==配置文件
@Configuration //告诉Spring这是一个配置类
@ComponentScan(value = "com.hh",includeFilters={
      @ComponentScan.Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
      @ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}),
      @ComponentScan.Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})

},useDefaultFilters=false)
//Filter[] excludeFilters()  指定扫描的时候按照什么规则排除那些组件
//ComponentScan.Filter[] includeFilters() default {}; 指定扫描的时候只需要包含那些组件
//FilterType.ANNOTATION  按照注解
//FilterType.ASSIGNABLE_TYPE  按照类型
//FilterType.ASPECTJ   按照ASPECTJ表达式,不太常用
//FilterType.REGEX   按照正则指定
//FilterType.CUSTOM   使用自定义规则


public class MainConfig {
	//...
}

4.3、排除扫描

注解排除excludeFilters的返回类型为Filter[]

Filter 的排除类型 FilterType 有以下几种:

  • FilterType.ANNOTATION 按照注解
  • FilterType.ASSIGNABLE_TYPE 按照类型
  • FilterType.ASPECTJ 按照ASPECTJ表达式,不太常用
  • FilterType.REGEX 按照正则指定
  • FilterType.CUSTOM 使用自定义规则

下面以ANNOTATION注解排除来示例:

@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.hh", excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})
})
public class MainConfig {
    // ...
}

4.4、指定扫描

  • ComponentScan.Filter[] includeFilters() default {}; 指定扫描的时候只需要包含那些组件

  • 注意要关闭默认的扫描过滤器

  • Filter 的指定类型 FilterType 同上

@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.hh", includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})
}, useDefaultFilters = false)
public class MainConfig {
    // ...
}

4.5、按类型扫描

  • FilterType.ASSIGNABLE_TYPE 按照类型
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre", includeFilters = {
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BookController.class})
}, useDefaultFilters = false)
public class MainConfig {
    // ...
}

4.6、自定义扫描

4.6.1、编写TypeFilter的实现类(理解)

  • public class MyTypeFilter implements TypeFilter {
        /**
         *
         * @param metadataReader :读取到的当前正在扫描的类的信息
         * @param metadataReaderFactory:可以获取到其他任何类的信息
         * @return
         * @throws IOException
         */
        public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
            //获取当前类注解的信息
            ClassMetadata classMetadata = metadataReader.getAnnotationMetadata();
            //获取当前正在扫描的类的类信息
            ClassMetadata classMetadata1 = metadataReader.getClassMetadata();
            //获取当前类资源(类的路径)
            Resource resource = metadataReader.getResource();
            String className = classMetadata.getClassName();
            System.out.println("--->"+className);
            if(className.contains("er")){
                return true;
            }
            return false;
        }
    }
    

4.6.2、修改配置类

@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.hh", includeFilters = {
        @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
}, useDefaultFilters = false)
public class MainConfig {
    // ...
}

5、@scope注解-设置组件作用域

5.1、@scope(默认单实例)

5.1.1、编写配置类

@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.hh")
public class MainConfig2 {

    @Bean("person")
    public Person person(){
        System.out.println("给容器中添加Person--");
        return new Person("吉吉", 3);
    }
}

5.1.2、编写测试

@Test
public  void Test2(){
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
    String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
    for (String name : beanDefinitionNames) {
        System.out.println(name);
    }
    System.out.println("ioc容器创建完成......");
    Object person1 = applicationContext.getBean("person");
    Object person2 = applicationContext.getBean("person");
    System.out.println(person1==person2);//true    默认是单实例 要是Scope改为prototype 则是false
}

5.2、@scope参数

  • ConfigurableBeanFactory#SCOPE_PROTOTYPE prototype 单实例

  • ConfigurableBeanFactory#SCOPE_SINGLETON singleton 多实例

  • org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST request

  • org.springframework.web.context.WebApplicationContext#SCOPE_SESSION session

后两个需要导入 Spring web 的jar包,且基本不用,只讨论前两个

ConfigurableBeanFactory中有两个常量:

  • String SCOPE_SINGLETON = "singleton";

    • 单实例的(默认值) :ioc容器启动会调用方法创建对象放到ioc容器中,以后每次获取都是直接从容器中拿,可以理解成(map.get())
  • String SCOPE_PROTOTYPE = “prototype”;

    • 多实例的:ioc容器启动并不会去调用方法创建对象放在容器中,每次获取的时候才会调用方法创建对象

WebApplicationContext中的两个常量

  • String SCOPE_REQUEST = “request”;

    • 同一次请求创建一个实例
  • String SCOPE_SESSION = “session”;

    • 同一个session创建一个实例

5.3、多实例 prototype 测试

5.3.1、修改配置类

@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.hh")
public class MainConfig2 {
    
    @Bean("person")
    @Scope("prototype")
    public Person person() {
        return new Person("haohao", 5);
    }

}

5.3.2、测试

@Test
public  void Test2(){
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
    System.out.println("ioc容器创建完成......");
    Object person1 = applicationContext.getBean("person");
    Object person2 = applicationContext.getBean("person");
    System.out.println(person1==person2);//false   默认是单实例 要是Scope改为prototype 则是false
}

5.4、懒加载

懒加载:

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

只需要在方法上添加注解 @Lazy

@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.hh")
public class MainConfig2 {

    @Lazy
    @Bean("person")
    public Person person(){
        System.out.println("给容器中添加Person--");
        return new Person("吉吉", 3);
    }

}

6、@Conditional注解(重点)

  • 按照一定的条件进行判断,满足条件给容器中注册Bean

@Conditional 源码

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {

   /**
    * All {@link Condition Conditions} that must {@linkplain Condition#matches match}
    * in order for the component to be registered.
    */
   Class<? extends Condition>[] value();

}

Condition接口源码:

@FunctionalInterface
public interface Condition {

    /**
     * ConditionContext:判断条件能使用的上下文环境
     * AnnotatedTypeMetadata:注释信息
     */
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);

}

6.1、业务场景

  • 如果系统是windows,就给容器中注册(“jiji”)
  • 如果系统是Linux,就给容器中注册(“haohao”)

6.2、编写WindowsConditional

编写windows条件,需要实现接口Condition,重写matches方法

public class WindowsConditional implements Condition {
    
    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;
    }
}

6.3、编写LinuxConditional

/**
 * 判断是否是Linux系统
 */
public class LinuxConditional implements Condition {
    /**
     *
     * @param context  :判断条件能使用的上下文环境
     * @param metadata  :注释信息
     * @return
     */
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //是否Linux环境
        //1、能获取到ioc使用的beanFactory
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        //2.获取类加载器
        ClassLoader classLoader = context.getClassLoader();
        //3、获取当前环境信息
        Environment environment = context.getEnvironment();
        //4.获取到bean定义的注册类
        BeanDefinitionRegistry registry = context.getRegistry();
        //5.可以判断容器中的bean注册情况,也可以给容器中注册bean
        registry.containsBeanDefinition("person");
        
        String property = environment.getProperty("os.name");
        if(property.contains("Linux")){
            return true;
        }

        return false;
    }
}

6.4、编写配置类

@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.hh")
public class MainConfig3 {

    @Conditional({WindowsConditional.class})
    @Bean("jiji")
    public Person person1(){
        return  new Person("吉吉国王",5);
    }
    @Conditional(LinuxConditional.class)
    @Bean("haohao")
    public Person person2(){
        return  new Person("好好",3);
    }


}

6.5、测试

@Test
public void Test3(){
    
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);
    Environment environment = applicationContext.getEnvironment();
    //动态获取环境变量的值 : Windows 10
    String property = environment.getProperty("os.name");
    System.out.println(property);// Windows 10
    Map<String, Person> person = applicationContext.getBeansOfType(Person.class);
    System.out.println(person);
}

结果:

{jiji=Person{name='吉吉国王', age=5}}

当@Conditional配置在类上时,表示只有满足条件时,这个配置类配置的所有bean才会生效

7、组件注册方法

给容器中注册组件:

  • 包扫描+组件标注注解(@controller/@Service/@Repository/@Component):自己写的类

  • @Bean:导入的第三方包

  • @Import:快速的给容器中导入一个组件

    • @Import(要导入到容器中的组件):容器就会自动注册这个组件,id 默认是全类名

    • ImportSelector 接口: 实现类返回需要导入的组件的全类名数组

    • ImportBeanDefinitionRegistrar实现类,会调用接口的方法,使用BeanDefinitionRegistry注册bean

    • 使用Spring提供的FactoryBean(工厂Bean)

      • 默认获取到的是工厂bean 调用getObject 创建的对象
      • 获取工厂Bean本身,我们需要给id前面加一个**&**,例如&colorFactoryBean

7.1、@Import(重点)

7.1.1、编写配置类

@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.hh")
//@Import导入组件,id默认是全类名 com.hh.bean.Color
@Import({Color.class, Red.class})
public class MainConfig3 {

}

7.1.2、编写测试

@Test
public void test4() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);
    String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
    for (String beanDefinitionName : beanDefinitionNames) {
        System.out.println(beanDefinitionName);
    }
}

测试:

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
mainConfig3
com.hh.bean.Color
com.hh.bean.Red

7.2、ImportSelector

7.2.1、编写MyImportSelector

/**
 * 自定义逻辑返回需要导入的组件
 */
public class MyImportSelector implements ImportSelector {
    /**
     * AnnotationMetadata  当前标注@Import注解的类的所有注解信息
     * @param importingClassMetadata
     * @return   返回值 就是导入到容器中的组件全类名
     */
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {

        return new String[]{"com.hh.bean.Blue","com.hh.bean.Yellow"};
    }
}

7.2.2、修改配置类

@Configuration // 告诉Spring这是一个配置类 
@ComponentScan(value = "com.hh") 
@Import({Color.class, Red.class, MyImportSelector.class}) // 导入组件,id默认是组件的全类名 
public class MainConfig3 {}

7.2.3、测试

@Test
public void test4() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);
    String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
    for (String beanDefinitionName : beanDefinitionNames) {
        System.out.println(beanDefinitionName);
    }
}

结果:

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
mainConfig3
com.hh.bean.Color
com.hh.bean.Red
com.hh.bean.Blue
com.hh.bean.Yellow

7.3、ImportBeanDefinitionRegistrar

7.3.1、编写MyImportBeanDefinitionRegistrar

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /**
     *
     * @param importingClassMetadata
     * @param registry
     * @param importBeanNameGenerator
     * AnnotationMetadata  当前类的注解信息
     * BeanDefinitionRegistry    BeanDefinition注册类
     *    把所有需要添加到容器中的bean:调用
     *    BeanDefinitionRegistry.registerBeanDefinition 手工注册进来
     */
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
        boolean definition1 = registry.containsBeanDefinition("com.hh.bean.Red");
        boolean definition2 = registry.containsBeanDefinition("com.hh.bean.Blue");
        if(definition1&&definition2){
            //指定bean的定义信息,bean的类型
            RootBeanDefinition beanDefinition = new RootBeanDefinition(Rainbow.class);
            //注册一个bean
            registry.registerBeanDefinition("rainBow",beanDefinition);
        }
    }
}

7.3.2、修改配置类

@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.hh")
@Import({Color.class, Red.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
public class MainConfig3 {}

7.3.3、测试

@Test
public void test4() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);
    String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
    for (String beanDefinitionName : beanDefinitionNames) {
        System.out.println(beanDefinitionName);
    }
}

测试结果:

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
mainConfig3
com.xjhqre.pojo.Color
com.xjhqre.pojo.Red
com.xjhqre.pojo.Blue
com.xjhqre.pojo.Yellow
rainBow

7.4、FactoryBean

FactoryBean是一个接口,我们需要自己实现

7.4.1、编写ColorFactoryBean

/**
 * 创建一个Spring定义的FactoryBean
 */
public class ColorFactoryBean implements FactoryBean<Color> {
    //返回一个Color对象,这个对象会添加到容器中
    public Color getObject() throws Exception {
        System.out.println("ColorFactoryBean...");
        return new Color();
    }

    public Class<?> getObjectType() {

        return Color.class;
    }
    /**
     *  控制是单例吗?
     *  true :这个bean是单实例,在容器中保存一份
     *  false: 多实例 , 每次获取都会创建一个新的bean
     */
    public boolean isSingleton() {
        return false;
    }
}

7.4.2、编写配置类

@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.hh")
public class MainConfig4 {

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

7.4.3、测试

@Test
public void test5() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig4.class);
    String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
    // 工厂bean获取的是调用getObject创建的对象
    Object colorFactoryBean = applicationContext.getBean("colorFactoryBean");
    System.out.println(colorFactoryBean.getClass());

    // 如果想要拿到colorFactoryBean对象,则需要在传入的id前加一个&标识
    Object colorFactoryBean2 = applicationContext.getBean("&colorFactoryBean");
    System.out.println(colorFactoryBean2.getClass());
}

测试结果:

class com.hh.pojo.Color
class com.hh.pojo.ColorFactoryBean

8、Bean生命周期

  • bean从创建 ----> 初始化 -----> 销毁的过程

  • 容器管理bean的生命周期

  • 我们可以自定义初始化和销毁方法:容器在bean进行到当前生命周期的时候调用我们自定义的初始化和销毁方法

构造(对象创建)

  • 单实例:在容器启动的时候创建对象
  • 多实例**:在每次获取的时候创建对象**

初始化

  • 对象完成创建,并赋值好后,调用初始化方法

销毁

  • 单实例:在容器关闭的时候销毁
  • 多实例:容器不会管理这个bean,所以不会调用销毁方法

初始化和销毁方法:

  • 通过@Bean注解指定init-methoddestroy-method
  • 通过让Bean实现 InitializingBean(定义初始化逻辑),DisposableBean(定义销毁逻辑)接口,重写里面的destroy和afterPropertiesSet方法
  • 可以使用JSR250:
    • @PostConstruct: 在bean创建完成并且属性赋值完成,来执行初始化方法
    • @PreDestroy : 在容器销毁bean之前通知我们进行清理工作
  • BeanPostProcessor:接口,bean后置处理器,在bean初始化前后进行一些处理
    •  postProcessBeforeInitialization :在初始化之前工作
      
    •  postProcessAfterInitialization :在初始化之后工作
      

Spring底层对 BeanPostProcessor 的使用

  •  bean赋值,注入其他组件,@AutoWired ,生命周期注解功能,@Async ,xxx 都用到BeanPostProcessor
    

8.1、自定义初始化和销毁方法

通过@Bean注解指定init-methoddestroy-method

8.1.1、创建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...");
    }
}

8.1.2、创建配置文件

@ComponentScan("com.hh.bean")
@Configuration
public class MainConfigOfLifeCycle {

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

}

8.1.3、测试

public class IOCTest_LifeCycle {
    @Test
    public void test1() throws InterruptedException {
        //1.创建ioc容器
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
     
        //关闭容器
        applicationContext.close();
    }
}

测试结果:

car构造方法
car初始化方法
car销毁方法

8.2、InitializingBean和DisposableBean 接口

  • 通过让bean实现InitializingBean接口来定义初始化逻辑

  • 通过让bean实现DisposableBean接口来定义销毁逻辑

InitializingBean中有一个方法afterPropertiesSet,该方法在bean创建并赋值后调用

8.2.1、创建Cat类

@Component
public class Cat implements InitializingBean, DisposableBean {

    public Cat() {
        System.out.println("创建猫对象");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("销毁猫对象");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("初始化猫对象");
    }
}

8.2.2、创建配置类

@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.hh.bean")
public class MainConfig6 {

    @Bean
    public Cat cat() {
        return new Cat();
    }
}

8.2.3、测试

@Test
public void test7() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig6.class);
    applicationContext.close();
}

测试结果:

创建猫对象
初始化猫对象
销毁猫对象

8.3、@PostConstruct和@PreDestroy

  • @PostConstruct:在bean创建完成并且属性赋值完成后进行初始化
@Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PostConstruct {
}
  • @PreDestroy:在容器销毁bean之前执行
@Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PreDestroy {
}

8.3.1、创建Dog类

public class Dog {

    public Dog() {
        System.out.println("创建狗对象");
    }
    //对象创建并赋值之后调用
    @PostConstruct
    public void init() {
        System.out.println("初始化狗对象");
    }
    //容器移除对象之前
    @PreDestroy
    public void destroy() {
        System.out.println("销毁狗对象");
    }
}

8.3.2、配置类

@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.hh.bean")
public class MainConfig7 {

    @Bean
    public Dog dog() {
        return new Dog();
    }
}

8.3.3、测试

@Test
public void test8() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig7.class);
    applicationContext.close();
}

测试结果:

创建狗对象
初始化狗对象
销毁狗对象

8.4、BeanPostProcessor

BeanPostProcessor接口中有两个方法:

  • postProcessBeforeInitialization:在初始化之前执行
  • postProcessAfterInitialization:在初始化之后执行

8.4.1、编写MyBeanPostProcessor

/**
 * 后置处理器:初始化前后进行处理
 * 将后置处理器加入到容器中
 */
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    /**
     * @param bean 刚创建好的实例
     * @param beanName 实例的id
     */
    
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("id为" + beanName + "的bean对象:" + bean + "执行postProcessBeforeInitialization");
        return bean;
    }
    
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("id为" + beanName + "的bean对象:" + bean + "postProcessAfterInitialization");
        return bean;
    }
}

8.4.2、配置类

@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.hh.bean")
public class MainConfig7 {

    @Bean
    public Dog dog() {
        return new Dog();
    }
}

8.4.3、测试

@Test
public void test8() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig7.class);
    applicationContext.close();
}

测试结果:

id为mainConfig7的bean对象:com.hh.config.MainConfig7$$EnhancerBySpringCGLIB$$aef15d18@7c469c48执行postProcessBeforeInitialization
id为mainConfig7的bean对象:com.hh.config.MainConfig7$$EnhancerBySpringCGLIB$$aef15d18@7c469c48postProcessAfterInitialization
创建狗对象
id为dog的bean对象:com.hh.bean.Dog@1534f01b执行postProcessBeforeInitialization
初始化狗对象
id为dog的bean对象:com.hh.bean.Dog@1534f01bpostProcessAfterInitialization
销毁狗对象

8.4.4、BeanPostProcessor原理

  • 执行populateBean(beanName, mbd, instanceWrapper);给bean进行属性赋值
  • 开始initializeBean初始化bean
    • 先执行applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);,遍历得到容器中所有的BeanPostProcessor;挨个执行beforeInitialization,一但返回null,跳出for循环,不会执行后面的BeanPostProcessor.postProcessorsBeforeInitialization
    • 然后执行invokeInitMethods(beanName, wrappedBean, mbd);执行自定义初始化
    • 最后执行applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
BeanPostProcessor 原理:
initializeBean{   applyBeanPostProcessorsBeforeInitialization(wrappedBean,beanName);
invokeInitMethod(beanName,wrappedBean,mbd);执行自定义初始化   applyBeanPostProcessorsAfterInitialization(wrappedBean,beanName);
}

9、@Value注解

使用@Value赋值,赋值方法:

  1. 直接在@Value中写基本数值
  2. 使用SpEL表达式:#{}
  3. 使用${}取出配置文件中的值(在运行环境变量里的值)

9.1、修改Person类

public class Person {

    @Value("hh")
    private String name;

    @Value("#{20-2}")
    private Integer age;

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", email='" + email + '\'' +
                '}';
    }
}

配置文件 person.properties
person.email =2066@qq.com    

9.2、配置类

//用@PropertySource读取外部配置文件中的k/v 保存到运行的环境变量中;加载完外部的配置文件之后使用${}取出配置文件的值
@PropertySource(value = {"classpath:/person.properties"})
@Configuration
public class MainConfig9 {

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

9.3、测试类

@Test
public void test9() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig9.class);
    Person person = applicationContext.getBean(Person.class);
    System.out.println(person);

    // 也可以使用环境变量取出(牛逼牛逼)
    ConfigurableEnvironment environment = applicationContext.getEnvironment();
    String property = environment.getProperty("person.email");
    System.out.println(property);
}

测试结果:

Person{name='hh', age=18, email='[email protected]'}
[email protected]

10、自动装配

Spring利用依赖注入(DI),完成对IOC容器中各个组件的依赖关系赋值

10.1、@AutoWired

  • 给属性自动注入值

  • 默认优先按照类型去容器中找对应的组件:applicationContext.getBean(BookDao.class),找到就赋值。

  • 如果该类型的组件有多个,再将属性名作为组件的id去容器中查找applicationContext.getBean(“bookDao”)

  • @AutoWired可以在构造器、参数、方法、属性上标注

  • 自动装配默认一定要将属性赋值好,没有就会报错

    •  可以使用@Autowired(required=false);
      

源码:

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {

   boolean required() default true;

}

10.1.1、required属性

  • @AutoWired可以给required属性赋值,类型为boolean,默认为true。表示该组件是否必须。

  • 当required=false时,表示这个组件不是必须的,在调用getBean方法时如果找不到对应的组件时不会直接抛出异常,而是返回一个null

10.1.2、标注在方法上

  • 标注在方法上,Spring容器创建当前对象,就会调用方法,完成赋值

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

@Autowired
public void setCar(Car car) {
    this.car = car;
}

10.1.3、标注在构造器上

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

  • 我们可以在有参构造函数上标注@Autowired,让IOC容器创建组件时调用该类的有参构造方法

  • 如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还是可以从容器中获取

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

10.1.3、标注在参数上

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

@Bean标注的方法创建对象的时候,方法参数的值从容器中获取

// 参数car会从IOC容器中获取,可以省略Car前的@Autowired
@Bean
public Color color(Car car) {
    return new Color();
}

10.2、@Qualifier

  • 使用@Qualifier指定需要装配的组件的id,而不是用属性名 如:@Qualifier(“bookDao”)

  • @Qualifier需要和@AutoWired一起使用

10.3、@Primary

  • 该注解可以让Spring进行自动装配的时候,默认使用首选的bean

  • 也可以继续使用@Qualifier指定需要装配的bean的名字

  • 无论是否有依赖注入,@Primary标注的bean都会被容器创建

源码:

/**
 * 指示当多个候选者有资格自动装配单值依赖项时,应优先考虑 bean。如果候选中恰好存在一个“主”bean,则它将是自动装配的值。
 * 这个注解在语义上等同于 Spring XML 中元素的primary属性。
 * 可用于任何直接或间接使用@Component注释的类或使用Bean注释的方法。
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Primary {
}

10.4、@Resource

可以和@Autowired一样实现自动装配功能,但默认是按组件名称进行装配

可以给属性name赋值,自定义组件名称

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

但该注解没有支持@Primary、@Autowired(require=false)的功能

10.5、@Inject

需要导入javax.inject依赖

和@Autowired功能一样,但没有required=false的功能

@Inject
private BookDao bookDao;

@Resource和@Inject都是java的规范

11、使用Spring容器底层组件

自定义组件想要使用Spring容器底层的一些组件(ApplicationContext、BeanFactory),需要自定义组件实现xxxAware接口

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

Aware的子接口

进阶学习(源码级) -Spring注解驱动开发_第1张图片

定义一个类使用Spring底层的组件

11.1、编写Red类

@Component
public class Red implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware {

    private ApplicationContext applicationContext;

    // 获取IOC容器
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("IOC容器:" + applicationContext);
        this .applicationContext = applicationContext;
    }

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

    // 解析String语句中的占位符 $ 或 #
    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        String s = resolver.resolveStringValue("你好${os.name}, 我是#{20*18}");
        System.out.println("解析的字符串:" + s);
    }
}

11.2、配置类

@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.hh.bean")
public class MainConfig10 {
}

11.3、测试

@Test
public void test10() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig10.class);
}

测试结果:

当前bean的名字:red
解析的字符串:你好Windows 10, 我是360
IOC容器:org.springframework.context.annotation.AnnotationConfigApplicationContext@77468bd9, started on Sun Feb 27 18:53:21 CST 2022

每一个xxxAware都有一个对应的xxxAwareProcessor,用来处理相关逻辑

以ApplicationContextAwareProcessor为例

12、@Profile注解

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

例子:在不同环境下使用不同的数据源,在开发环境下使用A数据源,在测试环境下使用B数据源,在生产环境下使用C数据源

需要导入c3p0依赖C3p0:JDBC DataSources/Resource Pools和数据库驱动依赖MySQL Connector

12.1、编写db.properties

jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.username=root
jdbc.password=123456

12.2、编写配置类

@Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定的话在任何环境下都能注册这个组件

加了环境标识的bean,只有在这个环境被激活的时候才能注册到容器中,默认环境为default,即@Profile("default")

@Profile注解写在类上时,只有当指定的环境被激活时,整个类才会被注册

@Configuration
@PropertySource("classpath:/db.properties")
public class MainConfigOfProfile implements EmbeddedValueResolverAware {

    @Value("${jdbc.username}")
    private String user;

    private StringValueResolver valueResolver;

    // 测试数据库
    @Profile("test")
    @Bean("testDataSource")
    public DataSource dataSourceTest(@Value("${jdbc.password}")String pwd) throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true");
        String driverClass = valueResolver.resolveStringValue("${jdbc.driverClassName}");
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }

    // 开发数据库
    @Profile("dev")
    @Bean("devDataSource")
    public DataSource dataSourceDev(@Value("${jdbc.password}")String pwd) throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/atguigu?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true");
        String driverClass = valueResolver.resolveStringValue("${jdbc.driverClassName}");
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }

    // 生产数据库
    @Profile("prod")
    @Bean("prodDataSource")
    public DataSource dataSourceProd(@Value("${jdbc.password}")String pwd) throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/ssm?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true");
        String driverClass = valueResolver.resolveStringValue("${jdbc.driverClassName}");
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        this.valueResolver = resolver;
    }
}

12.3、环境激活方式

激活运行环境方式:

  1. 命令行方式,在虚拟机选项中输入:-Dspring.profiles.active=test

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yd7OJT2w-1657193423611)(C:\Users\小曾\Desktop\博客图片\image-20220706102314163.png)]

  1. 使用代码方式:
    1. 使用无参构造器创建application,AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
    2. 设置需要激活的环境:applicationContext.getEnvironment().setActiveProfiles("test", "dev");
    3. 注册主配置类:applicationContext.register(MainConfig11.class);
    4. 刷新容器:applicationContext.refresh();

12.4、测试

测试中使用代码方式激活环境

@Test
public void test11() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
    applicationContext.getEnvironment().setActiveProfiles("test", "dev");
    applicationContext.register(MainConfig11.class);
    applicationContext.refresh();
    String[] dataSources = applicationContext.getBeanNamesForType(DataSource.class);
    for (String dataSource : dataSources) {
        System.out.println(dataSource);
    }
}

测试结果:

testDataSource
devDataSource

13、AOP

AOP:底层【动态代理】,指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式;

AOP使用步骤:

  1. 导入aop包;Spring AOP:(spring-aspects)
  2. 定义一个业务逻辑类(MathCalculator);在业务逻辑运行的时候将日志进行打印(方法运行之前、方法运行结束、方法正常返回、方法出现异常,xxx)
  3. 定义一个日志切面类(LogAspects):切面类里面的方法需要动态感知MathCalculator.div运行到哪里然后执行;
  4. 给切面类的目标方法标注何时何地运行(通知注解)
  5. 切面类和业务逻辑类(目标方法所在类)都加入到容器
  6. 必须告诉Spring哪个类是切面类(给切面类上加一个注解:@Aspect)
  7. 配置类中加 @EnableAspectJAutoProxy 【开启基于注解的aop模式】

通知方法说明:

  • 前置通知(@Before):logStart:在目标方法(div)运行之前运行
  • 后置通知(@After):logEnd:在目标方法(div)运行结束之后运行(无论方法正常结束还是异常结束)
  • 返回通知(@AfterReturning):logReturn:在目标方法(div)正常返回之后运行
  • 异常通知(@AfterThrowing):logException:在目标方法(div)出现异常以后运行
  • 环绕通知(@Around):动态代理,手动推进目标方法运行(joinPoint.procced())

*表示所有方法,…表示任意类型的形参

13.1、AOP测试

13.1.1、编写业务逻辑类MathCalculator

public class MathCalculator {

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

}

13.1.2、编写日志切面类LogAspects

  • 切面表达式 "execution(public int com.hh.aop.MathCalculator.*(..))"中 *表示所有方法,…表示任意类型的形参。

  • JoinPoint joinPoint必须写在形参的第一位

  • joinPoint可以获取被增强方法的签名,如方法名、参数列表、方法的返回值、方法抛出的异常等等

@Aspect  //告诉Spring哪个类是切面类
public class LogAspects {

    //抽取公共的切入点表达式
    //1、本类引用
    //2、若其他的切面引用,引用方法的全类名:"com.hh.aop.LogAspects.pointCut()"
    @Pointcut("execution(public int com.hh.aop.MathCalculator.*(..))")
    public void pointCut() {
    }


    //@Before在目标方法之前切入;切入点表达式(指定在哪个方法切入)
    @Before("pointCut()")
    public void logStart(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        System.out.println("" + joinPoint.getSignature().getName() + "运行。。。@Before:参数列表是:{" + Arrays.asList(args) + "}");
    }

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

    //JoinPoint一定要出现在参数表的第一位
    @AfterReturning(value = "pointCut()", returning = "result")
    public void logReturn(JoinPoint joinPoint, Object result) {
        System.out.println("" + joinPoint.getSignature().getName() + "正常返回。。。@AfterReturning:运行结果:{" + result + "}");
    }

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

13.1.3、编写配置类

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

    //业务逻辑类加入容器中
    @Bean
    public MathCalculator calculator() {
        return new MathCalculator();
    }

    //切面类加入到容器中
    @Bean
    public LogAspects logAspects() {
        return new LogAspects();
    }
}

13.1.5、测试

@Test
public void test() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAop.class);
    MathCalculator bean = applicationContext.getBean(MathCalculator.class);
    bean.div(1, 1);
}

测试结果:

div运行。。。@Before:参数列表是:{[1, 1]}
MathCalculator...div...
div正常返回。。。@AfterReturning:运行结果:{1}
div结束。。。@After

13.1.6、AOP使用总结

AOP使用的三个步骤:

  1. 业务逻辑组件切面类都加入到容器中;告诉Spring哪个是切面类(@Aspect
  2. 在切面类上的每一个通知方法上标注通知注解,告诉Spring何时何地运行(切入点表达式)
  3. 开启基于注解的aop模式;@EnableAspectJAutoProxy

13.2、AOP原理

  • 看给容器中注册了什么组件
  • 这个组件什么时候工作
  • 这个组件的功能是什么

13.2.1、@EnableAspectJAutoProxy

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 引入AspectJAutoProxyRegister.class对象
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

    // true——使用CGLIB基于类创建代理;false——使用java接口创建代理
    boolean proxyTargetClass() default false;

    // 是否通过aop框架暴露该代理对象,aopContext能够访问.
    boolean exposeProxy() default false;

}

@EnableAspectJAutoProxy是什么?

  • @Import(AspectJAutoProxyRegistrar.class):给容器中导入AspectJAutoProxyRegistrar
  • 利用AspectJAutoProxyRegistrar自定义给容器中注册bean;BeanDefinetion

AspectJAutoProxyRegistrar中有一个registerBeanDefinitions方法,在该方法中使用AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);语句注册了一个名为internalAutoProxyCreator的组件,该组件的类型为AnnotationAwareAspectJAutoProxyCreator

13.2.2、AnnotationAwareAspectJAutoProxyCreator分析

以下是AnnotationAwareAspectJAutoProxyCreator的层次结构:

进阶学习(源码级) -Spring注解驱动开发_第2张图片

这里主要关注后置处理器和自动装备BeanFactory相关的方法:SmartInstantiationAwareBeanPostProcessor(后置处理器), BeanFactoryAware(自动装配BeanFactory).

接下来去AnnotationAwareAspectJAutoProxyCreator及其父类中寻找有关后置处理器和BeanFactory相关的方法

AbstractAutoProxyCreator:

  • setBeanFactory
  • postProcessAfterInitialization
  • postProcessBeforeInstantiation
  • postProcessProperties

AbstractAdvisorAutoProxyCreator:

  • setBeanFactory,在该方法中调用了initBeanFactory方法

AnnotationAwareAspectJAutoProxyCreator:

  • initBeanFactory

13.2.3、注册AnnotationAwareAspectJAutoProxyCreator流程

断点测试程序执行流程:

注册AnnotationAwareAspectJAutoProxyCreator的后置处理器的方法registerBeanPostProcessors(beanFactory)在IOC创建的refresh()方法中

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

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

  2. 给容器中加别的BeanPostProcessor

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

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

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

  6. 注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象,保存在容器中;下面以创建internalAutoProxyCreator(

    类型为AnnotationAwareAspectJAutoProxyCreator

    )的后置处理器BeanPostProcessor步骤为例:

    1. 创建Bean的实例
    2. populateBean;给bean的各种属性赋值
    3. initializeBean:初始化bean步骤:
      1. invokeAwareMethods():处理Aware接口的方法回调
      2. applyBeanPostProcessorsBeforeInitialization():应用所有后置处理器的postProcessBeforeInitialization(),初始化前的逻辑
      3. invokeInitMethods();执行自定义的初始化方法
      4. applyBeanPostProcessorsAfterInitialization();执行所欲后置处理器的postProcessAfterInitialization();初始化后的逻辑
    4. AnnotationAwareAspectJAutoProxyCreator的后置处理器创建成功,生成aspectJAdvisorsBuilder
  7. 把BeanPostProcessor注册到BeanFactory中:beanFactory.addBeanPostProcessor(postProcessor)

注册AnnotationAwareAspectJAutoProxyCreator的后置处理器流程图:

进阶学习(源码级) -Spring注解驱动开发_第3张图片

13.2.4、AnnotationAwareAspectJAutoProxyCreator执行时机

我们来分析IOC创建方法refresh()中的finishBeanFactoryInitialization(beanFactory);方法

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

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

    2. 创建bean

      1. 先从缓存中获取当前bean:Object sharedInstance = getSingleton(beanName);,如果能获取到,说明bean是之前被创建过的,直接使用,否则再创建;只要创建好的Bean都会被缓存起来

      2. createBean();创建bean;

        1. resolveBeforeInstantiation(beanName, mbdToUse);解析BeforeInstantiation希望后置处理器在此能返回一个代理对象;如果能返回代理对象就使用,如果不能就继续

          1. 后置处理器先尝试返回对象;

            bean = applyBeanPostProcessorsBeforeInstantiation();

            拿到所有后置处理器,如果是InstantiationAwareBeanPostProcessor,就执行postProcessBeforeInstantiation

            如果不是则执行bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);

          2. 若后置处理器返回对象失败,则会真正的去创建一个bean实例:doCreateBean(beanName, mbdToUse, args);

AnnotationAwareAspectJAutoProxyCreator执行时机流程图:

进阶学习(源码级) -Spring注解驱动开发_第4张图片

总结:

  • AnnotationAwareAspectJAutoProxyCreator在所有bean创建之前会有一个拦截,它继承了InstantiationAwareBeanPostProcessor,会调用postProcessBeforeInstantiation()方法
  • AnnotationAwareAspectJAutoProxyCreator会在任何bean创建之前先尝试返回bean的实例,
  • BeanPostProcessor是在Bean对象创建完成初始化前后调用的
  • InstantiationAwareBeanPostProcessor是在创建Bean实例之前先尝试用后置处理器返回对象

13.2.5、AnnotationAwareAspectJAutoProxyCreator作用

AnnotationAwareAspectJAutoProxyCreator的类型是InstantiationAwareBeanPostProcessor

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

    postProcessBeforeInstantiation()

    1. 判断当前bean是否在advisedBeans中(里面保存了所有需要增强bean, 如MathCalculator)
    2. 判断当前bean是否是基础类型的,是否实现了Advice、Pointcut、Advisor、AopInfrastructureBean接口,或者是否是切面(是否标注了@Aspect注解)
    3. 判断是否需要跳过,获取候选的增强器(切面里面的通知方法)封装在List candidateAdvisors集合中,判断每一个增强器是否是 AspectJPointcutAdvisor 类型的,如果是则返回true,但我们的增强器是InstantiationModelAwarePointcutAdvisor类型,所以返回false
  2. 创建对象后,调用

    postProcessAfterInitialization

    ,该方法返回一个包装bean,

    return wrapIfNecessary(bean, beanName, cacheKey)

    包装如果需要的情况下

    1. 调用

      getAdvicesAndAdvisorsForBean

      方法获取当前bean的所有增强器(通知方法),封装在集合

      Object[] specificInterceptors

      1. 找到候选的所有的增强器(找哪些通知方法是需要切入当前bean方法的)
      2. 获取到能在bean使用的增强器。
      3. 给增强器排序
    2. 保存当前bean在advisedBeans中

    3. 如果当前bean需要增强,调用

      createProxy

      创建当前bean的代理对象;

      1. 获取所有增强器(通知方法)
      2. 保存到proxyFactory
      3. 调用proxyFactory.getProxy创建代理对象:Spring自动决定,创建JdkDynamicAopProxy(config)jdk动态代理或ObjenesisCglibAopProxy(config)cglib的动态代理;
    4. 给容器中返回当前组件使用cglib增强了的代理对象;

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

AnnotationAwareAspectJAutoProxyCreator配置代理流程图:

进阶学习(源码级) -Spring注解驱动开发_第5张图片

13.2.6、拦截器链分析

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

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

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

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

    1. List interceptorList保存所有拦截器 长度为5,里面有一个默认的ExposeInvocationInterceptor 和 4个增强器;
    2. 遍历所有的增强器,将其转为Interceptor:registry.getInterceptors(advisor);
    3. 将增强器转为List,如果是MethodInterceptor,直接加入到集合中;如果不是,使用AdvisorAdapter将增强器转为MethodInterceptor;转换完成返回MethodInterceptor数组;
    4. 如果没有拦截器链,直接执行目标方法,拦截器链(每一个通知方法又被包装为方法拦截器,利用MethodInterceptor机制)

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

    6. 拦截器链的触发过程;

      1. 如果没有拦截器执行执行目标方法,或者拦截器的索引和拦截器数组-1大小一样(指定到了最后一个拦截器)执行目标方法;
      2. 链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行;拦截器链的机制,保证通知方法与目标方法的执行顺序;
    7. 执行目标方法到获取拦截器链的流程图:

      进阶学习(源码级) -Spring注解驱动开发_第6张图片

      拦截器执行流程图:
      进阶学习(源码级) -Spring注解驱动开发_第7张图片

      13.2.7、总结

      1. @EnableAspectJAutoProxy 开启AOP功能

      2. @EnableAspectJAutoProxy 会给容器中注册一个组件 AnnotationAwareAspectJAutoProxyCreator

      3. AnnotationAwareAspectJAutoProxyCreator是一个后置处理器

      4. 容器的创建流程:

        1. registerBeanPostProcessors注册后置处理器;创建AnnotationAwareAspectJAutoProxyCreator对象

        2. finishBeanFactoryInitialization

          初始化剩下的单实例bean

          1. 创建业务逻辑组件和切面组件
          2. AnnotationAwareAspectJAutoProxyCreator拦截组件的创建过程
          3. 组件创建完之后,判断组件是否需要增强,如果需要则将切面的通知方法,包装成增强器(Advisor),给业务逻辑组件创建一个代理对象(cglib)
      5. 执行目标方法:

        1. 代理对象执行目标方法
        2. CglibAopProxy.intercept()
          1. 得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor)
          2. 利用拦截器的链式机制,依次进入每一个拦截器进行执行;
          3. 正常执行:前置通知-》目标方法-》后置通知-》返回通知
            出现异常:前置通知-》目标方法-》后置通知-》异常通知

      14、声明式事务

      14.1、环境搭建

      1. 导入相关依赖:数据源、数据库驱动、Spring-jdbc模块
      2. 配置数据源、JdbcTemplate(Spring提供的简化数据库操作的工具)操作数据

      14.1.1、创建数据库

      DROP TABLE IF EXISTS `tbl_user`;
      
      CREATE TABLE `tbl_user` (
        `id` int NOT NULL AUTO_INCREMENT,
        `username` varchar(50) DEFAULT NULL,
        `age` int DEFAULT NULL,
        PRIMARY KEY (`id`)
      ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
      

      14.1.2、编写UserDao

      @Repository
      public class UserDao {
      
          @Autowired
          private JdbcTemplate jdbcTemplate;
      
          public void insert() {
              String sql = "INSERT INTO `tbl_user`(username,age) VALUES(?,?)";
              String username = UUID.randomUUID().toString().substring(0, 5);
              jdbcTemplate.update(sql, username, 19);
          }
      
      }
      

      14.1.3、编写UserService

      @Service
      public class UserService {
      
          @Autowired
          private UserDao userDao;
      
          public void insertUser() {
              userDao.insert();
              System.out.println("插入完成...");
          }
      
      }
      

      14.1.4、编写配置类

      @Configuration
      @ComponentScan("com.hh.tx")
      @PropertySource("classpath:/db.properties")
      public class TxConfig implements EmbeddedValueResolverAware {
      
          private StringValueResolver valueResolver;
      
          @Bean
          public DataSource dataSource() throws PropertyVetoException {
              ComboPooledDataSource dataSource = new ComboPooledDataSource();
              dataSource.setUser(valueResolver.resolveStringValue("${jdbc.username}"));
              dataSource.setPassword(valueResolver.resolveStringValue("${jdbc.password}"));
              dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/springtx?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true");
              dataSource.setDriverClass(valueResolver.resolveStringValue("${jdbc.driverClassName}"));
              return dataSource;
          }
      
          @Bean
          public JdbcTemplate jdbcTemplate() throws Exception{
              //Spring对@Configuration类会特殊处理;给容器中加组件的方法,多次调用都只是从容器中找组件
              JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());
              return jdbcTemplate;
          }
      
          @Override
          public void setEmbeddedValueResolver(StringValueResolver resolver) {
              this.valueResolver = resolver;
          }
      }
      
      

      14.1.5、测试

      @Test
      public void test() {
          AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(TxConfig.class);
          UserService userService = applicationContext.getBean(UserService.class);
          userService.insertUser();
      }
      

      14.2、事务测试

      14.2.1、给方法标注@Transactional

      给方法上标注@Transactional表示当前方法是一个事务方法;并在方法中设置异常

      @Service
      public class UserService {
      
          @Autowired
          private UserDao userDao;
      
          @Transactional
          public void insertUser() {
              userDao.insert();
              int i = 1 / 0;
              System.out.println("插入完成...");
          }
      
      }
      

      14.2.2、开启注解事务管理功能

      在配置类标注@EnableTransactionManagement 开启基于注解的事务管理功能;

      @Configuration // 告诉Spring这是一个配置类
      @EnableTransactionManagement
      @ComponentScan("com.hh.tx")
      @PropertySource("classpath:/db.properties")
      public class TxConfig implements EmbeddedValueResolverAware {
          // ...
      }
      

      14.2.3、配置事务管理器

      配置类中加入事务管理器bean

      //注册事务管理器在容器中
      @Bean
      public PlatformTransactionManager transactionManager() throws Exception{
          return new DataSourceTransactionManager(dataSource());
      }
      

      14.2.4、测试

      @Test
      public void test() {
          AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(TxConfig.class);
          UserService userService = applicationContext.getBean(UserService.class);
          userService.insertUser();
      }
      

      测试结果:

      控制台打印 除数 0 异常,数据库未增加新记录

      14.3、事务源码分析

      事务执行原理:

      1. 标注@EnableTransactionManagement,spring利用TransactionManagementConfigurationSelector给容器中会导入两个组件:AutoProxyRegistrarProxyTransactionManagementConfiguration

      2. AutoProxyRegistrar功能:

        • 给容器中注册一个 InfrastructureAdvisorAutoProxyCreator 组件;
        • 该组件利用后置处理器机制在对象创建以后,包装对象,返回一个代理对象(增强器),代理对象执行方法利用拦截器链进行调用;
      3. ProxyTransactionManagementConfiguration功能:

        • 给容器中注册事务增强器:
          • 事务增强器要用事务注解的信息,利用AnnotationTransactionAttributeSource解析事务注解
          • 事务拦截器:TransactionInterceptor;保存了事务属性信息,事务管理器;他是一个 MethodInterceptor,在目标方法执行的时候执行拦截器链:
            • 先获取事务相关的属性
            • 再获取PlatformTransactionManager,如果事先没有添加指定任何transactionmanger,最终会从容器中按照类型获取一个PlatformTransactionManager
            • 执行目标方法,如果异常,获取到事务管理器,利用事务管理回滚操作;如果正常,利用事务管理器,提交事务

      15、扩展原理

      15.1、BeanFactoryPostProcessor

      BeanPostProcessor:

      • bean后置处理器,bean创建对象初始化前后进行拦截工作的

      • BeanFactoryPostProcessor:beanFactory的后置处理器:

        • 在BeanFactory标准初始化之后调用,来定制和修改BeanFactory的内容
        • 所有的bean定义已经保存加载到beanFactory,但是bean的实例还未创建

      BeanFactoryPostProcessor原理:

      1. ioc容器创建对象
      2. 执行refresh()方法中的invokeBeanFactoryPostProcessors(beanFactory);
      3. 直接在BeanFactory中找到所有类型是BeanFactoryPostProcessor的组件,并执行他们的方法
      4. 在初始化创建其他组件前面执行

      15.1.1、编写Dog类

      public class Dog {
      
          public Dog() {
              System.out.println("创建狗对象");
          }
      
          @PostConstruct
          public void init() {
              System.out.println("初始化狗对象");
          }
      
          @PreDestroy
          public void destroy() {
              System.out.println("销毁狗对象");
          }
      }
      

      15.1.2、编写MyBeanFactoryPostProcessor

      编写MyBeanFactoryPostProcessor方法,实现接口BeanFactoryPostProcessor,重写postProcessBeanFactory方法

      @Component
      public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
      
          @Override
          public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
              System.out.println("执行postProcessBeanFactory");
              int count = beanFactory.getBeanDefinitionCount();
             
              System.out.println("当前BeanFactory中有" + count + " 个Bean");
              String[] names = beanFactory.getBeanDefinitionNames();
              System.out.println(Arrays.asList(names));
          }
      
      }
      

      15.1.3、编写配置类

      @ComponentScan("com.hh.ext")
      @Configuration
      public class ExtConfig {
      
          @Bean
          public Dog dog() {
              return new Dog();
          }
      
      }
      

      15.1.4、测试

      @Test
      public void test() {
          AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ExtConfig.class);
          applicationContext.close();
      }
      

      测试结果:

      执行postProcessBeanFactory
      当前BeanFactory中有8Bean
      创建狗对象
      初始化狗对象
      销毁狗对象
      

      15.2、BeanDefinitionRegistryPostProcessor

      • BeanDefinitionRegistryPostProcessor继承自BeanFactoryPostProcessor

      • 其优先于BeanFactoryPostProcessor执行;在所有bean定义信息将要被加载,bean实例还未创建的时候执行

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

      BeanDefinitionRegistryPostProcessor执行流程:

      1. ioc创建对象
      2. refresh() --> invokeBeanFactoryPostProcessors(beanFactory);
      3. 从容器中获取到所有的BeanDefinitionRegistryPostProcessor组件
        1. 依次触发所有的postProcessBeanDefinitionRegistry()方法
        2. 再来触发postProcessBeanFactory()方法;
      4. 再来从容器中找到BeanFactoryPostProcessor组件;然后依次触发postProcessBeanFactory()方法

      15.2.1、编写MyBeanDefinitionRegistryPostProcessor

      其他配置类和测试类同上

      @Component
      public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
      
          @Override
          public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
              System.out.println("执行MyBeanDefinitionRegistryPostProcessor...bean的数量:" + beanFactory.getBeanDefinitionCount());
          }
      
          //BeanDefinitionRegistry Bean定义信息的保存中心,以后BeanFactory就是按照BeanDefinitionRegistry里面保存的每一个bean定义信息创建bean实例;
          @Override
          public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
              System.out.println("执行postProcessBeanDefinitionRegistry...bean的数量:" + registry.getBeanDefinitionCount());
              // 手动注册bean对象
              // 方法一:RootBeanDefinition beanDefinition = new RootBeanDefinition(Cat.class);
              // 方法二:
              AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Cat.class).getBeanDefinition();
              registry.registerBeanDefinition("car", beanDefinition);
          }
      
      }
      
      

      15.2.2、编写Cat类

      public class Cat implements InitializingBean, DisposableBean {
      
          public Cat() {
              System.out.println("创建猫对象");
          }
      
          @Override
          public void destroy() throws Exception {
              System.out.println("销毁猫对象");
          }
      
          @Override
          public void afterPropertiesSet() throws Exception {
              System.out.println("初始化猫对象");
          }
      }
      

      15.2.3、测试

      测试结果:

      执行postProcessBeanDefinitionRegistry...bean的数量:9
      执行MyBeanDefinitionRegistryPostProcessor...bean的数量:10
      执行postProcessBeanFactory
      当前BeanFactory中有10Bean
      创建狗对象
      初始化狗对象
      创建猫对象
      初始化猫对象
      销毁猫对象
      销毁狗对象
      

      结果分析:

      1. 先执行MyBeanDefinitionRegistryPostProcessor里的postProcessBeanDefinitionRegistry,在输出bean对象的数量后又创建了一个Cat类的bean
      2. 在执行MyBeanDefinitionRegistryPostProcessor里的postProcessBeanFactory方法。输出的bean数量加1
      3. 然后执行MyBeanFactoryPostProcessor里的postProcessBeanFactory方法
      4. 以上方法都执行完成后开始创建bean对象

      16、事件监听

      ApplicationListener:监听容器中发布的事件。事件驱动模型开发;

      我们需要自己写一个监听器,该监听器必须实现ApplicationListener接口,用于监听ApplicationEvent 及其下面的子事件;

      步骤:

      1. 写一个监听器(ApplicationListener实现类)来监听某个事件(ApplicationEvent及其子类)
      2. 把监听器加入到容器;
      3. 只要容器中有相关事件的发布,我们就能监听到这个事件;
        • ContextRefreshedEvent:容器刷新完成(所有bean都完全创建)会发布这个事件;
        • ContextClosedEvent:关闭容器会发布这个事件;
      4. 发布一个事件
        • applicationContext.publishEvent()

      16.1、编写监听器

      @Component
      public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {
      
          //当容器中发布此事件以后,方法触发
          @Override
          public void onApplicationEvent(ApplicationEvent event) {
              
              System.out.println("收到事件:" + event);
          }
      
      }
      

      16.2、测试

      @Test
      public void test14() {
          AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ExtConfig.class);
          applicationContext.publishEvent(new ApplicationEvent(new String("我发布的事件")) {
          });
          applicationContext.close();
      }
      

      测试结果:

      收到事件:org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@6bc168e5, started on Wed Mar 02 15:04:16 CST 2022]
      收到事件:SpringTest$1[source=我发布的事件]
      收到事件:org.springframework.context.event.ContextClosedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@6bc168e5, started on Wed Mar 02 15:04:16 CST 2022]
      
      

      16.3、源码分析

      事件发布顺序:

      1. ContextRefreshedEvent事件
        1. 容器创建对象:refresh()
        2. finishRefresh();容器刷新完成会发布ContextRefreshedEvent事件
      2. 自己发布的事件执行
      3. 容器关闭会发布ContextClosedEvent

      事件发布流程:

      1. 容器创建对象:refresh();

      2. finishRefresh();

      3. publishEvent(new ContextRefreshedEvent(this));

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

        2. multicastEvent派发事件:

        3. 获取到所有的ApplicationListener

          for (final ApplicationListener listener : getApplicationListeners(event, type))

          1. 如果有Executor,可以支持使用Executor进行异步派发;
          2. 否则,同步的方式直接执行listener方法;invokeListener(listener, event);拿到 listener 回调 onApplicationEvent方法;

      事件多播器(派发器)创建流程:

      1. 容器创建对象:refresh();
      2. initApplicationEventMulticaster();初始化ApplicationEventMulticaster
        1. 先去容器中找有没有id=“applicationEventMulticaster”的组件;
        2. 如果没有则直接创建this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);并且加入到容器中,我们就可以在其他组件要派发事件,自动注入到派发器 applicationEventMulticaster中;

      监听器创建流程:

      1. 容器创建对象:refresh();

      2. registerListeners();从容器中拿到所有的监听器,把他们注册到applicationEventMulticaster中;
        String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
        将监听器listener注册到派发器ApplicationEventMulticaster中:getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);

      16.4、@EventListener

      通过@EventListener注解,我们也可以用来监听事件

      16.4.1、编写UserService类

      @Service
      public class UserService {
      
          @EventListener(classes = {ApplicationEvent.class})
          public void listen(ApplicationEvent event) {
              System.out.println("UserService。。监听到的事件:" + event);
          }
      
      }
      

      16.4.2、测试

      @Test
      public void test() {
          AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ExtConfig.class);
          applicationContext.publishEvent(new ApplicationEvent(new String("我发布的事件")) {
          });
          applicationContext.close();
      }
      

      测试结果:

      UserService监听到的事件:org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@6bc168e5, started on Wed Mar 02 15:37:36 CST 2022]
      UserService监听到的事件:SpringTest$1[source=我发布的事件]
      UserService监听到的事件:org.springframework.context.event.ContextClosedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@6bc168e5, started on Wed Mar 02 15:37:36 CST 2022]
      
      

      16.4.3、源码分析

      spring使用EventListenerMethodProcessor处理器来解析方法上的@EventListener;其实现了SmartInitializingSingleton接口

      SmartInitializingSingleton原理:

      1. ioc容器创建对象并refresh();
      2. finishBeanFactoryInitialization(beanFactory); 初始化剩下的单实例bean;
        1. 先创建所有的单实例bean:getBean();
        2. 获取所有创建好的单实例bean,判断是否是SmartInitializingSingleton类型的;如果是就调用afterSingletonsInstantiated();

      17、Spring容器创建过程

      17.1、BeanFactory的创建及预准备工作

      Spring容器的refresh()创建刷新:

      1. prepareRefresh():刷新前的预处理;

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

        • getEnvironment().validateRequiredProperties();检验属性的合法等

        • earlyApplicationEvents= new LinkedHashSet();保存容器中的一些早期的事件;

      2. obtainFreshBeanFactory(); 获取BeanFactory;

        • refreshBeanFactory();刷新创建BeanFactory,设置序列化ID;

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

        • 将创建的BeanFactory(DefaultListableBeanFactory)返回;

      3. prepareBeanFactory(beanFactory); BeanFactory的预准备工作,对BeanFactory进行一些设置;

        • 设置BeanFactory的类加载器、支持表达式解析器…

        • 添加部分BeanPostProcessor【ApplicationContextAwareProcessor】

        • 设置忽略的自动装配的接口:EnvironmentAwareEmbeddedValueResolverAware

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

        • 添加BeanPostProcessor【ApplicationListenerDetector】

        • 添加编译时的AspectJ;

        • 给BeanFactory中注册一些能用的组件:environment【ConfigurableEnvironment】systemProperties【MapsystemEnvironment【Map

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

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

      BeanFactory的创建及预准备工作流程图:

      进阶学习(源码级) -Spring注解驱动开发_第8张图片

      17.2、执行BeanFactory后置处理器流程

      1. invokeBeanFactoryPostProcessors(beanFactory); 执行BeanFactoryPostProcessor;

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

        • 获取所有的BeanDefinitionRegistryPostProcessor
        • 先获取实现了PriorityOrdered优先级接口的BeanDefinitionRegistryPostProcessor,将其存入currentRegistryProcessors数组中
        • sortPostProcessors(currentRegistryProcessors, beanFactory);对所有BeanDefinitionRegistryPostProcessor进行排序
        • registryProcessors.addAll(currentRegistryProcessors);注册所有`BeanDefinitionRegistryPostProcessor``
        • invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());执行所有BeanDefinitionRegistryPostProcessor
        • 在获取实现了Ordered优先级接口的BeanDefinitionRegistryPostProcessor,之后的步骤同上面的 3 ~ 5
        • 最后获取没有实现任何优先级接口的BeanDefinitionRegistryPostProcessor,之后的步骤同上面的 3 ~ 5
        • 处理完BeanDefinitionRegistryPostProcessor之后才会处理BeanFactoryPostProcessor,处理过程和BeanDefinitionRegistryPostProcessor相同

      17.3、注册Bean后置处理器

      1. registerBeanPostProcessors(beanFactory); 注册BeanPostProcessor,Bean的后置处理器

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

        • 获取所有的 BeanPostProcessor;后置处理器都默认可以通过PriorityOrdered、Ordered接口来执行优先级
        • 先注册PriorityOrdered优先级接口的BeanPostProcessor,把每一个BeanPostProcessor;添加到BeanFactory中,beanFactory.addBeanPostProcessor(postProcessor);
        • 再注册Ordered接口的
        • 接着注册没有实现任何优先级接口的
        • 最终注册MergedBeanDefinitionPostProcessor
        • 注册一个ApplicationListenerDetector;来在Bean创建完成后检查是否是ApplicationListener,如果是则调用applicationContext.addApplicationListener((ApplicationListener) bean);方法将其添加进容器中

      17.4、国际化功能

      1. initMessageSource();

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

        • 获取BeanFactory
        • 看容器中是否有id为messageSource的,类型是MessageSource的组件
          如果有赋值给messageSource,如果没有自己创建一个DelegatingMessageSource;
          MessageSource:取出国际化配置文件中的某个key的值;能按照区域信息获取;
        • 把创建好的MessageSource注册在容器中,以后获取国际化配置文件的值的时候,可以自动注入MessageSource;
          beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
          MessageSource.getMessage(String code, Object[] args, String defaultMessage, Locale locale);

      17.5、初始化事件派发器

      1. initApplicationEventMulticaster(); 初始化事件派发器;
        • 获取BeanFactory
        • 从BeanFactory中获取类型为ApplicationEventMulticasterapplicationEventMulticaster
        • 如果上一步没有获取成功;则创建一个SimpleApplicationEventMulticaster
        • 将创建的ApplicationEventMulticaster添加到BeanFactory中,以后其他组件直接自动注入

      17.6、初始化子类中的bean

      1. onRefresh(); 留给子容器(子类)
        • 子类重写这个方法,在容器刷新的时候可以自定义逻辑;

      17.7、注册监听器

      1. registerListeners(); 给容器中将所有项目里面的ApplicationListener注册进来;
        • 从容器中拿到所有的ApplicationListener
        • 将每个监听器添加到事件派发器中;
          getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
        • 派发之前步骤产生的事件;

      17.8、完成bean工厂初始化,实例化bean

      1. finishBeanFactoryInitialization(beanFactory); 初始化所有剩下的单实例bean;
      • beanFactory.preInstantiateSingletons(); 初始化后剩下的单实例bean
      1. 获取容器中的所有Bean,依次进行初始化和创建对象

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

      3. 如果Bean不是抽象的,是单实例的,不是是懒加载:

        1. 判断是否是FactoryBean;是否是实现FactoryBean接口的Bean,FactoryBean是创建对象的模板;

        2. 如果不是工厂Bean。则利用getBean(beanName); 创建对象

          getBean(beanName) —> doGetBean(name, null, null, false);

          1. 先获取缓存中保存的单实例Bean。如果能获取到说明这个Bean之前被创建过(所有创建过的单实例Bean都会被缓存起来),从private final Map singletonObjects = new ConcurrentHashMap(256);获取的

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

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

          4. 获取Bean的定义信息;

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

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

            1. createBean(beanName, mbd, args);

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

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

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

              1. 【创建Bean实例】;createBeanInstance(beanName, mbd, args);利用工厂方法或者对象的构造器创建出Bean实例

              2. applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);调用MergedBeanDefinitionPostProcessorpostProcessMergedBeanDefinition(mbd, beanType, beanName);

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

                赋值之前

                1. 先拿到InstantiationAwareBeanPostProcessor后置处理器:postProcessAfterInstantiation();
                2. 再拿到InstantiationAwareBeanPostProcessor后置处理器:postProcessPropertyValues()
                3. 最后应用Bean属性的值;为属性利用setter方法等进行赋值;
              4. 【Bean初始化】initializeBean(beanName, exposedObject, mbd);

                1. 【执行Aware接口方法】invokeAwareMethods(beanName, bean);执行xxxAware接口的方法
                2. 【在初始化之前应用 Bean 后处理器】applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
                3. 【调用初始化方法】 invokeInitMethods(beanName, wrappedBean, mbd);
                  1. 是否是InitializingBean接口的实现;执行接口规定的初始化;
                  2. 是否自定义初始化方法;
                4. 【初始化后应用 Bean 后处理器】applyBeanPostProcessorsAfterInitialization
              5. 注册Bean的销毁方法;

            5. 将创建的Bean添加到缓存中singletonObjects;

        3. 所有Bean都利用getBean创建完成以后;检查所有的Bean是否是SmartInitializingSingleton接口的;如果是;就执行afterSingletonsInstantiated();

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

      • initLifecycleProcessor();初始化和生命周期有关的后置处理器;默认从容器中找是否有lifecycleProcessor的组件【LifecycleProcessor】;如果没有new DefaultLifecycleProcessor();加入到容器;

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

      • publishEvent(new ContextRefreshedEvent(this));发布最终事件

      • liveBeansView.registerApplicationContext(this);

      17.9、Spring容器创建流程图

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OZAOKCip-1657800719983)(C:\Users\小曾\Desktop\博客图片\af05b51213fec0c7192b9038150a8d09.png)]

      17.10、总结

      1. Spring容器在启动的时候,先会保存所有注册进来的Bean的定义信息;

        • xml注册bean

        • 注解注册Bean;@Controller 、@Service、@Repository、 @Component、@Bean、@Import

      2. Spring容器会合适的时机创建这些Bean

        • 用到这个bean的时候;利用getBean创建bean;创建好以后保存在容器中;

        • 统一创建剩下所有的bean的时候;finishBeanFactoryInitialization();

      3. 后置处理器;BeanPostProcessor

        • 每一个bean创建完成,都会使用各种后置处理器进行处理;来增强bean的功能;
          • AutowiredAnnotationBeanPostProcessor:处理自动注入
          • AnnotationAwareAspectJAutoProxyCreator:来做AOP功能;
      4. 事件驱动模型

        • ApplicationListener:事件监听;

        • ApplicationEventMulticaster:事件派发:

      18、Servlet3.0

      以前使用web.xml配置文件来 配置 servlet

      
      <servlet>
          <servlet-name>springmvcservlet-name>
          <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
          
          <init-param>
              <param-name>contextConfigLocationparam-name>
              <param-value>classpath:applicationContext.xmlparam-value>
          init-param>
          
          <load-on-startup>1load-on-startup>
      servlet>
      
      
      
      <servlet-mapping>
          <servlet-name>springmvcservlet-name>
          <url-pattern>/url-pattern>
      servlet-mapping>
      

      现在使用注解方式就可以搞定,对比学习

      18.1、测试

      jsp页面:

      <%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
      DOCTYPE html>
      <html>
      <head>
          <title>JSP - Hello Worldtitle>
      head>
      <body>
      <h1><%= "Hello World!" %>
      h1>
      <br/>
      <a href="HelloServlet">Hello Servleta>
      body>
      html>
      

      编写一个类:

      @WebServlet(name = "HelloServlet", value = "/HelloServlet")
      public class HelloServlet extends HttpServlet {
          @Override
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              response.getWriter().write("hello hh");
          }
      }
      

      18.2、ServletContainerInitializer扫描包

      Shared libraries(共享库) / runtimes pluggability(运行时插件能力)

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

      使用方法:

      1. 编写 ServletContainerInitializer 的实现类 MyServletContainerInitializer
      2. 在resources目录下创建: META-INF/services/javax.servlet.ServletContainerInitializer文件
      3. 在文件中写入MyServletContainerInitializer的全类名

      代码演示:

      MyServletContainerInitializer:

      //容器启动的时候会将@HandlesTypes指定的这个类型下面的子类(实现类,子接口等)传递过来;
      @HandlesTypes(value={HashMap.class})
      public class MyServletContainerInitializer implements ServletContainerInitializer {
      
          /**
           * 应用启动的时候,会运行onStartup方法;
           *
           * Set> arg0:感兴趣的类型的所有子类型;
           * ServletContext arg1:代表当前Web应用的ServletContext;一个Web应用对应一个ServletContext;
           */
          @Override
          public void onStartup(Set<Class<?>> arg0, ServletContext sc) throws ServletException {
              // TODO Auto-generated method stub
              System.out.println("感兴趣的类型:");
              for (Class<?> claz : arg0) {
                  System.out.println(claz);
              }
          }
      
      }
      

      创建javax.servlet.ServletContainerInitializer文件

      18.3、ServletContainerInitializer注册servlet、Listener、filter

      18.3.1、编写MyServlet

      public class MyServlet extends HttpServlet {
          @Override
          protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
              resp.getWriter().write("userServlet响应");
          }
      }
      

      18.3.2、编写MyListener

      public class MyListener implements ServletContextListener {
      
          // 监听ServletContext销毁
          @Override
          public void contextDestroyed(ServletContextEvent arg0) {
              System.out.println("监听ServletContext销毁");
          }
      
          // 监听ServletContext启动初始化
          @Override
          public void contextInitialized(ServletContextEvent arg0) {
              System.out.println("监听ServletContext启动初始化");
          }
      
      }
      

      18.3.3、编写MyFilter

      public class MyFilter implements Filter {
      
          @Override
          public void destroy() {
          }
      
          @Override
          public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException {
              // 过滤请求
              System.out.println("过滤请求");
              // 放行 
              arg2.doFilter(arg0, arg1);
      
          }
      
          @Override
          public void init(FilterConfig arg0) throws ServletException {
          }
      
      }
      

      18.3.4、编写MyServletContainerInitializer

      public class MyServletContainerInitializer implements ServletContainerInitializer {
      
          /**
           * 1)、使用ServletContext注册Web组件(Servlet、Filter、Listener)
           * 2)、使用编码的方式,在项目启动的时候给ServletContext里面添加组件;
           *        必须在项目启动的时候来添加;
           *        1)、ServletContainerInitializer得到的ServletContext;
           *        2)、ServletContextListener得到的ServletContext;
           */
          @Override
          public void onStartup(Set<Class<?>> arg0, ServletContext sc) throws ServletException {
      
              // 注册组件  ServletRegistration
              ServletRegistration.Dynamic servlet = sc.addServlet("userServlet", new MyServlet());
              // 配置servlet的映射信息
              servlet.addMapping("/user");
      
      
              // 注册Listener
              sc.addListener(MyListener.class);
      
              // 注册Filter  FilterRegistration
              FilterRegistration.Dynamic filter = sc.addFilter("userFilter", MyFilter.class);
              // 配置Filter的映射信息,过滤所有请求
              filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
          }
      
      }
      
      

      18.4、整合SpringMVC分析

      1. web容器在启动的时候,会扫描每个jar包下的META-INF/services/javax.servlet.ServletContainerInitializer
      2. 加载这个文件指定的类SpringServletContainerInitializer
      3. spring的应用一启动会加载感兴趣的WebApplicationInitializer接口的下的所有组件;
      4. 并且为WebApplicationInitializer组件创建对象(组件不是接口,不是抽象类)
        1. AbstractContextLoaderInitializer:调用createRootApplicationContext()方法创建根容器;
        2. AbstractDispatcherServletInitializer:
          1. 调用createServletApplicationContext()方法创建一个web的ioc容器;
          2. 创建DispatcherServlet
          3. 将创建的DispatcherServlet添加到ServletContext中;
        3. AbstractAnnotationConfigDispatcherServletInitializer:注解方式配置的DispatcherServlet初始化器
          1. createRootApplicationContext()创建根容器,调用getRootConfigClasses();传入一个配置类
          2. createServletApplicationContext();创建web的ioc容器: 调用getServletConfigClasses();获取配置类;

      结论:以注解方式来启动SpringMVC的步骤:

      1. 继承AbstractAnnotationConfigDispatcherServletInitializer
      2. 实现抽象方法指定DispatcherServlet的配置信息

      18.5、整合SpringMVC测试

      18.5.1、依赖导入

      <dependency>
          <groupId>org.springframeworkgroupId>
          <artifactId>spring-webmvcartifactId>
          <version>5.3.14version>
      dependency>
      
      <dependency>
          <groupId>javax.servletgroupId> 
          <artifactId>servlet-apiartifactId>
          <version>3.0-alpha-1version>
          <scope>providedscope>
      dependency>
      

      18.5.1、编写HelloService

      @Service
      public class HelloService {
      
          public String sayHello(String name) {
              return "Hello " + name;
          }
      
      }
      

      18.5.2、编写HelloController

      @Controller
      public class HelloController {
      
          @Autowired
          HelloService helloService;
      
          @ResponseBody
          @RequestMapping("/hello")
          public String hello() {
              String hello = helloService.sayHello("tomcat..");
              return hello;
          }
      
      }
      

      18.5.3、编写RootConfig

      //Spring的容器不扫描controller;父容器
      @ComponentScan(value = "com.hh", excludeFilters = {
              @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
      })
      public class RootConfig {
      }
      

      18.5.4、编写AppConfig

      //SpringMVC只扫描Controller;子容器
      //useDefaultFilters=false 禁用默认的过滤规则;
      @ComponentScan(value="com.hh",includeFilters={
              @ComponentScan.Filter(type= FilterType.ANNOTATION,classes={Controller.class})
      },useDefaultFilters=false)
      @EnableWebMvc
      public class AppConfig {
      }
      

      18.5.5、编写MyWebAppInitializer

      public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
          
          //获取根容器的配置类(Spring的配置文件)父容器;
          @Override
          protected Class<?>[] getRootConfigClasses() {
              return new Class<?>[]{RootConfig.class};
          }
      
          //获取web容器的配置类(SpringMVC配置文件)子容器;
          @Override
          protected Class<?>[] getServletConfigClasses() {
              return new Class<?>[]{AppConfig.class};
          }
      
          //获取DispatcherServlet的映射信息
          //  /:拦截所有请求(包括静态资源(xx.js,xx.png)),但是不包括*.jsp;
          //  /*:拦截所有请求;连*.jsp页面都拦截;jsp页面是tomcat的jsp引擎解析的;
          @Override
          protected String[] getServletMappings() {
              return new String[]{"/"};
          }
      }
      

      18.6、配置springMVC

      通过继承WebMvcConfigurerAdapter类,重写里面的方法来配置springMVC。

      例如:配置视图解析器、静态资源访问、拦截器等等

      18.6.1、编写success.jsp

      在WEB-INF目录下新建Views目录,在Views目录下创建success.jsp页面

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <html>
      <head>
          <title>Titletitle>
      head>
      <body>
      success
      body>
      html>
      

      18.6.2、编写拦截器

      public class MyInterceptor implements HandlerInterceptor {
      
          // 目标方法运行之前执行
          @Override
          public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                  throws Exception {
              System.out.println("目标方法运行之前执行");
              return true;
          }
      
          // 目标方法执行正确以后执行
          @Override
          public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                                 ModelAndView modelAndView) throws Exception {
              // TODO Auto-generated method stub
              System.out.println("目标方法执行正确以后执行");
      
          }
      
          // 页面响应以后执行
          @Override
          public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
                  throws Exception {
              // TODO Auto-generated method stub
              System.out.println("页面响应以后执行");
          }
      
      }
      

      18.6.3、修改HelloController

      @Controller
      public class HelloController {
      
          @Autowired
          HelloService helloService;
      
          @ResponseBody
          @RequestMapping("/hello")
          public String hello() {
              String hello = helloService.sayHello("xjhqre");
              return hello;
          }
      
          //  /WEB-INF/views/success.jsp
          @RequestMapping("/suc")
          public String success(){
              return "success";
          }
      
      }
      

      18.6.4、修改AppConfig

      //SpringMVC只扫描Controller;子容器
      //useDefaultFilters=false 禁用默认的过滤规则;
      @ComponentScan(value="com.example.SpringMVC_Annotation",includeFilters={
              @ComponentScan.Filter(type= FilterType.ANNOTATION,classes={Controller.class})
      },useDefaultFilters=false)
      @EnableWebMvc
      public class AppConfig extends  WebMvcConfigurerAdapter{
      
          //视图解析器
          @Override
          public void configureViewResolvers(ViewResolverRegistry registry) {
              //默认所有的页面都从 /WEB-INF/ xxx .jsp
              //registry.jsp();
              registry.jsp("/WEB-INF/views/", ".jsp");
          }
      
          //静态资源访问
          @Override
          public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
              configurer.enable();
          }
      
          //拦截器
          @Override
          public void addInterceptors(InterceptorRegistry registry) {
              //super.addInterceptors(registry);
              // "/**":任意多的路径
              registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
          }
      
      }
      

      18.7、spring3.0异步请求

      异步请求模型:

      进阶学习(源码级) -Spring注解驱动开发_第9张图片

      编写HelloAsyncServlet

      @WebServlet(value="/async",asyncSupported=true)
      public class HelloAsyncServlet extends HttpServlet {
      
          @Override
          protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
              //1、支持异步处理asyncSupported=true
              //2、开启异步模式
              System.out.println("主线程开始。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
              AsyncContext startAsync = req.startAsync();
      
              //3、业务逻辑进行异步处理;开始异步处理
              startAsync.start(new Runnable() {
                  @Override
                  public void run() {
                      try {
                          System.out.println("副线程开始。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
                          sayHello();
                          startAsync.complete();
                          //4、获取响应
                          ServletResponse response = startAsync.getResponse();
                          response.getWriter().write("hello async...");
                          System.out.println("副线程结束。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
                      } catch (Exception e) {
                      }
                  }
              });
              System.out.println("主线程结束。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
          }
      
          public void sayHello() throws Exception{
              System.out.println(Thread.currentThread()+" processing...");
              Thread.sleep(3000);
          }
      }
      

      测试结果:

      主线程开始。。。Thread[http-nio-8080-exec-9,5,main]==>1646310054633
      主线程结束。。。Thread[http-nio-8080-exec-9,5,main]==>1646310054633
      副线程开始。。。Thread[http-nio-8080-exec-5,5,main]==>1646310054633
      Thread[http-nio-8080-exec-5,5,main] processing...
      副线程结束。。。Thread[http-nio-8080-exec-5,5,main]==>1646310057634
      

      18.8、springMVC异步请求

      18.8.1、方法一:callable

      步骤:

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

      编写AsyncController:

      @Controller
      public class AsyncController {
      
          /**
           * 1、控制器返回Callable
           * 2、Spring异步处理,将Callable 提交到 TaskExecutor 使用一个隔离的线程进行执行
           * 3、DispatcherServlet和所有的Filter退出web容器的线程,但是response 保持打开状态;
           * 4、Callable返回结果,SpringMVC将请求重新派发给容器,恢复之前的处理;
           * 5、根据Callable返回的结果。SpringMVC继续进行视图渲染流程等(从收请求-视图渲染)。
           */
          @ResponseBody
          @RequestMapping("/async01")
          public Callable<String> async01() {
              System.out.println("主线程开始..." + Thread.currentThread() + "==>" + System.currentTimeMillis());
      
              Callable<String> callable = new Callable<String>() {
                  @Override
                  public String call() throws Exception {
                      System.out.println("副线程开始..." + Thread.currentThread() + "==>" + System.currentTimeMillis());
                      Thread.sleep(2000);
                      System.out.println("副线程开始..." + Thread.currentThread() + "==>" + System.currentTimeMillis());
                      return "Callable async01()";
                  }
              };
      
              System.out.println("主线程结束..." + Thread.currentThread() + "==>" + System.currentTimeMillis());
              return callable;
          }
      
      }
      

      测试结果:

      目标方法运行之前执行
      主线程开始...Thread[http-nio-8080-exec-8,5,main]==>1646311553329
      主线程结束...Thread[http-nio-8080-exec-8,5,main]==>1646311553329
      副线程开始...Thread[MvcAsync2,5,main]==>1646311553329
      副线程开始...Thread[MvcAsync2,5,main]==>1646311555330
      目标方法运行之前执行
      目标方法执行正确以后执行
      页面响应以后执行
      

      18.8.2、方法二:DeferredResult

      进阶学习(源码级) -Spring注解驱动开发_第10张图片

      执行步骤:

      1. controller层收到请求,消息入队
      2. 队列监听到消息入队,处理消息
      3. 如果超时任务触发,将超时状态改为true,并直接返回浏览器任务超时
      4. 队列处理完任务后,判断是否超时,如果超时,中断任务,否则将任务加入完成队列
      5. 完成队列监听器监听到消息入队,取出消息,使用延期返回对象返回

      编写DeferredResultQueue:

      public class DeferredResultQueue {
      
          private static Queue<DeferredResult<Object>> queue = new ConcurrentLinkedQueue<DeferredResult<Object>>();
      
          public static void save(DeferredResult<Object> deferredResult) {
              System.out.println("加入队列");
              queue.add(deferredResult);
          }
      
          public static DeferredResult<Object> get() {
              System.out.println("从队列中取出");
              return queue.poll();
          }
      
      }
      

      编写AsyncController:

      @Controller
      public class AsyncController {
      
          @ResponseBody
          @RequestMapping("/createOrder")
          public DeferredResult<Object> createOrder() {
              // 设置超时时间
              DeferredResult<Object> deferredResult = new DeferredResult<>((long) 3000, "创建订单失败");
      
              DeferredResultQueue.save(deferredResult);
      
              return deferredResult;
          }
      
          @ResponseBody
          @RequestMapping("/create")
          public String create() {
              //创建订单
              String order = UUID.randomUUID().toString();
              DeferredResult<Object> deferredResult = DeferredResultQueue.get();
              deferredResult.setResult(order);
              return "success ===> " + order;
          }
      
      }
      

      编写AsyncController:

      @Controller
      public class AsyncController {
      
          /**
           * 1、控制器返回Callable
           * 2、Spring异步处理,将Callable 提交到 TaskExecutor 使用一个隔离的线程进行执行
           * 3、DispatcherServlet和所有的Filter退出web容器的线程,但是response 保持打开状态;
           * 4、Callable返回结果,SpringMVC将请求重新派发给容器,恢复之前的处理;
           * 5、根据Callable返回的结果。SpringMVC继续进行视图渲染流程等(从收请求-视图渲染)。
           */
          @ResponseBody
          @RequestMapping("/async01")
          public Callable<String> async01() {
              System.out.println("主线程开始..." + Thread.currentThread() + "==>" + System.currentTimeMillis());
      
              Callable<String> callable = new Callable<String>() {
                  @Override
                  public String call() throws Exception {
                      System.out.println("副线程开始..." + Thread.currentThread() + "==>" + System.currentTimeMillis());
                      Thread.sleep(2000);
                      System.out.println("副线程开始..." + Thread.currentThread() + "==>" + System.currentTimeMillis());
                      return "Callable async01()";
                  }
              };
      
              System.out.println("主线程结束..." + Thread.currentThread() + "==>" + System.currentTimeMillis());
              return callable;
          }
      
      }
      

      测试结果:

      目标方法运行之前执行
      主线程开始...Thread[http-nio-8080-exec-8,5,main]==>1646311553329
      主线程结束...Thread[http-nio-8080-exec-8,5,main]==>1646311553329
      副线程开始...Thread[MvcAsync2,5,main]==>1646311553329
      副线程开始...Thread[MvcAsync2,5,main]==>1646311555330
      目标方法运行之前执行
      目标方法执行正确以后执行
      页面响应以后执行
      

      18.8.2、方法二:DeferredResult

      执行步骤:

      1. controller层收到请求,消息入队
      2. 队列监听到消息入队,处理消息
      3. 如果超时任务触发,将超时状态改为true,并直接返回浏览器任务超时
      4. 队列处理完任务后,判断是否超时,如果超时,中断任务,否则将任务加入完成队列
      5. 完成队列监听器监听到消息入队,取出消息,使用延期返回对象返回

      编写DeferredResultQueue:

      public class DeferredResultQueue {
      
          private static Queue<DeferredResult<Object>> queue = new ConcurrentLinkedQueue<DeferredResult<Object>>();
      
          public static void save(DeferredResult<Object> deferredResult) {
              System.out.println("加入队列");
              queue.add(deferredResult);
          }
      
          public static DeferredResult<Object> get() {
              System.out.println("从队列中取出");
              return queue.poll();
          }
      
      }
      

      编写AsyncController:

      @Controller
      public class AsyncController {
      
          @ResponseBody
          @RequestMapping("/createOrder")
          public DeferredResult<Object> createOrder() {
              // 设置超时时间
              DeferredResult<Object> deferredResult = new DeferredResult<>((long) 3000, "创建订单失败");
      
              DeferredResultQueue.save(deferredResult);
      
              return deferredResult;
          }
      
          @ResponseBody
          @RequestMapping("/create")
          public String create() {
              //创建订单
              String order = UUID.randomUUID().toString();
              DeferredResult<Object> deferredResult = DeferredResultQueue.get();
              deferredResult.setResult(order);
              return "success ===> " + order;
          }
      
      }
      

      思维导图:
      进阶学习(源码级) -Spring注解驱动开发_第11张图片

      你可能感兴趣的:(spring,java,后端)