Spring注解开发

文章目录

  • 一、IOC
      • 1.组件注册
        • 1.1 @Configuration
        • 1.2 @Bean
        • 1.3 @ComponentScan
        • 1.4 @Import
        • 1.5 FactoryBean
      • 2. Spring生命周期
        • 2.1 @Bean(initMethod="init",destroyMethod="detory")
        • 2.2 @PostConstruct @PreDestroy (JSR250)
        • 2.3 BeanPostProcessor
      • 3. 属性赋值
        • 3.1 @Value赋值
        • 3.2 @PropertySource
      • 4. 自动装配
        • 4.1 @Autowired
        • 4.2 @Qualifier("id")
        • 4.3 @Primary
        • 4.4 @Resource (JSR250)
        • 4.5 @Inject (JSR330)
        • 4.6 @Profile("test")
  • 二、AOP
        • 1. 前置通知(@Before)
        • 2. 后置通知(@After)
        • 3. 返回通知(@AfterReturning)
        • 4. 异常通知(@AfterThrowing)
        • 5. 环绕通知(@Around)
        • 6. 公共切入点(@Pointcut)
        • 7. 切面类(@Aspect)
        • 8. @EnableAspectJAutoProxy
        • 9. AOP注解流程
  • 三、声明式事务
        • 1. @Transactional
        • 2. @EnableTransactionManagement
        • 3. Spring事务原理
  • 四、扩展原理
        • 1. BeanFactory
        • 2. FactoryBean
        • 3. BeanPostProcessor
        • 4. BeanFactoryPostProcessor
        • 5. BeanDefinitionRegistryPostProcessor
        • 6. ApplicationListener
  • 五、Spring容器创建
        • 流程图
  • 六、servlet3.0
        • 1.@WebServlet() @webListener() @webFilter()
        • 2.Shared libraries(共享库) / runtimes pluggability(运行时插件能力)
        • 3. 注册三大组件
        • 4. Servlet3.0 异步请求
          • asyncSupported=true
  • 七、SpringMVC
        • 1. SpringMVC容器创建
        • 2. 父容器配置
        • 3. web子容器配置 定制与接管SpringMVC
          • 1. @EnableWebMvc 开启SpringMVC定制配置功能
          • 2. WebMvcConfigurerAdapter 参考SpringMVC官方文档自定义配置
        • 4. SpringMVC 异步请求
          • 1. 返回Callable (SpringMVC维护了一个异步的线程池)
          • 2. 返回DeferredResult

一、IOC

1.组件注册

1.1 @Configuration

​ 告诉spring这是一个配置类,对应于application.xml文件

  • 通过AnnotationConfigApplicationContext来加载配置类

  • Spring对@Configuration类会特殊处理;给容器中加组件的方法,第一次是创建,之后的调用都是从容器中找组件

    //使用application.xml时使用ClassPathXmlApplicationContext加载容器
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
    Person bean = (Person) applicationContext.getBean("person");
    
    //使用@Configuration时使用AnnotationConfigApplicationContext加载容器
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
    		Person bean = applicationContext.getBean(Person.class);
    

1.2 @Bean

  • 用于配置类中,给容器注册一个bean,类型为返回值的类型,id默认是用方法名作为id,对应于application.xml中的

    @Bean("person")
    	public Person person01(){
    		return new Person("lisi", 20);
    	}
    
    • @Scope 调整bean的作用域,对应于xml中的scope属性
      • @Scope(“singleton”) 默认是单例,ioc容器启动会调用方法创建对象放到ioc容器中,以后每次获取就是直接从容器(map.get())中拿
        • @Lazy 只能在单实例Bean中使用,容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化
      • @Scope(“prototype”) 多例 ioc容器启动并不会去调用方法创建对象放在容器中,每次获取的时候才会调用方法创建对象
      • @Scope(“request”) 同一次请求创建一个实例
      • @Scope(“session”) 同一个session创建一个实例
    • @Conditional({Condition}) 按照一定的条件进行判断,满足条件给容器中注册bean

1.3 @ComponentScan

