一、简介
  JdkDynamicAopProxy 代理类是spring 默认的JDK动态的代理类实现。它实现了Java 动态代理接口InvocationHandler接口和Spring定义的AopProxy接口。AopProxy定义了返回代理的对象。
二、阅读
public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
Assert.notNull(config, "AdvisedSupport must not be null");
if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
throw new AopConfigException("No advisors and no TargetSource specified");
}
this.advised = config;
}
  从其构造函数来看,该类的入参是AdvisedSupport,并把入参设置到成员变量中。其中AdvisedSupport类是Aop代理的配置管理类,里面包括了代理的对象和被代理对象需要织入的通知Advice.
从该类实现了AopProxy接口,那么久可以返回代理的对象,其实现如下:
 
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
  首先从AopProxyUtils.completeProxiedInterfaces工具方法得到需要代理的接口,接着通过findDefinedEqualsAndHashCodeMethods私有方法判断这些需要的代理接口proxiedInterfaces是否重新定义了equals和hashcode方法,并把接口记录到this.equalsDefined 和 this.hashCodeDefined成员变量中。最后核心其实是通过Java的动态代理类Proxy 产生一个代理对象。所以每调用一次getProxy都会产生新的一个代理对象。
  接着我们需要看下的就是InvocationHandler接口的invoke方法是如何实现的。注入和原理如下:
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false; // 通过this.advised得到代理的目标类,
TargetSource targetSource = this.advised.targetSource;
Object target = null; try {
// 如何代理接口没有定义equals方法,则调用JdkDynamicAopProxy的重写的equals方法
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
// 如何代理接口没有定义equals方法,则调用JdkDynamicAopProxy的重写的equals方法
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
//如果该方法申明类是DecoratingProxy,则直接最终的被代理类的类Class
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
//如果目标对象是Advice类型,则直接使用反射进行调用
//opaque-->标记是否需要阻止通过该配置创建的代理对象转换为Advised类型,默认值为false,表示代理对象可以被转换为Advised类型 else 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;
// 看是否需要暴露代理对象,如果需要放到threadLocal上
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
} // Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null); // 得到该调用方法的拦截链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
// 如果拦截链为空,直接返回调到该方法
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// 否则创建 MethodInvocation 执行拦截调用
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
} // Massage return value if necessary.
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target &&
returnType != Object.class && 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.
// 如果返回值是this,那么返回值替换为代理对象,而不是原对象。
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);
}
}
}
  从上面我们知道有几个特殊的接口,当方法的代理类是DecoratingProxy,或者是Advised都做了特殊的处理。原因在于在创建JdkDynamicAopProxy 对象是,有Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true); 这么一句语句。它返回的代理接口就可能包括DecortingProxy 和Advised。具体如下:
static Class<?>[] completeProxiedInterfaces(AdvisedSupport advised, boolean decoratingProxy) {
Class<?>[] specifiedInterfaces = advised.getProxiedInterfaces();
// 首先得到用户设置的代理接口,如果没有检查和设置代理类是否是一个接口
if (specifiedInterfaces.length == 0) {
// No user-specified interfaces: check whether target class is an interface.
Class<?> targetClass = advised.getTargetClass();
if (targetClass != null) {
if (targetClass.isInterface()) {
advised.setInterfaces(targetClass);
}
else if (Proxy.isProxyClass(targetClass)) {
advised.setInterfaces(targetClass.getInterfaces());
}
specifiedInterfaces = advised.getProxiedInterfaces();
}
}
// 如果用户代理的接口设置中没有SpringProxy接口,需要把代理类实现SpringProxy接口,该接口是个标记接口,说明是Spring代理
boolean addSpringProxy = !advised.isInterfaceProxied(SpringProxy.class);
// 如果代理类可以转化为Advised类型,并且用户没有指定Advised,则添加该接口,
//所以这里可以知道一般Spring的代理类都实现了该Advised接口
boolean addAdvised = !advised.isOpaque() && !advised.isInterfaceProxied(Advised.class);
//如果decoratingProxy==true,并且用户未添加DecoratingProxy接口,则代理需要实现该接口DecoratingProxy
boolean addDecoratingProxy = (decoratingProxy && !advised.isInterfaceProxied(DecoratingProxy.class));
int nonUserIfcCount = 0;
if (addSpringProxy) {
nonUserIfcCount++;
}
if (addAdvised) {
nonUserIfcCount++;
}
if (addDecoratingProxy) {
nonUserIfcCount++;
}
Class<?>[] proxiedInterfaces = new Class<?>[specifiedInterfaces.length + nonUserIfcCount];
System.arraycopy(specifiedInterfaces, 0, proxiedInterfaces, 0, specifiedInterfaces.length);
int index = specifiedInterfaces.length;
if (addSpringProxy) {
proxiedInterfaces[index] = SpringProxy.class;
index++;
}
if (addAdvised) {
proxiedInterfaces[index] = Advised.class;
index++;
}
if (addDecoratingProxy) {
proxiedInterfaces[index] = DecoratingProxy.class;
}
// 所以从上面分析得知,proxiedInterfaces会返回用户制定的代理接口+SpringProxy、Advised、DecoratingProxy
return proxiedInterfaces;
}
三、测试 
public interface Human {
Human getInstance();
}

  

