Spring注解驱动的相关学习
导入 Spring context 相关依赖
1、容器
AnnotationConfigApplicationContext -- 配置类 , 包扫描
组件添加
Spring 的配置有两种方式
1、applicationContext.xml文件的配置
<!-- 包扫描、只要标注了@Controller、@Service、@Repository,@Component 自动扫描并加入被标注的组件 -->
<context:component-scan base-package="com.atguigu" use-default-filters="false"></context:component-scan>
2、配置类的配置 config class
@Configuration // 告诉Spring这是一个配置类
// 相当于xml配置文件中的 <context:component-scan.... , excludeFilters 不扫描的包 ;注:要includeFilters生效的话,需在后面加上useDefaultFilters=false属性 禁用默认的过滤规则
@ComponentScan(value = "/*", excludeFilters = {
@Filter(type=FilterType.ANNOTATION,classes=Controller.class)
})
@RequestBody //在参数前标注,使传过来的json自动转换成对应的pojo
@Bean(name = "") //给ioc注册bean 在方法上标注; name:为该bean的id,不写则id默认为方法名
@Component //告诉Spring这是个组件; 在类上标注
@Conditional({Condition接口的实现.class}) //在类或方法上标准;满足括弧内条件的则会被注册到ioc容器中
@Service @Controller @Repository //告诉spring这是组件; 在类上标注
@Scope //设置组件的作用域,控制bean是单实例的还是多实例的 SCOPE_SNGLETON、SCOPE_PROTOTYPE; 在组件上标注
@Lazy //懒加载: 容器启动时不创建对象。第一次使用(获取)bean创建对象,并初始化 单实例bean在默认情况下是在启动容器时创建对象
@Improt(要导入的组件.class); //在配置类上标注; ioc容器会自动注册这个没有组件标注的新组件,id默认是组件的全类名 。。也可以用数组的方式导入多个组件({1、多个组件.class,2、ImportSelector的实现类.class--自定义要导入的组件,3、ImportBeanDefinitionRegistrar的实现类.class--手动注册bean到容器中})
4、 Spring提供的FactoryBean接口 //默认工厂bean获取的是调用getObject创建的对象,加前缀&则获取工厂bean本身; 注册写法 @Bean \n FactoryBean的实现类作为返回值的方法
bean的生命周期
bean的创建---初始化---销毁的过程
构造(创建对象)
单实例:在容器启动的时候创建对象 @Scope默认
多实例:在每次获取的时候创建对象 @Scope("prototype")
初始化: 对象创建完成,并赋值好,调用初始化方法
销毁: 容器关闭的时候 多实例不会销毁,容器不会管理这个bean
容器管理bean的生命周期:
自定义初始化和销毁方法;容器在bean进行到当前生命周期的时候来调用自定义的初始化和销毁方法;
1、指定初始化和销毁方法:
在xml配置文件中指定init-method和destroy-method
在配置类中 @Bean(init-method="",destroy-method="")
2、通过让Bean实现InitializingBean(定义初始化逻辑),DisposableBean(定义销毁逻辑) --在bean类中实现这两个接口
3、可以使用JSR250 @PostConstruct:在bean创建完成并且属性赋值完成;来执行初始化方法;@PerDestroy:在容器销毁bean之前通知我们进行清理操作 --在bean类中的init和destroy方法上标注这两个注解
4、BeanPostProcessor接口的实现类;后置处理器(BeanPostProccessor的实现类):拦截所有组件并在初始化前后进行处理工作,将后置处理器加入到容器中即可开始工作
@Value() 1、基本数值;2、可以写SpEL:#{可以进行运算};3、可以写${key}:取出配置文件{properties文件}中的值(在运行环境变量里面的值) ——标注在属性上
xml配置文件中导入properties文件
<conntext:property-placeholder location="classpath:~.properties" />
@PropertySource(value={"classpath:/~","file:/~/~"}) //配置类导入外部配置文件,在配置类上标注,读取外部配置文件中的k/v保存到运行的环境变量中,可以用${}取出配置文件中的值 -- 环境变量 ConfigurableEnviroment e = ioc容器.getEnvironment; 可以直接用 e.getProperty(key); //得到配置文件中的value 用@Value(${})取出值
* 自动装配;
* Spring利用依赖注入(DI),完成对IOC容器中中各个组件的依赖关系赋值;
*
* 1)、@Autowired:自动注入: //Autowired 注入方式是byType(通过class类型在spring容器中查找)
* 1)、默认优先按照类型去容器中找对应的组件:applicationContext.getBean(BookDao.class);找到就赋值
* 2)、如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找
* applicationContext.getBean("bookDao")
* 3)、@Qualifier("bookDao"):使用@Qualifier指定需要装配的组件的id,而不是使用属性名
* 4)、自动装配默认一定要将属性赋值好,没有就会报错;
* 可以使用@Autowired(required=false);
* 5)、@Primary:让Spring进行自动装配的时候,默认使用首选的bean;
* 也可以继续使用@Qualifier指定需要装配的bean的名字
* BookService{
* @Autowired
* BookDao bookDao;
* }
* 若标注在方法上(setBean方法),Spring容器创建当前对象,就会掉用方法,完成赋值; 方法使用的bean参数,自定义类型的值从ioc容器中获取
*
* 2)、Spring还支持使用@Resource(JSR250)和@Inject(JSR330)[java规范的注解]
* @Resource: //注入方式是 byName (通过id在spring容器中查找)
* 可以和@Autowired一样实现自动装配功能;默认是按照组件名称进行装配的;
* 没有能支持@Primary功能没有支持@Autowired(reqiured=false);
* @Inject:
* 需要导入javax.inject的包,和Autowired的功能一样。没有required=false的功能;
* @Autowired:Spring定义的; @Resource、@Inject都是java规范
*
* AutowiredAnnotationBeanPostProcessor:解析完成自动装配功能;
*
* 3)、 @Autowired:构造器,参数,方法,属性;都是从容器中获取参数组件的值
* 1)、[标注在方法位置]:@Bean+方法参数;参数从容器中获取;默认不写@Autowired效果是一样的;都能自动装配
* 2)、[标在构造器上]:如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还是可以自动从容器中获取
* 3)、放在参数位置:
*
* 4)、自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory,xxx);
* 自定义组件实现xxxAware;在创建对象的时候,会调用接口规定的方法注入相关组件;Aware;
* 把Spring底层一些组件注入到自定义的Bean中;
* xxxAware:功能使用xxxProcessor进行后置操作;
* ApplicationContextAware==》ApplicationContextAwareProcessor;
*
* //使用 xxxAware 取properties配置文件值的方式
* public class Main impliments EmbeddedValueResolverAware{
* private StringValueResolver valueResolver; //可以用来解析配置文件
*
* public void setEmbeddedValueResolver(StringValueResolver resolver){
* this.valueResolver = resolver; //赋值
* String string = valueResolver.resolveStringValue(${key});
* }
* }
*
@Qualifier("Bean id") //指定自动装配的组件; 在@Autowired上下标注
@Primary //指定Bean为首选的自动装配Bean; 在注册Bean时标注(@Bean上下标注)
@Profile("环境标识") //指定组件在哪个环境的情况下才能被注册到容器中。不指定,任何环境下都能注册这个组件; 在组件上标注 @Profile("default") 默认环境
1、加了环境标识的Bean,只有在这个环境被激活的时候才能注册到容器中,默认是default
2、写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能生效
3、没有标注环境标识的bean在任何环境下都是加载的
激活环境方式
1)、使用命令行动态参数:在虚拟机参数位置 改变运行时环境: run as -> Run Configurations -> Arguments -> VM arguments : -Dspring.profiles.active=激活的环境标识
2)、使用代码的方式激活某种环境:在ioc容器中取出环境熟悉 -> 再给环境属性赋值(设置需要激活的环境): ioc.getEnvironment().setActiveProfiles("环境标识","环境标识",..); -> 注册配置类 ioc.register(配置类.class); -> 启动刷新容器 ioc.refresh();
AOP 面向切面编程: 动态代理
指在程序运行期间动态的将某段代码切入到指定位置进行运行的编程模式
只有Spring管理的bean才能使用这些切面功能
1、导入AOP模块 spring-aspects
2、定义一个业务逻辑类:在业务逻辑运行的时候讲日志进行打印(方法之前、方法运行结束、方法出现异常,等等)
3、定义一个日志切面类:切面类里面的方法需要动态感知 业务逻辑类方法 运行到哪里然后执行
通知方法:
前置通知(@Bebore("修饰符 返回值 方法全类名(参数)")) 在目标方法运行之前运行
后置通知(@After("@Pointcut标注下的方法的方法名")) 在目标方法运行结束后运行(无论方法正常结束还是异常结束)
返回通知(@AfterReturning(value="切入的目标方法",returning="指定谁来封装返回值 作为参数")) 在目标方法正常返回之后运行
异常通知(@AfterThrowing(value="切入的目标方法",throwing="指定谁来封装异常 作为参数")) 在目标方法出现异常以后运行
环绕通知 动态代理,手动推进目标方法运行(joinPoint())
切入方法表达: 修饰符 返回值 方法全类名(参数类型); --> 可用 "*" 表示全部类型, 参数位置可用 ".." 表示个数
切面类:
@Pointcut("execution(public int 切入方法的全类名(参数))") //定义一个切入点,在本类使用这个切入点时 @Before("pointCut()")
public void ponitCut(){};
@Before("pointCut")
public void start(){} //可以设置一个参数 JoinPoint joinpoint; joinpoint 可以取出目标传入的方法名(joinpoint.getSignature().getName),参数(joinpoint.getArgs)等等
4、给切面类的目标方法标注何时何地运行(通知注解)
5、将切面类个业务逻辑类(目标方法所在类)都加入到容器中
6、必须告诉Spring哪个类是切面类(给切面类加一个注解 : @Aspect) //在类上标注
7、给配置类中加 @EnableAspectJAutoProxy 【开启基于注解的aop的模式】 Spring中有很多的 @EnableXXX
在配置文件中 <aop:aspectj-autoproxy></aop:aspectj-autoproxy> <!-- 开启基于注解的切面功能 -->
在配置类中标注 @EnableAspectJAotuProxy
不要自己创建bean,从ioc中取出bean来调用目标方法才能有切面功能 一定要开启基于注解的功能
总结 三步:
1、将业务逻辑组件和切面类都加入到容器中;告诉Spring哪个是切面类(@Aspect)
2、在切面类上的每一个通知方法上标注通知注解,告诉Spring何时何地运行(切入点表达式)
3、开启基于注解的aop模式: @EnableAspectJAutoProxy
AOP原理: 【看给容器中注册了什么组件,这个组件什么时候工作,这个组件的功能是什么】
@EnableAspectJAutoProxy
1、@EnableAspectJAutoProxy是什么:
@Import(AspectJAutoProxyRegistrar.class) : 给容器中导入AspectJAutoProxyRegistrar
利用AspectJAutoProxyRegistrar自定义给容器中注册bean:BeanDefinetion
给容器中注册一个AnnotationAwareAspectJAutoProxyCreator;
2、 AnnotationAwareAspectJAutoProxyCreator:
AnnotationAwareAspectJAutoProxyCreator
->AspectJAwareAdvisorAutoProxyCreator
->AbstractAdvisorAutoProxyCreator
->AbstractAutoProxyCreator
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
关注后置处理器(在bean初始化完成前后做事情)、自动装配BeanFactory
AbstractAutoProxyCreator.setBeanFactory()
AbstractAutoProxyCreator.有后置处理器的逻辑;
AbstractAdvisorAutoProxyCreator.setBeanFactory()-》initBeanFactory()
AnnotationAwareAspectJAutoProxyCreator.initBeanFactory()
* 流程:
* 1)、传入配置类,创建ioc容器
* 2)、注册配置类,调用refresh()刷新容器;
* 3)、registerBeanPostProcessors(beanFactory);注册bean的后置处理器来方便拦截bean的创建;
* 1)、先获取ioc容器已经定义了的需要创建对象的所有BeanPostProcessor
* 2)、给容器中加别的BeanPostProcessor
* 3)、优先注册实现了PriorityOrdered接口的BeanPostProcessor;
* 4)、再给容器中注册实现了Ordered接口的BeanPostProcessor;
* 5)、注册没实现优先级接口的BeanPostProcessor;
* 6)、注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象,保存在容器中;
* 创建internalAutoProxyCreator的BeanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】
* 1)、创建Bean的实例
* 2)、populateBean;给bean的各种属性赋值
* 3)、initializeBean:初始化bean;
* 1)、invokeAwareMethods():处理Aware接口的方法回调
* 2)、applyBeanPostProcessorsBeforeInitialization():应用后置处理器的postProcessBeforeInitialization()
* 3)、invokeInitMethods();执行自定义的初始化方法
* 4)、applyBeanPostProcessorsAfterInitialization();执行后置处理器的postProcessAfterInitialization();
* 4)、BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)创建成功;--》aspectJAdvisorsBuilder
* 7)、把BeanPostProcessor注册到BeanFactory中;
* beanFactory.addBeanPostProcessor(postProcessor);
* =======以上是创建和注册AnnotationAwareAspectJAutoProxyCreator的过程========
*
* AnnotationAwareAspectJAutoProxyCreator => InstantiationAwareBeanPostProcessor
* 4)、finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作;创建剩下的单实例bean
* 1)、遍历获取容器中所有的Bean,依次创建对象getBean(beanName);
* getBean->doGetBean()->getSingleton()->
* 2)、创建bean
* 【AnnotationAwareAspectJAutoProxyCreator在所有bean创建之前会有一个拦截,InstantiationAwareBeanPostProcessor,会调用postProcessBeforeInstantiation()】
* 1)、先从缓存中获取当前bean,如果能获取到,说明bean是之前被创建过的,直接使用,否则再创建;
* 只要创建好的Bean都会被缓存起来
* 2)、createBean();创建bean;
* AnnotationAwareAspectJAutoProxyCreator 会在任何bean创建之前先尝试返回bean的实例
* 【BeanPostProcessor是在Bean对象创建完成初始化前后调用的】
* 【InstantiationAwareBeanPostProcessor是在创建Bean实例之前先尝试用后置处理器返回对象的】
* 1)、resolveBeforeInstantiation(beanName, mbdToUse);解析BeforeInstantiation
* 希望后置处理器在此能返回一个代理对象;如果能返回代理对象就使用,如果不能就继续
* 1)、后置处理器先尝试返回对象;
* bean = applyBeanPostProcessorsBeforeInstantiation():
* 拿到所有后置处理器,如果是InstantiationAwareBeanPostProcessor;
* 就执行postProcessBeforeInstantiation
* if (bean != null) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
*
* 2)、doCreateBean(beanName, mbdToUse, args);真正的去创建一个bean实例;和3.6流程一样;
* 3)、
*
*
* AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】 的作用:
* 1)、每一个bean创建之前,调用postProcessBeforeInstantiation();
* 关心MathCalculator和LogAspect的创建
* 1)、判断当前bean是否在advisedBeans中(保存了所有需要增强bean)
* 2)、判断当前bean是否是基础类型的Advice、Pointcut、Advisor、AopInfrastructureBean,
* 或者是否是切面(@Aspect)
* 3)、是否需要跳过
* 1)、获取候选的增强器(切面里面的通知方法)【List<Advisor> candidateAdvisors】
* 每一个封装的通知方法的增强器是 InstantiationModelAwarePointcutAdvisor;
* 判断每一个增强器是否是 AspectJPointcutAdvisor 类型的;返回true
* 2)、永远返回false
*
* 2)、创建对象
* postProcessAfterInitialization;
* return wrapIfNecessary(bean, beanName, cacheKey);//包装如果需要的情况下
* 1)、获取当前bean的所有增强器(通知方法) Object[] specificInterceptors
* 1、找到候选的所有的增强器(找哪些通知方法是需要切入当前bean方法的)
* 2、获取到能在bean使用的增强器。
* 3、给增强器排序
* 2)、保存当前bean在advisedBeans中;
* 3)、如果当前bean需要增强,创建当前bean的代理对象;
* 1)、获取所有增强器(通知方法)
* 2)、保存到proxyFactory
* 3)、创建代理对象:Spring自动决定
* JdkDynamicAopProxy(config);jdk动态代理;
* ObjenesisCglibAopProxy(config);cglib的动态代理;
* 4)、给容器中返回当前组件使用cglib增强了的代理对象;
* 5)、以后容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程;
*
*
* 3)、目标方法执行 ;
* 容器中保存了组件的代理对象(cglib增强后的对象),这个对象里面保存了详细信息(比如增强器,目标对象,xxx);
* 1)、CglibAopProxy.intercept();拦截目标方法的执行
* 2)、根据ProxyFactory对象获取将要执行的目标方法拦截器链;
* List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
* 1)、List<Object> interceptorList保存所有拦截器 5
* 一个默认的ExposeInvocationInterceptor 和 4个增强器;
* 2)、遍历所有的增强器,将其转为Interceptor;
* registry.getInterceptors(advisor);
* 3)、将增强器转为List<MethodInterceptor>;
* 如果是MethodInterceptor,直接加入到集合中
* 如果不是,使用AdvisorAdapter将增强器转为MethodInterceptor;
* 转换完成返回MethodInterceptor数组;
*
* 3)、如果没有拦截器链,直接执行目标方法;
* 拦截器链(每一个通知方法又被包装为方法拦截器,利用MethodInterceptor机制)
* 4)、如果有拦截器链,把需要执行的目标对象,目标方法,
* 拦截器链等信息传入创建一个 CglibMethodInvocation 对象,
* 并调用 Object retVal = mi.proceed();
* 5)、拦截器链的触发过程;
* 1)、如果没有拦截器执行执行目标方法,或者拦截器的索引和拦截器数组-1大小一样(指定到了最后一个拦截器)执行目标方法;
* 2)、链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行;
* 拦截器链的机制,保证通知方法与目标方法的执行顺序;
*
* 总结:
* 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)、效果:
* 正常执行:前置通知-》目标方法-》后置通知-》返回通知
* 出现异常:前置通知-》目标方法-》后置通知-》异常通知
声明式事务
1、导入相关依赖 - 数据源、数据库驱动、Spring-jdbc模块
2、配置数据源、JdbcTemplate(Spring提供的简化数据库操作的工具)操作数据
//数据源
@Bean
public DataSource dataSource(){
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setUser("root");
ds.setPassword();
ds.setDriverClass();
ds.setJdbcUrl();
return ds;
}
//配置Spring自带的Template
@Bean
public JdbcTemplate jdbcTemplate(){
//Spring对配置类会特殊处理;给容器中加组件的方法,多次调用都是从容器中获取该组件
JdbcTemplate jt = new JdbcTemplate(dateSource());
return jt;
}
@Transactional //声明该方法是一个事务方法;Spring就会自动对该方法进行事务管理; 在方法上标注; 注:需开启@EnableTransactionManagement基于注解的事务管理功能 和 配置事务管理器
@EnableTransactional //开启基于注解的事务管理功能; 在配置类上标注
配置文件中是 <tx:annotation-driven> <!-- 开启基于事务的驱动 -->
3、在方法上标注@Transactional 表示当前方法是一个事务方法
4、@EnableTransactionManagement 开启基于注解的功能 @EnableXXX
5、配置事务管理器(管理数据源),注册事务管理器在容器中
@Bean
public PlatformTransactionManager transactionManager(){
return new DataSourceTransactionManager(datasource());
}
2、扩展原理
BeanPostProcessor:bean后置处理器,bean创建对象初始化前后进行拦截工作的
BeanFactoryProcessor:beanFactory的后置处理器 - 在BeanFactory标准初始化之后调用,来定制和修改BeanFactory的内容;
所有的bean定义已经保存加载到beanFactory,但是bean的实例还未创建
* BeanFactoryPostProcessor原理:
* 1)、ioc容器创建对象
* 2)、invokeBeanFactoryPostProcessors(beanFactory);
* 如何找到所有的BeanFactoryPostProcessor并执行他们的方法;
* 1)、直接在BeanFactory中找到所有类型是BeanFactoryPostProcessor的组件,并执行他们的方法
* 2)、在初始化创建其他组件前面执行
*
* 2、BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor
* postProcessBeanDefinitionRegistry();
* 在所有bean定义信息将要被加载,bean实例还未创建的;
*
* 优先于BeanFactoryPostProcessor执行;
* 利用BeanDefinitionRegistryPostProcessor给容器中再额外添加一些组件;
*
* 原理:
* 1)、ioc创建对象
* 2)、refresh()-》invokeBeanFactoryPostProcessors(beanFactory);
* 3)、从容器中获取到所有的BeanDefinitionRegistryPostProcessor组件。
* 1、依次触发所有的postProcessBeanDefinitionRegistry()方法
* 2、再来触发postProcessBeanFactory()方法BeanFactoryPostProcessor;
*
* 4)、再来从容器中找到BeanFactoryPostProcessor组件;然后依次触发postProcessBeanFactory()方法
*
*
* 3、ApplicationListener:监听容器中发布的事件。事件驱动模型开发;
* public interface ApplicationListener<E extends ApplicationEvent>
* 监听 ApplicationEvent 及其下面的子事件;
*
* 步骤:
* 1)、写一个监听器(ApplicationListener实现类)来监听某个事件(ApplicationEvent及其子类)
* @EventListener;
* 原理:使用EventListenerMethodProcessor处理器来解析方法上的@EventListener;
*
* 2)、把监听器加入到容器;
* 3)、只要容器中有相关事件的发布,我们就能监听到这个事件;
* ContextRefreshedEvent:容器刷新完成(所有bean都完全创建)会发布这个事件;
* ContextClosedEvent:关闭容器会发布这个事件;
* 4)、发布一个事件:
* applicationContext.publishEvent(event);
*
* 原理:
* ContextRefreshedEvent、IOCTest_Ext$1[source=我发布的时间]、ContextClosedEvent;
* 1)、ContextRefreshedEvent事件:
* 1)、容器创建对象:refresh();
* 2)、finishRefresh();容器刷新完成会发布ContextRefreshedEvent事件
* 2)、自己发布事件;
* 3)、容器关闭会发布ContextClosedEvent;
*
* 【事件发布流程】:
* 3)、publishEvent(new ContextRefreshedEvent(this));
* 1)、获取事件的多播器(派发器):getApplicationEventMulticaster()
* 2)、multicastEvent派发事件:
* 3)、获取到所有的ApplicationListener;
* for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
* 1)、如果有Executor,可以支持使用Executor进行异步派发;
* Executor executor = getTaskExecutor();
* 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);
*
* SmartInitializingSingleton 原理:->afterSingletonsInstantiated();
* 1)、ioc容器创建对象并refresh();
* 2)、finishBeanFactoryInitialization(beanFactory);初始化剩下的单实例bean;
* 1)、先创建所有的单实例bean;getBean();
* 2)、获取所有创建好的单实例bean,判断是否是SmartInitializingSingleton类型的;
* 如果是就调用afterSingletonsInstantiated();
3、web
servlet3.0
@WebServlet("/xxx") //代替了web.xml中servlet的注册; 在servlet上标注
(value="请求",asyncSupported=true) //开启异步支持
Shared libearies(共享库) / runtimes pluggability(运行时插件能力)
1、Servlet容器启动会扫描,当前应用里面每一个jar包的
ServletContainerInitializer的实现
2、提供ServletContainerInitializer的实现类;
必须绑定在,META-INF/services/javax.servlet.ServletContainerInitializer
文件的内容就是ServletContainerInitializer实现类的全类名;
总结:容器在启动应用的时候,会扫描当前应用每一个jar包里面
META-INF/services/javax.servlet.ServletContainerInitializer
指定的实现类,启动并运行这个实现类的方法;传入感兴趣的类型;
@HandlesTypes(value={xxx.class,xxx.class}) //容器启动时会见指定的类型下面的所有子类(实现类,子接口)传递进来,作为onStartuo(Set<Class<?>> args方法的args传入到方法中; 在ServletContainerInitializer类上标注
应用启动时会运行ServletContainerInitializer实现类的onStartup方法
1)、在ServletContainerInitializer实现类中使用ServletContext注册导入的第三方Web组件(Servlet、Filter、Listener)
2)、使用编码的方式,在项目启动时给ServletContext里面添加组件
必须在项目启动的时候来添加
1、ServletContainerInitializer得到的ServletContexxt
2、ServletContextListener得到的ServletContesxt
在onStartup(Set<Class<?>> args,SetvletContext sc)方法中
Dynamic servlet = sc.addServlet(自定义组件名,组件对象) //注册组件 ServletRegistration
//配置servlet的映射信息
servlet.addMapping("/xxx"); //映射请求
//监听器的注册 Listener
sc.addListener(xxxListener.class);
//注册Filter FilterRegistration
FilterRegistration.Dynamic filter = sc.addFilter("userFilter", UserFilter.class);
//配置Filter的映射信息
filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
整合Spring-MVC
1、web容器在启动的时候,会扫描每个jar包下的META-INF/services/javax.servlet.ServletContainerInitializer
2、加载这个文件指定的类SpringServletContainerInitializer
3、spring的应用一启动会加载感兴趣的WebApplicationInitializer接口的下的所有组件;
4、并且为WebApplicationInitializer组件创建对象(组件不是接口,不是抽象类)
1)、AbstractContextLoaderInitializer:创建根容器;createRootApplicationContext();
2)、AbstractDispatcherServletInitializer:
创建一个web的ioc容器;createServletApplicationContext();
创建了DispatcherServlet;createDispatcherServlet();
将创建的DispatcherServlet添加到ServletContext中;
getServletMappings();
3)、AbstractAnnotationConfigDispatcherServletInitializer:注解方式配置的DispatcherServlet初始化器
创建根容器:createRootApplicationContext()
getRootConfigClasses();传入一个配置类
创建web的ioc容器: createServletApplicationContext();
获取配置类;getServletConfigClasses();
总结:
以注解方式来启动SpringMVC;继承AbstractAnnotationConfigDispatcherServletInitializer;
实现抽象方法指定DispatcherServlet的配置信息; web容器启动的时候创建对象;调用方法来初始化容器以前的前端控制器
配置信息:
1、获取根容器的配置类; (Spring的配置文件(类)) 父容器 Spring的容器不扫描Controller //return new Class<?>[]{写好的对应配置类};
2、获取web容器的配置类 (SpringMVC的配置文件(类)) 子容器 SpringMVC的容器只扫描Controller //return new Class<?>[]{写好的对应配置类};
3、获取DispatcherServlet的映射信息 //return new String[]{"/"};
/ : 拦截所有请求(包括静态资源(xx.js,xx.png等等)),但是不包括 *.jsp
/* :拦截所有请求;连 *.jsp页面都拦截;jsp页面是tomcat的jsp引擎解析的
===========================
定制SpringMVC;
1)、@EnableWebMvc:开启SpringMVC定制配置功能; 在SpringMVC的配置类上标注
<mvc:annotation-driven/>;
2)、配置组件(视图解析器、视图映射、静态资源映射、拦截器。。。)
SpringMVC配置类 extends WebMvcConfigurerAdapter 然后重写需要定制功能的方法
configureViewResolvers() //配置视图解析器 .jsp("前缀","后缀");
configureDefaultServletHanling() //静态资源访问 .enable();
addInterceptors() //拦截器interceptor类 .addInterceptor(new interceptor的实现类).addPathPatterns("拦截哪些请求"); /** 表示任意路径任意请求
异步请求
普通请求方式: 一次请求结束前会一直占用同一个线程,导致servlet3.0的线程池的线程不够使用而不能处理下一个请求
异步请求方式: AsyncContext
servlet异步方式
@WebServlet(value="/async",asyncSupported=true) //1、支持异步处理
...
doGet(){
//2、开启异步模式
AsyncContext startAsync = request.startAsync();
//3、业务逻辑进行异步处理;开始异步处理
startAsync.start(new Runnable(){
@Override
public void run(){
//业务逻辑
...
//获取异步的上下文
AsyncContext asyncContext = request.getAsyncContext();
//4、获取响应
ServletResponse response = asyncContext.getResponse();
}
})
}
异步效果:
主线程处理请求的时候会立即返回结束,从而不长时间占用一个线程;而副线程还在处理业务逻辑;这下新的请求进来也可以进行处理
SpringMVC异步方式
方式1:
1、控制器返回Callable
2、Spring异步处理,将Callable提交到TaskExecutor使用一个隔离的线程进行执行
3、DispatcherServlet和所有的Filter退出web容器的线程,但是response保持打开的状态
4、Callable返回结果,SpringMVC将重新派发给容器,恢复之前的处理
5、根据Callable返回的结果。SpringMVC继续进行视图渲染流程等(从收请求-视图渲染)
在Controller组件中,写一个以Callable<String>为返回值的方法
@RequestMapping("xxx")
public Callable<String> async1(){
Callable<String> callable = new Callable<String>(){
@Override
public String call throws Exception{
return "";
}
};
return callable;
}
异步效果:
主线程处理请求的时候会立即返回结束,从而不长时间占用一个线程;而副线程还在处理业务逻辑,当副线程完成后会从重新到收到请求的位置继续进行处理;这下新的请求进来也可以进行处理
异步拦截器:
1、原生的API的AsyncListener
2、SpringMVC:实现AsyncHnadlerInterceptor;
方式2:
在Controller组件中,写一个以DeferredResult<String>为返回值的方法,此时还在等待状态,待返回的对象经过处理之后才算结束
@RequestMapping("xxx")
@ResponseBody
public DeferredResult<String> createOrder(){
DeferredResult<Object> deferredResult = new DeferredResult<>((long)3000,"create fail");
return deferredResult; //还需要有操作来处理这个对象才能响应成功
}
异步效果:
可以通过应用1发请求 -> 经消息中间件 -> 传递到应用2处理请求并返回结果 -> 再通过消息中间件 -> 由应用1返回并结束请求
、