写在配置类上,配置注解扫描,value为需要扫描的包路径(是重复注解)

  • 默认扫描配置的路径下的**@Controller**、@Service@Repository@Component

  • @Controller、@Service、@Repository,@Component注册的bean的名字默认是类名小写

  • 属性 excludeFilters = Filter[],指定扫描时排除那些组件

  • 属性 includeFilters = Filter[],指定扫描的时候只需要包含哪些组件,需要先设置useDefaultFilters = false

    @ComponentScans(
    		value = {
    				@ComponentScan(value="com.atguigu",includeFilters = {
    						@Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
    						@Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}),
    						@Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})
    				},useDefaultFilters = false)	
    		}
    		)
    
    public class MainConfig {
        ...
    	}
    

1.4 @Import

写在配置类上面 快速给容器中导入一个组件

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

  • ImportSelector:返回需要导入的组件的全类名数组(springboot中经常使用)

  • ImportBeanDefinitionRegistrar:手动注册bean到容器中

  • @Configuration
    @Import({Color.class,Red.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
    //@Import导入组件,id默认是组件的全类名
    public class MainConfig2 {
    	...
    }
    

1.5 FactoryBean

​ 工厂Bean接口

  • 默认获取到的是工厂bean调用getObject创建的对象

  • 要获取工厂Bean本身,我们需要给id前面加一个&,即&colorFactoryBean

  • public class ColorFactoryBean implements FactoryBean<Color> {
    	//返回一个Color对象,这个对象会添加到容器中
    	@Override
    	public Color getObject() throws Exception {
    		System.out.println("ColorFactoryBean...getObject...");
    		return new Color();
    	}
    	@Override
    	public Class<?> getObjectType() {
    		return Color.class;
    	}
    	@Override
    	public boolean isSingleton() {
    		// TODO Auto-generated method stub
    		return false;
    	}
    
    }
    
    @Bean
    public ColorFactoryBean colorFactoryBean(){
        return new ColorFactoryBean();
    }	
    
    //获取FactoryBean 
    Object bean = applicationContext.getBean("&colorFactoryBean");
    

2. Spring生命周期

2.1 @Bean(initMethod=“init”,destroyMethod=“detory”)

  • @Bean(initMethod=“init”,destroyMethod=“detory”) 指定初始化方法和销毁方法,相当于xml中bean标签的属性init-method和destroy-method
    • 单实例bean在ioc容器关闭时销毁,在多实例bean中初始化方法不会在启动时执行,不会自动销毁,需要手动销毁
  • 让Bean实现InitializingBean接口定义初始化逻辑,实现DisposableBean接口定义销毁逻辑、

2.2 @PostConstruct @PreDestroy (JSR250)

  • **@PostConstruct **写在方法上 在bean创建完成并且属性赋值完成;来执行初始化方法(JSR250)
  • **@PreDestroy ** 写在方法上 在容器销毁bean之前通知我们进行清理工作 (JSR250)
 	//对象创建并赋值之后调用
 	@PostConstruct
 	public void init(){
 		System.out.println("Dog....@PostConstruct...");
 	}
 	
 	//容器移除对象之前
 	@PreDestroy
 	public void detory(){
 		System.out.println("Dog....@PreDestroy...");
 	}

2.3 BeanPostProcessor

Spring底层中 BeanPostProcessor 后置处理器 ,用于bean赋值,注入其他组件,@Autowired,生命周期注解功能,@Async,xxx

  • postProcessBeforeInitialization 在初始化之前工作

    遍历得到容器中所有的BeanPostProcessor;挨个执行beforeInitialization,

  • postProcessAfterInitialization 在初始化之后工作

    常用的有:

  • ApplicationContextAwareProcessor: 实现了在bean初始化时将ioc容器注入bean中

  • BeanValidationPostProcessor :可以在bean创建完成时对数据进行校验

  • InitDestroyAnnotationBeanPostProcessor 处理@PostConstruct和@PreDestroy

  • AutowiredAnnotationBeanPostProcessor 在对象创建完之后处理@Autowired标注的所有属性

3. 属性赋值

3.1 @Value赋值

  1. 基本数值

  2. 可以写SpEL; #{}

  3. 可以写${};取出配置文件【properties】中的值(在运行环境变量里面的值),需要结合@PropertySource注解使用

    	@Value("张三")
    	private String name;
    	@Value("#{20-2}")
    	private Integer age;
    	@Value("${person.nickName}")
    	private String nickName;
    

3.2 @PropertySource

(可重复注解)读取配置文件 value传String数组,可以导入多个配置文件


<context:property-placeholder location="classpath:person.properties"/>
// 使用@PropertySource读取外部配置文件中的k/v保存到运行的环境变量中;
// 加载完外部的配置文件以后使用${}取出配置文件的值
@PropertySource(value={"classpath:/person.properties"})
@Configuration
public class MainConfigOfPropertyValues {
	@Bean
	public Person person(){
		return new Person();
	}

}
// 所设置的配置文件中的值也能通过IOC容易取到
// 文件中的所有值都会被加载到ConfigurableEnvironment中
ConfigurableEnvironment environment = applicationContext.getEnvironment();
String property = environment.getProperty("person.nickName");
System.out.println(property);

4. 自动装配

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

4.1 @Autowired

可以用于构造器,方法,属性,参数上

  1. 默认优先按照类型去容器中找对应的组件

    applicationContext.getBean(BookDao.class);找到就赋值

  2. 如果找到多个相同类型的组件

    则将属性的名称作为组件的id去容器中查找 applicationContext.getBean(“bookDao”)

  3. 自动装配默认一定要将属性赋值好,没有就会报错;可以使用@Autowired(required=false),这样没有找到Bean就不会装配

  4. [标注在方法上]

    标注在方法,Spring容器创建当前对象,就会调用方法,完成赋值;方法使用的参数,自定义类型的值从ioc容器中获取

    @Bean+方法参数;参数从容器中获取;默认不写@Autowired效果是一样的;都能自动装配

  5. [标注在构造器上]

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

    如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,构造器要用的组件,都是从容器中获取

  6. [标注在参数位置]

    参数位置的组件还是可以自动从容器中获取

4.2 @Qualifier(“id”)

​ 使用@Qualifier指定需要装配的组件的id,而不是使用属性名

4.3 @Primary

​ 让Spring进行自动装配的时候,默认使用首选的bean(未指定时使用首选)

​ 也可以继续使用@Qualifier指定需要装配的bean的名字(指定后就按指定的装备)

4.4 @Resource (JSR250)

可以和@Autowired一样实现自动装配功能;默认是按照组件名称进行装配的;
不支持@Primary,不支持@Autowired(reqiured=false);

4.5 @Inject (JSR330)

需要导入javax.inject的包,和Autowired的功能一样。支持@Primary,不支持required=false;

  • 自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory,xxx);

    自定义组件实现xxxAware;在创建对象的时候,会调用接口规定的方法注入相关组件;Aware;

    把Spring底层一些组件注入到自定义的Bean中;

    xxxAware:功能使用xxxProcessor;

    ApplicationContextAware==》ApplicationContextAwareProcessor;

4.6 @Profile(“test”)

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

  • 加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。

  • 默认是default环境写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效

  • 没有标注环境标识的bean在任何环境下都是加载的;

  • 切换环境

    • 1、使用命令行动态参数: 在虚拟机参数位置加载 -Dspring.profiles.active=test

    • 2、代码的方式激活

      	//设置需要激活的环境
      	applicationContext.getEnvironment().setActiveProfiles("dev");
      	//注册主配置类
      	applicationContext.register(MainConfigOfProfile.class);
          //启动刷新容器
          applicationContext.refresh();
      

二、AOP

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

1. 前置通知(@Before)

在目标方法(div)运行之前运行

  • //@Before在目标方法之前切入;切入点表达式(指定在哪个方法切入)@Before("pointCut()")
    public void logStart(JoinPoint joinPoint){   
        ...
    }
    

2. 后置通知(@After)

  • 在目标方法(div)运行结束之后运行(无论方法正常结束还是异常结束

  • @After("com.atguigu.aop.LogAspects.pointCut()")
    

3. 返回通知(@AfterReturning)

  • 在目标方法(div)正常返回之后运行

  • @AfterReturning(value="pointCut()",returning="result")
    public void logReturn(JoinPoint joinPoint,Object result){   
    	...
    }
    

4. 异常通知(@AfterThrowing)

  • 在目标方法(div)出现异常以后运行

  • @AfterThrowing(value="pointCut()",throwing="exception")
    public void logException(JoinPoint joinPoint,Exception exception){   
    	...
    }
    

5. 环绕通知(@Around)

  • 动态代理,手动推进目标方法运行(joinPoint.procced())

6. 公共切入点(@Pointcut)

  • //抽取公共的切入点表达式
    //1、本类引用
    //2、其他的切面引用
    @Pointcut("execution(public int com.atguigu.aop.MathCalculator.*(..))")
    public void pointCut(){};
    

7. 切面类(@Aspect)

  • 告诉Spring哪个类是切面类

    • @Aspect
      public class LogAspects {
      	...
      }
      

8. @EnableAspectJAutoProxy

  • 加在配置类上,开启基于注解的aop模式

    ​ @Import(AspectJAutoProxyRegistrar.class):导入AspectJAutoProxyRegistrar

    ​ AspectJAutoProxyRegistrar用于自定义给容器中注册bean;

    ​ 给容器中注册一个AnnotationAwareAspectJAutoProxyCreator(一个后置处理器);

    ​ AnnotationAwareAspectJAutoProxyCreator 实现了SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware

9. AOP注解流程

  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();
    3. 得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor)
    4. 利用拦截器的链式机制,依次进入每一个拦截器进行执行;
    5. 效果:

    ​ 正常执行:前置通知-》目标方法-》后置通知-》返回通知
    出现异常:前置通知-》目标方法-》后置通知-》异常通知

三、声明式事务

1. @Transactional

  • 给方法上标注,表示当前方法是一个事务方法

2. @EnableTransactionManagement

  • 开启基于注解的事务管理功能,同时需要配置事务管理器才会生效

  • 配置事务管理器

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

3. Spring事务原理

  • @EnableTransactionManagement

​ 利用@Import(TransactionManagementConfigurationSelector.class)给容器中会导入组件
导入两个组件
AutoProxyRegistrar
ProxyTransactionManagementConfiguration

  • AutoProxyRegistrar

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

    • ProxyTransactionManagementConfiguration 做了什么?
    • ​ 给容器中注册事务增强器
      • 事务增强器要用事务注解的信息,AnnotationTransactionAttributeSource解析事务注解
      • 事务拦截器TransactionInterceptor保存了事务属性信息,事务管理器;
        • 是一个 MethodInterceptor,在目标方法执行的时候执行拦截器链;

事务拦截器:

  1. 先获取事务相关的属性

  2. 再获取PlatformTransactionManager,如果事先没有添加指定任何transactionmanger

    最终会从容器中按照类型获取一个PlatformTransactionManager;

  3. 执行目标方法

    1. 如果异常,获取到事务管理器,利用事务管理回滚操作
    2. 如果正常,利用事务管理器,提交事务

四、扩展原理

1. BeanFactory

就是IOC容器或对象工厂

所有的bean都是由BeanFactory(也就是IOC容器)来进行管理的 。如XMLBeanFactory就是一种典型的BeanFactory。常用的ApplicationContext接口也是由BeanFactory接口派生而来,ApplicationContext包含BeanFactory的所有功能,通常建议比BeanFactory优先 。(管理所有的Bean)

2. FactoryBean

參考1.5 是一个工厂Bean,可以生成某一个类型Bean实例,它最大的一个作用是:可以让我们自定义Bean的创建过程。(本身是Bean,但是能够生产Bean)

public interface FactoryBean<T> {
    //返回的对象实例
    T getObject() throws Exception;
    //Bean的类型
    Class<?> getObjectType();
    //true是单例,false是非单例  在Spring5.0中此方法利用了JDK1.8的新特性变成了default方法,返回true
    boolean isSingleton();
}

3. BeanPostProcessor

bean后置处理器,是Spring中定义的接口,在Spring容器 的创建过程中(具体为Bean初始化前后)会回调BeanPostProcessor中定义的两个方法。

public interface BeanPostProcessor {
	//bean初始化前调用
	Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
	//bean初始化后调用
	Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}

4. BeanFactoryPostProcessor

beanFactory的后置处理器

  • invokeBeanFactoryPostProcessors(beanFactory)

  • 在BeanFactory标准初始化之后调用,来定制和修改BeanFactory的内容;

  • 时机:所有的bean定义已经加载到beanFactory中,但是bean的实例还未创建,*在**初始化创建其他组件前面执行

  • public interface BeanFactoryPostProcessor {
    	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
    
    }
    

5. BeanDefinitionRegistryPostProcessor

  • extends BeanFactoryPostProcessor

  • 包含postProcessBeanDefinitionRegistry()方法

  • 优先于BeanFactoryPostProcessor执行

  • BeanDefinitionRegistryPostProcessor可以给容器中再额外添加一些组件;

  • 时机:所有bean定义信息将要被加载,bean实例还未创建

  • public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
    	// BeanDefinitionRegistry Bean定义信息的保存中心,
        // 以后BeanFactory就是按照BeanDefinitionRegistry里面保存的每一个bean定义信息创建bean实例;
    	void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
    }
    
    
  • @Component
    public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
        @Override
        //方法1.继承自BeanFactoryPostProcessor,在方法2后执行
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
           ...
        }
        
        @Override
        //方法2.继承自BeanDefinitionRegistryPostProcessor,先执行
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
            //可以在postProcessBeanDefinitionRegistry方法中自己动态添加bean到容器中
            AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Blue.class).getBeanDefinition();
            registry.registerBeanDefinition("hello", beanDefinition);
        }
    }
    
    
    