public class ChineseHuman implements Human {

	private String desc;

	public ChineseHuman() {
} public ChineseHuman(String desc) {
this.desc = desc;
} public String getDesc() {
return desc;
} public void setDesc(String desc) {
this.desc = desc;
} @Override
public Human getInstance() {
System.out.println("human desc:" + desc);
return this;
}
}

  

public class JdkDynamicAopProxyTests {

	@Test
public void testJdkProxy() {
AdvisedSupport advisedSupport = new AdvisedSupport();
advisedSupport.setInterfaces(Human.class);
advisedSupport.setTargetSource(new TargetSource() {
@Override
public Class<?> getTargetClass() {
return ChineseHuman.class;
} @Override
public boolean isStatic() {
return false;
} @Override
public Object getTarget() throws Exception {
return new ChineseHuman("中国人");
} @Override
public void releaseTarget(Object target) throws Exception { }
});
JdkDynamicAopProxy jdkDynamicAopProxy = new JdkDynamicAopProxy(advisedSupport); Human human = (Human) Proxy.newProxyInstance(JdkDynamicAopProxy.class.getClassLoader(), new Class[]{Human.class}, jdkDynamicAopProxy); System.out.println(human.getInstance()); // JDK原生代理,必然为true,因为本身是生成的是代理的接口的对象
System.out.println("human instanceof Human:" + (human instanceof Human)); //false
System.out.println("human instanceof ChineseHuman:" + (human instanceof ChineseHuman));
//false
System.out.println("human instanceof Advised:" + (human instanceof Advised));
//false
System.out.println("human instanceof SpringProxy:" + (human instanceof SpringProxy));
//false
System.out.println("human instanceof DecoratingProxy:" + (human instanceof DecoratingProxy));
//true, 有个面试题是这么问的,java动态代理为啥代理是接口,不能是类,
// 其根原在于JDK的动态代理实现方式是代理对象继承了java.lang.reflect.Proxy,并且java单继承,所以无法代理类
System.out.println("human instanceof Proxy:" + (human instanceof Proxy)); System.out.println("Spring JDK代理扩展========="); human = (Human) jdkDynamicAopProxy.getProxy(); System.out.println(human.getInstance()); // 该代理对象的getInstance返回是其本身,原因也在于上面分析的红色部分原因。
System.out.println("human== human.getInstance():" + (human == human.getInstance())); // true
System.out.println("human instanceof Human:" + (human instanceof Human));
//false
System.out.println("human instanceof ChineseHuman:" + (human instanceof ChineseHuman));
//true
System.out.println("human instanceof Advised:" + (human instanceof Advised));
// true
System.out.println("human instanceof SpringProxy:" + (human instanceof SpringProxy));
// true
System.out.println("human instanceof DecoratingProxy:" + (human instanceof DecoratingProxy));
//true
System.out.println("human instanceof Proxy:" + (human instanceof Proxy));
}
}

  从上面的测试可以知道,一个对象被Spring的JDK动态代理后,其代理对象会实现其用户指定的代理接口外,还会实现Advised,SpringProxy,DecoratingProxy接口。

 
 
 
 

