1 代理模式概述
1 生活中的代理案例在这里插入图片描述
- 房屋中介代理
- 客户手中没有房源,找一个中介
- 商品代购
- 代购者可以拿到比较低成本的商品,拥有自己的渠道
2 为什么要使用代理
- 对于消费者而言,可以减少成本, 只需要关心自己需要的商品,不需要去寻找渠道或者是找房源
3 代理模式在java中的应用
- 统一异常处理
- Mybatis 使用了代理
- Spring aop实现原理
- 日志框架
4 概述
- 代理模式(proxy pattern):是23种设计模式中的一种,属于结构性的模式。指一个对象本身不做实际的操作,而是通过其它对象来得到自己想得到的结果
- 意义:目标对象只需要关心自己的实现细节,通过代理对象来实现功能的增强,可以扩展目标对象的功能
- 体现了一个非常重要的编程思想:不能随便修改源码,如果需要修改源码,通过代理模式来扩展。
5 图示
- 图示
2 代理的实现方式
1 java中代理图示
- 图示
- 元素组成
- 接口, 定义行为和规范的
- 被代理类,是目标对象
- 代理类,做功能增强
2 静态代理
2.1 案例
- 通过代理模式实现事务操作
2.2 实现案例
- 创建domain对象
- 创建service接口来管理规范
- 创建实现类,被代理类
- 创建事务类对象
- 创建代理类对象
- 测试代理类对象
- 运行结果
2.3 存在的问题
- 不利于代码的扩展,比如接口中新添加一个抽象方法时,所有实现类都需要重新实现,否则报错
- 代理对象需要创建很多,这种设计很不方便和麻烦
3 动态代理
1 概述
- 概述:在不改变原有功能代码的前提下,能够动态的实现方法的增强
2 JDK动态代理
2.1 基础准备
- 创建service
package com.ff.service;
/**
* @author
* @version 1.0
*/
public interface IStudentService {
public void save();
Student query(Long id);
}
- 创建service实现类(需要代理的类)
public class IStudentServiceImpl implements IStudentService{
public void save() {
System.out.println("保存学生信息");
}
public Student query(Long id) {
System.out.println("查询操作");
Student student = new Student();
student.setAge(18);
student.setName("sy");
return student;
}
}
- 增强类
public class DaoTransaction {
public void before() {
System.out.println("开启事务操作");
}
public void after() {
System.out.println("关闭事务");
}
}
2.2 实现InvocationHandler接口
-
InvocationHandler接口,用来做方法拦截
-
实现接口
public class TransactionHandler implements InvocationHandler {
//增强类对象
private DaoTransaction transaction;
//需要代理的对象
private Object object;
public TransactionHandler(DaoTransaction transaction, Object object) {
this.transaction = transaction;
this.object = object;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object ret = null;
//判断当前方法是否是save,是才做事务
if(method.getName().equals("save")) {
transaction.before();
ret = method.invoke(object,args);
transaction.after();
} else {
ret = method.invoke(object,args);
}
return ret;
}
}
-
分别介绍里面的参数
- Proxy:代理实例,可以通过
newProxyInstance
创建代理实例
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
-
- ClassLoader类加载器,直接通过需要代理的类获得就行
- Class[]:目标类所实现的所有接口
- InvocationHandler:方法拦截处理器,可以在里面实现方法的增强
- Method:执行目标方法的,invoke方法执行
- args:参数数组
- Proxy:代理实例,可以通过
2.3 测试
- 方法
@Test
public void testSave(){
//增强类对象
DaoTransaction daoTransaction = new DaoTransaction();
//目标执行类
IStudentServiceImpl iStudentService = new IStudentServiceImpl();
//方法拦截处理器
TransactionHandler handler = new TransactionHandler(daoTransaction, iStudentService);
//获取代理实例对象
IStudentService proxyStudentService =(IStudentService) Proxy.newProxyInstance(IStudentServiceImpl.class.getClassLoader(), IStudentServiceImpl.class.getInterfaces(), handler);
proxyStudentService.save();
}
2.4 底层运行原理
- 生成代理类的字节码来学习
/**
* 使用来生成字节码学习使用
*/
private void saveProxyClass(String path) throws IOException {
byte[] $proxy1s = ProxyGenerator.generateProxyClass("$proxy1",
IStudentServiceImpl.class.getInterfaces());
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(new File(path + "$Proxy1.class"));
fileOutputStream.write($proxy1s);
} catch (Exception e) {
e.printStackTrace();
} finally {
if(fileOutputStream != null) {
fileOutputStream.flush();
fileOutputStream.close();
}
}
}
- 生成字节码反编译结果
public final class $proxy1 extends Proxy implements IStudentService {
private static Method m1;
private static Method m4;
private static Method m2;
private static Method m0;
private static Method m3;
public $proxy1(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 Student query(Long var1) throws {
try {
return (Student)super.h.invoke(this, m4, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
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 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);
}
}
public final void save(Student var1) throws {
try {
super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m4 = Class.forName("com.ff.service.IStudentService").getMethod("query", Class.forName("java.lang.Long"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m3 = Class.forName("com.ff.service.IStudentService").getMethod("save", Class.forName("com.ff.domain.Student"));
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
- 执行原理图
这里写一个自己的理解,毕竟纠结了几天的东西,感觉终于搞懂了
- 首先我们在我们的主测试程序中通过
Proxy.newProxyInstance()
来进行了一个Proxy类的初始化并返回一个目标类的代理类对象,在这里我们需要注意一点是,我们此时赋值的handler是在其中对Proxy类中的h进行了赋值。 - 而后我们去看它这一步生成的代理类对象,我们可以发现我们自己生成出来的$Proxy和proxyStudentService其实是一个对象!!,而后我们直接用proxyStudentService.save()来执行
- 随后我们必然是执行代理对象的save方法,发现代理对象中其实是
super.h.invoke(this,m3,new Object[]{val})
,细心的你会发现,这里父类的Proxy中的h,在第一步中就被我们自己的handler赋值了,所以我们理所当然他对这个方法拦截器进行了重写,我们当然会执行自己重写的invoke方法 - 程序进入了我们自己的handler之后,我们就执行,这个是就如同静态代理一样,我们的加强类进行了业务上的加强,并将我们的invoke(这里的invoke是我们最初的被代理类对象的)进行融合加强。
- 值得注意的是,我们的代理类和实现类都实现了接口,可以说不实现接口,就没有jdk动态代理。
3 CGLIB动态代理
- jdk动态代理有一个前提,需要代理的类必须实现接口,如果没有实现接口,只能通过CGLIB来实现,其实·就是对于JDK动态代理的一个补充
- 注意:
- 类不能被final修饰
- 方法不能被final修饰
3.1 基础准备
- 导包
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
- 准备需要代理的目标类
public class IStudentServiceImpl implements IStudentService{
public void save(Student student) {
System.out.println("保存学生信息");
}
public Student query(Long id) {
System.out.println("查询操作");
Student student = new Student();
student.setAge(18);
student.setName("sy");
return student;
}
}
public interface IStudentService {
public void save(Student student);
Student query(Long id);
}
3.2实现方法拦截
- 实现方法拦截
public class CglibInterceptor implements MethodInterceptor {
DaoTransaction transaction;
public CglibInterceptor(DaoTransaction transaction) {
this.transaction = transaction;
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//事务增强
transaction.before();
Object ret = methodProxy.invokeSuper(o, objects);
transaction.after();
return ret;
}
}
3.3测试
@Test
public void testSave() throws IOException {
//得到方法拦截器
CglibInterceptor interceptor = new CglibInterceptor(new DaoTransaction());
//使用CGLIB框架生成目标类的子类(代理类)实现增强
Enhancer enhancer = new Enhancer();
//设置父类字节码
enhancer.setSuperclass(IStudentServiceImpl.class);
//设置拦截处理
enhancer.setCallback(interceptor);
IStudentServiceImpl service = (IStudentServiceImpl)enhancer.create();
service.save(new Student());
}
3.4底层运行原理
- 底层运行原理图
- 文字说明:
- 通过继承的方式去获取目标对象的方法
- 通过传递方法拦截器
MethodInterceptor
实现方法拦截,在这里做具体的增强 - 调用生成的代理类对象具体执行重写的save方法,直接去调用方法拦截器里面的intercept方法
- 前后加上了增强操作,从而实现了不修改代码实现业务增强
4 总结
代理类型 | 实现机制 | 回调方式 | 适用场景 | 效率 |
---|---|---|---|---|
JDK动态代理 | 通过实现接口,通过反射获取接口里面的方法,并且自定义InvocationHandler 接口,实现方法拦截 | 调用invoke方法 | 目标类有接口 | 1.8高于CGLIB |
CGLIB动态代理 | 继承机制,通过继承重写目标方法,使用MethodInterceptor调用父类的目标方法从而实现代理而实现代理 | 调用interceptor方法 | 不能使用final修饰的类和方法 | 第一次调用生成字节码比较耗时间,多次调用性能还行 |
5 另一种CGLIB写法
@Test
public void testSave() throws IOException {
//生成目标代理类
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"D:\\ideaProject\\CGLIB-test-01\\src");
//得到方法拦截器
final IStudentServiceImpl service1 = new IStudentServiceImpl();
CglibInterceptor interceptor = new CglibInterceptor(new DaoTransaction());
//使用CGLIB框架生成目标类的子类(代理类)实现增强
Enhancer enhancer = new Enhancer();
//设置父类字节码
enhancer.setSuperclass(IStudentServiceImpl.class);
//设置拦截处理
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
DaoTransaction daoTransaction = new DaoTransaction();
daoTransaction.before();
Object obj = null;
obj = method.invoke(service1,objects);//save
//obj = methodProxy.invokeSuper(o,objects);//CGLIB$save$0
daoTransaction.after();
return obj;
}
});
IStudentServiceImpl service = (IStudentServiceImpl)enhancer.create();
service.save(new Student());
}
- enhancer.create()来完成的初始化,把我们的父类和拦截器进行了记录缓存。