现象:在如今互联网时代,项目的复杂度不断的提升,有些场景下需要一定的设计优化来支撑业务的扩展,如为了不改动原始类,但需要对其做相应事件扩展,例如:日志,事物,功能增强等。
思想:想办法用一个B类代表另一个A类的功能,不改变其A类本质。
结果:代理模式出现,这只是一个思想,实现的方式有很多种,如:静态代理、JDK动态代理、CGLib动态代理 等等其他的。
本文主要写代理模式主要的几种代码的使用,其原理会接下来的文章种输出。
主要的代理方式
静态代理主要过程(整了一个外套--代理类)
优点:代理类在编译期生成,效率高。
缺点:代理类会很多,后期维护复杂。
代码逻辑
接口类UserService
public interface UserService { String getUserName(String username); }
接口实现类UserServiceImpl
public class UserServiceImpl implements UserService{ @Override public String getUserName(String username) { System.err.println(username); return username; } }
代理类UserStaticProxy
public class UserStaticProxy implements UserService{ private UserService userService; public UserStaticProxy(UserService userService) { this.userService = userService; } @Override public String getUserName(String username) { System.err.println("开始------"); String userName = userService.getUserName(username); System.err.println("结束------"); return userName; } }
测试类StaticProxyTest
public class StaticProxyTest { public static void main(String[] args) { UserService userService = new UserServiceImpl(); userService.getUserName("test"); System.err.println("_______________________________________________"); UserStaticProxy userStaticProxy = new UserStaticProxy(new UserServiceImpl()); userStaticProxy.getUserName("lisi"); } }
问题:为什么会出现动态代理呢,静态代理哪里不好了 回答:若是又出现一个类型需要代理,需要再次写一个代理类去维护包装,或者类型中新增一个方法功能代理类也需要跟着维护,复杂过于耦合。所以就有想法如何解决这样的尴尬困境。
这时候出现了JDK动态代理,完美解决了新增类型和新增方法的一些尴尬问题。主要将静态代理中代理类涉及到目标类有关的以其他种方式体现,变得可动态。
主要流程:
代码逻辑
接口类SanGuoService
public interface SanGuoService { void warfare(String username); }
目标类 LiuBeiServiceImpl 实现 SanGuoService
public class LiuBeiServiceImpl implements SanGuoService{ @Override public void warfare(String username) { System.err.println("LiuBei-warfare:"+username); } }
代理工具 JDKSimpleDynamicProxy 可实现 InvocationHandler 调用处理器,也可以逻辑内部匿名实现当作参数传递
主要为了生成代理类
public class JDKSimpleDynamicProxy implements InvocationHandler { private Object object; //创建代理类实例 public <T> T getNewProxy(T t){ object = t; return (T) Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(),this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.err.println("------开始----"); Object invoke = method.invoke(object, args); System.err.println("------结束----"); return invoke; } }
JDK动态代理测试类 JDKSimpleDynamicProxyTest
public class JDKSimpleDynamicProxyTest { public static void main(String[] args) { JDKSimpleDynamicProxy jdkSimpleDynamicProxy = new JDKSimpleDynamicProxy(); LiuBeiServiceImpl liuBeiService = new LiuBeiServiceImpl(); SanGuoService sanGuoService = jdkSimpleDynamicProxy.getNewProxy(liuBeiService); sanGuoService.warfare("刘备"); } }
问题:有了JDK动态代理那么方便好用高扩展,为什么又要搞出一个CGLib动态代理呢。
回答:当一个类型没有接口,则不能使用JDK动态代理了,除非给硬塞一个接口,很滑稽,那怎么办呢!所有设计了一个可以对目标类直接代理的方式,直接继承,CGLib动态代理。
其实CGLib和JDK动态代理使用流程上还是很相似的,只是内部逻辑实现不一样,若封装的好是看不出来的。
主要流程:
目标类:XiangYuServiceImpl 没有接口类
public class XiangYuServiceImpl { public void warfare(String username) { System.err.println("XiangYu-warfare:"+username); } }
代理工具类:CGLibDynamicProxy 这类的MethodInterceptor上面说过可以这里实现,也可以业务逻辑自己实现参数传入,因为内部只需要一个参数实例
public class CGLibDynamicProxy implements MethodInterceptor { public <T> T create(Class<T> classN){ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(classN); enhancer.setCallback(this); return (T)enhancer.create(); } @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.err.println(proxy.getClass().getName()); System.err.println("1111111"); Object invoke = methodProxy.invokeSuper(proxy, args); System.err.println("22222222"); return invoke; } }
测试类:CGLibDynamicProxyTest
public class CGLibDynamicProxyTest { public static void main(String[] args) { CGLibDynamicProxy cgLibDynamicProxy = new CGLibDynamicProxy(); XiangYuServiceImpl xiangYuService = cgLibDynamicProxy.create(XiangYuServiceImpl.class); xiangYuService.warfare("guanglin"); } }
其实这三种代理模式都是想尽办法创建一个代理类调用目标类来实现代理。
动态代理的应用:Spring AOP、RPC调用、Hibernate的懒加载 等待
代理模式 | 代理类创建时机 | 创建方式 | 代理类行为 | 原理 | 调用处理器 | 效率方面 |
性能方面 |
静态代理 | 编译时期 | 手动创建 | 实现接口 | 直接调用目标类 | 无 | 高效 | 高效 |
JDK动态代理 | 运行时 | 基于JVM对字节码的操作实现 | 实现接口 | 反射调用 | InvocationHandler | 慢 |
JDK1.6/1.7上的对比
JDK1.8上的对比
|
CGLib动态代理 | 运行时 | 基于ASM对字节码的操作 | 继承目标类 | super调用父类 | MethodInterceptor | 慢 |