SPRING 阅读--JdkDynamicAopProxy的更多相关文章

  1. Spring阅读方法

    转自:http://www.cnblogs.com/xing901022/p/4178963.html 最近没什么实质性的工作,正好有点时间,就想学学别人的代码.也看过一点源码,算是有了点阅读的经验, ...

  2. Java最新趋势之Spring阅读

    (原文地址:点我) This Week in Spring: Cloud Native and the State of Java This compilation of news and tutor ...

  3. Sping学习笔记(一)----Spring源码阅读环境的搭建

    idea搭建spring源码阅读环境 安装gradle Github下载Spring源码 新建学习spring源码的项目 idea搭建spring源码阅读环境 安装gradle 在官网中下载gradl ...

  4. spring applicationContext.xml和hibernate.cfg.xml设置

    applicationContext.xml配置 <?xml version="1.0" encoding="UTF-8"?> <beans ...

  5. 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。

    基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别. 我还是喜欢基于Schema风格的Spring事务管理,但也有很多人在用基于@Tras ...

  6. Spring -- <tx:annotation-driven>注解基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)的区别。

    借鉴:http://jinnianshilongnian.iteye.com/blog/1508018 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional ...

  7. Spring <tx:annotation-driven>注解 JDK动态代理和CGLIB动态代理 区别。

    基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别. 我还是喜欢基于Schema风格的Spring事务管理,但也有很多人在用基于@Tras ...

  8. 全网最新最详细最明白教程之Spring源码搭建,没有之一,超详细

    相关帖子有很多但是都不是最新的Gradle,我在使用Gradle最新版编译的时候简直坑死我了,弄了好久.接下来给大家详细说一下这个安装过程,以及相关的软件版本号. 相关软件.依赖的版本号: Gradl ...

  9. Confluence 使用常见问题列表

    Confluence 6 管理 Atlassian 提供的 App 摘要: Confluence 用户可以使用桌面应用来编辑一个已经上传到 Confluence 的文件,然后这个文件自动保存回 Con ...

随机推荐

  1. Srapy 爬取知乎用户信息

    今天用scrapy框架爬取一下所有知乎用户的信息.道理很简单,找一个知乎大V(就是粉丝和关注量都很多的那种),找到他的粉丝和他关注的人的信息,然后分别再找这些人的粉丝和关注的人的信息,层层递进,这样下 ...

  2. js写一个简单的日历

    思路:先写一个结构和样式,然后写本月的时间,之后计算上下月份的关系 <!DOCTYPE html> <html lang="en"> <head> ...

  3. 每日一题 - 剑指 Offer 47. 礼物的最大价值

    题目信息 时间: 2019-07-02 题目链接:Leetcode tag:动态规划 难易程度:中等 题目描述: 在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0). ...

  4. css 分割线样式_css实现文章分割线的多种方法总结

    这篇文章整理css如何实现文章分割线的多种方式,分割线在页面中可以起到美化作用,那么就来看看使用css实现分割线样式的多种方法.效果如下: 方式一:单个标签实现分隔线: html: <div c ...

  5. css完美解决网页在iphoneX的头部刘海显示问题

    一.解决iphonX白条,网站扩展到整个屏幕 网页在iphoneX的浏览器屏幕显示上,默认情况下在头部的2侧会出现白条背景,网站被限制在了一个“安全区域”内,移除白色背景的方法 方法一:设置body的 ...

  6. 如何基于 echarts 实现区间柱状图(包括横向)?

    目录 需求 借鉴 echarts 的 demo 最终实现思路 实现效果 遇到的问题: 代码映射 源码 最后 始终如一 需求 需要利用 echarts 实现区间柱状图,效果如下: 效果来源于:g2-柱状 ...

  7. nth-child,nth-last-child,after,before,tab-highlight-color,first-child,last-child

    nth-child:定义第几个元素或者是奇数或者是偶数,或者满足某个数字倍数的dom的样式 如 li:nth-child(3n),结果如下,li:nth-child(2)结果如下

  8. HDU3686 Traffic Real Time Query System 题解

    题目 City C is really a nightmare of all drivers for its traffic jams. To solve the traffic problem, t ...

  9. day75 bbs项目☞后台管理+修改头像

    目录 一.后台管理之添加文章 二.修改用户头像 bbs项目总结 一.后台管理之添加文章 添加文章有两个需要注意的问题: 文章的简介切取,应该想办法获取到当前文章的文本内容后再截取字符 XSS攻击,由于 ...

  10. Pandas基础知识图谱

    所有内容整理自<利用Python进行数据分析>,使用MindMaster Pro 7.3制作,emmx格式,源文件已经上传Github,需要的同学转左上角自行下载或者右击保存图片.该图谱只 ...