6. ApplicationListener

​ 功能:监听容器中发布的事件(ApplicationEvent),完成事件驱动模型开发

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

}
//发布事件;
applicationContext.publishEvent(new ApplicationEvent(new String("我的事件")) {
});
  • @EventListener 让任意一个bean也能实现监听事件

    • EventListenerMethodProcessor 解析方法上的@EventListener
      • implements SmartInitializingSingleton, ApplicationContextAware
    @Service
    public class UserService {
    	@EventListener(classes={ApplicationEvent.class})
    	public void listen(ApplicationEvent event){
    		System.out.println("UserService。。监听到的事件:"+event);
    	}
    
    }
    
  • SmartInitializingSingleton

    public interface SmartInitializingSingleton {
    
    	/**
    	 *时机:所有的单实例Bean创建完之后会触发该方法
    	 */
    	void afterSingletonsInstantiated();
    
    }
    

五、Spring容器创建

流程图

六、servlet3.0

1.@WebServlet() @webListener() @webFilter()

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

容器在启动应用的时候,会扫描当前应用每一个jar包里面
META-INF/services/javax.servlet**.ServletContainerInitializer**
指定的实现类,启动并运行这个实现类的方法,传入感兴趣的类型;

  • @HandlesTypes 容器启动的时候会将@HandlesTypes指定的这个类型下面的子类(实现类,子接口等)传递过来

