中国自适应网站建设,怎么查出这个网站是谁做的,网络优化策划书,php做的网站怎么打开目录1 代理模式定义2 代理模式的应用场景3 代理模式的通用写法4 从静态代理到动态代理5 静态模式在业务中的应用6 动态代理在业务中的应用7 手写JDK动态代理实现原理7.1 JDK动态代理的实现原理7.2 CGLib动态代理容易踩的坑8 CGLib代理调用API及原理分析9 CGLib和JDK动态代理对比…
目录1 代理模式定义2 代理模式的应用场景3 代理模式的通用写法4 从静态代理到动态代理5 静态模式在业务中的应用6 动态代理在业务中的应用7 手写JDK动态代理实现原理7.1 JDK动态代理的实现原理7.2 CGLib动态代理容易踩的坑8 CGLib代理调用API及原理分析9 CGLib和JDK动态代理对比10 代理模式与Spring生态10 静态代理和动态代理的本质区别11 代理模式的优缺点12 Spring中的代理选择原则1 代理模式定义
都知道 SpringAOP 是用代理模式实现到底是怎么实现的我们来一探究竟并且自己仿真手写 还原部分细节。
代理模式ProxyPattern是指为其他对象提供一种代理以控制对这个对象的访问属于结构型模式。
在某些情况下一个对象不适合或者不能直接引用另一个对象而代理对象可以在客户端和目标 对象之间起到中介的作用。 官方原文Provide a surrogate or placeholder for another object to control access to it. 首先来看代理模式的通用UML类图 代理模式一般包含三种角色
抽象主题角色Subject抽象主题类的主要职责是声明真实主题与代理的共同接口方法该类可以是接口也可以是抽象类
真实主题角色RealSubject该类也被称为被代理类该类定义了代理所表示的真实对象是负责执行系统真正的逻辑业务对象
代理主题角色Proxy也被称为代理类其内部持有 RealSubject 的引用因此具备完全的对 RealSubject的代理权。客户端调用代理对象的方法同时也调用被代理对象的方法但是会在代理对 象前后增加一些处理代码。
在代码中一般代理会被理解为代码增强实际上就是在原代码逻辑前后增加一些代码逻辑而使调用者无感知。代理模式属于结构型模式分为静态代理和动态代理。
2 代理模式的应用场景
生活中的租房中介、售票黄牛、婚介、经纪人、快递、事务代理、非侵入式日志监听等都是代理 模式的实际体现。当无法或不想直接引用某个对象或访问某个对象存在困难时可以通过也给代理对象 来间接访问。使用代理模式主要有两个目的一是保护目标对象二是增强目标对象。
3 代理模式的通用写法
下面是代理模式的通用代码展示。
首先创建代理主题角色ISubject类
public interface ISubject {void request();
}创建真实主题角色RealSubject类
public class RealSubject implements ISubject {public void request() {System.out.println(real service is called.);}
}创建代理主题角色Proxy类
public class Proxy implements ISubject {private ISubject subject;public Proxy(ISubject subject){this.subject subject;}public void request() {before();subject.request();after();}public void before(){System.out.println(called before request().);}public void after(){System.out.println(called after request().);}
}客户端调用代码
public class Client {public static void main(String[] args) {Proxy proxy new Proxy(new RealSubject());proxy.request();}
}运行结果
called before request().
real service is called.
called after request().4 从静态代理到动态代理
举个例子有些人到了适婚年龄其父母总是迫不及待地希望早点抱孙子。而现在在各种压力之下 很多人都选择晚婚晚育。于是着急的父母就开始到处为自己的子女相亲比子女自己还着急。下面来看代码实现。
静态代理
创建顶层接口IPerson的代码如下
public interface IPerson {void findLove();
}儿子张三要找对象实现ZhangSan类
public class ZhangSan implements IPerson {public void findLove() {System.out.println(儿子要求肤白貌美大长腿);}
}父亲张老三要帮儿子张三相亲实现Father类
public class ZhangLaosan implements IPerson {private ZhangSan zhangsan;public ZhangLaosan(ZhangSan zhangsan) {this.zhangsan zhangsan;}public void findLove() {// beforeSystem.out.println(张老三开始物色);zhangsan.findLove();// afterSystem.out.println(开始交往);}
}来看测试代码
public class Test {public static void main(String[] args) {ZhangLaosan zhangLaosan new ZhangLaosan(new ZhangSan());zhangLaosan.findLove();}
}运行结果:
张老三开始物色
儿子要求肤白貌美大长腿
开始交往但上面的场景有个弊端就是自己父亲只会给自己的子女去物色对象别人家的孩子是不会管的。
但社会上这项业务发展成了一个产业出现了媒婆、婚介所等还有各种各样的定制套餐。如果还使用静态代理成本就太高了需要一个更加通用的解决方案满足任何单身人士找对象的需求。
这就是由静态代理升级到了动态代理。
采用动态代理基本上只要是人IPerson就可以提供相亲服务。
动态代理的底层实现一般不用我们自己亲自去实现已经有很多现成的API。
在Java生态中目前最普遍使用的是JDK自带的代理和Cglib提供的类库。
下面我们首先基于JDK的动态代理支持如来升级一下代码。
首先创建媒婆婚介所类JdkMeipo
public class JdkMeipo implements InvocationHandler {private IPerson target;// 反射获取public IPerson getInstance(IPerson target){this.target target;Class? clazz target.getClass();return (IPerson) Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {before();Object result method.invoke(this.target,args);after();return result;}private void after() {System.out.println(双方同意开始交往);}private void before() {System.out.println(我是媒婆已经收集到你的需求开始物色);}
}再创建一个类ZhaoLiu
public class ZhaoLiu implements IPerson {public void findLove() {System.out.println(赵六要求有车有房学历高);}public void buyInsure() {}
}测试代码如下
public class Test {public static void main(String[] args) {JdkMeipo jdkMeipo new JdkMeipo();IPerson zhangsan jdkMeipo.getInstance(new Zhangsan());zhangsan.findLove();IPerson zhaoliu jdkMeipo.getInstance(new ZhaoLiu());zhaoliu.findLove();}
}运行效果如下
我是媒婆已经收集到你的需求开始物色
张三要求肤白貌美大长腿
双方同意开始交往
我是媒婆已经收集到你的需求开始物色
赵六要求有车有房学历高
双方同意开始交往5 静态模式在业务中的应用
这里“小伙伴们”可能会觉得还是不知道如何将代理模式应用到业务场景中我们来看一个实际的业务场景。
在分布式业务场景中通常会对数据库进行分库分表分库分表之后使用 Java操作时就可能需要配置多个数据源我们通过设置数据源路由来动态切换数据源。
先创建Order订单类
Data
public class Order {private Object orderInfo;//订单创建时间进行按年分库private Long createTime;private String id;
}复制
创建OrderDao持久层操作类
public class OrderDao {public int insert(Order order){System.out.println(OrderDao创建Order成功!);return 1;}
}创建IOrderService接口
public interface IOrderService {int createOrder(Order order);
}创建OrderService实现类
public class OrderService implements IOrderService {private OrderDao orderDao;public OrderService(){//如果使用Spring应该是自动注入的//我们为了使用方便在构造方法中将orderDao直接初始化了orderDao new OrderDao();}public int createOrder(Order order) {System.out.println(OrderService调用orderDao创建订单);return orderDao.insert(order);}
}接下来使用静态代理主要完成的功能是根据订单创建时间自动按年进行分库。
根据开闭原则我们修改原来写好的代码逻辑通过代理对象来完成。
先创建数据源路由对象使用ThreadLocal的单例实现DynamicDataSourceEntity类
public class DynamicDataSourceEntity {public final static String DEFAULE_SOURCE null;private final static ThreadLocalString local new ThreadLocalString();private DynamicDataSourceEntity(){}public static String get(){return local.get();}public static void restore(){local.set(DEFAULE_SOURCE);}//DB_2018//DB_2019public static void set(String source){local.set(source);}public static void set(int year){local.set(DB_ year);}
}创建切换数据源的代理类OrderServiceSaticProxy
public class OrderServiceStaticProxy implements IOrderService {private SimpleDateFormat yearFormat new SimpleDateFormat(yyyy);private IOrderService orderService;public OrderServiceStaticProxy(IOrderService orderService) {this.orderService orderService;}public int createOrder(Order order) {before();Long time order.getCreateTime();Integer dbRouter Integer.valueOf(yearFormat.format(new Date(time)));System.out.println(静态代理类自动分配到【DB_ dbRouter 】数据源处理数据 );DynamicDataSourceEntity.set(dbRouter);this.orderService.createOrder(order);DynamicDataSourceEntity.restore();after();return 0;}private void before(){ System.out.println(Proxy before method.); }private void after(){ System.out.println(Proxy after method.); }
}来看测试代码
public class DbRouteProxyTest {public static void main(String[] args) {try {Order order new Order();SimpleDateFormat sdf new SimpleDateFormat(yyyy/MM/dd);Date date sdf.parse(2020/03/01);order.setCreateTime(date.getTime());IOrderService orderService new OrderServiceStaticProxy(new OrderService());orderService.createOrder(order);} catch (Exception e) {e.printStackTrace();}}
}运行结果如下
Proxy before method.
静态代理类自动分配到【DB_2020】数据源处理数据
OrderService调用orderDao创建订单
OrderDao创建Order成功!
Proxy after method.结果符合我们的预期。现在再来回顾一下类图看是不是和我们最先画的一致如下图所示。 动态代理和静态代理的基本思路是一致的只不过动态代理功能更加强大随着业务的扩展适应性更强。
6 动态代理在业务中的应用
上面的案例理解了我们再来看数据源动态路由业务帮助“小伙伴们”加深对动态代理的印象。
创建动态代理的类OrderServiceDynamicProxy
package com.oldluedu.vip.pattern.proxy.dbroute.proxy;import com.oldluedu.vip.pattern.proxy.dbroute.db.DynamicDataSourceEntity;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.SimpleDateFormat;
import java.util.Date;public class OrderServiceDynamicProxy implements InvocationHandler {private SimpleDateFormat yearFormat new SimpleDateFormat(yyyy);private Object target;public Object getInstance(Object target) {this.target target;Class? clazz target.getClass();return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {before(args[0]);Object object method.invoke(target, args);after();return object;}private void before(Object target) {try {System.out.println(Proxy before method.);Long time (Long) target.getClass().getMethod(getCreateTime).invoke(target);Integer dbRouter Integer.valueOf(yearFormat.format(new Date(time)));System.out.println(动态代理类自动分配到【DB_ dbRouter 】数据源处理数据);DynamicDataSourceEntity.set(dbRouter);} catch (Exception e) {e.printStackTrace();}}private void after() {System.out.println(Proxy after method.);}
}测试代码如下
public class DbRouteProxyTest {public static void main(String[] args) {try {Order order new Order();SimpleDateFormat sdf new SimpleDateFormat(yyyy/MM/dd);Date date sdf.parse(2020/03/01);order.setCreateTime(date.getTime());IOrderService orderService (IOrderService) new OrderServiceDynamicProxy().getInstance(new OrderService());orderService.createOrder(order);} catch (Exception e) {e.printStackTrace();}}
}运行效果如下
Proxy before method.
静态代理类自动分配到【DB_2020】数据源处理数据
OrderService调用orderDao创建订单
OrderDao创建Order成功!
Proxy after method.依然能够达到相同运行效果。但是使用动态代理实现之后我们不仅能实现 Order的数据源动态 路由还可以实现其他任何类的数据源路由。当然有个比较重要的约定必须实现getCreateTime() 方法因为路由规则是根据时间来运算的。我们可以通过接口规范来达到约束的目的在此就不再举例。
7 手写JDK动态代理实现原理
不仅知其然还得知其所以然。既然JDK动态代理功能如此强大那么它是如何实现的呢我们现 在来探究一下原理并模仿JDK动态代理动手写一个属于自己的动态代理。
我们都知道JDK动态代理采用字节重组重新生成对象来替代原始对象以达到动态代理的目的。
7.1 JDK动态代理的实现原理
获取被代理对象的引用并且获取它的所有接口反射获取。JDK Proxy类重新生成一个新的类实现了被代理类所有接口的方法。动态生成Java代码把增强逻辑加入到新生成代码中。编译生成新的Java代码的class文件。加载并重新运行新的class得到类就是全新类。
7.2 CGLib动态代理容易踩的坑
无法代理final修饰的方法。
以上过程就叫字节码重组。JDK中有一个规范在ClassPath下只要是$开头的.class文件一般都是自动生成的。那么我们有没有办法看到代替后的对象的“真容”呢做一个这样测试我们将内存中 的对象字节码通过文件流输出到一个新的.class文件然后利用反编译工具查看其源代码。
public class Test {public static void main(String[] args) {JdkMeipo jdkMeipo new JdkMeipo();IPerson zhangsan jdkMeipo.getInstance(new Zhangsan());zhangsan.findLove();// 通过反编译工具可以查看源代码try {byte[] bytes ProxyGenerator.generateProxyClass($Proxy0, new Class[]{IPerson.class});FileOutputStream os new FileOutputStream(F://$Proxy0.class);os.write(bytes);os.close();} catch (IOException e) {e.printStackTrace();}}
}运行以上代码我们能在E盘下找到一个Proxy0.class文件。使用Jad反编译得到Proxy0.jad 文件打开它可以看到如下内容
import com.oldluedu.vip.pattern.proxy.dynamicproxy.jdkproxy.IPerson;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;public final class $Proxy0 extends Proxy implements IPerson {private static Method m1;private static Method m3;private static Method m2;private static Method m4;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});} catch (RuntimeException | Error var3) {throw var3;} catch (Throwable var4) {throw new UndeclaredThrowableException(var4);}}public final void findLove() throws {try {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);}}public final void buyInsure() throws {try {super.h.invoke(this, m4, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}public final int hashCode() throws {try {return (Integer)super.h.invoke(this, m0, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}static {try {m1 Class.forName(java.lang.Object).getMethod(equals, Class.forName(java.lang.Object));m3 Class.forName(com.oldluedu.vip.pattern.proxy.dynamicproxy.jdkproxy.IPerson).getMethod(findLove);m2 Class.forName(java.lang.Object).getMethod(toString);m4 Class.forName(com.oldluedu.vip.pattern.proxy.dynamicproxy.jdkproxy.IPerson).getMethod(buyInsure);m0 Class.forName(java.lang.Object).getMethod(hashCode);} catch (NoSuchMethodException var2) {throw new NoSuchMethodError(var2.getMessage());} catch (ClassNotFoundException var3) {throw new NoClassDefFoundError(var3.getMessage());}}
}我们发现$Proxy0继承了Proxy类同时还实现了Person接口而且重写了findLove()等方法。 在静态块中用反射查找到了目标对象的所有方法而且保存了所有方法的引用重写的方法用反射调用 目标对象的方法。“小伙伴们”此时一定会好奇这些代码是哪里来的呢其实是JDK帮我们自动生成 的。现在我们不依赖JDK自己来动态生成源代码、动态完成编译然后替代目标对象并执行。
创建GPInvocationHandler接口
public interface GPInvocationHandler {Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
}创建GPProxy类
/*** 用来生成源代码的工具类*/
public class GPProxy {public static final String ln \r\n;public static Object newProxyInstance(GPClassLoader classLoader, Class? [] interfaces, GPInvocationHandler h){try {//1、动态生成源代码.java文件String src generateSrc(interfaces);//System.out.println(src);//2、Java文件输出磁盘String filePath GPProxy.class.getResource().getPath();//System.out.println(filePath);File f new File(filePath $Proxy0.java);FileWriter fw new FileWriter(f);fw.write(src);fw.flush();fw.close();//3、把生成的.java文件编译成.class文件JavaCompiler compiler ToolProvider.getSystemJavaCompiler();StandardJavaFileManager manage compiler.getStandardFileManager(null,null,null);Iterable iterable manage.getJavaFileObjects(f);JavaCompiler.CompilationTask task compiler.getTask(null,manage,null,null,null,iterable);task.call();manage.close();//4、编译生成的.class文件加载到JVM中来Class proxyClass classLoader.findClass($Proxy0);Constructor c proxyClass.getConstructor(GPInvocationHandler.class);f.delete();//5、返回字节码重组以后的新的代理对象return c.newInstance(h);}catch (Exception e){e.printStackTrace();}return null;}private static String generateSrc(Class?[] interfaces){StringBuffer sb new StringBuffer();sb.append(GPProxy.class.getPackage() ; ln);sb.append(import interfaces[0].getName() ; ln);sb.append(import java.lang.reflect.*; ln);sb.append(public class $Proxy0 implements interfaces[0].getName() { ln);sb.append(GPInvocationHandler h; ln);sb.append(public $Proxy0(GPInvocationHandler h) { ln);sb.append(this.h h;);sb.append(} ln);for (Method m : interfaces[0].getMethods()){Class?[] params m.getParameterTypes();StringBuffer paramNames new StringBuffer();StringBuffer paramValues new StringBuffer();StringBuffer paramClasses new StringBuffer();for (int i 0; i params.length; i) {Class clazz params[i];String type clazz.getName();String paramName toLowerFirstCase(clazz.getSimpleName());paramNames.append(type paramName);paramValues.append(paramName);paramClasses.append(clazz.getName() .class);if(i 0 i params.length-1){paramNames.append(,);paramClasses.append(,);paramValues.append(,);}}sb.append(public m.getReturnType().getName() m.getName() ( paramNames.toString() ) { ln);sb.append(try{ ln);sb.append(Method m interfaces[0].getName() .class.getMethod(\ m.getName() \,new Class[]{ paramClasses.toString() }); ln);sb.append((hasReturnValue(m.getReturnType()) ? return : ) getCaseCode(this.h.invoke(this,m,new Object[]{ paramValues }),m.getReturnType()) ; ln);sb.append(}catch(Error _ex) { });sb.append(catch(Throwable e){ ln);sb.append(throw new UndeclaredThrowableException(e); ln);sb.append(});sb.append(getReturnEmptyCode(m.getReturnType()));sb.append(});}sb.append(} ln);return sb.toString();}private static MapClass,Class mappings new HashMapClass, Class();static {mappings.put(int.class,Integer.class);}private static String getReturnEmptyCode(Class? returnClass){if(mappings.containsKey(returnClass)){return return 0;;}else if(returnClass void.class){return ;}else {return return null;;}}private static String getCaseCode(String code,Class? returnClass){if(mappings.containsKey(returnClass)){return (( mappings.get(returnClass).getName() ) code ). returnClass.getSimpleName() Value();}return code;}private static boolean hasReturnValue(Class? clazz){return clazz ! void.class;}private static String toLowerFirstCase(String src){char [] chars src.toCharArray();chars[0] 32;return String.valueOf(chars);}}创建GPClassLoader类
public class GPClassLoader extends ClassLoader {private File classPathFile;public GPClassLoader(){String classPath GPClassLoader.class.getResource().getPath();this.classPathFile new File(classPath);}Overrideprotected Class? findClass(String name) throws ClassNotFoundException {String className GPClassLoader.class.getPackage().getName() . name;if(classPathFile ! null){File classFile new File(classPathFile,name.replaceAll(\\.,/) .class);if(classFile.exists()){FileInputStream in null;ByteArrayOutputStream out null;try{in new FileInputStream(classFile);out new ByteArrayOutputStream();byte [] buff new byte[1024];int len;while ((len in.read(buff)) ! -1){out.write(buff,0,len);}return defineClass(className,out.toByteArray(),0,out.size());}catch (Exception e){e.printStackTrace();}}}return null;}
}创建GPMeipo类
public class GpMeipo implements GPInvocationHandler {private IPerson target;public IPerson getInstance(IPerson target){this.target target;Class? clazz target.getClass();return (IPerson) GPProxy.newProxyInstance(new GPClassLoader(),clazz.getInterfaces(),this);}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {before();Object result method.invoke(this.target,args);after();return result;}private void after() {System.out.println(双方同意开始交往);}private void before() {System.out.println(我是媒婆已经收集到你的需求开始物色);}
}客户端测试代码如下
public class Test {public static void main(String[] args) {GpMeipo gpMeipo new GpMeipo();IPerson zhangsan gpMeipo.getInstance(new Zhangsan());zhangsan.findLove();}
}运行效果如下
我是媒婆已经收集到你的需求开始物色
张三要求肤白貌美大长腿
双方同意开始交往到此手写JDK动态代理就完成了。“小伙伴们”是不是又多了一个面试用的“撒手锏”呢
8 CGLib代理调用API及原理分析
简单看一下CGLib代理的使用还是以媒婆为例创建CglibMeipo类
public class CGlibMeipo implements MethodInterceptor {public Object getInstance(Class? clazz) throws Exception{//相当于Proxy代理的工具类Enhancer enhancer new Enhancer();enhancer.setSuperclass(clazz);enhancer.setCallback(this);return enhancer.create();}public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {before();Object obj methodProxy.invokeSuper(o,objects);after();return obj;}private void before(){System.out.println(我是媒婆我要给你找对象现在已经确认你的需求);System.out.println(开始物色);}private void after(){System.out.println(OK的话准备办事);}
}创建单身客户类Customer
public class Customer {public void findLove(){System.out.println(儿子要求肤白貌美大长腿);}
}有个小细节CGLib代理的目标对象不需要实现任何接口它是通过动态继承目标对象实现动态代 理的。来看测试代码
public class CglibTest {public static void main(String[] args) {try {Customer obj (Customer)new CGlibMeipo().getInstance(Customer.class);obj.findLove(); } catch (Exception e) {e.printStackTrace();}}
}CGLib 代理的实现原理又是怎样的呢我们可以在测试代码中加上一句代码将 CGLib 代理后 的.class文件写入磁盘然后反编译来一探究竟代码如下
public class CglibTest {public static void main(String[] args) {try {//JDK是采用读取接口的信息//CGLib覆盖父类方法//目的都是生成一个新的类去实现增强代码逻辑的功能//JDK Proxy 对于用户而言必须要有一个接口实现目标类相对来说复杂//CGLib 可以代理任意一个普通的类没有任何要求//CGLib 生成代理逻辑更复杂效率,调用效率更高生成一个包含了所有的逻辑的FastClass不再需要反射调用//JDK Proxy生成代理的逻辑简单执行效率相对要低每次都要反射动态调用//CGLib 有个坑CGLib不能代理final的方法System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,E://cglib_proxy_classes);Customer obj (Customer) new CGlibMeipo().getInstance(Customer.class);System.out.println(obj);obj.findLove();} catch (Exception e) {e.printStackTrace();}}
}重新执行代码我们会发现在E://cglib_proxy_class目录下多了三个.class文件如下图所示。 通过调试跟踪发现Customer
EnhancerByCGLIB
3feeb52a.class 就是 CGLib 代理生成的代 理类继承了Customer类。
package com.oldluedu.vip.pattern.proxy.dynamicproxy.cglibproxy;import java.lang.reflect.Method;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;public class Customer$$EnhancerByCGLIB$$6d99cfc2 extends Customer implements Factory {...final void CGLIB$findLove$0() {super.findLove();}public final void findLove() {MethodInterceptor var10000 this.CGLIB$CALLBACK_0;if (var10000 null) {CGLIB$BIND_CALLBACKS(this);var10000 this.CGLIB$CALLBACK_0;}if (var10000 ! null) {var10000.intercept(this, CGLIB$findLove$0$Method, CGLIB$emptyArgs, CGLIB$findLove$0$Proxy);} else {super.findLove();}}...
}复制
我们重写了Customer类的所有方法通过代理类的源码可以看到代理类会获得所有从父类继承 来的方法并且会有 MethodProxy 与之对应比如 Method CGLIBfindLove0Method、 MethodProxy CGLIBfindLove0
//代理方法methodProxy.invokeSuper()方法会调用
final void CGLIB$findLove$0() {super.findLove();
}//被代理方法methodProxy.invoke()方法会调用这就是为什么在拦截器中调用 methodProxy.invoke 会发生死循环一直在调用拦截器
public final void findLove() {MethodInterceptor var10000 this.CGLIB$CALLBACK_0;if (var10000 null) {CGLIB$BIND_CALLBACKS(this);var10000 this.CGLIB$CALLBACK_0;}if (var10000 ! null) {var10000.intercept(this, CGLIB$findLove$0$Method, CGLIB$emptyArgs, CGLIB$findLove$0$Proxy);} else {super.findLove();}
}调用过程为代理对象调用 this.findLove()方法→调用拦截器→methodProxy.invokeSuper→ CGLIBfindLove0→被代理对象findLove()方法。
此时我们发现拦截器 MethodInterceptor中就是由 MethodProxy的invokeSuper()方法调用代 理方法的MethodProxy非常关键我们分析一下它具体做了什么。
package net.sf.cglib.proxy;import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import net.sf.cglib.core.AbstractClassGenerator;
import net.sf.cglib.core.CodeGenerationException;
import net.sf.cglib.core.GeneratorStrategy;
import net.sf.cglib.core.NamingPolicy;
import net.sf.cglib.core.Signature;
import net.sf.cglib.reflect.FastClass;
import net.sf.cglib.reflect.FastClass.Generator;public class MethodProxy {private Signature sig1;private Signature sig2;private MethodProxy.CreateInfo createInfo;private final Object initLock new Object();private volatile MethodProxy.FastClassInfo fastClassInfo;public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {MethodProxy proxy new MethodProxy();proxy.sig1 new Signature(name1, desc);proxy.sig2 new Signature(name2, desc);proxy.createInfo new MethodProxy.CreateInfo(c1, c2);return proxy;}...private static class CreateInfo {Class c1;Class c2;NamingPolicy namingPolicy;GeneratorStrategy strategy;boolean attemptLoad;public CreateInfo(Class c1, Class c2) {this.c1 c1;this.c2 c2;AbstractClassGenerator fromEnhancer AbstractClassGenerator.getCurrent();if (fromEnhancer ! null) {this.namingPolicy fromEnhancer.getNamingPolicy();this.strategy fromEnhancer.getStrategy();this.attemptLoad fromEnhancer.getAttemptLoad();}}}...
}继续看invokeSuper()方法 public Object invokeSuper(Object obj, Object[] args) throws Throwable {try {init();FastClassInfo fci fastClassInfo;return fci.f2.invoke(fci.i2, obj, args);} catch (InvocationTargetException e) {throw e.getTargetException();}}private static class FastClassInfo{FastClass f1;FastClass f2;int i1;int i2;}上面的代码调用就是获取代理类对应的FastClass并执行代理方法。还记得之前生成的三个.class 文件吗Customer
EnhancerByCGLIB
3feeb52a
FastClassByCGLIB
6aad62f1.class 就是代理类的FastClassCustomer
FastClassByCGLIB
2669574a.class 就是被代理类的FastClass。
CGLib代理执行代理方法的效率之所以比JDK的高是因为CGlib采用了FastClass机制它的原 理简单来说就是为代理类和被代理类各生成一个类这个类会为代理类或被代理类的方法分配一个 indexint类型这个index当作一个入参FastClass就可以直接定位要调用的方法并直接进行调 用省去了反射调用所以调用效率比JDK代理通过反射调用高。下面我们反编译一个FastClass看看 public Object invokeSuper(Object obj, Object[] args) throws Throwable {try {init();FastClassInfo fci fastClassInfo;return fci.f2.invoke(fci.i2, obj, args);} catch (InvocationTargetException e) {throw e.getTargetException();}}private static class FastClassInfo{FastClass f1;FastClass f2;int i1;int i2;}CGLib代理执行代理方法的效率之所以比JDK的高是因为CGlib采用了FastClass机制它的原理简单来说就是为代理类和被代理类各生成一个类这个类会为代理类或被代理类的方法分配一个 indexint类型这个index当作一个入参FastClass就可以直接定位要调用的方法并直接进行调 用省去了反射调用所以调用效率比JDK代理通过反射调用高。下面我们反编译一个FastClass看看
public class test {public int getIndex(Signature signature) {String s signature.toString();s;s.hashCode();JVM INSTR lookupswitch 11:default 223…JVM INSTR pop;return -1;}//部分代码省略//根据 index 直接定位执行方法public Object invoke(int i, Object obj, Object[] aobj) throws InvocationTargetException {(Customer) obj;i;JVM INSTR tableswitch 0 10:default161 goto _L1 _L2 _L3 _L4 _L5 _L6 _L7 _L8 _L9 _L10 _L11 _L12 _L2:eat();return null;_L3:findLove();return null; …throw new IllegalArgumentException(Cannot find matching method/constructor);}
}FastClass 并不是跟代理类一起生成的而是在第一次执行 MethodProxy 的 invoke()或 invokeSuper()方法时生成的并放在了缓存中。
//MethodProxy 的 invoke()或 invokeSuper()方法都调用了 init()方法
private void init()
{/* * Using a volatile invariant allows us to initialize the FastClass and* method index pairs atomically.* * Double-checked locking is safe with volatile in Java 5. Before 1.5 this * code could allow fastClassInfo to be instantiated more than once, which* appears to be benign.*/if (fastClassInfo null){synchronized (initLock){if (fastClassInfo null){CreateInfo ci createInfo;FastClassInfo fci new FastClassInfo();fci.f1 helper(ci, ci.c1);fci.f2 helper(ci, ci.c2);fci.i1 fci.f1.getIndex(sig1);fci.i2 fci.f2.getIndex(sig2);fastClassInfo fci;}}}
}至此CGLib代理的原理我们就基本搞清楚了对代码细节有兴趣的“小伙伴”可以自行深入研究。
9 CGLib和JDK动态代理对比
1JDK动态代理实现了被代理对象的接口CGLib代理继承了被代理对象。
2 JDK动态代理和CGLib代理都在运行期生成字节码 JDK动态代理直接写Class字节码 CGLib 代理使用ASM框架写Class字节码CGlib代理实现更复杂生成代理类比JDK动态代理效率低。
3JDK动态代理调用代理方法是通过反射机制调用的CGLib代理是通过FastClass机制直接调用方法的CGLib代理的执行效率更高。
10 代理模式与Spring生态
1、代理模式在Spring中的应用
先看ProxyFactoryBean核心方法getObject()源码如下
Nullable
public Object getObject() throws BeansException {this.initializeAdvisorChain();if (this.isSingleton()) {return this.getSingletonInstance();} else {if (this.targetName null) {this.logger.info(Using non-singleton proxies with singleton targets is often undesirable. Enable prototype proxies by setting the targetName property.);}return this.newPrototypeInstance();}
}在getObject()方法中主要调用 getSingletonInstance()和 newPrototypeInstance()。在 Spring 的配置中如果不做任何设置那么 Spring 代理生成的 Bean 都是单例对象。如果修改 scope则每次 创建一个新的原型对象。newPrototypeInstance()里面的逻辑比较复杂我们后面再做深入研究这里 先做简单了解。
Spring 利用动态代理实现 AOP 时有两个非常重要的类JdkDynamicAopProxy 类和CglibAopProxy类来看一下类图如下图所示。 10 静态代理和动态代理的本质区别
1静态代理只能通过手动完成代理操作如果被代理类增加了新的方法代理类需要同步增加 违背开闭原则。
2动态代理采用在运行时动态生成代码的方式取消了对被代理类的扩展限制遵循开闭原则。
3若动态代理要对目标类的增强逻辑进行扩展结合策略模式只需要新增策略类便可完成无须修改代理类的代码。
11 代理模式的优缺点
代理模式具有以下优点
1代理模式能将代理对象与真实被调用目标对象分离。
2在一定程度上降低了系统的耦合性扩展性好。
3可以起到保护目标对象的作用。
4可以增强目标对象的功能。
当然代理模式也有缺点
1代理模式会造成系统设计中类的数量增加。
2在客户端和目标对象中增加一个代理对象会导致请求处理速度变慢。
3增加了系统的复杂度。
12 Spring中的代理选择原则
1当Bean有实现接口时Spring就会用JDK动态代理。
2当Bean没有实现接口时Spring会选择CGLib代理。
3Spring可以通过配置强制使用CGLib代理只需在Spring的配置文件中加入如下代码
aop:aspectj-autoproxy proxy-target-classtrue/