java设计模式(一)动态代理模式,JDK与CGLIB分析
-本想着这个知识点放到Spring Aop说说可能更合适一点,但因为上一篇有所提到就简单分析下,不足之处请多多评论留言,相互学习,有所提高才是关键!
什么是代理模式:
记得有本24种设计模式的书讲到代理模式的经典例子,说的是西门庆、王婆、潘金莲的故事,比如西门庆找潘金莲,不好意思啊,则么办,找那个王婆做代理, 代理模式就是找个东西代替自己完成自己的活,这个就称之为代理。如宋哲经纪人,找房子中介......具体看看代码怎么写
区别
- JDK动态代理只能对实现了接口的类生成代理,而不能针对类
- CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法(继承)
--JDK动态代理
- 定义一个接口,保存和修改方法
public interface UserDao { public void save(); public void update(); }
public class UserDaoImpl implements UserDao { @Override public void save() { System.out.println("sava doing ........."); } @Override public void update() { System.out.println("update doing ........."); } }
- 测试一把:
public class TestProxy { @Test public void run1(){ //创建对象方式调用方法执行。 UserDao dao = new UserDaoImpl(); dao.save(); dao.update(); System.out.println("============================="); } } sava doing ......... update doing ......... =============================
- 加入代理模式 java.lang.reflect.Proxy
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import com.kk.Dao.UserDao; /** * 使用JDK的方式生成代理对象 * @author Administrator */ public class MyProxyUtils { public static UserDao getProxy(final UserDao dao) { // 使用Proxy类生成代理对象 UserDao proxy = (UserDao) Proxy.newProxyInstance(dao.getClass().getClassLoader(), dao.getClass().getInterfaces(), new InvocationHandler() { // 代理对象方法一直线,invoke方法就会执行一次 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //在此可以利用代理对象来增强方法 比如在执行方法前记录一条日志。 当然进行权限校验,日志记录,性能监控,事务控制等都可以在此加强。这也是aop思想 if("save".equals(method.getName())){ System.out.println("记录日志..."); } //让dao类的save或者update方法正常的执行下去 UserDao invoke =(UserDao) method.invoke(dao, args); return invoke; } }); // 返回代理对象 return proxy; } }
- 测试第二把:利用代理对象测试
public class TestProxy { @Test public void run1(){ UserDao dao = new UserDaoImpl(); // 使用工具类,获取到代理对象 UserDao proxy = MyProxyUtils.getProxy(dao); // 通过代理对象调用方法 proxy.save(); proxy.update(); } } 记录日志... sava doing ......... update doing .........
由此可以看出来,在我方法执行前,我在没有改变原方法前加了一段记录日志/代码,这就是一种aop思想,降低代码耦合度。
CGLIB代理方式:
- 加入cglib-2.2.2.jar: 及三方依赖 asm-3.3.1.jar
-
编写BookDaoImpl实现类
public class BookDaoImpl { public void save() { System.out.println("sava doing ........."); } public void update() { System.out.println("update doing ........."); } }
- CGLIB创建代理方式工具类
package com.kk.Utils; import java.lang.reflect.Method; import com.kk.DaoImpl.BookDaoImpl; import com.kk.DaoImpl.UserDaoImpl; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class MyCglibUtils { /** * 使用CGLIB方式生成代理的对象 * * @return */ public static BookDaoImpl getProxy( BookDaoImpl bookDaoImpl) { Enhancer enhancer = new Enhancer(); // 设置父类 enhancer.setSuperclass(bookDaoImpl.getClass()); // 设置回调函数 enhancer.setCallback(new MethodInterceptor() { // 代理对象的方法执行,回调函数就会执行 public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { if(method.getName().equals("save")){ System.out.println("记录日志..."); } // 正常执行 return methodProxy.invokeSuper(obj, args); } }); // 生成代理对象 BookDaoImpl proxy = (BookDaoImpl) enhancer.create(); return proxy; } }
- 测试一把:
@Test public void run1(){ //目标对象 BookDaoImpl dao = new BookDaoImpl(); // 通过代理对象调用方法 dao.save(); dao.update(); System.out.println("================================"); BookDaoImpl proxy = MyCglibUtils.getProxy(dao); proxy.save(); proxy.update(); } sava doing ......... update doing ......... ================================ 记录日志... sava doing ......... update doing .........
同理:通过代理对象可以帮我们完成代码增强等功能,注意:JDK代理是通过接口模式,而CGLIB则通过实现类方式帮我们创建代理对象。后期Spring会更深了解。
Spring在选择用JDK还是CGLiB的依据:
- 当Bean实现接口时,Spring就会用JDK的动态代理
- 当Bean没有实现接口时,Spring使用CGlib是实现
- 可以强制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)
CGlib比JDK快?
- 使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。
- 在对JDK动态代理与CGlib动态代理的代码实验中看,1W次执行下,JDK7及8的动态代理性能比CGlib要好20%左右。
技术交流群,海量学习资料免费获取,备注来意:就说博客上看到的,
Q群:289683917