3. 注册三大组件

  • 使用ServletContext注册Web组件(Servlet、Filter、Listener)

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

  1. ServletContainerInitializer得到的ServletContext;
  2. ServletContextListener得到的ServletContext;
@HandlesTypes(value={HelloService.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);
		}
		
		//注册组件  ServletRegistration  
		ServletRegistration.Dynamic servlet = sc.addServlet("userServlet", new UserServlet());
		//配置servlet的映射信息
		servlet.addMapping("/user");
		//注册Listener
		sc.addListener(UserListener.class);
		//注册Filter  FilterRegistration
		FilterRegistration.Dynamic filter = sc.addFilter("userFilter", UserFilter.class);
		//配置Filter的映射信息
		filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
		
	}

}

4. Servlet3.0 异步请求

asyncSupported=true
@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();
               //获取到异步上下文
               AsyncContext asyncContext = req.getAsyncContext();
               //4、获取响应
               ServletResponse response = asyncContext.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);
   }
}

七、SpringMVC

1. SpringMVC容器创建

  1. web容器在启动的时候,会扫描每个jar包下的META-INF/services/javax.servlet.ServletContainerInitializer

  2. 加载这个文件指定的类SpringServletContainerInitializer

  3. spring的应用一启动会加载感兴趣的WebApplicationInitializer接口的下的所有组件;

  4. 并且为WebApplicationInitializer组件创建对象(条件:组件不是接口,不是抽象类)

    1. bstractContextLoaderInitializer:创建根容器;createRootApplicationContext();
    2. AbstractDispatcherServletInitializer:

    ​ 创建一个web的ioc容器;createServletApplicationContext();
    创建了DispatcherServlet;createDispatcherServlet();
    将创建的DispatcherServlet添加到ServletContext中;
    getServletMappings();

    1. AbstractAnnotationConfigDispatcherServletInitializer:注解方式配置的DispatcherServlet初始化器

      • 创建根容器:createRootApplicationContext()

        getRootConfigClasses();传入一个配置类

      • 创建web的ioc容器: createServletApplicationContext()

        获取配置类;getServletConfigClasses();

