从JDK动态代理到spring AOP
引言:
所谓JDK动态代理(Dynamic Proxy),就是指在运行时生成目标类的代理类,并能通过反射机制调用目标类的代码,在调用目标代码的前后可以加入横切逻辑,实现目标方法增加的目的。在讲述动态代理之前,我们先了解下静态代理,在这之后,我们将学习JDK动态代理,并延伸学习spring AOP,因为spring AOP的原理主要就是动态代理机制,当然这包括JDK 动态代理与CGLIB两种方式。
1. 静态代理:
静态代理是指由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。看下边的例子:
1.1 首选声明一个接口
/** * * @author hyp * @date 2013-3-19 * 做菜的接口 */ public interface MakeFoodInterface { /** * 制作麻婆豆腐 * @param df 豆腐 * @return */ public String makeMpdf(String df); /** * 制作鱼香豆丝 * @param rou 肉 * @param suger 糖 * @return */ public String makeYxrs(String rou,String suger); }
1.2 接口的实现
/** * 做菜的实现类,四川菜 * @author hyp * @date 2013-3-19 */ public class MakeFoodSCImpl implements MakeFoodInterface{ @Override public String makeMpdf(String df) { System.out.println("makMpdf--------------"+df); return df; } @Override public String makeYxrs(String rou, String suger) { System.out.println("makeYxrs--------------"+rou+suger); return rou+suger; } }
1.3 我们自己实现的代理类,
用来在方法前后添加一些我们想要进行的操作
/** * 四川菜的代理类,方法加强 * @author hyp * @date 2013-3-19 */ public class MakeFoodSCImplProxy implements MakeFoodInterface { private MakeFoodSCImpl makeFoodSC; /** * 构造函数,传入一个四川菜的实例 * @param makeFoodSC */ public MakeFoodSCImplProxy(MakeFoodSCImpl makeFoodSC) { super(); this.makeFoodSC = makeFoodSC; } /** * 方法加强,在做菜前后进行一些别的操作 */ @Override public String makeMpdf(String df) { System.out.println("MakeFoodSCImplProxy方法调用前-------------------------"); String ret= this.makeFoodSC.makeMpdf(df); System.out.println("MakeFoodSCImplProxy方法调用后-------------------------"); return ret; } @Override public String makeYxrs(String rou, String suger) { return null; } }
1.4 测试类
/** * 静态代理类测试 * @author hyp * @date 2013-3-19 */ public class StaticProxyTest { public static void main(String[] args) { MakeFoodSCImplProxy makeFoodProxy = new MakeFoodSCImplProxy(new MakeFoodSCImpl()); makeFoodProxy.makeMpdf("mpdf"); } }
1.5 运行结果:
MakeFoodSCImplProxy方法调用前-------------------------
makMpdf--------------mpdf
MakeFoodSCImplProxy方法调用后-------------------------
6,总结:我们通过实现目标类的接口,实现接口方法的时候,在调用目标对象方法前后,进行了我们添加的操作,优点是没有改变原代码的结构,缺点是对于每个方法,我们都必须手动去实现。
2. 动态代理:
动态代理是指在运行过程中生成目标类的代理,主要用到了Proxy和InvocationHandler。其中InvocationHandler是一个接口,可以通过实现该接口定义横切逻辑,在并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编织在一起。
目标接口与类都和上一个例子一样,下边我们看如何实现InvocationHandler,并获得代理对象:
2.1 代理实例,必须要实现InvocationHandler接口
public class MyInvocationHandler implements InvocationHandler { private Object originalObject; // 将欲代理的对象传入,返回一个代理对象 public Object newProxy(Object obj) { this.originalObject = obj; // 三个参数,第一个是欲代理对象的类加载器,第二个是得到这个类的接口集合,第三个参数是一个handler return (Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj .getClass().getInterfaces(), this)); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("MyInvocationHandler---------方法调用前"); System.out.println(proxy.getClass()+";"+originalObject.getClass()); //传入目标对象,调用 目标对象的方法 method.invoke(originalObject, args); System.out.println("MyInvocationHandler---------方法调用后"); return null; } }
2.2 测试例子:
/** * 采用JDK动态代理,生成目标对象的代理类 * @author hyp * @date 2013-3-19 */ public class ProxyTest { public static void main(String[] args) { MakeFoodSCImpl foodSC = new MakeFoodSCImpl(); MyInvocationHandler invocationHandler = new MyInvocationHandler(); MakeFoodInterface foodSCProxy = (MakeFoodInterface)invocationHandler.newProxy(foodSC); foodSCProxy.makeMpdf("mpdf"); } }
2.3 运行结果:
MyInvocationHandler---------方法调用前
class $Proxy0;class test.proxy.MakeFoodSCImpl
makMpdf--------------mpdf
MyInvocationHandler---------方法调用后
2.4 总结:
通过实现 InvocationHandler,我们实现了自己的目标类代理实例类,代理实例类拥有一个目标类的变量,通过目标类变量,我们用Proxy.newProxyInstance方法获取代理实例,并实现invoke方法,我们通过反射调用目标类的方法,并做到让目标类方法的目的。
Cglib动态代理:
JDK的动态代理,是通过实现目标类的接口,来生成一个代理类。但如果目标类没有实现接口,我们可以通过另一种方式来实现动态代理,那就是cglib ,cglib 通过生成目标类的子类,获取的代理对象是目标类的子类,我们也可能对方法加强,当然目标类不能是final类型,以下是具体实现代码:
在这里我们让目标类不在实现接口,其它和上边程序一样。
2.5 通过cglib
2.5.1. 获得代理类
import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * * @author hyp * @date 2013-3-19 * 通过cglib生成目标类的代理类,代理类其实是目标类的子类 */ public class CglibProxyFactory implements MethodInterceptor { //原对象(或叫目标对象) private Object originalObject; //获取目标对象的代理类 public Object getProxy(Object originalObject){ this.originalObject= originalObject; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.originalObject.getClass()); enhancer.setCallback(this); Object o= enhancer.create(); System.out.println(o.getClass()); System.out.println(originalObject.getClass()); return o; } @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("CglibProxyFactory 方法执行前---------------"); Object ret = method.invoke(this.originalObject, args); System.out.println("CglibProxyFactory 方法执行后---------------"); return ret; } }
2.5.2. cglib测试
public class CglibTest { public static void main(String[] args) { MakeFoodSC foodSC= new MakeFoodSC(); CglibProxyFactory cglibFactory = new CglibProxyFactory(); //获取代理类,这里是目标类的子类 MakeFoodSC proxyFoodSC= (MakeFoodSC)cglibFactory.getProxy(foodSC); proxyFoodSC.makeMpdf("mpdf"); } }
2.5.3. 运行结果:
class test.cglib.MakeFoodSC$$EnhancerByCGLIB$$a4ffb679
class test.cglib.MakeFoodSC
CglibProxyFactory 方法执行前---------------
makMpdf--------------mpdf
CglibProxyFactory 方法执行后---------------
2.5.4. 总结:
结果和jdk动态代理一样,区别是目标对象不用实现任务接口
3. Spring AOP:
Spring aop(Aspect Oriented Programming)的原理就是通过动态代理实现,我们通过<aop:aspectj-autoproxy>这个配置让spring 生成目标对象的代理类,下边看具体代码:
3.1 目标类(Target Object)
这里的A接口就是对方法makeMpdf()的声明,这里不帖出,
/** * @author hyp * @date 2013-3-19 */ public class MakeFood implements A{ @Override public String makeMpdf(String df){ System.out.println("MakeFood-------------------"+df); return df; } }
3.2 通知(Advice)
import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; /** * @author hyp * @date 2013-3-19 */ public class MakeFoodAdvice implements MethodBeforeAdvice{ @Override public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { System.out.println("before Method invoke"); } }
3.3 切入点(PointCut)
import java.lang.reflect.Method; import org.springframework.aop.ClassFilter; import org.springframework.aop.MethodMatcher; import org.springframework.aop.Pointcut; /** * @author hyp * @date 2013-3-19 */ public class MakeFoodPointCut implements Pointcut { @Override public ClassFilter getClassFilter() { return new MakeFoodFilter(); } @Override public MethodMatcher getMethodMatcher() { return new MakeFoodMethodMather(); } } class MakeFoodFilter implements ClassFilter{ @Override public boolean matches(Class clazz) { System.out.println("mathches--------"+clazz.getSimpleName()); if(MakeFood.class.equals(clazz)){ return true; } return false; } } class MakeFoodMethodMather implements MethodMatcher{ @Override public boolean isRuntime() { return false; } @Override public boolean matches(Method method, Class clazz, Object[] arg2) { System.out.println("matches(3 args)-------"+clazz.getSimpleName()+method.getName()); if(MakeFood.class.equals(clazz)&&method.getName().equals("makeMpdf")){ return true; } return false; } @Override public boolean matches(Method method, Class clazz) { System.out.println("matches(2 args)-------"+clazz.getSimpleName()+method.getName()); if(clazz.getSimpleName().equals("MakeFood")&&method.getName().equals("makeMpdf")){ return true; } return false; } }
3.4 配置信息
<bean id="makeFoodAdvice" class="test.springAop.MakeFoodAdvice" />
<bean id="makeFoodPointCut" class="test.springAop.MakeFoodPointCut" />
<bean id="makeFood" class="test.springAop.MakeFood" />
<bean class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="makeFoodAdvice"></property>
<property name="pointcut" ref="makeFoodPointCut"></property>
</bean>
<aop:aspectj-autoproxy proxy-target-class="false"></aop:aspectj-autoproxy>
3.5 运行结果:
mathches--------MakeFoodPointCut
mathches--------MakeFood
matches(2 args)-------MakeFoodmakeMpdf
matches(2 args)-------MakeFoodmakeMpdf
before Method invoke
MakeFood-------------------mpdf
这里边,通过输出我们可以看到ClassFilter的matches方法是执行了多次,MethodMatcher的matches方法也执行也多次,是什么原因呢,下边会讲到。
我们先讲一下Spring的切入点框架的核心接口Pointcut,这个类的作用是将通知应用到特定的类和方法,将pointcut 分成两个部分有利于重用类和方法的匹配部分。
ClassFilter接口用来将切入点限定在一个给定的类集合中(ClassFilter接口决定了一个类是否符合通知的要求)。如果ClassFilter接口的matches()方法总是返回true,那么所有的目标类都将被匹配。实现这个接口的类决定了以参数传入进来的类是否应该被通知。实现这个接口的类一般根据类名决定,但这并不是必须的
MethodMatcher接口有三个方法,但是每个方法用在被代理对象生命周期的特定时刻。matches(Method m, Class targetClass)方法根据目标类和方法决定一个方法是否该被通知。因此可以静态地判断,所以可以在AOP代理被创建的时候调用一次这个方法。这个方法的结果最终决定了通知是否被织入。
如果matches(Method, Class)方法返回true,isRunime()被调用来决定MethodMatcher的类型。有两种类型:静态的和动态的。静态切入点的意思是通知总是被执行。如果一个切入点是静态的,isRuntime()方法应该返回false。动态切入点根据运行时方法的参数值决定通知是否应该被执行。如果切入点是动态的,isRuntime()方法应该返回true。和matches(Method, Class)方法一样,isRuntime()方法也是只在代理类被创建时调用一次。
如果一个切入点是静态的,matches(Method, Class, Object[])方法永远不会被调用,因为不需要根据参数运行时值来决定通知是否需要被应用。对于动态切入点,目标方法每次被调用的时候,matches(Method, Class, Object[])方法就要被调用。每次这个方法被调用的时候,就增加了运行时的负担,因此要尽可能的使用静态切入点。
matches(Method, Class) 方法被用来测试这个切入点是否匹配目标类的给定方法。这个测试可以在AOP代理创建的时候执行,避免在所有方法调用时都需要进行测试。如果2个参数的匹配方法对某个方法返回true,并且MethodMatcher的 isRuntime()也返回true,那么3个参数的匹配方法将在每次方法调用的时候被调用。这使切入点能够在目标通知被执行之前立即查看传递给方法调用的参数。
大部分MethodMatcher都是静态的,意味着isRuntime()方法返回false。这种情况下3个参数的匹配方法永远不会被调用。
通过这些理论知识的学习我们再来看上边的输出结果,mathches--------MakeFoodPointCut
mathches--------MakeFood
这个输出了两次,分别是对MakeFoodPointCut与MakeFood进行了判断,这个动作发生在类加载时,
matches(2 args)-------MakeFoodmakeMpdf 也是发生在类加载时,
而后边的输出
matches(2 args)-------MakeFoodmakeMpdf
before Method invoke
MakeFood-------------------mpdf
发生在方法调用这里,我们可能通过注释
/* A makeFood=(A)bf.getBean("makeFood");
makeFood.makeMpdf("mpdf");*/这个来获得验证。
3.6 理解Advisor
在配置文件中,我们通过DefaultPointcutAdvisor,这个将advice和pointcut组合在一起,这就相当于一个切面的功能。在Spring中,一个advisor就是一个aspect的完整的模块化表示。一般地,一个advisor包括一个通知和一个切入点。
3.7 Spring aop 相关概念:
Aspect(切面),切面就是你要实现的横切功能。
Jointpoint(连接点),连接点是应用程序执行过程中插入切面的地点(执行点)。这个地点可以是方法调用,异常抛出,或者甚至是要修改的字段。切面代码在这些地方插入你的应用流程中,添加新的行为。
Advice(通知),通知是切面的实际实现。它通知应用系统新的行为。在日志例子中,日志通知(logging advice)包含了实际实现日志功能的代码,例如向日志文件中写日志。通知在连接点被插入到应用程序。
PointCut(切入点),切入点定义了通知应该应用到哪些连接点。通知可以应用到AOP框架支持的任何连接点。
Introduction(引入),引入允许你为已经存在的类添加新方法和属性。
Target Object,目标对象,即被代理的对象。
AOP Proxy,AOP代理。代理是将通知应用到目标对象后创建的对象。AOP代理,AOP框架创建的对象,包含通知。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
Weaving(织入),织入是将切面应用到目标对象从而创建一个新的代理对象的过程。切面在指定的连接点被织入到目标对象中。分为静态织入(AspectJ)和动态织入(Spring AOP)。
3.8 Spring 中的通知类型
通知类型 |
接口 |
说明 |
Before |
org.springframework.aop.BeforeAdvice |
在目标方法被调用之前调用 |
After |
org.springframework.aop.AfterReturningAdvice |
在目标方法被调用之后调用 |
Around |
org.aopalliance.intercept.MethodInterceptor |
拦截对目标方法的调用 |
Throws |
org.springframework.aop.ThrowsAdvice |
当目标方法抛出异常时调用 |
Introdcution |
org.springframework.aop.IntroductionInterceptor |
引入整个类 |
相关推荐
现在让我们深入探讨JDK动态代理和Spring AOP的原理。 首先,JDK动态代理基于Java的反射机制,通过`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口来实现。Proxy类用于创建一个代理对象,...
在Java中,我们可以使用JDK的动态代理或者Spring AOP来实现代理模式。 JDK动态代理主要依赖于`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口。Proxy类是生成代理对象的工厂,而...
在Spring中,AOP主要通过两种动态代理技术实现:JDK动态代理和CGLIB动态代理。 首先,让我们详细了解一下JDK动态代理。JDK动态代理基于Java的接口实现,它适用于目标对象实现了至少一个接口的情况。在运行时,JDK...
Spring AOP 的底层实现技术 --- Jdk 动态代理原理 JDK 动态代理是 Spring AOP 的底层实现技术,允许开发者在运行期创建接口的代理实例。在 JDK 1.3 以后,JDK 动态代理技术提供了实现 AOP 的绝好底层技术。JDK 动态...
本篇将详细讲解Spring中的AOP实现,特别是JDK动态代理的应用。 首先,我们要了解什么是AOP(Aspect Oriented Programming,面向切面编程)。AOP是一种编程范式,旨在解决应用程序中分散的、横切关注点的问题,如...
本文将深入探讨Spring是如何通过JDK的动态代理来实现AOP的。 首先,我们要理解JDK动态代理的基本原理。在Java中,动态代理主要由`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口组成。...
本篇文章将探讨如何通过JDK动态代理实现Spring AOP的基础知识。 首先,我们要理解什么是JDK动态代理。在Java中,动态代理机制允许我们在运行时创建一个实现了特定接口的新类。这个新类的实例可以代理目标对象,执行...
Spring AOP提供了两种代理方式:JDK动态代理和CGLIB代理。JDK动态代理用于目标对象实现接口的情况,而CGLIB代理则用于目标对象没有实现接口的情况。Spring默认使用JDK动态代理,但如果目标对象没有实现接口,它会...
Spring 框架中 JDK 动态代理和 CGLIB 动态代理是 Spring AOP 中一个非常重要的知识点。Spring AOP 框架会根据实际情况选择使用 JDK 的动态代理还是 CGLIB 的动态代理。 JDK 动态代理是 Java 自带的动态代理机制,它...
Spring AOP支持不同的代理策略,包括JDK动态代理和CGLIB代理。如果被代理的类没有实现接口,Spring AOP会采用CGLIB来生成代理对象。CGLIB(Code Generation Library)是一个开源的代码生成库,它允许运行时在内存中...
Spring AOP允许我们通过代理来实现横切关注点,如日志、事务管理等,而JDK动态代理则是Spring AOP实现的一种方式。本文将深入探讨Spring如何利用JDK动态代理技术来实现这一功能,并通过实例解析其底层实现。 首先,...
Spring框架是AOP实现的一个典范,它提供了两种主要的动态代理方式:JDK动态代理和CGLib动态代理。 **JDK动态代理**: JDK动态代理基于Java的反射API实现,适用于接口代理。当目标对象实现了至少一个接口时,Spring...
Spring AOP默认使用JDK动态代理,但如果目标类没有实现接口,它会自动切换到CGLIB。在"通过Configuration文件实现AOP.docx"文档中,可能会详细讲述如何在Spring配置文件中配置AOP代理,包括如何选择使用JDK动态代理...
本文将深入探讨Spring AOP中的JDK动态代理机制。 首先,我们要理解什么是动态代理。动态代理是在运行时创建一个实现了一组给定接口的代理类,这个代理类可以拦截并处理方法调用,从而实现一些额外的功能。JDK的动态...
在 Spring 中,AOP 的实现主要依赖于代理模式,有两种代理方式:JDK 动态代理和 CGLIB 动态代理。 JDK 动态代理是基于接口的,它要求被代理的目标对象必须实现至少一个接口。Spring 使用 `java.lang.reflect.Proxy`...
3. **代理选择**:Spring默认优先使用JDK动态代理,如果无法满足条件(即目标类没有实现接口),则会自动切换到CGLIB。可以通过配置`proxyTargetClass="true"`来强制使用CGLIB。 4. **代理对象的获取**:通常,...
2. **动态代理**:Spring AOP 的默认代理方式是动态代理,它包括JDK动态代理和CGLIB代理。当目标对象实现了至少一个接口时,Spring将使用JDK的动态代理机制。JDK动态代理通过实现InvocationHandler接口,并在运行时...
Spring AOP(面向切面编程)也与此密切相关,它使用动态代理来实现切面的织入,从而提供横切关注点,如日志、事务管理等。现在,我们深入探讨Java动态代理和Spring AOP。 首先,让我们从Java动态代理开始。在Java中...