深入Java设计模式之代理模式
代理模式的分类
我们有多种不同的方式来实现代理。如果按照代理创建的时期来进行分类的话, 可以分为两种:静态代理、动态代理。静态代理是由程序员创建或特定工具自动生成源代码,在对其编译。在程序员运行之前,代理类.class文件就已经被创建了。动态代理是在程序运行时通过反射机制动态创建的。
静态代理
优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。
缺点:我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。
动态代理
动态代理分为Cglib动态代理和JDK动态代理
优点:大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度
缺点:代码复杂度增加
JDK动态代理
package com.lf.shejimoshi.proxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.lf.shejimoshi.proxy.entity.UserManager;
import com.lf.shejimoshi.proxy.entity.UserManagerImpl;
//JDK动态代理实现InvocationHandler接口
public class JdkProxy implements InvocationHandler {
private Object target ;//需要代理的目标对象
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK动态代理,监听开始!");
Object result = method.invoke(target, args);
System.out.println("JDK动态代理,监听结束!");
return result;
}
//定义获取代理对象方法
private Object getJDKProxy(Object targetObject){
//为目标对象target赋值
this.target = targetObject;
//JDK动态代理只能针对实现了接口的类进行代理,newProxyInstance 函数所需参数就可看出
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
}
public static void main(String[] args) {
JdkProxy jdkProxy = new JdkProxy();//实例化JDKProxy对象
UserManager user = (UserManager) jdkProxy.getJDKProxy(new UserManagerImpl());//获取代理对象
user.addUser("admin", "123123");//执行新增方法
}
}
Proxy.newProxyInstance( ClassLoader paramClassLoader, Class<?>[] paramArrayOfClass, InvocationHandler paramInvocationHandler)进行观察,简单来看就是先生成新的class文件,然后加载到jvm中,然后使用反射,先用class取得他的构造方法,然后使用构造方法反射得到他的一个实例。
标红的是最复杂的。然后cglib的实现原理基本一致,唯一的区别在于生成新的class文件方式和结果不一样。
Cglib代理
package com.lf.shejimoshi.proxy.cglib;
import java.lang.reflect.Method;
import com.lf.shejimoshi.proxy.entity.UserManager;
import com.lf.shejimoshi.proxy.entity.UserManagerImpl;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
//Cglib动态代理,实现MethodInterceptor接口
public class CglibProxy implements MethodInterceptor {
private Object target;//需要代理的目标对象
//重写拦截方法
@Override
public Object intercept(Object obj, Method method, Object[] arr, MethodProxy proxy) throws Throwable {
System.out.println("Cglib动态代理,监听开始!");
Object invoke = method.invoke(target, arr);//方法执行,参数:target 目标对象 arr参数数组
System.out.println("Cglib动态代理,监听结束!");
return invoke;
}
//定义获取代理对象方法
public Object getCglibProxy(Object objectTarget){
//为目标对象target赋值
this.target = objectTarget;
Enhancer enhancer = new Enhancer();
//设置父类,因为Cglib是针对指定的类生成一个子类,所以需要指定父类
enhancer.setSuperclass(objectTarget.getClass());
enhancer.setCallback(this);// 设置回调
Object result = enhancer.create();//创建并返回代理对象
return result;
}
public static void main(String[] args) {
CglibProxy cglib = new CglibProxy();//实例化CglibProxy对象
UserManager user = (UserManager) cglib.getCglibProxy(new UserManagerImpl());//获取代理对象
user.delUser("admin");//执行删除方法
}
}
动态代理的结果问题
Cglib和JDK动态代理的区别?
Cglib | JDK | |
是否提供子类代理 | 是 | 否 |
是否提供接口代理 | 是(可强制) | 是 |
区别 | 必须依赖于CGLib的类库,利用ASM框架,对代理对象类生成的class文件加载进来,通过修改其字节码生成子类来处理 | 实现InvocationHandler 使用Proxy.newProxyInstance产生代理对象(匿名类) 被代理的对象必须要实现接口 在调用具体方法前调用InvokeHandler来处理 |
什么时候用Cglib什么时候用JDK动态代理?
1、目标对象生成了接口 默认用JDK动态代理
2、如果目标对象使用了接口,可以强制使用cglib
3、如果目标对象没有实现接口,必须采用cglib库,Spring会自动在JDK动态代理和cglib之间转换
JDK动态代理和Cglib字节码生成的区别?
1、JDK动态代理只能对实现了接口的类生成代理,而不能针对类
2、Cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,并覆盖其中方法的增强,但是因为采用的是继承,所以该类或方法最好不要生成final,对于final类或方法,是无法继承的
Cglib比JDK快?
测试代码如下:
测试环境jdk1.8
createJdkProxy:76
JdkProxy class:com.sun.proxy.$Proxy0
callJdkProxy:117
createCglibProxy:1195
CglibProxy class:com.lzhsite.technology.designPattern.proxy.dynamicProxy.IDBQuery$$EnhancerByCGLIB$$136e9b81
callCglibProxy:26
createJavassistDynProxy:464
JavassistDynProxy class:com.lzhsite.technology.designPattern.proxy.dynamicProxy.IDBQuery_$$_javassist_0
callJavassistDynProxy:6346
createJavassistBytecodeDynamicProxy:1080
JavassistBytecodeDynamicProxy class:com.lzhsite.technology.designPattern.proxy.dynamicProxy.IDBQueryJavaassistBytecodeProxy
callJavassistBytecodeDynamicProxy:24
可以看出jdk的创建速度优于cglib,cglib的调用速度优于jdk
Spring如何选择是用JDK还是cglib?
1、当bean实现接口时,会用JDK代理模式
2、当bean没有实现接口,用cglib实现
3、可以强制使用cglib(在spring配置中加入<aop:aspectj-autoproxy proxyt-target-class=”true”/>)
参考文章:
相关文章
- 图解 & 深入浅出Java初始化与清理:构造器必知必会
- Java实现 LeetCode 1两数之和
- Java实现 LeetCode 232 用栈实现队列
- Java实现 洛谷 P1085 不高兴的津津
- Java实现 洛谷 P1217 [USACO1.5]回文质数 Prime Palindromes
- java实现哈密顿回路问题
- Java通过mysql-connector-java-8.0.11连接MySQL Server 8.0遇到的几个问题
- (转载)java多态(2)-------Java转型(向上或向下转型)
- Java集合框架面试题
- 杨晓峰-Java核心技术-6 动态代理 反射 MD
- java动态代理(JDK和CGLIB)笔记
- Atitit.http代理的实现 代码java php c# python
- 华为校招机试 - 发广播(Java & JS & Python)
- Java的算数运算符、关系运算符、逻辑运算符、位运算符
- Java代理模式:如何优雅地控制对象访问?
- 当年的java考试:Java景区预约登记管理系统(maven整合servlet)
- 【java】Java连接mysql数据库及mysql驱动jar包下载和使用
- java 代理模式 总结
- Java Class 对象模型的数据结构分析
- 解决idea出现的java.lang.OutOfMemoryError: Java heap space的问题
- 【Android 安全】DEX 加密 ( Java 工具开发 | 加密解密算法 API | 编译代理 Application 依赖库 | 解压依赖库 aar 文件 )
- 启动tomcat时 错误: 代理抛出异常 : java.rmi.server.ExportException: Port already in use: 1099的解决办法
- JAVA windows 环境配置
- Java代理模式:如何优雅地控制对象访问?
- 【java】Java 抽象类
- Java线程池七个参数详解
- Java(3):Java语言中的数组
- JAVA开发讲义(二)-Java程序设计之数据之谜三