动态代理3--Spring AOP分析
Spring AOP的基本实现方式
Spring AOP,一种模块化机制,能够动态的对切点添加行为,而不破坏原有的代码结构。
这是一个非常好地动态代理的应用方式。Spring AOP实现依赖于JDK的动态代理库和CGLIB字节码库技术两种来分别实现。
在Spring AOP中,JdkDynamicAopProxy实现基于JDK动态代理生成代理对象,CglibAopProxy来实现基于CGLIB的动态代理对象生成。并通过DefaultAopProxyFactory进行调用。此处採用策略模式。针对不同场景。调用不同的实现。
例如以下我们对详细实现进行分析。假设对SpringAOP有疑惑的话,能够參考例如以下文章,一篇是我的博客:http://blog.csdn.net/mergades/article/details/46841079
还有IBM Devloper社区的一篇:http://www.ibm.com/developerworks/cn/java/j-lo-springaopcglib/
DefaultAopProxyFactory
Spring AOP内部採用AopProxy对使用不同的代理实现机制进行了适度的抽象,针对不同的代理实现机制提供相应的AopProxy子类实现。DefaultAopProxyFactory实现了AopProxyFactory。当中AopProxyFactory接口定义createAopProxy方法。来决定依据哪种详细的策略来实现代理类。详细实现则由DefaultAopProxyFactory实现,我们查看其相应的createAopProxy方法。
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {@Overridepublic 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()) {//假设是接口,则通过JDK的实现,否则通过CGLIBreturn new JdkDynamicAopProxy(config);}return new ObjenesisCglibAopProxy(config);}else {return new JdkDynamicAopProxy(config);}}/*** Determine whether the supplied {@link AdvisedSupport} has only the* {@link org.springframework.aop.SpringProxy} interface specified* (or no proxy interfaces specified at all).*/private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {Class<?>[] interfaces = config.getProxiedInterfaces();return (interfaces.length == 0 || (interfaces.length == 1 && SpringProxy.class.equals(interfaces[0])));}}
JdkDynamicAopProxy
基于JDK动态代理的实现。该类实现了InvocationHandler接口。那么我们依据动态代理的知识能够知道,不管调用目标类的什么方法,都会运行该类的Invoke方法,Invoke方法就是Spring AOP增加切面的主要方法。
我们查看相应的Invoke方法,尽管Invoke方法总体看起来非常长非常复杂,可是仅仅要我们包握住几个重点就可以了解。
@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {MethodInvocation invocation;Object oldProxy = null;boolean setProxyContext = false;- //1。获取目标对象。
TargetSource targetSource = this.advised.targetSource;Class<?> targetClass = null;Object target = null;- //2,推断对JDK原生方法
try {if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {// The target does not implement the equals(Object) method itself.return equals(args[0]);}if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {// The target does not implement the hashCode() method itself.return hashCode();}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;- //3,设置代理对象
if (this.advised.exposeProxy) {// Make invocation available if necessary.oldProxy = AopContext.setCurrentProxy(proxy);setProxyContext = true;}// May be null. Get as late as possible to minimize the time we "own" the target,// in case it comes from a pool.
//4,获取目标类
target = targetSource.getTarget();if (target != null) {targetClass = target.getClass();}// Get the interception chain for this method.
//5,获取通知链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.
//6。推断是否存在通知链。并运行相应方法,获取返回值
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.retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);}else {// We need to create a method invocation...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.
//7,对返回值进行处理
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);}}}
以上是invoke方法的实现。这种方法是动态代理机制较为核心的方法。
以下我们查看在该类中的getProxy方法,查看SpringAOP是怎样获取一个代理对象的。
@Overridepublic 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);
//获取是否定义equals和hashCode方法
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
//调用JDK Proxy生成代理
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);}
以上两个方法。我们抛开Spring详细各种细节的实现。全然能够看做是一个简单的动态代理模式的应用。
ObjenesisCglibAopProxy
ObjenesisCglibAopProxy基于CGLIb的AOP代理对象的生成。在DefaultAopProxyFactory类中,通过调用此方法来实现CGLIB生成代理对象。
@Override@SuppressWarnings("unchecked")protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {try {
//详细生成代理,详细实现源代码没有公开
Factory factory = (Factory) this.objenesis.newInstance(enhancer.createClass());factory.setCallbacks(callbacks);return factory;}catch (ObjenesisException ex) {// Fallback to regular proxy construction on unsupported JVMsif (logger.isDebugEnabled()) {logger.debug("Unable to instantiate proxy using Objenesis, falling back to regular proxy construction", ex);}return super.createProxyClassAndInstance(enhancer, callbacks);}}
总结
如上的各种实现即为Spring AOP对动态代理的应用。我们通过查看以上代码能够看到动态代理的作用,能够不改变原有代码而动态的增加我们自己的操作。这样的方式能够实现对我们代码全然的解耦。
动态代理3--Spring AOP分析的更多相关文章
- java:struts框架2(方法的动态和静态调用,获取Servlet API三种方式(推荐IOC(控制反转)),拦截器,静态代理和动态代理(Spring AOP))
1.方法的静态和动态调用: struts.xml: <?xml version="1.0" encoding="UTF-8"?> <!DOCT ...
- java中代理,静态代理,动态代理以及spring aop代理方式,实现原理统一汇总
若代理类在程序运行前就已经存在,那么这种代理方式被成为 静态代理 ,这种情况下的代理类通常都是我们在Java代码中定义的. 通常情况下, 静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类. ...
- 从动态代理到Spring AOP(上)
一.前言 虽然平时日常开发很少用到动态代理,但是动态代理在底层框架等有着非常重要的意义.比如Spring AOP使用cglib和JDK动态代理,Hibernate底层使用了javassit和cglib ...
- 从动态代理到Spring AOP(中)
一.前言 上一章节主要介绍了JDK动态代理和CGLIB动态代理:https://www.cnblogs.com/GrimMjx/p/11194283.html 这一章主要结合我们之前学习的动态代理的基 ...
- 反射实现 AOP 动态代理模式(Spring AOP 的实现原理)
枚举 在某些情况下,一个类的对象是有限而且固定的,比如季节类,它只有4个对象.这种实例有限而且固定的类,在Java里被称为枚举类. 枚举就是要让某个类型的变量的取值只能为若干个固定值中的一个,否则,编 ...
- 反射实现 AOP 动态代理模式(Spring AOP 的实现 原理)
好长时间没有用过Spring了. 突然拿起书.我都发现自己对AOP都不熟悉了. 其实AOP的意思就是面向切面编程. OO注重的是我们解决问题的方法(封装成Method),而AOP注重的是许多解决解决问 ...
- 通过JDK动态代理实现 Spring AOP
1.新建一个目标类 接口:public interface IUserService //切面编程 public void addUser(); public void updateUser( ); ...
- Spring AOP分析(3) -- CglibAopProxy实现AOP
上文探讨了应用JDK动态代理实现Spring AOP功能的方式,下面将继续探讨Spring AOP功能的另外一种实现方式 -- CGLIB. 首先,来看看类名CglibAopProxy,该类实现了两个 ...
- JDK动态代理给Spring事务埋下的坑!
一.场景分析 最近做项目遇到了一个很奇怪的问题,大致的业务场景是这样的:我们首先设定两个事务,事务parent和事务child,在Controller里边同时调用这两个方法,示例代码如下: 1.场景A ...
- (转)面试必备技能:JDK动态代理给Spring事务埋下的坑!
一.场景分析 最近做项目遇到了一个很奇怪的问题,大致的业务场景是这样的:我们首先设定两个事务,事务parent和事务child,在Controller里边同时调用这两个方法,示例代码如下: 1.场景A ...
随机推荐
- note for git
1.download https://git-for-windows.github.io/ 2.command add file to git: git add filename & git ...
- centos7配置静态IP步骤
centos7按照初始安装时候的developer类型一路装好,在vmware里已经设置为bridge模式,按理说是会自动按照DHCP联网成功的,结果却发现连网卡都没有激活,这里记录下. 1:我要把L ...
- [Python3网络爬虫开发实战] 1.4.1-MySQL的安装
MySQL是一个轻量级的关系型数据库,本节中我们来了解下它的安装方式. 1. 相关链接 官方网站:https://www.mysql.com/cn 下载地址:https://www.mysql.com ...
- python3.x Day6 socketserver
socketserver是啥? 简化了编写网络服务器,就是对于socket的再一次封装sockerserver包含4个类可以使用:A=socketserver.TCPServer() #用于TCP/I ...
- 分布式集群环境下运行Wordcount程序
1.分布式环境的Hadoop提交作业方式与本地安装的Hadoop作业提交方式相似,但有两点不同: 1)作业输入输出都存储在HDFS 2)本地Hadoop提交作业时将作业放在本地JVM执行,而分布式集群 ...
- rbac组件之权限初始化(五)
当用户登陆后,根据用户的角色要为用户生成对应的权限菜单,此时需要将登陆的用户信息获取且获取角色信息,从数据库中获取菜单以及权限信息,并且存入session中. 1.权限流程 第一次请求的页面是登陆页面 ...
- Django所包含属性
Django包含的属性 定义属性 概述: 1.django根据属性的类型确定以下信息 2.当前选择的数据库支持字段的类型 3.渲染管理表单时使用的默认html空间 4.在管理站点最低限度的验证 注意: ...
- SQlServer中的MD5加密
SELECT sys.fn_varbintohexstr(HASHBYTES('MD5', '我'));
- BBS+Blog项目流程及补充知识点
项目流程: 1. 产品需求 (1)基于用户认证组件和Ajax实现登陆验证(图片验证码) (2)基于forms组件和Ajax实现注册功能 (3)设计系统首页(文章列表渲染) (4)设计个人站点页面 (5 ...
- 【Tomcat】Tomcat性能分析
一.预研任务介绍和预研目标 任务介绍: Apache Tomcat是目前较为流行的web服务器,以其技术先进.性能稳定著称,其次它还是一个免费开源的项目. Tomcat性能分析的意义在于能为日常工作中 ...