一、容器
1.1 AnnotationConfigApplicationContext
之前写法
1 | <bean id="person" class="com.atguigu.bean.Person" scope="prototype" > |
1 | ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); |
配置类
1 | //告诉Spring这是一个配置类 |
包扫描
1 | public static void main(String[] args) { |
1.2 组件添加
@ComponentScan
1 | ( |
@ComponentScan value:指定要扫描的包
excludeFilters = Filter[] :指定扫描的时候按照什么规则排除那些组件
includeFilters = Filter[] :指定扫描的时候只需要包含哪些组件
FilterType.ANNOTATION:按照注解
FilterType.ASSIGNABLE_TYPE:按照给定的类型;
FilterType.ASPECTJ:使用ASPECTJ表达式
FilterType.REGEX:使用正则指定
FilterType.CUSTOM:使用自定义规则
MyTypeFilter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23public class MyTypeFilter implements TypeFilter {
/**
* metadataReader:读取到的当前正在扫描的类的信息
* metadataReaderFactory:可以获取到其他任何类信息的
*/
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
throws IOException {
//获取当前类注解的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//获取当前正在扫描的类的类信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//获取当前类资源(类的路径)
Resource resource = metadataReader.getResource();
String className = classMetadata.getClassName();
System.out.println("--->"+className);
if(className.contains("er")){
return true;
}
return false;
}
}
@Bean
1 | /** |
1 | //第一种 |
1 | //第二种 |
1 |
|
1 | /** |
@Configuration
@Component
@Service
@Controller
@Repository
@Conditional
按照一定的条件进行判断,满足条件给容器中注册bean
也可放在类上:类中组件统一设置。满足当前条件,这个类中配置的所有bean注册才能生效
1 | //如果系统是windows,给容器中注册("bill") |
1 | //判断是否linux系统 |
@Primary
@Primary:让Spring进行自动装配的时候,默认使用首选的bean,也可以继续使用@Qualifier指定需要装配的bean的名字
1 |
|
@Lazy
懒加载:容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化;
1 |
|
@Scope
- prototype:多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。每次获取的时候才会调用方法创建对象;
- singleton:单实例的(默认值):ioc容器启动会调用方法创建对象放到ioc容器中。以后每次获取就是直接从容器(map.get())中拿 (默认)
- request:同一次请求创建一个实例
- session:同一个session创建一个实例
1 | //默认是单实例的 |
@Import
给容器中注册组件
- 包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)[自己写的类]
- @Bean[导入的第三方包里面的组件]
- @Import[快速给容器中导入一个组件]
- @Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名
- ImportSelector:返回需要导入的组件的全类名数组;
- ImportBeanDefinitionRegistrar:手动注册bean到容器中
- 使用Spring提供的 FactoryBean(工厂Bean);
- 默认获取到的是工厂bean调用getObject创建的对象
- 要获取工厂Bean本身,我们需要给id前面加一个& (&colorFactoryBean)
1 |
|
1 | //自定义逻辑返回需要导入的组件 |
1 | public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { |
工厂模式
1 |
|
1 | //创建一个Spring定义的FactoryBean |
1.3 组件赋值
@Value
1 | public class Person { |
@Autowired
1 | /** |
1 @Qualifier
2 其他方式
@Resources(JSR250)
- 可以和@Autowired一样实现自动装配功能;默认是按照组件名称进行装配的;
- 没有能支持@Primary功能没有支持@Autowired(reqiured=false);
1 | "bookDao2") (name= |
@Inject(JSR330,需要导入javax.inject)
- 需要导入javax.inject的包,和Autowired的功能一样。没有required=false的功能;
1 |
|
@PropertySource
之前:
1 | <context:property-placeholder location="classpath:person.properties"/> |
1 | //使用@PropertySource读取外部配置文件中的k/v保存到运行的环境变量中;加载完外部的配置文件以后使用${}取出配置文件的值 |
@PropertySources
@Profile
1 | /** |
Environment
pom.xml
1 | <dependency> |
dbconfig.properties
1 | root = |
-Dspring.profiles.active=test
-Dspring.profiles.active=dev
-Dspring.profiles.active=prod
1 | //1、使用命令行动态参数: 在虚拟机参数位置加载 -Dspring.profiles.active=test |
1.4 组件注入
方法参数
1 | public void setCar(@Autowired Car car) { |
构造器注入
1 | //构造器要用的组件,都是从容器中获取 |
方法注入
1 |
|
ApplicationContextAware
ApplicationContextAwareProcessor
1 | if (bean instanceof Aware) { |
xxxAware
Aware接口
1 | // |
1 |
|
1.5 AOP
底层:动态代理
指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式
使用步骤:
- 导入aop模块;Spring AOP:(spring-aspects)
- 定义一个业务逻辑类(MathCalculator);在业务逻辑运行的时候将日志进行打印(方法之前、方法运行结束、方法出现异常,xxx)
- 定义一个日志切面类(LogAspects):切面类里面的方法需要动态感知MathCalculator.div运行到哪里然后执行
- 给切面类的目标方法标注何时何地运行(通知注解)
- 将切面类和业务逻辑类(目标方法所在类)都加入到容器中
- 必须告诉Spring哪个类是切面类(给切面类上加一个注解:@Aspect)
- 给配置类中加 @EnableAspectJAutoProxy 【开启基于注解的aop模式】(
在Spring中很多的 @EnableXXX
)
通知方法:
- 前置通知(@Before):logStart:在目标方法(div)运行之前运行
- 后置通知(@After):logEnd:在目标方法(div)运行结束之后运行(无论方法正常结束还是异常结束)
- 返回通知(@AfterReturning):logReturn:在目标方法(div)正常返回之后运行
- 异常通知(@AfterThrowing):logException:在目标方法(div)出现异常以后运行
- 环绕通知(@Around):动态代理,手动推进目标方法运行(joinPoint.procced())
1 |
|
1 | public class MathCalculator { |
1 | /** |
原理:
看给容器中注册了什么组件,这个组件什么时候工作,这个组件的功能是什么?
@EnableAspectJAutoProxy是什么?
@Import(AspectJAutoProxyRegistrar.class):给容器中导入AspectJAutoProxyRegistrar
利用AspectJAutoProxyRegistrar自定义给容器中注册bean;BeanDefinetion
internalAutoProxyCreator=AnnotationAwareAspectJAutoProxyCreator
给容器中注册一个AnnotationAwareAspectJAutoProxyCreator
AnnotationAwareAspectJAutoProxyCreator
AnnotationAwareAspectJAutoProxyCreator->AspectJAwareAdvisorAutoProxyCreator->AbstractAdvisorAutoProxyCreator->AbstractAutoProxyCreator
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
后置处理器(在bean初始化完成前后做事情)、自动装配BeanFactory
流程:
传入配置类,创建ioc容器
注册配置类,调用 refresh() 刷新容器
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
① invokeAwareMethods():处理Aware接口的方法回调
② applyBeanPostProcessorsBeforeInitialization():应用后置处理器的postProcessBeforeInitialization()
③ invokeInitMethods();执行自定义的初始化方法
④ applyBeanPostProcessorsAfterInitialization();执行后置处理器的postProcessAfterInitialization()
4)、BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)创建成功;–》aspectJAdvisorsBuilder
7)、把BeanPostProcessor注册到BeanFactory中;
beanFactory.addBeanPostProcessor(postProcessor)
以上是创建和注册AnnotationAwareAspectJAutoProxyCreator的过程
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实例之前先尝试用后置处理器返回对象的】
① resolveBeforeInstantiation(beanName, mbdToUse);解析BeforeInstantiation
希望后置处理器在此能返回一个代理对象;如果能返回代理对象就使用,如果不能就继续
后置处理器先尝试返回对象
bean = applyBeanPostProcessorsBeforeInstantiation()
拿到所有后置处理器,如果是InstantiationAwareBeanPostProcessor;
就执行postProcessBeforeInstantiation
if (bean != null) { bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);}
② doCreateBean(beanName, mbdToUse, args);真正的去创建一个bean实例;和3.6流程一样;
AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】 的作用:
每一个bean创建之前,调用postProcessBeforeInstantiation()
MathCalculator和LogAspect的创建
1)、判断当前bean是否在advisedBeans中(保存了所有需要增强bean)
2)、判断当前bean是否是基础类型的Advice、Pointcut、Advisor、AopInfrastructureBean,或者是否是切面(@Aspect)
3)、是否需要跳过
① 获取候选的增强器(切面里面的通知方法)【List<Advisor> candidateAdvisors】
每一个封装的通知方法的增强器是 InstantiationModelAwarePointcutAdvisor
判断每一个增强器是否是 AspectJPointcutAdvisor 类型的;返回true
② 永远返回false
创建对象
postProcessAfterInitialization;
return wrapIfNecessary(bean, beanName, cacheKey);//包装如果需要的情况下
1)、获取当前bean的所有增强器(通知方法) Object[] specificInterceptors
① 找到候选的所有的增强器(找哪些通知方法是需要切入当前bean方法的)
② 获取到能在bean使用的增强器。
③ 给增强器排序
2)、保存当前bean在advisedBeans中;
3)、如果当前bean需要增强,创建当前bean的代理对象;
① 获取所有增强器(通知方法)
② 保存到proxyFactory
③ 创建代理对象:Spring自动决定
JdkDynamicAopProxy(config);jdk动态代理;
ObjenesisCglibAopProxy(config);cglib的动态代理;
4)、给容器中返回当前组件使用cglib增强了的代理对象;
5)、以后容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程;
目标方法执行
容器中保存了组件的代理对象(cglib增强后的对象),这个对象里面保存了详细信息(比如增强器,目标对象,xxx);
1)、CglibAopProxy.intercept();拦截目标方法的执行
2)、根据ProxyFactory对象获取将要执行的目标方法拦截器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
① List<Object> interceptorList保存所有拦截器 5 | 一个默认的ExposeInvocationInterceptor 和 4个增强器
② 遍历所有的增强器,将其转为Interceptor | registry.getInterceptors(advisor);
③ 将增强器转为List<MethodInterceptor>; | 如果是MethodInterceptor,直接加入到集合中,如果不是,使用AdvisorAdapter将增强器转为MethodInterceptor;转换完成返回MethodInterceptor数组;
3)、如果没有拦截器链,直接执行目标方法
拦截器链(每一个通知方法又被包装为方法拦截器,利用MethodInterceptor机制)
4)、如果有拦截器链,把需要执行的目标对象,目标方法,拦截器链等信息传入创建一个 CglibMethodInvocation 对象,并调用 Object retVal = mi.proceed();
5)、拦截器链的触发过程;
① 如果没有拦截器执行执行目标方法,或者拦截器的索引和拦截器数组-1大小一样(指定到了最后一个拦截器)执行目标方法;
② 链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行;
拦截器链的机制,保证通知方法与目标方法的执行顺序
总结:
@EnableAspectJAutoProxy 开启AOP功能
@EnableAspectJAutoProxy 会给容器中注册一个组件 AnnotationAwareAspectJAutoProxyCreator
AnnotationAwareAspectJAutoProxyCreator是一个后置处理器
容器的创建流程:
1)、registerBeanPostProcessors()注册后置处理器;创建AnnotationAwareAspectJAutoProxyCreator对象
2)、AnnotationAwareAspectJAutoProxyCreator拦截组件的创建过程
3)、组件创建完之后,判断组件是否需要增强
是:切面的通知方法,包装成增强器(Advisor);给业务逻辑组件创建一个代理对象(cglib);
执行目标方法:
1)、代理对象执行目标方法
2)、CglibAopProxy.intercept();
① 得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor)
② 利用拦截器的链式机制,依次进入每一个拦截器进行执行;
③ 效果:
正常执行:前置通知->目标方法->后置通知->返回通知
出现异常:前置通知->目标方法->后置通知->异常通知
1.6 声明式事务
环境搭建:
导入相关依赖
数据源、数据库驱动、Spring-jdbc模块
配置数据源、JdbcTemplate(Spring提供的简化数据库操作的工具)操作数据
给方法上标注 @Transactional 表示当前方法是一个事务方法
@EnableTransactionManagement 开启基于注解的事务管理功能
配置事务管理器来控制事务
1
2
3
4
public PlatformTransactionManager transactionManager() throws Exception{
return new DataSourceTransactionManager(dataSource());
}
1 |
|
1 |
|
1 |
|
原理:
@EnableTransactionManagement
利用TransactionManagementConfigurationSelector给容器中会导入组件
导入两个组件 AutoProxyRegistrar | ProxyTransactionManagementConfiguration
AutoProxyRegistrar:
给容器中注册一个 InfrastructureAdvisorAutoProxyCreator 组件
InfrastructureAdvisorAutoProxyCreator:利用后置处理器机制在对象创建以后,包装对象,返回一个代理对象(增强器),代理对象执行方法利用拦截器链进行调用;
ProxyTransactionManagementConfiguration 做了什么?
给容器中注册事务增强器
1)、事务增强器要用事务注解的信息,AnnotationTransactionAttributeSource解析事务注解
2)、事务拦截器:
TransactionInterceptor;保存了事务属性信息,事务管理器;
他是一个 MethodInterceptor;在目标方法执行的时候,执行拦截器链;
事务拦截器作用
: ① 先获取事务相关的属性
② 再获取PlatformTransactionManager,如果事先没有添加指定任何transactionmanger
最终会从容器中按照类型获取一个PlatformTransactionManager
③ 执行目标方法
如果异常,获取到事务管理器,利用事务管理回滚操作;
如果正常,利用事务管理器,提交事务
二、扩展原理
2.1 BeanFactoryPostProcessor:beanFactory的后置处理器
在BeanFactory标准初始化之后调用,来定制和修改BeanFactory的内容;所有的bean定义已经保存加载到beanFactory,但是bean的实例还未创建
1 | "com.atguigu.ext") ( |
1 |
|
BeanFactoryPostProcessor原理:
ioc容器创建对象
invokeBeanFactoryPostProcessors(beanFactory) 如何找到所有的BeanFactoryPostProcessor并执行他们的方法
1)、直接在BeanFactory中找到所有类型是BeanFactoryPostProcessor的组件,并执行他们的方法
2)、在初始化创建其他组件前面执行
2.2 BeanDefinitionRegistryPostProcessor
BeanFactoryPostProcessor子类,可自定义添加bean定义
在所有bean定义信息将要被加载,bean实例还未创建的
优先于BeanFactoryPostProcessor执行
利用BeanDefinitionRegistryPostProcessor给容器中再额外添加一些组件
1 |
|
原理:
ioc创建对象
refresh() -> invokeBeanFactoryPostProcessors(beanFactory)
从容器中获取到所有的BeanDefinitionRegistryPostProcessor组件
1)、依次触发所有的postProcessBeanDefinitionRegistry()方法
2)、再来触发postProcessBeanFactory()方法BeanFactoryPostProcessor
再来从容器中找到BeanFactoryPostProcessor组件;然后依次触发postProcessBeanFactory()方法
2.3 ApplicationListener
监听容器中发布的事件。事件驱动模型开发
public interface ApplicationListener<E extends ApplicationEvent>
监听 ApplicationEvent 及其下面的子事件
步骤:
写一个监听器(ApplicationListener实现类)来监听某个事件(ApplicationEvent及其子类)
@EventListener;
原理:使用EventListenerMethodProcessor处理器来解析方法上的@EventListener
把监听器加入到容器
只要容器中有相关事件的发布,我们就能监听到这个事件
ContextRefreshedEvent:容器刷新完成(所有bean都完全创建)会发布这个事件
ContextClosedEvent:关闭容器会发布这个事件
发布一个事件
applicationContext.publishEvent()
1 |
|
1 |
|
原理:
ContextRefreshedEvent、IOCTest_Ext$1[source=我发布的时间]、ContextClosedEvent
ContextRefreshedEvent事件
1)、容器创建对象:refresh()
2)、finishRefresh();容器刷新完成会发布ContextRefreshedEvent事件
自己发布事件
容器关闭会发布ContextClosedEvent
事件发布流程:publishEvent(new ContextRefreshedEvent(this))
获取事件的多播器(派发器):getApplicationEventMulticaster()
multicastEvent派发事件
获取到所有的ApplicationListener
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)){}
1)、如果有Executor,可以支持使用Executor进行异步派发
Executor executor = getTaskExecutor()
2)、否则,同步的方式直接执行listener方法;invokeListener(listener, event)
拿到listener回调onApplicationEvent方法
事件多播器(派发器)
容器创建对象:refresh()
initApplicationEventMulticaster();初始化ApplicationEventMulticaster
1)、先去容器中找有没有id=“applicationEventMulticaster”的组件
2)、如果没有this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
并且加入到容器中,我们就可以在其他组件要派发事件,自动注入这个applicationEventMulticaster
容器中有哪些监听器
容器创建对象:refresh()
registerListeners()
从容器中拿到所有的监听器,把他们注册到applicationEventMulticaster中
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
1
2//将listener注册到ApplicationEventMulticaster中
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
SmartInitializingSingleton 原理:->afterSingletonsInstantiated()
ioc容器创建对象并refresh()
finishBeanFactoryInitialization(beanFactory);初始化剩下的单实例bean
1)、先创建所有的单实例bean;getBean()
2)、获取所有创建好的单实例bean,判断是否是SmartInitializingSingleton类型的
如果是就调用afterSingletonsInstantiated()
2.4 Spring容器创建过程
1 | Spring容器的refresh()【创建刷新】; |
三、web
3.1 servlet3.0
1 | Shared libraries(共享库) / runtimes pluggability(运行时插件能力) |
1 | //容器启动的时候会将@HandlesTypes指定的这个类型下面的子类(实现类,子接口等)传递过来; |
SpringMVC
1 | 1、web容器在启动的时候,会扫描每个jar包下的META-INF/services/javax.servlet.ServletContainerInitializer |
1 | //web容器启动的时候创建对象;调用方法来初始化容器以前前端控制器 |
1 | package com.atguigu.config; |
1 | package com.atguigu.config; |
1 | package com.atguigu.controller; |
1 | package com.atguigu.service; |
1 | package com.atguigu.controller; |
3.2 异步请求
1 | package com.atguigu.controller; |
1 | package com.atguigu.service; |
<学习自尚硅谷雷神>