zoukankan      html  css  js  c++  java
  • 程序员收藏不看系列:近三万字总结Spring注解开发!

    注册组件

    @Configuration

    @Configuration等于一个配置文件,如果某个Java类上标注了这个注解,则表示这个类是一个配置类。

    @Bean

    将一个Java类装配到Spring的IOC容器中,默认是singleton。id默认是方法名。

    @Data
    @AllConstructorArgs
    @ToString
    public class Person(){
        private String name;
        private Integer age;
    }
    @Configuration
    public class MainConfig{
        @Bean
        public Person persion(){
            return new Person("laowang",25);
        }
    }
    测试:
    public class Test1{
        @Test
        public void test1(){
             AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
             Person person = applicationContext.getBean(Person.class);
             System.out.println(person);
        }
    }
    

    @ComponentScan

    在配置类上进行标注,指定Spring的扫描规则。

    @Configuration
    @ComponentScan(value="com.laowang.xxx")//使用某个包路径来指定Spring组件的扫描路径,如果想要装配的Bean不在这个指定的路径下,则不会被扫描。
    public class MainConfig{
        @Bean
        public Person persion(){
            return new Person("laowang",25);
        }
    }
    

    excludeFilters排除

    指定按照某些规则来排除组件。

    使用TpyeFilter指定过滤规则

    @Filter的FilterType

    • 「FilterType.ANNOTATION」 按注解排除
    @Configuration
    @ComponentScan(value="com.laowang.xxx",excludeFilters={
        @Filter(type=FilterType.ANNOTATION,
                classes={Controller.class})//排除Controller注解 
    })
    public class MainConfig{
        @Bean
        public Person persion(){
            return new Person("laowang",25);
        }
    }
    
    • 「FilterType.ASPECTJ」 按AspectJ表达式排除
    • 「FilterType.ASSIGNABLE_TYPE」 按类排除
    @Configuration
    @ComponentScan(value="com.laowang.xxx",excludeFilters={
        @Filter(type=FilterType.ASSIGNABLE_TYPE,
                classes={Controller.class})//排除Controller注解 
    })
    public class MainConfig{
        @Bean
        public Person persion(){
            return new Person("laowang",25);
        }
    }
    
    • 「FilterType.CUSTOM」 按自定义规则排除:需要创建一个类并实现TypeFilter接口
    public class MyTypeFilter implememts TypeFilter{
        /**
        * metadataReader读取当前扫描类的信息
        * metadataReaderFactory可以获取他类的任何信息
        **/
        @Override
        public boolean match(MetadataReader metadataReader,MetadataReaderFactory metadataReaderFactory){
            //获取当前类的注解信息
            AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
            //获取当前正在扫描的类的信息
            ClassMetadata classMetadata = metadataReader.getClassMetadata();
            //获取当前类的资源信息(类路径等)
            Resource resource = metadataReader.getResource();
            String className = classMetadata.getClassName();
            System.out.println("--->" + className);//返回类的全路径名
            if(className.contains("er")){//如果含有er,则装配
                return true;
            }
            return false;
            }
        }
    }
    @Configuration
    @ComponentScan(value="com.laowang.xxx",excludeFilters={
        @Filter(type=FilterType.CUSTOM,
                classes={MyTypeFilter.class})//使用自定义规则的Filter
    })
    public class MainConfig{
        @Bean
        public Person persion(){
            return new Person("laowang",25);
        }
    }
    
    • 「FilterType.REGEX」 按正则排除

    includeFilters包含

    与excludeFilters相反,指定需要哪些组件要被包含扫描。但是要配合userDefaultFilters=false配合使用,因为默认的扫描规则是扫描所有的,如果不配置,则会使用默认的规则进行扫描。

    @Configuration
    @ComponentScan(value="com.laowang.xxx",includeFilters={
        @Filter(type=FilterType.ANNOTATION,
                classes={Controller.class})},//包含Controller注解
                userDefaultFilters=false//禁用默认的扫描规则
    )
    public class MainConfig{
        @Bean
        public Person persion(){
            return new Person("laowang",25);
        }
    }
    

    @ComponentScans

    在jdk8以后,@ComponentScan是个可重复注解@Repeatable。也可以使用@ComponentScans指定扫描策略

    @ComponentScans({
        ComponentScan(value="com.laowang.xxx",includeFilters={
        @Filter(type=FilterType.ANNOTATION,
                classes={Controller.class})},
                userDefaultFilters=false)
    })
    

    @Scope

    设置组件的作用域,如果是单例,则该Bean在装配的时候,Spring会自动创建并放到IOC容器中。如果是多实例,只会在使用到该Bean的时候才创建然后装配。

    • singleton 单例(默认)
    @Scope
    
    • prototype 多实例
    @Scope("prototype")
    
    • request 在web环境中,可以设置为request
    • session 在web环境中,可以设置为session

    @Lazy-bean

    针对singleton的实例,容器启动的时候先不创建,在第一次使用的时候创建,以后每次都从容器中去取。

    @Conditional

    按照条件注册Bean。

    @Configuration
    public class MainConfig{
        //如果是windows系统,则注册bill,如果是linux则注册linus
        @Condition({WindowsCondition.class})
        @Bean("bill")
        public Person person1(){
            return new Person("Bill",50);
        }
        @Condition({LinuxCondition.class})
        @Bean("linus")
        public Person person2(){
            return new Person("linus",48);
        }
    }
    public class LinuxCondition implements Condition{
        /**
        * context:判断条件能使用的上下文
        * annotatedTypeMetadata:注释信息
        **/
        @Override
        public boolean matches(ConditionContext context,AnnotatedTypeMetadata annotatedTypeMetadata){
            //判断是否是Linux系统
            //能获取到ioc使用的beanFactory
            ConfigurableListableBeanFactory factory = context.getBeanFactory();
            //获取类加载信息
            ClassLoader classLoader = context.getClassLoader();
            //获取当前信息环境
            Environment environment = context.getEnvironment();
            //获取到Bean定义的注册类
            BeanDefinitionRegistry registry = context.getRegistry();
            String property = environment.getProperty("os.name");
            if(property.contains("linux")){
                return true;
            }
            return false;
        }
    }
    public class WindowsCondition implements Condition{
         @Override
        public boolean matches(ConditionContext context,AnnotatedTypeMetadata annotatedTypeMetadata){
                //获取当前信息环境
                Environment environment = context.getEnvironment();
                String property = environment.getProperty("os.name");
                if(property.contains("windows 10")){
                    return true;
                }
                return false;
            }
    }
    //  <font style="color:red">备注:可以在运行的时候加参数:-Dos.name=linux进行测试。</font>
    

    Condition接口

    public interface Condition{
        boolean matches(CondtionContenxt ctxt,AnnotatedTypeMetadata metadata);
    }
    

    ConditionContext 接口

    public interface ConditionContext{
        BeanDefinitionRegistry getRegistry();//返回BeanDefinitionRegistry检查bean定义
        ConfigurableListableBeanFactory getBeanFactory();//返回ConfigurableListableBeanFactory检查bean是否存在,甚至探查bean的属性
        Environment getEnvironment();//返回Environment检查环境变量是否存在以及它的值是什么
        ResourceLoader getResourceLoader();//返回ResourceLoader所加载的资源。
        ClassLoader getClassLoader();//返回ClassLoader加载并检查类是否存在。
    }
    

    AnnotatedTypeMetadata

    能够让我们检查带有@Bean注解的方法上还有什么其他的注解。

    @Import

    快速给容器导入组件,组件名默认为全类名

    public class Color{}
    @Cofiguration
    @Import({Color.class,xxx.class})
    public class MainConfig{
      
    }
    

    ImportSelector

    导入选择器,需要自定义类并实现这个接口。

    public calss Green{}
    public class Blue{}
    public class MyImportSelector implements ImportSelector{
        //返回值就是要导入到组件中的类的全类名
        //annotationMetadata:当前标注@Import注解类的所有注解信息
        @Override
        public String[] selectImports(AnnotationMetadata annotationMetadata){
        
            //方法不要返回null
            return new String[]{"xxx.xxx.Green","xxx.xxx.Blue"};
        }
    }
    @Cofiguration
    @Import({Color.class,xxx.class,MyImportSelector.class})
    public class MainConfig{
      
    }
    

    ImportBeanDefinitionRegistry

    给容器中添加组件。

    public class Rainbow{
      
    }
    public class MyImportBeanDefinitionRegistry implements ImportBeanDefinitionRegistry{
        //把所有需要添加到容器中的Bean,可以通过BeanDefinitionRegistry.registerBeanDefinition注册进来。
        @Override
        public void registerBeanDefinitions(AnnotationMetadata annotationMetadata,BeanDefinitionRegistry registry){
            boolean definition = registry.containsBeanDefinition("xxx.xxx.Green");
            if(definition){
                RootBeanDefinition beanDefinition = new RootBeanDefinition(Rainbow.class);
                registry.registerBeanDefinition("rainbow",beanDefinition);
            }
        }
    }
    @Cofiguration
    @Import({Color.class,xxx.class,MyImportSelector.class,MyImportBeanDefinitionRegistry.class})
    public class MainConfig{
    }
    

    FactoryBean

    工厂Bean,使用getObject()方法来注册Bean。

    public class ColorFactoryBean implements FactoryBean<Color>{
        //返回的对象会添加到容器中
        @Override
        public Color getObject() throws Exception{
            return new Color();
        }
        //设置返回对象的类型
        @Override
        public Class<?> getObjectType(){
            return Color.class;
        }
        //控制Bean是否是单例
        @Override
        public boolean isSingleton(){
            return false;
        }
    }
    @Cofiguration
    public class MainConfig{
        @Bean 
        ColorFactoryBean colorFactoryBean(){
            return new ColorFactoryBean();
            //注意,IOC容器会将ColorFactoryBean和Color同时注册。但是使用applicationContext.getBean("ColorFactoryBean");打印的结果是xxx.xxx.Color。因为这个装配的Bean实际是装配了Color这个类,而不是ColorFactoryBean这个类。
            //而如果想要获取到这个ColorFactoryBean类的信息,则在获取Bean的时候加上&符号,即applicationContext.getBean("&ColorFactoryBean");打印的结果才是colorFactoryBean。
        }
    }
    

    生命周期

    @Bean指定初始化和销毁方法

    Bean的生命周期:创建---初始化---销毁的过程。可以自定义初始化和销毁方法。

    • 初始化:在对象完成,并赋值后,调用初始化方法。
    • 销毁:在容器关闭的时候销毁。
    • 单实例Bean会自动初始化和销毁,但是一旦该Bean被装配为多实例,在实际调用的时候才创建该Bean并且容器关闭的时候不会销毁该Bean,需要我们手动销毁。也就是说,在该Bean被装配为多实例后,容器只会帮忙创建而将不再管理该Bean。

    指定init和detroy方法

    public class Car {
        public Car(){
            System.out.println("car constructor....");
        }
        public void init(){
            System.out.println("car init....");
        }
        public void destory(){
            System.out.println("car destroy....");
        }
    }
    @Configuration
    public class MainConfig{
        @Bean(initMethod="init",destroyMethod="destroy")
        public Car car(){
            return new Car();
        }
    }
    //  注意:如果想要看到Bean销毁的效果,需要手动调用applcationContext.close()方法。
    

    InitializingBean和DisposableBean

    @Compoent
    public class Trunk implements InitializingBean,DisposableBean {
        public Trunk(){
            System.out.println("trunk constructor....");
        }
        @Override
        public void destory() throws Exception{
            System.out.println("trunk destory....");
        }
        @Override
        public void afterPropertiesSet() throws Exception{
            System.out.println("trunk afterPropertiesSet....");
        }
    }
    

    @PostConstruct和@PreDestroy

    JSR250的规范。

    @Component
    public class Car {
        public Car(){
            System.out.println("car constructor....");
        }
        @PostConstruct
        public void init(){
            System.out.println("car init....");
        }
        @PreDestroy
        public void destory(){
            System.out.println("car destroy....");
        }
    }
    

    BeanPostProcessor接口

    在Bean初始化前后做一些事情,具体需要实现postProcessorBeforeInitialization和postProcessorAfterInitialization方法。

    public class MyBeanPostProcessor implements BeanPostProcessor{
        /**
        * 可以返回origin bean 也可以返回wrapper bean
        **/
        @Override
        public Object postProcessorBeforeInitialization(Object bean,String beanName) throws Exception{
            System.out.println("postProcessorBeforeInitialization...");
            return bean;
        }
        @Override
        public Object postProcessorAfterInitialization(Object bean,String beanName) throws Exception{
             System.out.println("postProcessorAfterInitialization...");
             return bean;
        }
    }
    

    属性赋值

    @Value

    • 可以写基本值
    • 可以写SpEL表达式:#{}
    • 可以写${}取出配置文件的值

    @PropertySource

    指定配置文件。

    @PropertySource(value={"classpath:/xxx.properties"})
    

    自动装配

    @Autowired&@Qualifier&@Primary

    • @Autowired默认按类型查找,如果找到多个相同类型的组件,再将属性名作为id查找。可以设置required=false,非必须。
    • @Autowired如果放到构造器上,则传入的对象是从容器中获取。如果只有一个有参的构造函数,@Autowired可以省略。
    • @Qualifier 明确指定要装配的组件id
    • @Primary Spring自动装配的时候默认使用首选的Bean。

    @Resource&@Inject

    • JSR 250 @Resource,默认按名称进行装配。
    • JSR 330 @Inject,需要引入javax.inject包,与@Autowired功能一样。

    @Profile

    Spring提供的可以根据当前环境动态激活和切换一系列组件(Bean)的功能。指定组件在哪个环境下才能被注册到容器。不指定的话,任何环境都能注册。

    放在组件上

    参数:value表示条件,默认值是default,比如:

    @Profile("test")
    @Bean("testDB")
    xxx
    @Profile("dev")
    @Bean("devDB")
    xxx
    @Profile("prod")
    @Bean("prodDB")
    xxx
    
    • 开发环境
    • 测试环境
    • 生产环境

    放在类上

    如果该注解放在类上,则这个类里面所有的组件都只有这个环境下才能会被注册。

    使用

    • 可以在运行时增加参数:-Dspring.profiles.active=test来调用相应的环境。
    • 使用代码的方式
    1. 创建一个ApplicationContext对象:
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
    2. 设置需要激活的环境,可以设置多个。
    applicationContext.getEnvironment().setActiveProfiles("test","dev");
    3. 注册主配置类
    applicationContext.register(MainConfig.class);
    4. 启动刷新容器
    applicationContext.refresh();
    

    Aware接口

    BeanNameAware

    组件名解析器

    ApplicationContextAware

    可注入Spring容器上下文。

    EmbeddedValueResolverAware

    值解析器
    等等,如下图所示:

    xxxProcessor

    用来处理各种Aware注入的问题,以ApplicationContextAwareProcessor为例,底层源码如下:

    class ApplicationContextAwareProcessor implements BeanPostProcessor {
        private final ConfigurableApplicationContext applicationContext;
        private final StringValueResolver embeddedValueResolver;
    
        public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) {
            this.applicationContext = applicationContext;
            this.embeddedValueResolver = new EmbeddedValueResolver(applicationContext.getBeanFactory());
        }
        //此处的作用是在Bean初始化之前做一些事情。
        @Nullable
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            AccessControlContext acc = null;
            //判断该Bean是否是实现了xxxAware接口。
            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();
            }
            //判断是否有权限
            if (acc != null) {
                AccessController.doPrivileged(() -> {
                    this.invokeAwareInterfaces(bean);
                    return null;
                }, acc);
                //然后调用设置Aware的方法。
            } else {
                this.invokeAwareInterfaces(bean);
            }
    
            return bean;
        }
        //实际实现:判断具体是哪个Aware,最终将相应的Aware赋值给该bean的setXxxAware的方法。
        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);
                }
            }
    
        }
        //后置处理做一些事情。
        public Object postProcessAfterInitialization(Object bean, String beanName) {
            return bean;
        }
    }
    

    AOP

    概念

    面向切面编程,需要增加功能的时候,不在源代码上进行修改,减少代码的耦合。使用面向切面的方式来对要执行的代码的前后逻辑进行控制。

    • @Before前置通知
    • @After后置通知
    • @AfterReturning返回通知
    • @AfterThrowing异常通知
    • @Around环绕通知:动态代理,手动推进方法执行(joinPoint.procceed())。例:
    //注意,必须开启这个注解,表示启用注解版的切面功能。
    @EnableAspectJAutoProxy
    @Configuration
    public class MainConfig{
        @Bean
        public Compute compute(){
            return new Compute();
        }
        @Bean
        public LogAspect logAspect(){
            return new logAspect();
        }
    }
    public class Compute {
        public int div(int i,int j){
            return i/j;
        }
    }
    //告诉Spring当前类是个切面类。
    @Aspect
    public class LogAspect{
        //也可以在此处创建一个方法,并且下面注解中的参数就填这个方法名。并且带上@PointCut。
        @PointCut("execution()")
        public void pointCut(){
        
        }
        @Before(切入表达式||"pointCut()")
        public void logStart(){
            System.out.println("logStart...");
        }
        @After(切入表达式)
        public void logEnd(){
            System.out.println("logEnd...");
        }
        //可以获取返回值
        @AfterReturning(value=切入表达式,returning="result")
        public void logReturn(Object result){
            System.out.println("logReturn..."); 
        }
        //可以获取异常信息
        @AfterThrowing(value=切入表达式, throwing="exception")
        public void logExeption(JoinPoint joinPoint,Exception exception){
            //可以通过这个参数获取方法的名称、参数等信息。
            //获取方法名
            joinPoint.getSignature().getName();
            //获取参数。
            joinPoint.getArgs();
            System.out.println("logExeption...");
        }
    }
    //  注意:JoinPoint一定要写在参数第一个。也就是:JoinPoint joinPoint,Exception exception。而不能写到后面:Exception exception,JoinPoint joinPoint,否则会报错。
    

    切面表达式

    Spring借助AspectJ的切点表达式来定义Spring切面。

    execution表达式

    execution (<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?) 
    execution (* com.sample.service.impl..*.*(..))
    
    • execution(): 表达式主体。
    • 第一个号:表示返回类型,号表示所有的类型。
    • 包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。
    • 第二个号:表示类名,号表示所有的类。
    • (..):最后这个星号表示方法名,号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。

    通过方法签名定义切点

    匹配所有目标类的public方法。第一个代表返回类型,第二个代表方法名,而..代表任意入参的方法:

    execution(public * *(..))
    

    匹配目标类所有以To为后缀的方法。第一个代表返回类型,而To代表任意以To为后缀的方法:

    execution(* *To(..))
    

    通过类定义切点

    匹配Waiter接口的所有方法。第一个代表返回任意类型,com.baobaotao.Waiter.代表Waiter接口中的所有方法:

    execution(* com.baobaotao.Waiter.*(..))
    

    匹配Waiter接口及其所有实现类的方法:

    execution(* com.baobaotao.Waiter+.*(..))
    

    通过类包定义切点

    在类名模式串中,"."表示包下的所有类,而".."表示包、子孙包下的所有类。  匹配com.baobaotao包下所有类的所有方法:

    execution(* com.baobaotao.*(..))
    

    匹配com.baobaotao包、子孙包下所有类的所有方法,如com.baobaotao.dao,com.baobaotao.servier以及com.baobaotao.dao.user包下的所有类的所有方法都匹配。".."出现在类名中时,后面必须跟"*",表示包、子孙包下的所有类:

    execution(* com.baobaotao..*(..))
    

    匹配包名前缀为com的任何包下类名后缀为Dao的方法,方法名必须以find为前缀。如com.baobaotao.UserDao#findByUserId()、com.baobaotao.dao.ForumDao#findById()的方法都匹配切点:

    execution(* com..*.*Dao.find*(..))
    

    通过方法入参定义切点

    切点表达式中方法入参部分比较复杂,可以使用""和".."通配符,其中""表示任意类型的参数,而".."表示任意类型参数且参数个数不限。  匹配joke(String,int)方法,且joke()方法的第一个入参是String,第二个入参是int。它匹配NaughtyWaiter#joke(String,int)方法。如果方法中的入参类型是Java.lang包下的类,可以直接使用类名,否则必须使用全限定类名,如joke(java.util.List,int):

    execution(* joke(String,int)))
    

    匹配目标类中的joke()方法,该方法第一个入参为String,第二个入参可以是任意类型,如joke(Strings1,String s2)和joke(String s1,double d2)都匹配,但joke(String s1,double d2,String s3)则不匹配:

    execution(* joke(String,*)))
    

    匹配目标类中的joke()方法,该方法第 一个入参为String,后面可以有任意个入参且入参类型不限,如joke(String s1)、joke(String s1,String s2)和joke(String s1,double d2,String s3)都匹配:

    execution(* joke(String,..)))
    

    匹配目标类中的joke()方法,方法拥有一个入参,且入参是Object类型或该类的子类。它匹配joke(String s1)和joke(Client c)。如果我们定义的切点是execution(* joke(Object)),则只匹配joke(Object  object)而不匹配joke(String cc)或joke(Client c)。

    execution(* joke(Object+)))
    

    args()和@args()

    args()函数的入参是类名,@args()函数的入参必须是注解类的类名。虽然args()允许在类名后使用+通配符后缀,但该通配符在此处没有意义:添加和不添加效果都一样。

    args()

    该函数接受一个类名,表示目标类方法入参对象按类型匹配于指定类时,切点匹配,如下面的例子:

    args(com.baobaotao.Waiter)
    

    表示运行时入参是Waiter类型的方法,它和execution((com.baobaotao.Waiter))区别在于后者是针对类方法的签名而言的,而前者则针对运行时的入参类型而言。如args(com.baobaotao.Waiter)既匹配于addWaiter(Waiterwaiter),也匹配于addNaiveWaiter(NaiveWaiter naiveWaiter),而execution((com.baobaotao.Waiter))只匹配addWaiter(Waiterwaiter)方法;实际上,args(com.baobaotao.Waiter)等价于execution(**(com.baobaotao.Waiter+)),当然也等价于args(com.baobaotao.Waiter+)。

    @args()

    该函数接受一个注解类的类名,当方法的运行时入参对象标注发指定的注解时,方法匹配切点。

    组合方式

    execution(xxx) && within()
    
    • 可以使用&&或and表示且;||或or表示或;!或not表示非

    获取通知方法的参数

    @Aspect
    public class TrackCounter {
        @Pointcut(value = "execution(* com.laowang.aop.TestBean.play(int)) && args(trackNumber)")
        public void trackPlay(int trackNumber) {
        }
    
        @Before(value = "trackPlay(trackNumber)")
        public void countTrackBefore(int trackNumber) {
            System.out.println("------before------");
            System.out.println("count :" + trackNumber++);
        }
    
        @After(value = "trackPlay(trackNumber)")
        public void getCount(int trackNumber) {
            System.out.println("------After------");
            System.out.println("count :" + trackNumber);
        }
    }
    --------------------------------------------------
    /**
     * @author wangchao
     * @date 2020/10/26  13:58
     */
    @Configuration
    @EnableAspectJAutoProxy
    public class ConfigurationTrack {
        @Bean
        public TestBean testBean() {
            return new TestBean();
        }
    
        @Bean
        public TrackCounter trackCounter() {
            return new TrackCounter();
        }
    }
    ---------------------------------------------------
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = ConfigurationTrack.class)
    public class TestMain {
        @Autowired
        private TestBean testBean;
        @Autowired
        private TrackCounter trackCounter;
    
        @Test
        public void test1() {
            testBean.play(1);
            testBean.play(2);
            testBean.play(3);
            testBean.play(4);
        }
    }
    

    @DeclareParents

    以透明的方式为被通知的对象引入额外的接口。

    /**
     * @author wangchao
     * @date 2020/10/26  14:21
     * @desc aop用于扩展原始bean中的方法,可以扩展接口的实现类中的方法。
     */
    public interface Encorable {
        void performanceEncore();
    }
    
    -------------------------------------------------
    /**
     * @author wangchao
     * @date 2020/10/26  14:23
     */
    public class DefaultEncorable implements Encorable {
        @Override
        public void performanceEncore() {
            System.out.println("test extract method!");
        }
    }
    
    ----------------------------------------------
    /**
     * @author wangchao
     * @date 2020/10/26  14:25
     */
    @Aspect
    public class EncorableIntroducer {
        @DeclareParents(value = "com.laowang.aop.Encorable+", defaultImpl = DefaultEncorable.class)
        public static Encorable encorable;
    }
    

    源码分析

    @EnableAspectJAutoProxy

    这个注解调用了@Import(AspectJAutoProxyRegistrar.class):AspectJAutoProxyRegistrar可以给容器中导入自定义组件。实际上是注册了internalAutoProxyCreator这个组件。

    class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
        AspectJAutoProxyRegistrar() {
        }
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            //如果有需要就注册AspectJAnnotationAutoProxyCreator。
            AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
            AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
            if (enableAspectJAutoProxy != null) {
                if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                    AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                }
    
                if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                    AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
                }
            }
    
        }
    }
    ------------------------->
    @Nullable
    public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
        //调用注册方法
        return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, (Object)null);
    }
    
    @Nullable
    public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, @Nullable Object source) {
        //注册或升级AnnotationAwareAspectJAutoProxyCreator
        return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
    }
    -------------------------->
    @Nullable
        private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
            Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
            //判断是否已经有这个组件。
            if (registry.containsBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator")) {
                BeanDefinition apcDefinition = registry.getBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator");
                if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
                    int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
                    int requiredPriority = findPriorityForClass(cls);
                    if (currentPriority < requiredPriority) {
                        apcDefinition.setBeanClassName(cls.getName());
                    }
                }
    
                return null;
            } else {
                //否则就从RootBeanDefinition去注册一个AnnotationAwareAspectJAutoProxyCreator
                RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
                beanDefinition.setSource(source);
                beanDefinition.getPropertyValues().add("order", -2147483648);
                beanDefinition.setRole(2);
                //再注册internalAutoProxyCreator这个组件。
                registry.registerBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator", beanDefinition);
                return beanDefinition;
            }
        }
    

    AnnotationAwareAspectJAutoProxyCreator

    AnnotationAwareAspectJAutoProxyCreator
        -> AspectJAwareAdvisorAutoProxyCreator
            -> AbstractAdvisorAutoProxyCreator
                -> AbstractAutoProxyCreator implements SmartInstantiationAwareBeanPostProcessor,BeanFactoryAware
                后置处理器(在Bean初始化前后做一些事情)
    
    • AbstractAutoProxyCreator.setBeanFatory()方法。
    • AbstractAutoProxyCreator也有后置处理器的逻辑。
    • AbstractAdvisorAutoProxyCreator重写了setBeanFatory()方法,也就是说实际是调用了子类的setBeanFatory()方法。
    • AbstractAdvisorAutoProxyCreator.setBeanFatory() -> initBeanFactory();
    • AnnotationAwareAspectJAutoProxyCreator重写了initBeanFactory()方法。

    分析容器启动加载这些组件的流程

    • 创建IOC容器:调用AnnotationConfigApplicationContext的有参构造函数。
    //使用配置类的方式创建Spring容器
    public AnnotationConfigApplicationContext(Class... annotatedClasses) {
        //无参的构造器创建对象
        this();
        //注册配置类
        this.register(annotatedClasses);
        //刷新容器
        this.refresh();
    }
    //使用包扫描路径的方式创建Spring容器。
    public AnnotationConfigApplicationContext(String... basePackages) {
        //无参的构造器创建对象
        this();
        //扫描包
        this.scan(basePackages);
        //刷新容器
        this.refresh();
    }
    
    • refresh()方法:刷新容器,创建所有的Bean及初始化信息。
    public void refresh() throws BeansException, IllegalStateException {
            Object var1 = this.startupShutdownMonitor;
            synchronized(this.startupShutdownMonitor) {
                this.prepareRefresh();
                ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
                this.prepareBeanFactory(beanFactory);
    
                try {
                    this.postProcessBeanFactory(beanFactory);
                    this.invokeBeanFactoryPostProcessors(beanFactory);
                    //注册Bean的后置处理器,拦截Bean的创建。
                    this.registerBeanPostProcessors(beanFactory);
                    this.initMessageSource();
                    this.initApplicationEventMulticaster();
                    this.onRefresh();
                    this.registerListeners();
                    this.finishBeanFactoryInitialization(beanFactory);
                    this.finishRefresh();
                } catch (BeansException var9) {
                    if (this.logger.isWarnEnabled()) {
                        this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                    }
    
                    this.destroyBeans();
                    this.cancelRefresh(var9);
                    throw var9;
                } finally {
                    this.resetCommonCaches();
                }
    
            }
        }
    
    • registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory)方法,
    protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
            PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
        }
    
    - 先获取IOC容器中已经定义了的需要创建对象的所有BeanPostProcessor。
    - 给容器中添加其他的BeanPostProcessor。
    - 判断是否是实现了PriorityOrdered的类,如果是,就表示该类有优先加载的权限。
    - 再注册是否实现了Ordered接口的类。
    - 最后注册普通优先级的类。
    
    • 从IOC容器中尝试获取BeanPostProcessor,如果没有,则创建。

    • populateBean:给Bean的各种属性赋值。

    • initializeBean:初始化Bean。

    • invokeAwareMethods:处理Aware接口的方法回调。

    • applyBeanPostProcessorBeforeInitialization():应用后置处理器在初始化之前的操作。

    • invokeInitMethods():执行初始化方法。

    • applyBeanPostProcessorAfterInitialization()::应用后置处理器在初始化之后的操作。

    • BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)创建成功

    aspectJAdvisorsBuilder
    
    • 把BeanPostProcessor注册到BeanFactory中
    beanFactory.addBeanPostProcessor(postProcessor);
    
    • finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作

    • 创建剩下的单实例bean。

    • 后置处理器先尝试返回对象;bean = applyBeanPostProcessorsBeforeInstantiation():拿到所有后置处理器,如果是InstantiationAwareBeanPostProcessor;就执行postProcessBeforeInstantiation。

    • BeanPostProcessor是在Bean对象创建完成初始化前后调用的

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

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

    • 创建bean:AnnotationAwareAspectJAutoProxyCreator在所有bean创建之前会有一个拦截,InstantiationAwareBeanPostProcessor,会调用postProcessBeforeInstantiation()。

    • 先从缓存中获取当前bean,如果能获取到,说明bean是之前被创建过的,直接使用,否则再创建;只要创建好的Bean都会被缓存起来。

    • createBean();创建bean;AnnotationAwareAspectJAutoProxyCreator 会在任何bean创建之前先尝试返回bean的实例

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

    if (bean != null) {
      bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
    }
    
    • doCreateBean(beanName, mbdToUse, args);真正的去创建一个bean实例。

    AnnotationAwareAspectJAutoProxyCreator的作用

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

    a.关心MathCalculator和LogAspect的创建
    b.判断当前bean是否在advisedBeans中(保存了所有需要增强bean)
    c.判断当前bean是否是基础类型的Advice、Pointcut、Advisor、AopInfrastructureBean,或者是否是切面(@Aspect)
    d.是否需要跳过。
    e.获取候选的增强器(切面里面的通知方法)【ListcandidateAdvisors】每一个封装的通知方法的增强器是 InstantiationModelAwarePointcutAdvisor;判断每一个增强器是否是 AspectJPointcutAdvisor 类型的;返回true。
    f.永远返回false

    2、创建对象postProcessAfterInitialization;return wrapIfNecessary(bean, beanName, cacheKey);//包装如果需要的情况下

    a.JdkDynamicAopProxy(config);jdk动态代理;
    b.ObjenesisCglibAopProxy(config);cglib的动态代理;
    c.获取当前bean的所有增强器(通知方法)  Object[]  specificInterceptors
    d.找到候选的所有的增强器(找哪些通知方法是需要切入当前bean方法的)
    e.获取到能在bean使用的增强器。
    f.给增强器排序
    g.保存当前bean在advisedBeans中;
    h.如果当前bean需要增强,创建当前bean的代理对象;
    i.获取所有增强器(通知方法)
    j.保存到proxyFactory
    k.创建代理对象:Spring自动决定
    l.给容器中返回当前组件使用cglib增强了的代理对象;
    m.以后容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程;

    3、目标方法执行:容器中保存了组件的代理对象(cglib增强后的对象),这个对象里面保存详细信息(比如增强器,目标对象,xxx)。

    • 如果没有拦截器执行执行目标方法,或者拦截器的索引和拦截器数组-1大小一样(指定到了最后一个拦截器)执行目标方法。
    • 链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行。
    • 拦截器链的机制,保证通知方法与目标方法的执行顺序。
    • 如果是MethodInterceptor,直接加入到集合中
    • 如果不是,使用AdvisorAdapter将增强器转为MethodInterceptor;转换完成返回MethodInterceptor数组;

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

    b.根据ProxyFactory对象获取将要执行的目标方法拦截器链;List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

    c.List interceptorList保存所有拦截器(5个,一个默认的ExposeInvocationInterceptor 和 4个增强器)

    d.遍历所有的增强器,将其转为Interceptor(registry.getInterceptors(advisor))。

    e.将增强器转为List

    f.如果没有拦截器链,直接执行目标方法:拦截器链(每一个通知方法又被包装为方法拦截器,利用MethodInterceptor机制)

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

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

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

    总结

    • 1.@EnableAspectJAutoProxy 开启AOP功能

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

    • 3.AnnotationAwareAspectJAutoProxyCreator是一个后置处理器;

    • 4.容器的创建流程:

    • registerBeanPostProcessors()注册后置处理器;创建AnnotatiAwareAspectJAutoProxyCreator对象

    • finishBeanFactoryInitialization()初始化剩下的单实例bean

    1. 创建业务逻辑组件和切面组件
       2. AnnotationAwareAspectJAutoProxyCreator拦截组件的创建过程
       3. 组件创建完之后,判断组件是否需要增强
          - 是:切面的通知方法,包装成增强器(Advisor)。
          - 否:给业务逻辑组件创建一个代理对象(cglib)。
       4. 执行目标方法:
          
          1. 代理对象执行目标方法
          2. CglibAopProxy.intercept();
             - 得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor)
             - 利用拦截器的链式机制,依次进入每一个拦截器进行执行;
             - 效果:
               - 正常执行:前置通知-》目标方法-》后置通知-》返回通知
               - 出现异常:前置通知-》目标方法-》后置通知-》异常通知
    

    Servlet 3.0

    简介

    Servlet3.0提供了一些可插拔的注解,可以不适用配置文件的方式来玩转Servlet,Tomcat7以上版本才支持Servlet3.0。

    @WebServlet

    使用@WebServlet注解创建一个servlet并将该Servlet放到ServletContext容器中。

    @WebServlet(value="/hello")
    public class HelloServlet extends HttpServlet{
        @Override
        proteced void doGet(HttServletRequest req,HttpServletResponse res){
            res.getWriter().write("hello....");
        }
    }
    

    @WebFilter

    类似@WebServlet,@WebFilter可以往ServletContext容器中注册一个Filter组件,继承Filter。

    @WebFilter(“/foo”)
    public class MyFilter implements Filter {
    public void doFilter(HttpServletRequest req, HttpServletResponse
    res)
        {
        ...
        }
    }
    

    @WebListener

    @WebListener
    public class MyListener implements ServletContextListener{
    public void contextInitialized(ServletContextEvent sce) {
        ServletContext sc = sce.getServletContext();
        sc.addServlet("myServlet", "Sample servlet",
        "foo.bar.MyServlet", null, -1);
        sc.addServletMapping("myServlet", new String[] {
        "/urlpattern/*" });
        }
    }
    

    过滤规则

    • / 表示过滤所有请求,但是不会过滤.jsp,因为.jsp文件是交给tomcat的jsp引擎解析。
    • /* 表示过滤所有请求,包括*.jsp的请求。
    • /** 过滤多层所有请求。

    Servlet3.0-ServletContainerListener

    ServletContainerListener会在Servlet启动的时候会扫描所有jar文件中的META-INF/services/文件夹下的所有ServletContainerInitializer的实现文件,文件名为javax.servlet.ServletContainerInitializer,文件内容为ServletContainerInitializer的实现类的全类名。用来初始化注册自定义的组件(spi机制)。例:

    1、在测试项目中的src目录下创建一个META-INF/services/javax.servlet.ServletContainerInitializer文件,并在里面写上自己的ServletContainerInitializer实现类的全路径。

    //@HandlesTypes()这个注解可以将我们指定的类的子类或子接口注册进来,但是不包括扩这个类本身。
    @HandlesTypes(value={})
    public class MyServletContainerInitializer implements ServletContainerInitializer{
        //Set<Class<?>> arg0:注册我们感兴趣的类的所有子类。
        //ServletContext arg1:ServletContext容器,也可以通过这个参数手动注册组件(Servlet,Filter,Listener)。
        @Override
        public void onStartup(Set<Class<?>> arg0,ServletContext arg1){
            //注册Servlet组件,UserSerlvet为自定义的组件,需要实现HttpServlet接口。
            ServletRegistration.Dynamic servlet = arg1.addServlet("userSerlvet",new UserServlet());
            //添加Mapping,即匹配路径。
            servlet.addMapping("/user");
            //类似,注册Listener
            arg1.addListener(UserListener.class);
            //注册Filter,类似。
            FilterRegistration.Dynamic filter = arg1.addFilter("userFilter",UserFilter());
            filter.addMappingForUrlPatterns(EnumSet.of(DispatherType.REQUEST),true,"/**");
        }
    }
    

    Servlet3.0与SpringMVC整合分析

    1. web容器在启动的时候,会扫描每个jar包下的META-INF/services/javax.servlet.ServletContainerInitializer
    2. 加载这个文件指定的类SpringServletContainerInitializer
    3. spring的应用一启动会加载感兴趣的WebApplicationInitializer接口的下的所有组件;
    4. 并且为WebApplicationInitializer组件创建对象(组件不是接口,不是抽象类)

    a.创建根容器:createRootApplicationContext() getRootConfigClasses();传入一个配置类。
    b.创建web的ioc容器:createServletApplicationContext(); 获取配置类;getServletConfigClasses();
    c.AbstractContextLoaderInitializer:创建根容器;createRootApplicationContext();
    d.AbstractDispatcherServletInitializer:创建一个web的ioc容器;createServletApplicationContext(); 创建了DispatcherServlet;createDispatcherServlet();将创建的DispatcherServlet添加到ServletContext中;getServletMappings();
    e.AbstractAnnotationConfigDispatcherServletInitializer:注解方式配置的DispatcherServlet初始化器:

    总结

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

    SpringMVC整合

    /**
     * @author laowang
     * @Date 2019/3/18 14:48
     * @Description
     */
    public class MyServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
        //配置主容器
        @Override
        protected Class<?>[] getRootConfigClasses() {
            return new Class[]{RootConfig.class};
        }
    
        //配置副容器
        @Override
        protected Class<?>[] getServletConfigClasses() {
            return new Class[]{AppConfig.class};
        }
    
        //配置拦截规则
        @Override
        protected String[] getServletMappings() {
            return new String[]{"/"};
        }
    }
    
    /**
     * @author laowang
     * @Date 2019/3/18 14:58
     * @Description
     */
    //SpringMVC只扫描Controller,必须禁用默认的扫描规则。
    @ComponentScan(value = "com.laowang.spring.servlet3",includeFilters = {
            @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)
    },useDefaultFilters = false)
    public class AppConfig {
    }
    /**
     * @author laowang
     * @Date 2019/3/18 14:58
     * @Description
     */
    //Spring容器不扫描Controller
    @ComponentScan(value = "com.laowang.spring.servlet3",excludeFilters = {
            @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
    })
    public class RootConfig {
    }
    /**
     * @author laowang
     * @Date 2019/3/18 15:03
     * @Description
     */
    @Controller
    public class HelloController {
        @Autowired
        private HelloService helloService;
    
        @ResponseBody
        @RequestMapping("/hello")
        public String hello() {
            return helloService.sayHello("laowang");
        }
    }
    /**
     * @author laowang
     * @Date 2019/3/18 15:04
     * @Description
     */
    @Service
    public class HelloService {
    
        public String sayHello(String name) {
            return "hello:" + name;
        }
    }
    

    SpringMVC定制

    /**
     * @author laowang
     * @Date 2019/3/18 14:58
     * @Description
     */
    //SpringMVC只扫描Controller,必须禁用默认的扫描规则。
    @ComponentScan(value = "com.laowang.spring.servlet3",includeFilters = {
            @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)
    },useDefaultFilters = false)
    //开启WebMVC定制配置
    @EnableWebMvc
    public class AppConfig implements WebMvcConfigurer {
        //启用异步支持
        @Override
        public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
            configurer.registerCallableInterceptors();
        }
        //过滤静态资源
        @Override
        public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
            configurer.enable();
        }
        //指定视图解析器,可以自定义
        @Override
        public void configureViewResolvers(ViewResolverRegistry registry) {
            registry.jsp("/WEB-INF/views/","jsp");
        }
        //配置拦截规则
        @Override
        public void configurePathMatch(PathMatchConfigurer configurer) {
        }
        //配置自定义拦截器
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
        }
    }
    

    Servlet3.0异步请求

    /**
     * @author laowang
     * @Date 2019/3/18 15:32
     * @Description
     */
    //asyncSupported支持异步
    @WebServlet(value = "/async", asyncSupported = true)
    public class HelloServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //支持异步
            AsyncContext asyncContext = req.startAsync();
            System.out.println(Thread.currentThread() + "main thread start....." + System.currentTimeMillis());
            asyncContext.setTimeout(4000);
            asyncContext.start(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println(Thread.currentThread() + "sub thread start....." + System.currentTimeMillis());
                        sayHello();
                        //告诉容器异步操作执行完毕,可以进行线程回收。
                        asyncContext.complete();
                        System.out.println(Thread.currentThread() + "sub thread end....." + System.currentTimeMillis());
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
            //使用asyncContext获取response并写出数据。
            asyncContext.getResponse().getWriter().write("async------------->");
            System.out.println(Thread.currentThread() + "main thread end....." + System.currentTimeMillis());
        }
    
        public void sayHello() throws Exception {
            Thread.sleep(3000);
            System.out.println("hello~");
        }
    }
    

    执行效果

    Thread[http-bio-8080-exec-5,5,main]main thread start.....1552894856004
    Thread[http-bio-8080-exec-5,5,main]main thread end.....1552894856006
    Thread[http-bio-8080-exec-2,5,main]sub thread start.....1552894856006
    hello~
    Thread[http-bio-8080-exec-2,5,main]sub thread end.....1552894859007
    

    SpringMVC异步请求

    返回Callbale

    /**
     * @author laowang
     * @Date 2019/3/18 15:42
     * @Description
     */
    @Controller
    public class AsyncController {
        @ResponseBody
        @RequestMapping("/async1")
        public Callable<String> async1() {
            System.out.println(Thread.currentThread() + "main thread start....." + System.currentTimeMillis());
            Callable<String> callable = new Callable<String>() {
                @Override
                public String call() throws Exception {
                    System.out.println(Thread.currentThread() + "sub thread start....." + System.currentTimeMillis());
                    Thread.sleep(2000);
                    System.out.println(Thread.currentThread() + "sub thread end....." + System.currentTimeMillis());
                    return "async1";
                }
            };
            System.out.println(Thread.currentThread() + "main thread end....." + System.currentTimeMillis());
            return callable;
        }
    }
    

    执行效果

    preHandle...........
    Thread[http-bio-8080-exec-10,5,main]main thread start.....1552895185639
    Thread[http-bio-8080-exec-10,5,main]main thread end.....1552895185640
    Thread[MvcAsync1,5,main]sub thread start.....1552895185646
    Thread[MvcAsync1,5,main]sub thread end.....1552895187648
    preHandle...........
    postHandler...........
    

    原理

    1. 控制器返回Callable
    2. Spring异步处理,将Callable 提交到 TaskExecutor 使用一个隔离的线程进行执行
    3. DispatcherServlet和所有的Filter退出web容器的线程,但是response 保持打开状态;
    4. Callable返回结果,SpringMVC将请求重新派发给容器,恢复之前的处理;
    5. 根据Callable返回的结果。SpringMVC继续进行视图渲染流程等(从收请求-视图渲染)。
    preHandle.../springmvc-annotation/async1
    主线程开始...Thread[http-bio-8081-exec-3,5,main]==>1513932494700
    主线程结束...Thread[http-bio-8081-exec-3,5,main]==>1513932494700
    =========DispatcherServlet及所有的Filter退出线程============================
    
    ================等待Callable执行==========
    副线程开始...Thread[MvcAsync1,5,main]==>1513932494707
    副线程开始...Thread[MvcAsync1,5,main]==>1513932496708
    ================Callable执行完成==========
    
    ================再次收到之前重发过来的请求========
    preHandle.../springmvc-annotation/async1
    postHandle...(Callable的之前的返回值就是目标方法的返回值)
    afterCompletion...
    

    返回DefferedResult

    /**
     * @author laowang
     * @Date 2019/3/18 15:51
     * @Description
     */
    //中间件,用来保存队列
    public class DeferedQueue {
        private static Queue<DeferredResult<Object>> defferedQueue = new ConcurrentLinkedDeque<DeferredResult<Object>>();
    
        public static void save(DeferredResult<Object> object) {
            defferedQueue.add(object);
        }
    
        public static DeferredResult<Object> get() {
            return defferedQueue.poll();
        }
    }
    @Controller
    public class DeferedResultController{
        @ResponseBody
        @RequestMapping("/create-order")
        public DeferredResult<Object> createOrder() {
            DeferredResult<Object> deferredResult = new DeferredResult<>();
            DeferedQueue.save(deferredResult);
            return deferredResult;
        }
    
        @ResponseBody
        @RequestMapping("/create")
        public String create() {
            String order = UUID.randomUUID().toString();
            DeferredResult<Object> deferedResult = DeferedQueue.get();
            deferedResult.setResult(order);
            return order;
        }
    }
    

    写在最后

    欢迎大家关注我的公众号【风平浪静如码】,海量Java相关文章,学习资料都会在里面更新,整理的资料也会放在里面。

    觉得写的还不错的就点个赞,加个关注呗!点关注,不迷路,持续更新!!!

  • 相关阅读:
    TextView文字排版问题:
    Cent OS 6 主机名设置
    windows server 时间同步
    DELL服务器SAS 5 I_R 完全配置手册
    SAS 5/iR Adapter 驱动下载
    U盘加载硬盘控制卡驱动安装Windows 2003 指南
    邮件客户端导入邮件通讯录地址薄
    Symantec System Recovery
    windows server 备份与还原
    Acronis 备份使用
  • 原文地址:https://www.cnblogs.com/fengpinglangjingruma/p/14044386.html
Copyright © 2011-2022 走看看