AOP动态代理解析4-jdk代理的实现
JDKProxy的使用关键是创建自定义的InvocationHandler,而InvocationHandler中包含了需要覆盖的函数getProxy,而当前的方法正是完成了这个操作。在此确认一下JDKDynamicAopProxy也确实实现了InvocationHandler接口,那么我们就可以推断出,在JdkDynamicAopProxy中一定会有个invoke函数,并且JdkDynamicAopProxy会把AOP的核心逻辑写在其中。查看代码,果然有这样个函数:
JdkDynamicAopProxy.java 获取代理对象的主要方法
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);
}
主要切面逻辑在InvocationHandler的invoke方法中,而JdkDynamicAopProxy本身就实现了InvocationHandler
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Class<?> targetClass = null;
Object target = null;
try {
//equals方法的处理
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
return equals(args[0]);
}
//hash方法的处理
if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
return hashCode();
}
//Class类的isAssignableFrom(Class cls)方法:如果调用这个方法的class或接口与参数cls表示的类或接口相同,
//或者参数与cls表示的类或接口的父类,则返回true
//形象地:自身类.class.isAssignableFrom(自身类或子类.class)返回true。例:
//System.out.println(ArrayList.class.isAssignableFrom(Object.class));//false
//System.out.println(Object.class.isAssignableFrom(ArrayList.class));//true
if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
} Object retVal;
//有时候目标对象内部的自我调用将无法实施切面中的增强则需要通过此属性暴露代理
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
}
//获取当前方法的拦截器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
//如果没有发现任何拦截器那么直接调用切点方法
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
}
else {
//将拦截器封装在ReflectiveMethodInvocation,以便于使用其proceed进行链表用拦截器
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
//使用拦截器链
retVal = invocation.proceed();
} // Massage return value if necessary.
Class<?> returnType = method.getReturnType();
//返回结果
if (retVal != null && retVal == target && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned "this" and the return type of the method
// is type-compatible. Note that we can't help if the target sets
// a reference to itself in another returned object.
retVal = proxy;
}
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
上面的函数中最主要的工作就是创建了一个拦截器链,并使用ReflectiveMethodInvocation类进行了链的封装,而在ReflectiveMethodInvocation类的proceed方法中实现了拦截器的逐一调用,那么继续来研究,在proceed方法中是怎么实现前置增强在目标方法前调用后置增强在目标方法后调用的逻辑呢?
public Object proceed() throws Throwable {
//执行完所有增强后执行切点方法
if(currentInterceptorIndex == interceptorsAndDynamicMethodMatchers.size() - 1)
return invokeJoinpoint();
//获取下一个要执行的拦截器
Object interceptorOrInterceptionAdvice = interceptorsAndDynamicMethodMatchers.get(++currentInterceptorIndex);
if(interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher)
{ //动态匹配
InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;
if(dm.methodMatcher.matches(method, targetClass, arguments))
return dm.interceptor.invoke(this);
else
//不匹配则不执行拦截器
return proceed();
} else
{
//普通拦截器,直接调用拦截器,如:MethodBeforeAdviceInterceptor,AspectJAroundAdvice,AdpectJAfterAdvice
//将this作为参数传递以保证当前实例中调用链的执行
return ((MethodInterceptor)interceptorOrInterceptionAdvice).invoke(this);
}
}
在proceed方法中,获取代码逻辑并没有那么复杂,ReflectiveMethodInvocation中的主要职责是维护了链接调用的计数器,记录着当前调用链的位置,以便链可以有序地进行下去,那么在这个方法中并没有我们之前设想的维护各种增强的顺序,而是将此工作委托给了各个增强器,使各个增强器在内部进行逻辑实现。
AOP动态代理解析4-jdk代理的实现的更多相关文章
- spring源码系列8:AOP源码解析之代理的创建
回顾 首先回顾: JDK动态代理与CGLIB动态代理 Spring中的InstantiationAwareBeanPostProcessor和BeanPostProcessor的区别 我们得知 JDK ...
- Spring框架系列(12) - Spring AOP实现原理详解之JDK代理实现
上文我们学习了SpringAOP Cglib动态代理的实现,本文主要是SpringAOP JDK动态代理的案例和实现部分.@pdai Spring框架系列(12) - Spring AOP实现原理详解 ...
- java 代理模式(静态代理、动态代理、Cglib代理) 转载
Java的三种代理模式 1.代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩 ...
- AOP动态代理解析4-代理的创建
做完了增强器的获取后就可以进行代理的创建了 AnnotationAwareAspectJAutoProxyCreator->postProcessAfterInitialization-> ...
- spring的AOP动态代理--JDK代理和CGLIB代理区分以及注意事项
大家都知道AOP使用了代理模式,本文主要介绍两个代理模式怎么设置以及区别,对原文一些内容进行了引用后加入了自己的理解和更深入的阐述: 一.JDK代理和CGLIB代理的底层实现区别* JDK代理只能 ...
- Spring AOP源码分析(三):基于JDK动态代理和CGLIB创建代理对象的实现原理
AOP代理对象的创建 AOP相关的代理对象的创建主要在applyBeanPostProcessorsBeforeInstantiation方法实现: protected Object applyBea ...
- Spring AOP高级——源码实现(3)AopProxy代理对象之JDK动态代理的创建过程
spring-aop-4.3.7.RELEASE 在<Spring AOP高级——源码实现(1)动态代理技术>中介绍了两种动态代理技术,当然在Spring AOP中代理对象的生成也是运用 ...
- aop学习总结一------使用jdk动态代理简单实现aop功能
aop学习总结一------使用jdk动态代理实现aop功能 动态代理:不需要为目标对象编写静态代理类,通过第三方或jdk框架动态生成代理对象的字节码 Jdk动态代理(proxy):目标对象必须实现接 ...
- Spring AOP动态代理实现,解决Spring Boot中无法正常启用JDK动态代理的问题
Spring AOP底层的动态代理实现有两种方式:一种是JDK动态代理,另一种是CGLib动态代理. JDK动态代理 JDK 1.3版本以后提供了动态代理,允许开发者在运行期创建接口的代理实例,而且只 ...
- AOP动态代理解析5-cglib代理的实现
CGLIB是一个强大的高性能的代码生成包.它广泛地被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的Interception(拦截).EasyMock和jMock是通过使 ...
随机推荐
- 【STL】重载运算符
重载运算符 为什么要重载运算符: C++中预定义的运算符的操作对象只能是基本数据类型.但实际上,对于许多用户自定义类型(例如结构体),也需要类似的运算操作.这时就必须在C++中重新定义这些运算符,赋予 ...
- Android 中沉浸式状态栏实现
Android 中沉浸式状态栏实现方式如下 计算状态栏高度及调用沉浸式状态栏的相关API方法 package com.example.status; import android.annotation ...
- 【leetcode】Merge Intervals(hard)
Given a collection of intervals, merge all overlapping intervals. For example,Given [1,3],[2,6],[8,1 ...
- 【数据结构】book3_3 表达式求值
#include<iostream> #include <stdlib.h> using namespace std; typedef int Status; ; ; ; ; ...
- You know元音字母吗?
所谓元音字母,或者母音字母,就是语言里起着发声作用的字母.在英语中,A.E.I.O.U属于元音字母,其中U是半元音开音节和闭音节为数不多的5个元音字母看似简单,他们却能像变戏子一样跟辅音组合拼读成不同 ...
- [聊天框]让DIV的滚动条自动滚动到最底部 - 4种方法
要制作一个在线聊天的程序,在做最后的修饰时,需要对获得的信息即时滚动以保证用户总能看到最新消息. 聊天程序是基于AJAX设计的,没有用框架,消息容器是一个DIV,所以问题就在于如何控制DIV的滚动条. ...
- 【Bochs 官方手册翻译】 第一章 Bochs介绍
Bochs 是一个可以完全模拟 Intel x86 计算机的虚拟机系统.它包含了 Intel x86 CPU 仿真.常见设备仿真.以及定制 BIOS.Bochs 可以虚拟多种不同类型的 x86 CPU ...
- Log4j的使用
一,Log4j简介 日志的三个目的: 1. 监视代码中变量的变化情况,周期性的记录到文件中供其他应用进行统计分析工作 2.跟踪代码运行时轨迹,作为日后审计的依 ...
- js正则匹配以固定格式结尾的字符串并匹配是手机访问,则跳转
<script> //var pcUrl = "http://res.meadin.com/HotelData/98986_1.shtml"; var pcUrl = ...
- [Android Pro] 通过包名启动应用
Intent intent = packageManager.getLaunchIntentForPackage(WEIXIN_PKGNAME); intent.setFlags(Intent.FLA ...