//web容器启动的时候创建对象;调用方法来初始化容器以及前端控制器
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[]{"/"};
   }

2. 父容器配置

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

}

3. web子容器配置 定制与接管SpringMVC

1. @EnableWebMvc 开启SpringMVC定制配置功能
	
	
	<mvc:annotation-driven />
2. WebMvcConfigurerAdapter 参考SpringMVC官方文档自定义配置
//SpringMVC只扫描Controller 子容器
//useDefaultFilters=false 禁用默认的过滤规则;
@ComponentScan(value="com.test",includeFilters={
      @Filter(type=FilterType.ANNOTATION,classes={Controller.class})
},useDefaultFilters=false)
@EnableWebMvc
public class AppConfig  extends WebMvcConfigurerAdapter  {
   //定制
   //视图解析器
   @Override
   public void configureViewResolvers(ViewResolverRegistry registry) {
      registry.jsp("/WEB-INF/views/", ".jsp");
   }
   //静态资源访问
   @Override
   public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
      configurer.enable();
   }
   //拦截器
   @Override
   public void addInterceptors(InterceptorRegistry registry) {
      registry.addInterceptor(new MyFirstInterceptor()).addPathPatterns("/**");
   }

}

4. SpringMVC 异步请求

1. 返回Callable (SpringMVC维护了一个异步的线程池)
  • 第二次进来后目标方法不再执行
