Spring IOC加载流程:
初始化ApplicationContext;ApplicationContext实现类中完成的
通过BeanDefinitionReader加载配置文件,并封装成BeadDefinition
将BeanDefinition包装成BeanWrapper
将BeanWrapper保存至IOC容器中
(1)读取配置文件:通过ResourceLoader的getResource方法将类路径,URL,文件系统等方式读为Resource
(2)解析配置文件:解析是在BeanDefinitionReader中完成的,最常用的类是XMLBeanDefinitionReader来完成的,解析涉及到很多步骤,最常见的是将XML文件读取成Document文档,DefaultBeanDefinitionDocumentReader对Document对象进一步解析,DefaultBeanDefinitionDocumentReader又委托给BeanDefinitionParserDelegate进行解析,如果是标准的XML Namespace元素,则会委托合适的NameSpaceHandler进行解析最终解析的结果封装为BeanDefinitionHolder至此解析完成。
(3)Bean的注册:bean的注册是在BeanFactory里完成的,BeanFactory最常见的一个实现类是DefaultListableBeanFactory,,它实现了BeanDefinitionRegistry接口,所以其中的registerBeanDefinition()方法,可以对BeanDefinition进行注册这里附带一提,最常见的XmlWebApplicationContext不是自己持有BeanDefinition的,它继承自AbstractRefreshableApplicationContext,其持有一个DefaultListableBeanFactory的字段,就是用它来保存BeanDefinition所谓的注册,其实就是将BeanDefinition的name和实例,保存到一个Map中。刚才说到,最常用的实现DefaultListableBeanFactory,其中的字段就是beanDefinitionMap,是一个ConcurrentHashMap。
(4)实例化:注册也完成之后,在BeanFactory的getBean()方法之中,会完成初始化,也就是依赖注入的过程
Spring DI加载流程:
Spring DI的基本流程:
(1)循环读取BeanDefinition的缓存信息
(2)调用getBean方法创建对象实例
(3)将创建好的对象实例包装BeanWrapper对象
(4)将BeanWrapper对象缓存到IOC容器
(5)循环IOC容器执行注入
实例化策略根据配置实现不同的策略,将其封装为BeanWrapper
SimpleInstantiationStrategy
实例化有两种情况?
目标类配置了AOP,实例化对象代理类
目标类没有配置AOP,实例化原生对象
为什么要使用BeanWrapper?
统一一个对外访问对象的入口
扩展一些功能,缓存一些配置信息
DI原理:
**FactoryBean.getBeans()**触发依赖注入
createBeanInstance->用反射创建了对象实例->封装成BeanWrapper();
populateBean()–>根据beanName、BeanDefinition、BeanWrapper找到需要赋值的属性,把需要
赋值的属性封装为一个集合,集合的元素是PropertyValue,PropertyValue保存了需要赋值的Bean,赋值需要调用的方法,要赋什么值。
applyPropertyValues() 循环PropertyValue,挨个调用BeanWrapper的setValue()方法,用反射代用setter方法完成赋值。
DI是依赖注入,当IOC容器完成初始化后,针对非懒加载的Bean进行依赖注入或者当第一次getBean时
1)实例化 通过反射机制实例化Bean
2)依赖注入 判断其field是否有需要被注入的属性,如果有则进行注入该依赖的Bean属性
3) 循环依赖的问题,可以通过多级缓存来解决,二级缓存,三级缓存
Spring循环依赖三级缓存是否可以去掉第三级缓存?
BeanFactory是ApplicationContext的父类
BeanFactory与FactoryBean的区别:
BeanFactory是管理Bean的工厂
FactoryBean是创建对象的工厂Bean
Spring AOP面向切片编程:
基本概念:
(1)切面(Aspect)
切面是一个横切关注点的模块化,一个切面能够包含同一个类型的不同增强方法,比如说事务处理和日志处理可以理解为两个切面。切面由切入点和通知组成,它既包含了横切逻辑的定义,也包括了切入点的定义。 Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。
(2) 目标对象(Target)
目标对象指将要被增强的对象,即包含主业务逻辑的类对象。或者说是被一个或者多个切面所通知的对象。
(3) 连接点(JoinPoint)
程序执行过程中明确的点,如方法的调用或特定的异常被抛出。连接点由两个信息确定:
方法(表示程序执行点,即在哪个目标方法)
相对点(表示方位,即目标方法的什么位置,比如调用前,后等)
简单来说,连接点就是被拦截到的程序执行点,因为Spring只支持方法类型的连接点,所以在Spring中连接点就是被拦截到的方法。
(4) 切入点(PointCut)
切入点是对连接点进行拦截的条件定义。切入点表达式如何和连接点匹配是AOP的核心,Spring缺省使用AspectJ切入点语法。
一般认为,所有的方法都可以认为是连接点,但是我们并不希望在所有的方法上都添加通知,而切入点的作用就是提供一组规则(使用 AspectJ pointcut expression language 来描述) 来匹配连接点,给满足规则的连接点添加通知。
(5) 通知(Advice)
通知是指拦截到连接点之后要执行的代码,包括了“around”、“before”和“after”等不同类型的通知。Spring AOP框架以拦截器来实现通知模型,并维护一个以连接点为中心的拦截器链。
前置通知(Before)指某连接点之前执行的通知,但这个通知不能阻止连接点之前的代码的执行。
后置通知(After)指当某连接点退出时执行的通知(无论是正常返回还是异常退出)
返回后通知(After Return Advice)是某个连接点正常完成通知后执行的通知,不包括抛出异常的情况。
环绕通知(Around Advice)围绕在连接点前后,比如一个方法调用前后
异常通知(After Throwing Advice)是在方法抛出异常导致退出时执行的通知。
(6) 织入(Weaving)
织入是将切面和业务逻辑对象连接起来, 并创建通知代理的过程。织入可以在编译时,类加载时和运行时完成。在编译时进行织入就是静态代理,而在运行时进行织入则是动态代理。
(7) 增强器(Adviser)
Advisor是切面的另外一种实现,能够将通知以更为复杂的方式织入到目标对象中,是将通知包装为更复杂切面的装配器。Advisor由切入点和Advice组成。
Advisor这个概念来自于Spring对AOP的支撑,在AspectJ中是没有等价的概念的。Advisor就像是一个小的自包含的切面,这个切面只有一个通知。切面自身通过一个Bean表示,并且必须实现一个默认接口。
简单来讲,整个 aspect 可以描述为: 满足 pointcut 规则的 joinpoint 会被添加相应的 advice 操作。
两个步骤
AOP的入口:AOP的入口是由接入BeanPostProcessor后置处理器开始的,在bean初始化后提供回调入口,在AbstractAutowireCapableBeanFactory中initializeBean()方法中添加BeanPostProcessor后置处理器。
创建代理类:1,PostProcessAfterInitialization()方法中调用了wrapIfNecessary(),如果代理类实现了接口,使用JDK动态代理,如果没有实现接口,使用cglib动态代理。
代码织入:1,在JDK动态代理类中,如果动态代理类会实现InvokeHandler接口重写invoke方法,在Invoke方法中会通过getInterceptorsAndDynamicInterceptionAdvice方法从提供的配置实例config中获取Advisor列表,遍历处理这个Advisor切面,如果是PointCutAdvisor,则判断此Advisor能否应用到目标方法Method上,将满足条件的Advisor通过AdvisorAdapter转化为MethodInterceptor拦截器列表返回。接下来在Invoke中判断拦截器是否为空,如果为空,则直接调用Method.Invoke反射方法,如果拦截器列表不为空,则调用ReflectiveMethodInvocation.proceed()方法触发执行链的执行,Proceed()串其了整个Interceptor调用链。
两个重要的类:intercept 拦截器,责任链组件 invoke()
invocation 调用器,嗲用通知回调的组件,procceed()