一、注册组件
@ComponentScan
扫描指定包下的组件。
String[] value():扫描基础包范围
Filter[] includeFilters():扫描并获取符合过滤规则的组件
Filter[] excludeFilters():扫描并排除符合过滤规则的组件
- 过滤规则由@Filter定义
@Filter
定义扫描包的组件过滤规则。
FilterType type():过滤类型
- ANNOTATION:按照是否标记注解
- ASSIGNABLE_TYPE:按照类型,包括子类/实现类
- CUSTOM:自定义过滤规则
Class<?>[] classes():type对应的类
举例:扫描当前注解所在包下所有的类,只扫描出类上标注@Controller注解的类进行获取。
@ComponentScan(includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)})
自定义组件过滤规则:
@Configuration
@ComponentScan(includeFilters = {
@ComponentScan.Filter(type = FilterType.CUSTOM, classes = MyTypeFilter.class)})
public class MainConfig {
}
public class MyTypeFilter implements TypeFilter {
/**
* 过滤规则:获取到类名中包含 s 字符的类
* @param metadataReader:读取到当前正在扫描类的信息
* @param metadataReaderFactory:可以获取到其他任何类的信息
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
// 获取到当前扫描类的信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
// 如果扫描类包含字符 s 就返回true
if (classMetadata.getClassName().contains("s")) {
return true;
}
return false;
}
}
@Scope
容器对象的作用范围,默认对象是单实例bean。
String value():指定作用范围的类型
- prototype:多实例的,IOC容器启动并不会去调用方法创建对象在容器中,每次获取的时候
才会调用方法创建对象。
- singleton:单实例的,IOC容器启动会调用方法创建对象放到容器中。以后获取直接从容器
中拿(map.get())。
- request:同一次请求创建一个实例。
- session:同一个session创建一个实例。
举例:创建一个Person对象,需要每次获取都是不同的实例对象。
@Bean
@Scope("prototype")
public Person person() {
return Person();
}
@Lazy
懒加载,对于单实例bean在容器启动时不创建对象,而是在第一次使用(获取)bean时创建并初始化对象。
@Bean
@Lazy
public Person person() {
return Person();
}
@Conditional
定义创建bean对象的条件。
Class<? extends Condition>[] value():指定实现Condition接口的类用于匹配创建条件
举例:如果在Linux操作系统下,创建Person bean对象。
/**
* 使用@Conditional来定义,如果在Linux下就创建这个bean
* @Conditional:定义创建bean对象的条件
*/
@Bean("linus")
@Conditional(value = LinuxCondition.class)
public Person person() {
return new Person();
}
// 实现Condition来定义匹配条件
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 通过上下文来获取程序运行环境
Environment environment = context.getEnvironment();
// 获取操作系统
String property = environment.getProperty("os.name");
if (property.contains("linux")) {
return true;
}
return false;
}
}
也可以将注解放在类上面,满足注解条件的类,这个类中的bean才会生效。
@Configuration
// 满足该条件,这个类的bean才会生效
@Conditional(value = LinuxCondition.class)
public class MainConfig {
}
@Import
快速向容器中导入一个组件,使用@Import向容器中注册组件有三种方法,后两种方法在 Spring 底层源码用的非常多需要注意。
Class<?>[] value():需要向容器中导入的类
- @Import直接导入;
直接导入Dog和Person bean对象到IOC容器中。@Configuration @Import(value = {Dog.class, Person.class}) public class MainConfig { }
- ImportSelector:返回需要导入组件的全类名数组;
@Configuration @Import(value = MyImportSelector.class) public class MainConfig { } public class MyImportSelector implements ImportSelector { /** * 将Dog和Person bean对象添加进IOC容器 * @param annotationMetadata:当前标注@Import注解类的所有注解信息 * return:导入容器中的组件全类名 */ @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { // 默认返回一个空的字符串数组,返回null会报空指针异常 return new String[]{"com.kiger.springboot.bean.Dog", "com.kiger.springboot.bean.Person"}; } }
- ImportBeanDefinitionRegistrar:手动注册Bean到容器中
@Configuration @Import(value = MyImportBeanDefinitionRegistrar.class) public class MainConfig { } public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { /** * AnnotationMetadata:当前类的注解信息 * BeanDefinitionRegistry:BeanDefinition注册类 * 把所有需要添加到容器中的bean * 调用BeanDefinitionRegistry.registerBeanDefinition手工注册进来 */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // 判断BeanDefinitionRegistry是否存在Dog bean对象,如果存在就把 Person注册到容器中 boolean definition = registry.containsBeanDefinition("dog"); if (definition) { RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Person.class); registry.registerBeanDefinition("person", rootBeanDefinition); } } }
FactoryBean
使用FactoryBean来注册组件,需要实现FactoryBean接口。
@Configuration
public class MainConfig {
@Bean
public DogFactoryBean dogFactoryBean() {
return new DogFactoryBean();
}
}
// 创建一个Spring定义的FactoryBean
public class DogFactoryBean implements FactoryBean<Dog> {
/**
* 返回一个Dog对象,这个对象会添加到容器中
*/
@Override
public Dog getObject() throws Exception {
return new Dog();
}
/**
* 返回对象类型
*/
@Override
public Class<?> getObjectType() {
return Dog.class;
}
/**
* 创建的bean对象是否单例
* - true:单实例
* - false:多实例,每次获取都会调用方法创建
*/
@Override
public boolean isSingleton() {
return false;
}
}
注意:使用IOC容器获取FactoryBean时,默认获取到的是getObject()
方法所创建的bean对象。想要获取FactoryBean对象,可以在id前加 &(&DogFactoryBean)
小总结
通过上面的注解学习我们可以知道将一个组件注册进IOC容器可以有四种方法:
- 包扫描+组件标注
@Controller/@Service/@Repository/@Component - @Bean:导入第三方包里面的组件
自定义一些少量的组件 - @Import:快速向容器中导入组件
- 使用Spring提供的FactoryBean(工厂Bean)
二、生命周期
bean 的生命周期:创建->初始化->销毁
初始化:
- 单实例bean:在容器启动时进行创建完成并属性赋值之后进行初始化;
- 多实例bean:每一次获取时创建并初始化。
销毁:
- 单实例bean:关闭容器时,容器会销毁bean实例;
- 多实例bean:容器只负责创建,不会进行bean实例的销毁,可自行调用销毁方法。
initMethod/destroyMethod
使用@Bean注解指定bean的初始化和销毁方法。
@Bean(initMethod = "init", destroyMethod = "destroy")
public Dog dog() {
return new Dog();
}
init/destroy,可在Dog类中实现。
bean实现InitializingBean/DisposableBean接口
Spring提供了InitializingBean/DisposableBean两个接口,bean实现两个接口,就可以在bean创建完成/关闭容器,容器会调用实现的方法。
@Component
public class Cat implements InitializingBean, DisposableBean {
public Cat() {
System.out.println("cat constructor..");
}
// 在bean创建完成并且属性赋值完成之后调用
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("cat init..");
}
// 单实例bean在关闭容器时调用
@Override
public void destroy() throws Exception {
System.out.println("cat destroy..");
}
}
@PostConstruct/@PreDestroy
由JSR250提供的方法注解。
@Component
public class Cat {
public Cat() {
System.out.println("cat constructor..");
}
// 在对象创建并属性赋值完成之后
@PostConstruct
public void init() {
System.out.println("cat init..");
}
// 在容器移除对象之前
@PreDestroy
public void destroy() {
System.out.println("cat destroy..");
}
}
BeanPostProcessor
BeanPostProcessor:后置处理器对bean对象初始化前后进行后置处理。
// 对容器中的每一个bean对象的初始化都有效
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
/**
* 在bean对象的所有初始化方法之前进行处理
* @param bean:容器刚创建好的bean实例
* @param beanName:bean实例的名字
* @return Object:可以直接返回bean实例,也可以包装之后进行返回
*/
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
// 在bean对象的所有初始化方法完成之后进行处理
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
对容器中的每一个bean对象的初始化都有效。
从源码角度解释为什么初始化是在bean对象属性赋值完成之后进行的
在源码AbstractAutowireCapableBeanFactory类 doCreateBean
方法中有下面代码块:
通过注解可以得到这个代码块是初始化bean对象实例,但是在调用initializeBean
方法之前调用了populateBean
方法,这个方法的官方注释为:
Populate the bean instance in the given BeanWrapper with the
property values from the bean definition.
大致意思就是将bean中定义的属性值填充给bean实例。
从源码角度解释 BeanPostProcessor 实现原理
在postProcessBeforeInitialization()
方法加上断点进行debug,可以在源码AbstractAutowireCapableBeanFactory类 initializeBean
方法中看到:
红色标注的这三处分别是调用初始化方法前后置处理方法、初始化方法、初始化方法之后调用后置处理方法。
在applyBeanPostProcessorsBeforeInitialization
方法中会调用所有的BeanPostProcessor
的处理方法:
这也是BeanPostProcessor
实现原理。
三、属性赋值
@Value
在字段上标注@Value注解可以为该字段赋值,该注解有三种赋值方式:
- 基本数值;
- SpEL:#{ };
- &{ }:获取配置文件的值。(在springboot中常用)
@Value("jimi")
private String name;
@Value("#{18-10}")
private Integer age;
@Value("${cat.sex}")
private String sex;
// 配置文件 application.yml
cat:
sex: man
@PropertySource
导入外部配置文件,将配置文件中的值加载到环境变量中,可以使用上面的@Value(${})进行获取 k/v 的值。
String[] value():配置文件路径。
- 可以指定 classpath:类路径/file:文件路径
将类路径下的 application.yml 资源文件加载到环境变量中。
@Configuration
@PropertySource(value = {"classpath:application.yml"})
public class MainConfig {
}
四、自动装配
@Autowired(Spring注解)
这个注解是根据BeanPostProcessor
后置处理器来实现的,原理在之前已经讲过了。
依赖注入
boolean required():默认为true,必须要找到指定的组件,否则报错
- false:没找到就跳过,不会报错
举例:一般在Controller层会注入一个Service层的对象来调用服务方法。
@Controller
public class controller {
@Autowired(required=false) // 即使没找到也不会报错
private service ser;
}
@Autowired 自动装配规则:
- 默认按照类型去容器中找对应的组件;
applicationContext.getBean(serice.class);
- 如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找;
applicationContext.getBean("ser");
- 使用@Qualifier指定需要装配的组件id,而不是属性名;
@Qualifier("ser") @Controller public class controller { @Autowired private service ser; }
- @Primary在多个相同类型组件中,在其中一个组件上标注该注解,在加载时容器会首选加载该组件;(需要在没有明确指定的情况下)
@Bean @Primary public Cat cat() { return new Cat(); }
- @Autowired 可以标注在构造器、方法、参数、属性,标注在构造器和方法上,会自动调用该方法,并且从IOC容器中获取参数的实例。
标注在构造器上,IOC容器在创建该bean时会调用标注的构造器 @Bean标注的对象参数默认从容器中加载,省略了@Autowired注解
@Resource/@Inject(Java规范)
@Resource:由JSR250定义,默认按照组件名称进行装配;
String name():指定装载组件名称,相当于@Qualifier
- 没有@Primary的功能;
- 没有@Autowired(required = false)的功能;
@Inject:由JSR330定义,和@Autowired功能一样,但是需要导入javax.inject的包,没有 required = false 的功能。
Aware注入Spring底层组件
在一些类中如果想要使用Spring的一些底层组件,Aware提供了这些底层组件的接口,我们只需要实现这些接口方法就可以获取这些底层组件。
举例:获取ApplicationContext组件。
@Component
public class Cat implements ApplicationContextAware {
// 定义一个ApplicationContext对象
private ApplicationContext applicationContext;
// 通过该接口方法将ApplicationContext注入到Cat类中
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
每一个底层组件的Aware接口都会有对应的AwareProcessor来进行后置处理。
@Profile
指定在哪个环境的情况下才能被注册到容器中,被标注的组件只有在对应环境激活的情况下才能注册进容器,默认是default。(一般用于一些数据源的切换)
举例:Cat bean对象在测试环境下注册进容器。
@Profile("test") // dev/prod
@Bean
public Cat cat() {
return new Cat();
}
那么,如何激活环境程序运行在某种环境呢?
- 命令行参数(虚拟机参数栏添加)
-Dspring.profiles.active=test - 代码方式
// 1.创建IOC容器 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); // 2.激活环境 - 可以激活多个环境 applicationContext.getEnvironment(). setActiveProfiles("test", "dev"); // 3.注册主配置类 applicationContext.register(MainConfig.class);
五、AOP【动态代理】
指在程序运行期间动态的将某段代码加入到指定方法位置进行运行的编程方式。
①业务实现AOP流程
1.先定义一个业务逻辑类、切面类并使用@Component加入到容器
业务逻辑类:
// 业务逻辑类
public class MathCalculator {
// 该方法作为切入点
public int div(int i, int j) {
System.out.println("MathCalculator div...");
return i / j;
}
}
切面类:
// 一定要在切面类上标注@Aspect注解,告诉Spring那个是切面类
@Aspect
public class LogAspect {
/**
* 抽取公共的切入点表达式:指定在那个方法切入
* - 本类引用该表达式:pointCut() 直接写方法名
* - 其他切面类引用:com.kiger.aop.LogAspect.pointCut() 方法全名
*/
// @Pointcut("execution(public int com.kiger.aop.MathCalculator.div(int, int))") 指定方法
// MathCalculator类所有方法,参数类型不限
@Pointcut("execution(public int com.kiger.aop.MathCalculator.*(..))")
public void pointCut() {
}
@Before("pointCut()")
public void logStart(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
System.out.println("除法 " + methodName +
" 运行...参数列表是:{" + Arrays.asList(args) + "}");
}
@AfterReturning(value = "pointCut()", returning = "result")
// JoinPoint参数一定要出现在参数表的第一位
public void logReturn(JoinPoint joinPoint, Object result) {
System.out.println("@AfterReturning 正常返回...运行结果:" + result);
}
@AfterThrowing(value = "pointCut()", throwing = "exception")
public void logException(Exception exception) {
System.out.println("@AfterThrowing 除法异常...异常信息:" +
exception.getMessage());
}
}
2.在配置类中加@EnableAspectJAutoProxy【开启基于注解的AOP模式】
②AOP原理
1.@EnableAspectJAutoProxy 做了什么?
2.AnnotationAwareAspectJAutoProxyCreator 这个组件有什么作用?
先插入 AnnotationAwareAspectJAutoProxyCreator
类图:
可以看到 AnnotationAwareAspectJAutoProxyCreator
实现了两个接口:
SmartInstantiationAwareBeanPostProcessor
-> 后置处理器
BeanFactoryAware
-> 自动装配 BeanFactory
在前面已经分析了这两个接口的功能,那么我们就在从父类开始找到实现相应接口的方法打上断点进行debug调试,主要的接口方法如下:
前置通知:Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName)
后置通知:boolean postProcessAfterInstantiation(Object bean, String beanName)
装配BeanFactory:void setBeanFactory(BeanFactory beanFactory)
于是,我们就在下面的类中打上断点进行debug调试来探究组件的功能:
父
AbstractAutoProxyCreator
:
↑继承
AbstractAdvisorAutoProxyCreator
:
↑继承
AnnotationAwareAspectJAutoProxyCreator
:
在分析 AnnotationAwareAspectJAutoProxyCreator 调用方法之前,我们先看一下 AnnotationAwareAspectJAutoProxyCreator 是如何被创建并注册进 BeanFactory:
了解了 AnnotationAwareAspectJAutoProxyCreator(后置处理器) 的创建和注册的过程,接下来我们来探究 AnnotationAwareAspectJAutoProxyCreator (后置处理器)什么时候起作用、都做了些什么:
经过上面的流程图我们可以知道:
AnnotationAwareAspectJAutoProxyCreator 这个后置处理器会在创建 bean 之前进行拦截返回新的代理对象,和创建之后进行增强返回新的代理对象。
上面已经大致了解了AOP的原理,接下来继续探究执行目标方法这些通知方法是如何被调用的?
上面流程图分析了拦截器链的调用过程,通过拦截器链可以保证通知方法与目标方法的执行顺序。
③AOP小结
在业务逻辑中添加一个AOP切面非常容易,可以理解AOP实现原理还是比较困难需要进行不断的源码调试来探究在这个过程中都做了什么,接下来对AOP原理做一个整体的总结:
-
@EnableAspectJAutoProxy 开启AOP功能;
-
@EnableAspectJAutoProxy 向容器中注册了一个组件 AnnotationAwareAspectJAutoProxyCreator;
-
AnnotationAwareAspectJAutoProxyCreator 是一个后置处理器;
-
bean 的创建流程:
① registerBeanPostProcessors 注册后置处理器,创建 AnnotationAwareAspectJAutoProxyCreator
② finishBeanFactoryInitialization 初始化剩余的单实例 bean- 创建业务逻辑组件和切面组件
- AnnotationAwareAspectJAutoProxyCreator 拦截创建组件的创建
- 在组件创建完成之后,判断组件是否需要增强
- 是:将切面方法包装成增强器(Advisor),给业务逻辑对象创建一个代理对象(cglib)
-
执行目标方法
① 代理对象执行目标方法
② 使用 CglibAopProxy.intercept() 进行拦截- 得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor)
- 利用拦截器链式机制,依次进入每一个拦截器进行执行
- 过程:
正常执行:前置通知 》目标方法》后置通知 》返回通知
出现异常:前置通知 》目标方法》后置通知 》异常通知
六、声明式事务
@Transactional
SpringBoot 中只需要在 service 类或 service 方法上加上 @Transactional 用来保证事务的一致性,在 service 方法中可能会调用多个 Dao 方法,用来保证多个调用要么成功,要么回滚。
@Service
public class UserService {
@Autowired
private UserDao userDao;
@Transactional
public void inserUser() {
userDao.insert();
// otherDao.xxx();
int age = 10/0;
}
}
声明式事务原理小议
在 Spring 中想要激活声明式事务就需要在配置类上加上@EnableTransactionManagement
注解:
查看源码得知TransactionManagementConfigurationSelector
向容器中添加了两个组件AutoProxyRegistrar
和ProxyTransactionManagementConfiguration
:
默认 adviceMode=PROXY
,接下来分析这两个组件都做了什么:
AutoProxyRegistrar
AutoProxyRegistrar
实现了ImportBeanDefinitionRegistrar
向容器中注册了一个InfrastructureAdvisorAutoProxyCreator
组件:
InfrastructureAdvisorAutoProxyCreator 做了什么?
通过源码可以发现InfrastructureAdvisorAutoProxyCreator
和上文讲的AnnotationAwareAspectJAutoProxyCreator
非常相似,利用后置处理器机制在对象创建之后,包装对象,返回一个代理对象(包装了增强器),代理对象执行方法利用拦截器链进行调用,具体过程可以查看源码。
ProxyTransactionManagementConfiguration
向容器中注册事务增强器BeanFactoryTransactionAttributeSourceAdvisor
- 事务增强器需要事务注解信息,导入
AnnotationTransactionAttributeSource
解析事务注解 - 事务拦截器
TransactionInterceptor
保存了事务属性信息、事务管理器,它是一个MethodInterceptor
(代理对象会在拦截器链调用该拦截器的invoke方法):
1. 先获取事务相关属性
2. 在获取PlatformTransactionManager
,如果没有事先指定qualifier - 指定的事务管理器
属性,最终会从容器中按照类型获取PlatformTransactionManager
3. 执行目标方法
如果异常,获取到事务管理器,利用事务管理回滚这次操作;
如果正常,利用事务管理器,提交事务;
小结
通过AutoProxyRegistrar
向容器中注册组件InfrastructureAdvisorAutoProxyCreator
,这个组件在业务类方法对象创建之后通过ProxyTransactionManagementConfiguration
添加的事务拦截器TransactionInterceptor
将拦截器封装在对象中返回一个代理对象,代理对象执行目标方法之后,会调用拦截器链中的事务拦截器方法对事务进行处理(提交/回滚)。
七、扩展原理
1.BeanFactoryPostProcessor
BeanFactory 的后置处理器,在 BeanFactory 标准初始化之后调用,所有的 BeanDefinition 已经保存加载到 BeanFactory,但是 bean 的实例还没有创建。
1.在 IOC 容器创建对象,会在refresh()
方法中调用invokeBeanFactoryPostProcessors(beanFactory)
方法;
2.从 BeanFactory 中找到所有类型是 BeanFactoryPostProcessor
的组件,并根据实现不同接口的优先级来分到不同的集合;
3.不同优先级的集合执行BeanFactoryPostProcessor
接口方法。
遍历BeanFactoryPostProcessor
集合,并执行接口方法postProcessBeanFactory()
:
2.BeanDefinitionRegistryPostProcessor
是BeanFactoryPostProcessor
的子类接口,定义了postProcessBeanDefinitionRegistry()
方法(在所有 BeanDefinition 将要被加载,bean 实例还未创建的时候执行),优先于BeanFactoryPostProcessor
执行,利用BeanDefinitionRegistryPostProcessor
给容器中在额外注册一些 BeanDefinition。
1.在 IOC 容器创建对象,会在refresh()
方法中调用invokeBeanFactoryPostProcessors(beanFactory)
方法;
2.可以发现和BeanFactoryPostProcessor
执行的是同一个方法,但是在BeanFactoryPostProcessor
之前执行invokeBeanDefinitionRegistryPostProcessors()
方法,从 BeanFactory 中找到所有类型是 BeanDefinitionRegistryPostProcessor
的组件,并根据实现不同接口的优先级来分到不同的集合;
3.不同优先级的集合执行BeanDefinitionRegistryPostProcessor
接口方法。
遍历BeanDefinitionRegistryPostProcessor
集合,并执行接口方法postProcessBeanDefinitionRegistry()
:
4.接下来会执行BeanDefinitionRegistryPostProcessor
实现的父类BeanFactoryPostProcessor
接口的方法postProcessBeanFactory()
;
5.invokeBeanFactoryPostProcessors()
方法后半段是从容器中找到BeanFactoryPostProcessor
组件,然后依次调用postProcessBeanFactory()
方法(就回到了上面 BeanFactoryPostProcessor 原理)。
3.Spring容器创建流程
下面的流程图分析了SpringIOC容器的创建过程:
因为流程图太大无法上传,想要了解的可以在百度云下载
链接: https://pan.baidu.com/s/1WR6DMo_SOtiARaYmesaarQ
提取码: 3kiy
小结:
1.Spring在启动时,先保存所有注册进来的 bean 定义信息(XML、@Service、@Component、@Bean…)
2.Spring会在合适的时机创建这些 bean
- 用到这个 bean 的时候,利用
getBean()
方法,创建以后保存在容器中 - 统一创建剩下的所有 bean 的时候,
finishBeanFactoryInitialization(beanFactory)
3.后置处理器:BeanPostProcessor
- 每一个 bean 创建完成,都会使用各种后置处理器进行处理,来增强 bean 的功能;
AutowiredAnnotationBeanPostProcessor:处理自动注入
AnnotationAwareAspectJAutoProxyCreator:AOP功能
AsyncAnnotationBeanPostProcessor:异步处理
…
4.事件驱动模型:
ApplicationListener:事件监听器
ApplicationEventMuticaster:事件派发器
八、Spring异步请求
这里分享一篇博客:SpringBoot中异步请求和异步调用
非常清晰的讲解了如何应用 Servlet3.0 的异步请求以及整合SpringMVC和使用。