深入源码解析spring aop实现的三个过程
Spring AOP的面向切面编程,是面向对象编程的一种补充,用于处理系统中分布的各个模块的横切关注点,比如说事务管理、日志、缓存等。它是使用动态代理实现的,在内存中临时为方法生成一个AOP对象,这个对象包含目标对象的所有方法,在特定的切点做了增强处理,并回调原来的方法。
Spring AOP的动态代理主要有两种方式实现,JDK动态代理和cglib动态代理。JDK动态代理通过反射来接收被代理的类,但是被代理的类必须实现接口,核心是InvocationHandler和Proxy类。cglib动态代理的类一般是没有实现接口的类,cglib是一个代码生成的类库,可以在运行时动态生成某个类的子类,所以,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
AOP实现中,可以看到三个主要的步骤,一个是代理对象的生成,然后是拦截器的作用,然后是Aspect编织的实现。
ProxyFactoryBean生成AopProxy
ProxyFactoryBean生成AOP proxy
1 /**
2 * Return a proxy. Invoked when clients obtain beans from this factory bean.
3 * Create an instance of the AOP proxy to be returned by this factory.
4 * The instance will be cached for a singleton, and create on each call to
5 * <code>getObject()</code> for a proxy.
6 * @return a fresh AOP proxy reflecting the current state of this factory
7 */
8 public Object getObject() throws BeansException {
9 initializeAdvisorChain();
10 if (isSingleton()) {
11 return getSingletonInstance();
12 }
13 else {
14 if (this.targetName == null) {
15 logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
16 "Enable prototype proxies by setting the 'targetName' property.");
17 }
18 return newPrototypeInstance();
19 }
20 }
初始化Advisor chain
1 /**
2 * Create the advisor (interceptor) chain. Aadvisors that are sourced
3 * from a BeanFactory will be refreshed each time a new prototype instance
4 * is added. Interceptors added programmatically through the factory API
5 * are unaffected by such changes.
6 */
7 private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
8 if (this.advisorChainInitialized) {
9 return;
10 }
11
12 if (!ObjectUtils.isEmpty(this.interceptorNames)) {
13 if (this.beanFactory == null) {
14 throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +
15 "- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames));
16 }
17
18 // Globals can't be last unless we specified a targetSource using the property...
19 if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX) &&
20 this.targetName == null && this.targetSource == EMPTY_TARGET_SOURCE) {
21 throw new AopConfigException("Target required after globals");
22 }
23
24 // Materialize interceptor chain from bean names.
25 for (int i = 0; i < this.interceptorNames.length; i++) {
26 String name = this.interceptorNames[i];
27 if (logger.isTraceEnabled()) {
28 logger.trace("Configuring advisor or advice '" + name + "'");
29 }
30
31 if (name.endsWith(GLOBAL_SUFFIX)) {
32 if (!(this.beanFactory instanceof ListableBeanFactory)) {
33 throw new AopConfigException(
34 "Can only use global advisors or interceptors with a ListableBeanFactory");
35 }
36 addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
37 name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
38 }
39
40 else {
41 // If we get here, we need to add a named interceptor.
42 // We must check if it's a singleton or prototype.
43 Object advice = null;
44 if (this.singleton || this.beanFactory.isSingleton(this.interceptorNames[i])) {
45 // Add the real Advisor/Advice to the chain.
46 advice = this.beanFactory.getBean(this.interceptorNames[i]);
47 }
48 else {
49 // It's a prototype Advice or Advisor: replace with a prototype.
50 // Avoid unnecessary creation of prototype bean just for advisor chain initialization.
51 advice = new PrototypePlaceholderAdvisor(this.interceptorNames[i]);
52 }
53 addAdvisorOnChainCreation(advice, this.interceptorNames[i]);
54 }
55 }
56 }
57
58 this.advisorChainInitialized = true;
59 }
增加advisor chain(AdvisedSupport.java)
1 private void addAdvisorInternal(int pos, Advisor advisor) throws AopConfigException {
2 Assert.notNull(advisor, "Advisor must not be null");
3 if (isFrozen()) {
4 throw new AopConfigException("Cannot add advisor: Configuration is frozen.");
5 }
6 if (pos > this.advisors.size()) {
7 throw new IllegalArgumentException(
8 "Illegal position " + pos + " in advisor list with size " + this.advisors.size());
9 }
10 this.advisors.add(pos, advisor);
11 updateAdvisorArray();
12 adviceChanged();
13 }
Spring AOP中拦截器链
1.开始步骤--获取AopProxy主流程
ProxyCreatorSupport.java
/**
* Subclasses should call this to get a new AOP proxy. They should <b>not</b>
* create an AOP proxy with <code>this</code> as an argument.
*/
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
return getAopProxyFactory().createAopProxy(this);
}
2.获取AopProxy实现 --DefaultAopProxyFactory.java
ProxyFactoryBean类继承了AdvisedSupport类,后者继承了ProxyConfig类并定义了操作advisor 和interceptor的接口,以支持AOP。当BeanFactory实例化ProxyFactoryBean时,根据配置文件的定义将关于 advice,pointcut,advisor,所代理的接口和接口实现类的所有信息传给ProxyFactoryBean。
当客户程序调用BeanFactory的getBean方法时,ProxyFactory使用JdkDynamicAopProxy实例化 BeanImpl类,并用JdkDynamicAopProxy的invoke方法执行advice。至于执行advice的时机,由 ProxyFactoryBean调用RegexpMethodPointcutAdvisor进行判断。
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface()) {
return new JdkDynamicAopProxy(config);
}
if (!cglibAvailable) {
throw new AopConfigException(
"Cannot proxy target class because CGLIB2 is not available. " +
"Add CGLIB to the class path or specify proxy interfaces.");
}
return CglibProxyFactory.createCglibProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
3.获取AopProxy的执行路径
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
4.激发拦截器链主过程
1 /**
2 * Implementation of <code>InvocationHandler.invoke</code>.
3 * <p>Callers will see exactly the exception thrown by the target,
4 * unless a hook method throws an exception.
5 */
6 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
7 MethodInvocation invocation = null;
8 Object oldProxy = null;
9 boolean setProxyContext = false;
10
11 TargetSource targetSource = this.advised.targetSource;
12 Class targetClass = null;
13 Object target = null;
14
15 try {
16 if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
17 // The target does not implement the equals(Object) method itself.
18 return (equals(args[0]) ? Boolean.TRUE : Boolean.FALSE);
19 }
20 if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
21 // The target does not implement the hashCode() method itself.
22 return new Integer(hashCode());
23 }
24 if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
25 method.getDeclaringClass().isAssignableFrom(Advised.class)) {
26 // Service invocations on ProxyConfig with the proxy config...
27 return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
28 }
29
30 Object retVal = null;
31
32 if (this.advised.exposeProxy) {
33 // Make invocation available if necessary.
34 oldProxy = AopContext.setCurrentProxy(proxy);
35 setProxyContext = true;
36 }
37
38 // May be <code>null</code>. Get as late as possible to minimize the time we "own" the target,
39 // in case it comes from a pool.
40 target = targetSource.getTarget();
41 if (target != null) {
42 targetClass = target.getClass();
43 }
44
45 // Get the interception chain for this method.
46 List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
47
48 // Check whether we have any advice. If we don't, we can fallback on direct
49 // reflective invocation of the target, and avoid creating a MethodInvocation.
50 if (chain.isEmpty()) {
51 // We can skip creating a MethodInvocation: just invoke the target directly
52 // Note that the final invoker must be an InvokerInterceptor so we know it does
53 // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
54 retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
55 }
56 else {
57 // We need to create a method invocation...
58 invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
59 // Proceed to the joinpoint through the interceptor chain.
60 retVal = invocation.proceed();
61 }
62
63 // Massage return value if necessary.
64 if (retVal != null && retVal == target && method.getReturnType().isInstance(proxy) &&
65 !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
66 // Special case: it returned "this" and the return type of the method
67 // is type-compatible. Note that we can't help if the target sets
68 // a reference to itself in another returned object.
69 retVal = proxy;
70 }
71 return retVal;
72 }
73 finally {
74 if (target != null && !targetSource.isStatic()) {
75 // Must have come from TargetSource.
76 targetSource.releaseTarget(target);
77 }
78 if (setProxyContext) {
79 // Restore old proxy.
80 AopContext.setCurrentProxy(oldProxy);
81 }
82 }
83 }
5.获取拦截器链DefaultAdvisorChainFactory.java
public List getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, Class targetClass) {
// This is somewhat tricky... we have to process introductions first,
// but we need to preserve order in the ultimate list.
List interceptorList = new ArrayList(config.getAdvisors().length);
boolean hasIntroductions = hasMatchingIntroductions(config, targetClass);
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
Advisor[] advisors = config.getAdvisors();
for (int i = 0; i < advisors.length; i++) {
Advisor advisor = advisors[i];
if (advisor instanceof PointcutAdvisor) {
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
if (MethodMatchers.matches(mm, method, targetClass, hasIntroductions)) {
if (mm.isRuntime()) {
// Creating a new object instance in the getInterceptors() method
// isn't a problem as we normally cache created chains.
for (int j = 0; j < interceptors.length; j++) {
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptors[j], mm));
}
}
else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
}
else if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (config.isPreFiltered() || ia.getClassFilter().matches(targetClass)) {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
else {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
return interceptorList;
}
6.激发拦截链工作实现 ---ReflectiveMethodInvocation.java public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
} Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
Spring AOP中Aspect编织的实现
1.前面我们谈到拦截器起作用时,实现代码(ReflectiveMethodInvocation.java)如下:
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
} Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
2.前置Advice MethodBeforeAdviceInterceptor.java
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
return mi.proceed();
}
3.后置Advice AfterReturningAdviceInterceptor.java
public Object invoke(MethodInvocation mi) throws Throwable {
Object retVal = mi.proceed();
this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
return retVal;
}
总结
没图没真相
Spring框架的AOP机制可以让开发者把业务流程中的通用功能抽取出来,单独编写功能代码。在业务流程执行过程中,Spring框架会根据业务流程要求,自动把独立编写的功能代码切入到流程的合适位置。
深入源码解析spring aop实现的三个过程的更多相关文章
- 源码解析Spring AOP的加载与生效
本次博主主要进行Spring AOP这里的解析,因为在工作中使用后,却不知道背后的实现原理并在使用的过程中发现了一些认知缺陷,所以决定写这么一篇文章以供大家参考参考,进入正题. 本次博主使用了@Asp ...
- Spring5源码解析-Spring框架中的单例和原型bean
Spring5源码解析-Spring框架中的单例和原型bean 最近一直有问我单例和原型bean的一些原理性问题,这里就开一篇来说说的 通过Spring中的依赖注入极大方便了我们的开发.在xml通过& ...
- SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的
系列文章目录和关于我 一丶什么是SpringBoot自动装配 SpringBoot通过SPI的机制,在我们程序员引入一些starter之后,扫描外部引用 jar 包中的META-INF/spring. ...
- Mybaits 源码解析 (四)----- SqlSession的创建过程(看懂框架源码再也不用死记硬背面试题)
SqlSession是mybatis的核心接口之一,是myabtis接口层的主要组成部分,对外提供了mybatis常用的api.myabtis提供了两个SqlSesion接口的实现,常用的实现类是De ...
- QT源码解析(七)Qt创建窗体的过程,作者“ tingsking18 ”(真正的创建QPushButton是在show()方法中,show()方法又调用了setVisible方法)
前言:分析Qt的代码也有一段时间了,以前在进行QT源码解析的时候总是使用ue,一个函数名在QTDIR/src目录下反复的查找,然后分析函数之间的调用关系,效率实在是太低了,最近总结出一个更简便的方法, ...
- 时序数据库 Apache-IoTDB 源码解析之文件格式简介(三)
上一章聊到在车联网或物联网中对数据库的需求,以及 IoTDB 的整体架构,详情请见: 时序数据库 Apache-IoTDB 源码解析之系统架构(二) 打一波广告,欢迎大家访问IoTDB 仓库,求一波 ...
- 源码解析.Net中Host主机的构建过程
前言 本篇文章着重讲一下在.Net中Host主机的构建过程,依旧延续之前文章的思路,着重讲解其源码,如果有不知道有哪些用法的同学可以点击这里,废话不多说,咱们直接进入正题 Host构建过程 下图是我自 ...
- spring源码解析之AOP原理
一.准备工作 在这里我先简单记录下如何实现一个aop: AOP:[动态代理] 指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式: 1.导入aop模块:Spring AOP:(s ...
- spring源码解析——spring源码导入eclipse
一.前言 众所周知,spring的强大之处.几乎所有的企业级开发中,都使用了spring了.在日常的开发中,我们是否只知道spring的配置,以及简单的使用场景.对其实现的代码没有进行深入的了 ...
随机推荐
- 在win64上使用bypy进行百度网盘文件上传
阿里云服务器的带宽为2M,网站每日的备份包都3G多了,离线下载太费时间了,打算每日将备份包自动上传到自己的百度云盘里.1.先安装Python 执行python -V ,发现没安装python2.去py ...
- $CH5104\ I-country$ 线性$DP$
CH Sol ”凸联通块“是什么意思呢? 其实就是图形的左端点先减小再增大,右端点先增大再减小 阶段 考虑到第k行,已经选了i个格子 状态 1.第i行的左端点与右端点 2.这一行的左端点相对于上一行的 ...
- InterpreterPattern(解释器模式)-----Java/.Net
解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式.这种模式实现了一个表达式接口,该接口解释一个特定的上下文.这种模式被用在 SQL 解析.符号处 ...
- spring之通过注解方式配置Bean(一)
(1)组件扫描:spring能够从classpath下自动扫描.侦测和实例化具有特定注解的组件. (2)特定组件包括: @Component:基本注解,标识一个受spring管理的组件: @Respo ...
- 非常完整的线性DP及记忆化搜索讲义
基础概念 我们之前的课程当中接触了最基础的动态规划. 动态规划最重要的就是找到一个状态和状态转移方程. 除此之外,动态规划问题分析中还有一些重要性质,如:重叠子问题.最优子结构.无后效性等. 最优子结 ...
- Python基础(二):操作基本数据类型
Python是一门解释型语言,它的优势在于代码简洁,易于理解,可以通过大量已封装好的内建方法和第三方模块方法完成日常所需的操作. 字符串 索引 起始下标为0 (从前往后数),末尾下标为-1(从后往前数 ...
- kubernetes基础——一文读懂k8s
容器 容器与虚拟机对比图(左边为容器.右边为虚拟机) 容器技术是虚拟化技术的一种,以Docker为例,Docker利用Linux的LXC(LinuX Containers)技术.CGroup(Co ...
- 删除centos自带的openjdk
[wj@master hadoop]$ rpm -qa | grep javajava-1.7.0-openjdk-1.7.0.191-2.6.15.5.el7.x86_64python-javapa ...
- Jaeger容器化部署
概述 Jaeger是由Uber开源的分布式追踪系统,一套完整的Jager追踪系统包括Jaeger-client.Jaeger-agent.Jaeger-collector.Database和Jaege ...
- 临近年关,修复ASPNETCore因浏览器内核版本引发的单点登陆故障
临近年关,咨询师提出360,搜狗急速浏览器无法单点登陆到公司核心产品WD, 报重定向过多. 现象 经过测试, 出现单点登陆故障的是搜狗,360等主打双核(默认Chrome内核)的浏览器, 较新式的Ed ...