/*
1、控制器返回Callable
2、Spring异步处理,将Callable 提交到 TaskExecutor 使用一个隔离的线程进行执行
3、DispatcherServlet和所有的Filter退出web容器的线程,但是 response 保持打开状态;
4、Callable返回结果,SpringMVC将请求重新派发给容器,恢复之前的处理;
5、根据Callable返回的结果。SpringMVC继续进行视图渲染流程等(从收请求-视图渲染)。
		preHandle...(/springmvc-annotation/async01)
		主线程开始...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/async01)
		postHandle...(Callable的之前的返回值就是目标方法的返回值)
		afterCompletion...
*/
@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;
}
2. 返回DeferredResult
  • setResult表示返回结果
@ResponseBody
@RequestMapping("/createOrder")
public DeferredResult<Object> createOrder(){
  	DeferredResult<Object> deferredResult = new DeferredResult<>((long)3000, "create 			fail...");
     //创建订单但是不处理 临时保存
   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;
	}


//模拟一个队列
public class DeferredResultQueue {
	
	private static Queue<DeferredResult<Object>> queue = new ConcurrentLinkedQueue<DeferredResult<Object>>();
	
	public static void save(DeferredResult<Object> deferredResult){
		queue.add(deferredResult);
	}
	
	public static DeferredResult<Object> get( ){
		return queue.poll();
	}

}

你可能感兴趣的:(Spring)