不才黄某 码农沉思录java
做者:几米憧憬 原文:https://note.youdao.com/share/?id=1b50d57ce1f7544da238d2051046ccc6&type=note#/
本文系读者投稿,已获做者原创受权。
若是你有好文章,能够戳这里投稿。spring
静态代理,工程师编辑代理类代码,实现代理模式;在编译期就生成了代理类。设计模式
基于 JDK 实现动态代理,经过jdk提供的工具方法Proxy.newProxyInstance动态构建全新的代理类(继承Proxy类,并持有InvocationHandler接口引用 )字节码文件并实例化对象返回。(jdk动态代理是由java内部的反射机制来实例化代理对象,并代理的调用委托类方法)数组
基于CGlib 动态代理模式 基于继承被代理类生成代理子类,不用实现接口。只须要被代理类是非final 类便可。(cglib动态代理底层是借助asm字节码技术缓存
基于 Aspectj 实现动态代理(修改目标类的字节,织入代理的字节,在程序编译的时候 插入动态代理的字节码,不会生成全新的Class )框架
静态代理是代理类在编译期间就建立好了,不是编译器生成的代理类,而是手动建立的类。在编译时就已经将接口,被代理类,代理类等肯定下来。软件设计中所指的代理通常是指静态代理,也就是在代码中显式指定的代理。maven
package org.vincent.proxy.staticproxy;/** * @author PengRong * @package org.vincent.proxy.staticproxy * @date 2018/12/15 - 17:12 * @ProjectName JavaAopLearning * @Description: 静态代理类接口, 委托类和代理类都须要实现的接口规范。 * 定义了一个猫科动物的两个行为接口,吃东西,奔跑。 * 做为代理类 和委托类之间的约束接口 */public interface Cat { public String eatFood(String foodName); public boolean running(); }
package org.vincent.proxy.staticproxy;/** * @author PengRong * @package org.vincent.proxy.staticproxy * @date 2018/12/15 - 17:15 * @ProjectName JavaAopLearning * @Description: 狮子 实现了猫科动物接口Cat, 并实现了具体的行为。做为委托类实现 */public class Lion implements Cat { private String name; private int runningSpeed; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getRunningSpeed() { return runningSpeed; } public void setRunningSpeed(int runningSpeed) { this.runningSpeed = runningSpeed; } public Lion() { } @Override public String eatFood(String foodName) { String eat = this.name + " Lion eat food. foodName = " + foodName; System.out.println(eat); return eat; } @Override public boolean running() { System.out.println(this.name + " Lion is running . Speed :" + this.runningSpeed); return false; } }
package org.vincent.proxy.staticproxy;/** * @author PengRong * @package org.vincent.proxy.staticproxy * @date 2018/12/15 - 17:19 * @ProjectName JavaAopLearning * @Description: 饲养员 实现Cat接口,做为静态代理类实现。代理狮子的行为。 * 代理类中能够新增一些其余行为,在实践中主要作的是参数校验的功能。 */public class FeederProxy implements Cat { private Cat cat; public FeederProxy(){} public FeederProxy(Cat cat) { if (cat instanceof Cat) { this.cat = cat; } } public void setCat(Cat cat) { if (cat instanceof Cat) { this.cat = cat; } } @Override public String eatFood(String foodName) { System.out.println("proxy Lion exec eatFood "); return cat.eatFood(foodName); } @Override public boolean running() { System.out.println("proxy Lion exec running."); return cat.running(); } }
package org.vincent.proxy;import org.vincent.proxy.staticproxy.Cat;import org.vincent.proxy.staticproxy.FeederProxy;import org.vincent.proxy.staticproxy.Lion;/** * @author PengRong * @package org.vincent.proxy * @date 2018/12/15 - 18:31 * @ProjectName JavaAopLearning * @Description: 静态代理类测试 */public class staticProxyTest { public static void main(String[] args) { Lion lion = new Lion(); lion.setName("狮子 小王"); lion.setRunningSpeed(100); /** * new 静态代理类,静态代理类在编译前已经建立好了,和动态代理的最大区别点 */ Cat proxy = new FeederProxy(lion); System.out.println(Thread.currentThread().getName()+" -- " + proxy.eatFood("水牛")); proxy.running(); } }
静态代理很好的诠释了代理设计模式,代理模式最主要的就是有一个公共接口(Cat),一个委托类(Lion),一个代理类(FeederProxy),代理类持有委托类的实例,代为执行具体类实例方法。 上面说到,代理模式就是在访问实际对象时引入必定程度的间接性,由于这种间接性,能够附加多种用途。这里的间接性就是指客户端不直接调用实际对象的方法,客户端依赖公共接口并使用代理类。 那么咱们在代理过程当中就能够加上一些其余用途。 就这个例子来讲在 eatFood方法调用中,代理类在调用具体实现类以前添加System.out.println("proxy Lion exec eatFood ");语句 就是添加间接性带来的收益。代理类存在的意义是为了增长一些公共的逻辑代码。ide
静态代理是代理类在代码运行前已经建立好,并生成class文件;动态代理类 是代理类在程序运行时建立的代理模式。函数
动态代理类的代理类并非在Java代码中定义的,而是在运行时根据咱们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优点在于能够很方便的对代理类的函数进行统一的处理,而不用修改每一个代理类中的方法。 想一想你有100个静态代理类,如今有一个需求,每一个代理类都须要新增一个处理逻辑,你须要打开100个代理类在每一个代理方法里面新增处理逻辑吗? 有或者代理类有5个方法,每一个方法都须要新增一个处理逻辑, 你须要在每一个方法都手动新增处理逻辑吗? 想一想就挺无趣的。动态代理类帮你一键搞定。工具
java的java.lang.reflect包下提供了Proxy类和一个 InvocationHandler 接口,这个类Proxy定义了生成JDK动态代理类的方法 getProxyClass(ClassLoader loader,Class<?>... interfaces)生成动态代理类,返回class实例表明一个class文件。能够保存该 class 文件查看jdk生成的代理类文件长什么样
该生成的动态代理类继承Proxy类,(重要特性) ,并实现公共接口。
InvocationHandler这个接口 是被动态代理类回调的接口,咱们全部须要增长的针对委托类的统一处理逻辑都增长到invoke 方法里面在调用委托类接口方法以前或以后 结束战斗。
package org.vincent.proxy.dynamicproxy;/** * Created by PengRong on 2018/12/25. * 建立Person 接口 用于定义 委托类和代理类之间的约束行为 */public interface Person{ /** * * @param name 人名 * @param dst 工做目的地 */ void goWorking(String name, String dst); /** * 获取名称 * @return */ String getName( ); /** * 设置名称 * @param name */ void setName(String name); }
具体实现类,等下被委托,被代理的类 SoftwareEngineer.java
package org.vincent.proxy.dynamicproxy;/** * Created by PengRong on 2018/12/25. * 动态代理委托类实现, 实现接口 Person。 被动态生成的代理类代理 */public class SoftwareEngineer implements Person{ public SoftwareEngineer(){} public SoftwareEngineer(String name){ this.name=name; } private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public void goWorking(String name, String dst) { System.out.println("name ="+name+" , 去 "+dst +" 工做"); } }
InvocationHandler 接口实现 PersonInvocationHandler.java
package org.vincent.proxy.dynamicproxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.util.Arrays;/** * Created by PengRong on 2018/12/25. * PersonInvocationHandler 类 实现InvocationHandler接口,这个类中持有一个被代理对象(委托类)的实例target。该类别JDK Proxy类回调 * InvocationHandler 接口中有一个invoke方法,当一个代理实例的方法被调用时,代理方法将被编码并分发到 InvocationHandler接口的invoke方法执行。 */public class PersonInvocationHandler<T> implements InvocationHandler { /** * 被代理对象引用,invoke 方法里面method 须要使用这个 被代理对象 */ T target; public PersonInvocationHandler(T target) { this.target = target; } /** * 在 * @param proxy 表明动态生成的 动态代理 对象实例 * @param method 表明被调用委托类的接口方法,和生成的代理类实例调用的接口方法是一致的,它对应的Method 实例 * @param args 表明调用接口方法对应的Object参数数组,若是接口是无参,则为null; 对于原始数据类型返回的他的包装类型。 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { /** * 在转调具体目标对象以前,能够执行一些功能处理 */ System.out.println("被动态代理类回调执行, 代理类 proxyClass ="+proxy.getClass()+" 方法名: " + method.getName() + "方法. 方法返回类型:"+method.getReturnType() +" 接口方法入参数组: "+(args ==null ? "null" : Arrays.toString(args))); /** * 代理过程当中插入监测方法,计算该方法耗时 */ MonitorUtil.start(); Thread.sleep(1); /** 调用呗代理对象的真实方法,*/ Object result = method.invoke(target, args); MonitorUtil.finish(method.getName()); return result; } }
PersonInvocationHandler invoke 方法中添加的公共代码,这里简单以统计方法执行时间为逻辑
package org.vincent.proxy.dynamicproxy;/** * Created by PengRong on 2018/12/25. * 方法用时监控类 */public class MonitorUtil { private static ThreadLocal<Long> tl = new ThreadLocal<>(); public static void start() { tl.set(System.currentTimeMillis()); } /** * 结束时打印耗时 * @param methodName 方法名 */ public static void finish(String methodName) { long finishTime = System.currentTimeMillis(); System.out.println(methodName + "方法执行耗时" + (finishTime - tl.get()) + "ms"); } }
最后的是 怎么建立代理类
package org.vincent.proxy.jdkdynamicProxy;import org.vincent.proxy.dynamicproxy.Person;import org.vincent.proxy.dynamicproxy.PersonInvocationHandler;import org.vincent.proxy.dynamicproxy.SoftwareEngineer;import sun.misc.ProxyGenerator;import java.io.FileOutputStream;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;import java.nio.file.Path;import java.nio.file.Paths;import java.util.Arrays;import java.util.Properties;/** * 动态代理类测试 * Created by PengRong on 2018/12/25. */public class JdkDynamicProxyTest { public static void main(String[] args) throws Exception { // 打开保存JDK动态代理生成的类文件 saveGeneratedJdkProxyFiles(); /** * 第一种方法: 经过 Proxy.newProxyInstance 方法 获取代理对象 */ System.out.println("-------------------第一种建立代理类方法--------------"); //建立一个实例对象,这个对象是被代理的对象,委托类 Person person = new SoftwareEngineer("Vincent"); //建立一个与代理类相关联的InvocationHandler,每个代理类都有一个关联的 InvocationHandler,并将代理类引用传递进去 InvocationHandler Handler = new PersonInvocationHandler<>(person); //建立一个 代理对象 personProxy 来代理 person,建立的代理对象的每一个执行方法都会被替换执行Invocation接口中的invoke方法 Person personProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, Handler); /** 代理类信息 */ System.out.println("package = " + personProxy.getClass().getPackage() + " SimpleName = " + personProxy.getClass().getSimpleName() + " name =" + personProxy.getClass().getName() + " CanonicalName = " + "" + personProxy.getClass().getCanonicalName() + " 实现的接口 Interfaces = " + Arrays.toString(personProxy.getClass().getInterfaces()) + " superClass = " + personProxy.getClass().getSuperclass() + " methods =" + Arrays.toString(personProxy.getClass().getMethods())); // 经过 代理类 执行 委托类的代码逻辑 personProxy.goWorking(personProxy.getName(), "深圳"); System.out.println("-------------------第二种建立代理类方法--------------"); /** * 动态代理对象步骤 * 一、 建立一个与代理对象相关联的 InvocationHandler,以及真实的委托类实例 * 二、Proxy类的getProxyClass静态方法生成一个动态代理类stuProxyClass,该类继承Proxy类,实现 Person.java接口;JDK动态代理的特色是代理类必须继承Proxy类 * 三、经过代理类 proxyClass 得到他的带InvocationHandler 接口的构造函数 ProxyConstructor * 四、经过 构造函数实例 ProxyConstructor 实例化一个代理对象,并将 InvocationHandler 接口实例传递给代理类。 */ // 一、建立 InvocationHandler 实例并设置代理的目标类对象 Person persontwo = new SoftwareEngineer("Vincent"); InvocationHandler Handlertwo = new PersonInvocationHandler<>(persontwo); // 2 建立代理类,是一个字节码文件, 把 proxyClass 保存起来就能看到 他继承Proxy 类,实现Person接口 Class<?> proxyClass = Proxy.getProxyClass(Person.class.getClassLoader(), new Class<?>[]{Person.class}); /** 代理类信息 */ System.out.println("package = " + proxyClass.getPackage() + " SimpleName = " + proxyClass.getSimpleName() + " name =" + proxyClass.getName() + " CanonicalName = " + "" + proxyClass.getCanonicalName() + " 实现的接口 Interfaces = " + Arrays.toString(proxyClass.getInterfaces()) + " superClass = " + proxyClass.getSuperclass() + " methods =" + Arrays.toString(proxyClass.getMethods())); // 三、 经过 proxyClass 得到 一个带有InvocationHandler参数的构造器constructor Constructor<?> ProxyConstructor = proxyClass.getConstructor(InvocationHandler.class); // 四、经过构造器建立一个 动态代理类 实例 Person stuProxy = (Person) ProxyConstructor.newInstance(Handlertwo); /** 检测生成的类是不是代理类 */ System.out.println("stuProxy isProxy "+Proxy.isProxyClass(stuProxy.getClass())); /** 获取 代理类关联的 InvocationHandler 是哪一个*/ InvocationHandler handlerObject = Proxy.getInvocationHandler(stuProxy); System.out.println(handlerObject.getClass().getName()); stuProxy.goWorking(stuProxy.getName(), "广州"); // 保存代理類 saveClass("$PersonProxy0", proxyClass.getInterfaces(), "D:/123/"); } /** * 生成代理类 class 并保持到文件中 * * @param className 生成的代理类名称 * @param interfaces 代理类须要实现的接口 * @param pathdir 代理类保存的目录路径,以目录分隔符结尾 */ public static void saveClass(String className, Class<?>[] interfaces, String pathdir) { /** * 第一个参数是 代理类 名 。 * 第二个参数是 代理类须要实现的接口 */ byte[] classFile = ProxyGenerator.generateProxyClass(className, interfaces); /** * 若是目录不存在就新建全部子目录 */ Path path1 = Paths.get(pathdir); if (!path1.toFile().exists()){ path1.toFile().mkdirs(); } String path = pathdir + className + ".class"; try (FileOutputStream fos = new FileOutputStream(path)) { fos.write(classFile); fos.flush(); System.out.println("代理类class文件写入成功"); } catch (Exception e) { System.out.println("写文件错误"); } } /** * 设置保存Java动态代理生成的类文件。 * * @throws Exception */ public static void saveGeneratedJdkProxyFiles() throws Exception { Field field = System.class.getDeclaredField("props"); field.setAccessible(true); Properties props = (Properties) field.get(null); props.put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); } }
解析JDK生成的动态代理类
saveGeneratedJdkProxyFiles方法 打开了存储jdk生成的动态代理类 以 接口方法 goWorking 为例讲解
//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//package com.sun.proxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.lang.reflect.UndeclaredThrowableException;import org.vincent.proxy.dynamicproxy.Person;public final class $Proxy0 extends Proxy implements Person { private static Method m1; private static Method m4; private static Method m3; private static Method m2; private static Method m5; private static Method m0; public $Proxy0(InvocationHandler var1) throws { super(var1); } public final boolean equals(Object var1) throws { try { return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue(); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final void setName(String var1) throws { try { super.h.invoke(this, m4, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final String getName() throws { try { return (String)super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } /** * 对接口 goWorking 的调用 转变成 super.h.invoke(this, m5, new Object[]{var1, var2}); 调用。 * h 就是Proxy.java类的一个 InvocationHandler 接口 属性, * 咱们在建立 动态代理类实例时候都必须 传一个 InvocationHandler 接口的实例过去。 这里就是刚才咱们定义的 PersonInvocationHandler 。 * 回到事后是否是就回到了 PersonInvocationHandler.invoke方法里面,因此 PersonInvocationHandler 是咱们生成的动态代理类的拦截器,拦截全部方法调用。 */ public final void goWorking(String var1, String var2) throws { try { super.h.invoke(this, m5, new Object[]{var1, var2}); } catch (RuntimeException | Error var4) { throw var4; } catch (Throwable var5) { throw new UndeclaredThrowableException(var5); } } public final int hashCode() throws { try { return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue(); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }/** * 静态代码块,根据动态代理实现的公共接口类接口方法 获取到全部接口方法 的 Method 实例*/ static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")}); m4 = Class.forName("org.vincent.proxy.dynamicproxy.Person").getMethod("setName", new Class[]{Class.forName("java.lang.String")}); m3 = Class.forName("org.vincent.proxy.dynamicproxy.Person").getMethod("getName", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m5 = Class.forName("org.vincent.proxy.dynamicproxy.Person").getMethod("goWorking", new Class[]{Class.forName("java.lang.String"), Class.forName("java.lang.String")}); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
Jdk为咱们的生成了一个叫$Proxy0(这个名字后面的0是编号,有多个代理类会一次递增)的代理类,这个类文件时默认不会保存在文件,放在内存中的,咱们在建立代理对象时,就是经过反射得到这个类的构造方法,而后建立代理对象实例。经过对这个生成的代理类源码的查看,咱们很容易能看出,动态代理实现的具体过程。
咱们能够对 InvocationHandler 看作一个中介类,中介类持有一个被代理对象,被Proxy类回调。在invoke方法中调用了被代理对象的相应方法。经过聚合方式持有被代理对象的引用,把客户端对invoke的调用最终都转为对被代理对象的调用。
客户端代码经过代理类引用调用接口方法时,经过代理类关联的中介类对象引用来调用中介类对象的invoke方法,从而达到代理执行被代理对象的方法。也就是说,动态代理Proxy类提供了模板实现,对外提供扩展点,外部经过实现InvocationHandler接口将被代理类归入JDK代理类Proxy。
一、经过实现InvocationHandler接口建立本身的调用处理器 IvocationHandler handler = new InvocationHandlerImpl(...);
二、经过为Proxy类指定ClassLoader对象和一组interface代理类须要实现的接口,建立动态代理类类文件,默认JDK并不会保存这个文件到文件中;能够保存起来观察生成的代理类结构Class clazz = Proxy.getProxyClass(classLoader,new Class[]{...});
三、经过上面新建的代理clazz的反射机制获取动态代理类的一个构造函数,其构造函数入参类型是调用处理器接口(IvocationHandler)类型 Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
四、经过构造函数实例建立代理类实例,此时需将调用处理器对象做为参数被传入 Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler)); 为了简化对象建立过程,Proxy类中的newInstance工具方法封装了2~4,只需两步便可完成代理对象的建立。
JDK动态代理的代理类字节码在建立时,须要实现业务实现类所实现的接口做为参数。若是业务实现类是没有实现接口而是直接定义业务方法的话,就没法使用JDK动态代理了。(JDK动态代理重要特色是代理接口) 而且,若是业务实现类中新增了接口中没有的方法,这些方法是没法被代理的(由于没法被调用)。
动态代理只能对接口产生代理,不能对类产生代理
Cglib是针对类来实现代理的,他的原理是对代理的目标类生成一个子类,并覆盖其中方法实现加强,由于底层是基于建立被代理类的一个子类,因此它避免了JDK动态代理类的缺陷。
但由于采用的是继承,因此不能对final修饰的类进行代理。final修饰的类不可继承。
cglib 是基于asm 字节修改技术。导入 cglib 会间接导入 asm, ant, ant-launcher 三个jar 包。
<!-- cglib 动态代理依赖 begin --> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.5</version></dependency><!-- cglib 动态代理依赖 stop -->
cglib是针对类来实现代理的,原理是对指定的业务类生成他的一个子类,并覆盖其中的业务方法来实现代理。由于采用的是继承,因此不能对final修饰的类进行代理。
package org.vincent.proxy.cglibproxy;/** * @Package: org.vincent.proxy.cglibproxy <br/> * @Description: Cglib 代理模式中 被代理的委托类 <br/> * @author: lenovo <br/> * @Company: PLCC <br/> * @Copyright: Copyright (c) 2019 <br/> * @Version: 1.0 <br/> * @Modified By: <br/> * @Created by lenovo on 2018/12/26-17:55 <br/> */public class Dog { public String call() { System.out.println("wang wang wang"); return "Dog .."; } }
package org.vincent.proxy.cglibproxy;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;/** * @Package: org.vincent.proxy.cglibproxy <br/> * @Description: Cglib 方法拦截器,不用依赖被代理业务类的引用。 <br/> * @author: lenovo <br/> * @Company: PLCC <br/> * @Copyright: Copyright (c) 2019 <br/> * @Version: 1.0 <br/> * @Modified By: <br/> * @Created by lenovo on 2018/12/26-17:56 <br/> */public class CglibMethodInterceptor implements MethodInterceptor { /** * 用于生成 Cglib 动态代理类工具方法 * @param target 表明须要 被代理的 委托类的 Class 对象 * @return */ public Object CglibProxyGeneratory(Class target) { /** 建立cglib 代理类 start */ // 建立增强器,用来建立动态代理类 Enhancer enhancer = new Enhancer(); // 为代理类指定须要代理的类,也便是父类 enhancer.setSuperclass(target); // 设置方法拦截器回调引用,对于代理类上全部方法的调用,都会调用CallBack,而Callback则须要实现intercept() 方法进行拦截 enhancer.setCallback(this); // 获取动态代理类对象并返回 return enhancer.create(); /** 建立cglib 代理类 end */ } /** * 功能主要是在调用业务类方法以前 以后添加统计时间的方法逻辑. * intercept 由于 具备 MethodProxy proxy 参数的缘由 再也不须要代理类的引用对象了,直接经过proxy 对象访问被代理对象的方法(这种方式更快)。 * 固然 也能够经过反射机制,经过 method 引用实例 Object result = method.invoke(target, args); 形式反射调用被代理类方法, * target 实例表明被代理类对象引用, 初始化 CglibMethodInterceptor 时候被赋值 。可是Cglib不推荐使用这种方式 * @param obj 表明Cglib 生成的动态代理类 对象自己 * @param method 代理类中被拦截的接口方法 Method 实例 * @param args 接口方法参数 * @param proxy 用于调用父类真正的业务类方法。能够直接调用被代理类接口方法 * @return * @throws Throwable */ @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("before"); MonitorUtil.start(); Object result = proxy.invokeSuper(obj, args); //Object result = method.invoke(target, args); System.out.println("after"); MonitorUtil.finish(method.getName()); return result; } }
package org.vincent.proxy.cglibproxy;/** * Created by PengRong on 2018/12/25. * 方法用时监控类,做为一个切面 ,具备两个方法 */public class MonitorUtil { private static ThreadLocal<Long> tl = new ThreadLocal<>(); public static void start() { tl.set(System.currentTimeMillis()); } /** * 结束时打印耗时 * @param methodName 方法名 */ public static void finish(String methodName) { long finishTime = System.currentTimeMillis(); System.out.println(methodName + "方法执行耗时" + (finishTime - tl.get()) + "ms"); } }
package org.vincent.proxy.cglibproxy;import net.sf.cglib.core.DebuggingClassWriter;import net.sf.cglib.proxy.Enhancer;import org.junit.Test;import java.lang.reflect.Field;import java.util.Properties;/** * @Package: org.vincent.proxy.cglibproxy <br/> * @Description: TODO <br/> * @author: lenovo <br/> * @Company: PLCC <br/> * @Copyright: Copyright (c) 2019 <br/> * @Version: 1.0 <br/> * @Modified By: <br/> * @Created by lenovo on 2018/12/26-18:05 <br/> */public class CglibTest { @Test public void testCglib() throws Exception { System.out.println(System.getProperty("user.dir")); /** 开启 保存cglib生成的动态代理类类文件*/ saveGeneratedCGlibProxyFiles(System.getProperty("user.dir")); /** 第一种方法: 建立cglib 代理类 start */ // 建立增强器,用来建立动态代理类 Enhancer enhancer = new Enhancer(); // 为代理类指定须要代理的类,也便是父类 enhancer.setSuperclass(Dog.class); // new 一个新的方法拦截器 CglibMethodInterceptor cglibMethodInterceptor = new CglibMethodInterceptor(); // 设置方法拦截器回调引用,对于代理类上全部方法的调用,都会调用CallBack,而Callback则须要实现intercept() 方法进行拦截 enhancer.setCallback(cglibMethodInterceptor); // 获取动态代理类对象并返回 Dog dog = (Dog) enhancer.create(); /** 建立cglib 代理类 end */ System.out.println(dog.call()); // 对于上面这几步,能够新增一个工具方法 放置在 CglibMethodInterceptor 里面;也就有了第二种方法 // new 一个新的方法拦截器,该拦截器还顺带一个用于建立代理类的工具方法。看起来简单不少 cglibMethodInterceptor = new CglibMethodInterceptor(); dog = (Dog) cglibMethodInterceptor.CglibProxyGeneratory(Dog.class); System.out.println(dog.call()); } /** * 设置保存Cglib代理生成的类文件。 * * @throws Exception */ public void saveGeneratedCGlibProxyFiles(String dir) throws Exception { Field field = System.class.getDeclaredField("props"); field.setAccessible(true); Properties props = (Properties) field.get(null); System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, dir);//dir为保存文件路径 props.put("net.sf.cglib.core.DebuggingClassWriter.traceEnabled", "true"); } }
静态代理是经过在代码中显式编码定义一个业务实现类的代理类,在代理类中对同名的业务方法进行包装,用户经过代理类调用被包装过的业务方法;
JDK动态代理是经过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;
CGlib动态代理是经过继承业务类,生成的动态代理类是业务类的子类,经过重写业务方法进行代理;
静态代理在编译时产生class字节码文件,能够直接使用,效率高。动态代理必须实现InvocationHandler接口,经过invoke调用被委托类接口方法是经过反射方式,比较消耗系统性能,但能够减小代理类的数量,使用更灵活。 cglib代理无需实现接口,经过生成类字节码实现代理,比反射稍快,不存在性能问题,但cglib会继承目标对象,须要重写方法,因此目标对象不能为final类。
AOP的源码中用到了两种动态代理来实现拦截切入功能:jdk动态代理和cglib动态代理。两种方法同时存在,各有优劣。 jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。 总的来讲,反射机制在生成类的过程当中比较高效,执行时候经过反射调用委托类接口方法比较慢;而asm在生成类以后的相关代理类执行过程当中比较高效(能够经过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。 还有一点必须注意:jdk动态代理的应用前提,必须是委托类基于统一的接口。若是没有上述前提,jdk动态代理不能应用。 由此能够看出,jdk动态代理有必定的局限性,cglib这种第三方类库实现的动态代理应用更加普遍,且在效率上更有优点。
实现AOP关键特色是定义好两个角色 切点 和 切面 。 代理模式中被代理类 委托类处于切点角色,须要添加的其余好比 校验逻辑,事务,审计逻辑 属于非功能实现逻辑经过 切面类定义的方法插入进去。
定义切面接口,完成将通用公共方法注入到被代理类接口调用处理中
package org.vincent.aop.dynamicproxy;/** * @Package: org.vincent.aop.dynamicproxy <br/> * @Description: 定义切面接口,切面接口定义了两个切面方法,分别在切点接口方法执行前和执行后执行 <br/> * @author: lenovo <br/> * @Company: PLCC <br/> * @Copyright: Copyright (c) 2019 <br/> * @Version: 1.0 <br/> * @Modified By: <br/> * @Created by lenovo on 2018/12/26 <br/> */public interface IAspect { /** * 在切点接口方法执行以前执行 * @param args 切点参数列表 * @return */ boolean startTransaction(Object... args); /** * 在切点接口方法执行以后执行 */ void endTrasaction(); }
定义切面实现类
package org.vincent.aop.dynamicproxy;import java.util.Objects;/** * @Package: org.vincent.aop.dynamicproxy <br/> * @Description: 改类做为AOP 模型中切面角色类, 实现切面接口,切面接口定义了两个切面方法,分别在切点接口方法执行前和执行后执行 。 <br/> * @author: lenovo <br/> * @Company: PLCC <br/> * @Copyright: Copyright (c) 2019 <br/> * @Version: 1.0 <br/> * @Modified By: <br/> * @Created by lenovo on 2018/12/26 <br/> */public class CustomAspect implements IAspect { /** * 对参数 作判空处理 * @param args 切点参数列表 * @return */ @Override public boolean startTransaction(Object... args) { Objects.nonNull(args); boolean result = true; for (Object temp :args) { if (Objects.isNull(temp)){ result =false; break; } } return result; } public void endTrasaction() { System.out.println("I get datasource here and end transaction"); } }
定义切点角色接口 由于是基于JDK实现的Aop ,因此委托类须要基于接口实现。
package org.vincent.aop.dynamicproxy;/** * @Package: org.vincent.aop.dynamicproxy <br/> * @Description: AOP基于动态代理 实现 <br/> * @author: lenovo <br/> * @Company: PLCC <br/> * @Copyright: Copyright (c) 2019 <br/> * @Version: 1.0 <br/> * @Modified By: <br/> * @Created by lenovo on 2018/12/26 <br/> */public interface IUserService { void saveUser(String username, String password) throws Exception; }
委托类实现
package org.vincent.aop.dynamicproxy;/** * @Package: org.vincent.aop.dynamicproxy <br/> * @Description: UserService接口实现类UserServiceImpl 该类 做为AOP中切点角色,切面定义的方法插入到切点的接口方法 执行前和执行后执行。 <br/> * @author: lenovo <br/> * @Company: PLCC <br/> * @Copyright: Copyright (c) 2019 <br/> * @Version: 1.0 <br/> * @Modified By: <br/> * @Created by lenovo on 2018/12/26 <br/> */public class UserServiceImpl implements IUserService{ @Override public void saveUser(String username, String password) throws Exception { System.out.println("save user[username=" + username + ",password=" + password + "]"); } }
JDK动态代理生成器工具类
能够看到 generatorJDKProxy 方法入参只有两个参数 一个切点接口引用,一个切面接口引用;在InvocationHandler 内部类中能够完整看到切面类方法是怎么影响切点代码执行逻辑的。
package org.vincent.aop.dynamicproxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.Arrays;/** * @Package: org.vincent.aop.dynamicproxy <br/> * @Description: JDK动态代理类生成器 <br/> * @author: lenovo <br/> * @Company: PLCC <br/> * @Copyright: Copyright (c) 2019 <br/> * @Version: 1.0 <br/> * @Modified By: <br/> * @Created by lenovo on 2018/12/26-16:48 <br/> */public class JDKDynamicProxyGenerator { /** * @param targetPoint 须要被代理的委托类对象 * @param aspect 切面对象,该对象方法将在切点方法以前或以后执行 * @return */ public static Object generatorJDKProxy(IUserService targetPoint, final IAspect aspect) { return Proxy.newProxyInstance( /** * 委托类使用的类加载器 */ targetPoint.getClass().getClassLoader(), /** * 委托类实现的接口 */ targetPoint.getClass().getInterfaces(), /** * 生成的动态代理类关联的 执行处理器,代理咱们的业务逻辑被生成的动态代理类回调 * 具体逻辑代码执行,返回值为方法执行结果, 在aop模型中,委托类的接口方法称为切点。 */ new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 执行切面方法,对入参进行校验 boolean prepareAction = aspect.startTransaction(args); if (prepareAction){ // 具体逻辑代码执行,返回值为方法执行结果 Object result = method.invoke(targetPoint, args); aspect.endTrasaction(); return result; }else { throw new RuntimeException("args: "+ Arrays.toString(args)+"不能为null "); } } }); } }
测试类
package org.vincent.aop;import org.junit.Test;import org.vincent.aop.dynamicproxy.CustomAspect;import org.vincent.aop.dynamicproxy.IUserService;import org.vincent.aop.dynamicproxy.JDKDynamicProxyGenerator;import org.vincent.aop.dynamicproxy.UserServiceImpl;/** * @Package: org.vincent <br/> * @Description: 基于动态代理类AOP测试案例 <br/> * @author: lenovo <br/> * @Company: PLCC <br/> * @Copyright: Copyright (c) 2019 <br/> * @Version: 1.0 <br/> * @Modified By: <br/> * @Created by lenovo on 2018/12/26-16:56 <br/> */public class testAopJDKProxy { @Test public void testJDKProxy() throws Exception { System.out.println("无代理前 调用方法 userService.saveUser 输出......"); IUserService userService = new UserServiceImpl(); userService.saveUser("zby", "1234567890"); System.out.println("有代理后AOP 是怎么样的? Proxy......"); IUserService proxyUserService = (IUserService) JDKDynamicProxyGenerator.generatorJDKProxy(userService, new CustomAspect()); proxyUserService.saveUser("zby", "1234567890"); /** 制造异常,两个入参都是null */ proxyUserService.saveUser(null, null); } }
定义切面接口
package org.vincent.aop.cglib;/** * @Package: org.vincent.aop.dynamicproxy <br/> * @Description: 定义切面接口,切面接口定义了两个切面方法,分别在切点接口方法执行前和执行后执行 <br/> * @author: lenovo <br/> * @Company: PLCC <br/> * @Copyright: Copyright (c) 2019 <br/> * @Version: 1.0 <br/> * @Modified By: <br/> * @Created by lenovo on 2018/12/26 <br/> */public interface IAspect { /** * 在切点接口方法执行以前执行 */ void startTransaction(); /** * 在切点接口方法执行以后执行 */ void endTrasaction(); }
切面实现
package org.vincent.aop.cglib;/** * @Package: org.vincent.aop.dynamicproxy <br/> * @Description: 改类做为AOP 模型中切面角色类, 实现切面接口,切面接口定义了两个切面方法,分别在切点接口方法执行前和执行后执行 。 <br/> * @author: lenovo <br/> * @Company: PLCC <br/> * @Copyright: Copyright (c) 2019 <br/> * @Version: 1.0 <br/> * @Modified By: <br/> * @Created by lenovo on 2018/12/26 <br/> */public class CustomAspect implements IAspect { @Override public void startTransaction() { System.out.println("cglib. I get datasource here and start transaction"); } public void endTrasaction() { System.out.println("cglib I get datasource here and end transaction"); } }
Cglib 是基于类实现的动态代理即业务类只须要实现类便可,不用强制必须实现某个接口为了突出这个优势这里没有实现接口
package org.vincent.aop.cglib;/** * @Package: org.vincent.aop.dynamicproxy <br/> * @Description: 业务实现类UserServiceImpl 该类 做为AOP中切点角色,切面定义的方法插入到切点的接口方法 执行前和执行后执行。 <br/> * @author: lenovo <br/> * @Company: PLCC <br/> * @Copyright: Copyright (c) 2019 <br/> * @Version: 1.0 <br/> * @Modified By: <br/> * @Created by lenovo on 2018/12/26 <br/> */public class UserServiceImpl { public void saveUser(String username, String password) { System.out.println("cglib save user[username=" + username + ",password=" + password + "]"); } }
Cglib 动态代理生成器工具类
package org.vincent.aop.cglib;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;/** * @Package: org.vincent.aop.cglib <br/> * @Description: 基于Cglib代理类生成器工具类 <br/> * @author: lenovo <br/> * @Company: PLCC <br/> * @Copyright: Copyright (c) 2019 <br/> * @Version: 1.0 <br/> * @Modified By: <br/> * @Created by lenovo on 2018/12/26-17:04 <br/> */public class CglibProxyGenerator { /** * @param target 须要被代理的委托类对象,Cglib须要继承该类生成子类 * @param aspect 切面对象,改对象方法将在切点方法以前或以后执行 * @return */ public static Object generatorCglibProxy(final Object target, final IAspect aspect){ //3.1 new Enhancer Enhancer enhancer = new Enhancer(); //3.2 设置须要代理的父类 enhancer.setSuperclass(target.getClass()); //3.3 设置回调 enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { // 执行切面方法 aspect.startTransaction(); // 具体逻辑代码执行,返回值为方法执行结果 Object result = methodProxy.invokeSuper(proxy, args); // 执行切面方法 aspect.endTrasaction(); // 返回方法执行结果 return result; } }); // 3.4 建立代理对象 return enhancer.create(); } }
测试类
package org.vincent.aop;import org.junit.Test;import org.vincent.aop.cglib.CglibProxyGenerator;import org.vincent.aop.cglib.CustomAspect;import org.vincent.aop.cglib.UserServiceImpl;/** * @Package: org.vincent <br/> * @Description: 基于动态代理类AOP测试案例 <br/> * @author: lenovo <br/> * @Company: PLCC <br/> * @Copyright: Copyright (c) 2019 <br/> * @Version: 1.0 <br/> * @Modified By: <br/> * @Created by lenovo on 2018/12/26-16:56 <br/> */public class testAopCglibKProxy { @Test public void testCglibProxy() { System.out.println("before Proxy......"); UserServiceImpl userService = new UserServiceImpl(); userService.saveUser("zby", "1234567890"); System.out.println("引入Cglib Proxy代理库 后......"); UserServiceImpl proxyUserService = (UserServiceImpl) CglibProxyGenerator.generatorCglibProxy(userService, new CustomAspect()); proxyUserService.saveUser("zby", "1234567890"); } }
AspectJ 实现 AOP 效果
AOP 实现的关键就在于 AOP 框架自动建立的 AOP 代理,AOP 代理则可分为静态代理和动态代理两大类:
原生 AspectJ 不依赖Spring案例, 基于 AspectJ 的编译时加强进行 AOP 它是在编译期修改字节码,加强功能;并不会生成新的代理类字节码。
<!-- AspectJ begin--><dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.9.2</version></dependency><dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.2</version></dependency><!-- AspectJ stop-->