一、生活中常见的代理案例
- 房地产中介:客户手里没有房源信息,找一个中介帮忙
- 商品代购:代理者一般有好的资源渠道,降低购物成本(如海外代购,自己不用为了买东西出国)
二、为什么要使用代理
- 对于消费者来说,可以减少成本,只需要关心自己需要的商品,不需要寻找资源
三、代理模式在Java中的应用
- 统一异常处理
-
Mybatis使用了代理
- Spring Aop实现原理
- 日志框架
四、什么是代理模式
1、代理模式(Proxy Pattern):23种设计模式之一,属于结构模式,指的是一个对象本身不做实际操作,而是通过其他对象来得到自己想要的结果
2、意义:目标对象只需要关心自己的实现细节,通过代理对象实现功能的增强,可以扩展目标对象的功能
3、重要思想:不能随便修改源码,如果要修改源码,通过修改代理的方式实现功能的拓展
五、代理的图例释义
以房地产中介为例:
六、Java中代理的实现方式
1、元素组成:
接口,定义行为和规范
被代理类,是目标对象
代理类,做功能增强的
2、静态代理
2.1案例
通过代理模式实现事务操作
2.2实现案例
创建domain(POJO)对象
创建service接口定义规范
创建实现类(impl),被代理类
创建事务对象(前置通知与后置通知)
创建代理类对象(Proxy):实现(implement)接口,访问实现类
创建测试类测试代理类对象
2.3存在的问题
1)不利于代码拓展,比如说接口新添一个抽象方法,所有实现类都需要重新实现
2)代理对象要创建很多,非常不利于代码维护
3、动态代理(重点)
3.1概述:
不改变原有功能代码的前提下,动态的实现方法的增强
3.2JDK动态代理
3.2.1基础准备
- 创建POJO类
package cn.yxcode.domain;
import lombok.Data;
// Lombok是一个Java库,能自动插入编辑器并构建工具简化Java开发。
// 通过添加注解的方式,不需要为类编写getter或eques方法,同时可以自动化日志变量。
// 添加该插件请在pom.xml中进行配置
//创建学生类
@Data
public class Student {
private String name;
private int age;
}
- 创建service接口
-
public interface IstudentService{ /** *添加学生 */ void save(); /** *查询学生信息 *@param id *@return */ Student query(Long id); }
- 创建service实现类(需要代理的类)
package cn.yxcode.sevice.impl;
//创建service实现类(需要代理的类)
import cn.yxcode.sevice.IstudentService;
import cn.yxcode.sevice.cn.Student;
public class StudentServiceimpl implements IstudentService {
@Override
public void save() {
System.out.println("保存学生信息");
}
@Override
public Student query(Long id) {
System.out.println("查询操作");
Student student=new Student();
student.setName("yx");
student.setAge(20);
return student;
}
}
- 增强类(也叫做切面类)
package cn.yxcode.transaction;
//增强类
public class DaoTransaction {
public void before(){
System.out.println("开启事务操作");
}
public void after(){
System.out.println("关闭事务");
}
}
3.2.2实现InvocationHandler接口
InvocationHandler接口:用来做方法拦截
package cn.yxcode.handle;
import cn.yxcode.sevice.IstudentService;
import cn.yxcode.transaction.DaoTransaction;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//创建处理事务的一个类
public class TransactionHandle implements InvocationHandler {
//InvocationHandler接口:用来做方法拦截
/**
* proxy:可以通过newPoxyinstance创建代理实例
* Method:执行目标方法,invoke方法执行
* args:参数数组
*/
// 增强类对象
private DaoTransaction transaction;
// private IstudentService istudentService;由于未来开发不止一个service所以用Object代替效果
// 需要代理的对象
private Object obj;
public TransactionHandle(DaoTransaction transaction,Object obj){
this.transaction=transaction;
this.obj=obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object ret=null;
// 判断当前方法是否为save,是才做事务操作
if ("save".equals(method.getName())){
transaction.before();
ret= method.invoke(obj,args);
transaction.after();
}else {
ret= method.invoke(obj,args);
}
return ret;
}
}
3.2.3测试实现
package cn.yxcode;
import cn.yxcode.domain.Student;
import cn.yxcode.handle.TransactionHandle;
import cn.yxcode.sevice.IstudentService;
import cn.yxcode.sevice.impl.StudentServiceimpl;
import cn.yxcode.transaction.DaoTransaction;
import org.junit.Test;
import java.lang.reflect.Proxy;
public class TestStudent {
@Test
public void testSave(){
// 增强类对象(前置通知和后置通知)
DaoTransaction transaction =new DaoTransaction();
// 目标执行类(即接口和实现类)
IstudentService service= new StudentServiceimpl();
// 方法拦截处理器
TransactionHandle handle= new TransactionHandle(transaction,service);
// 获取代理实例对象
IstudentService ProxystudentService=(IstudentService) Proxy.newProxyInstance(StudentServiceimpl.class.getClassLoader(),StudentServiceimpl.class.getInterfaces(),handle);
ProxystudentService.save();
Student query=ProxystudentService.query(1);
System.out.println("查询信息成功,姓名为:"+query.getName()+", 年龄为:"+query.getAge());
}
}
//newProxyInstance(类加载器,目标类所实现的所有接口 ,方法拦截器实现方法的增强)
效果
七、总结
JDK动态代理的实现机制:
通过实现接口,通过反射机制获取接口里的方法,并且自定义InvocationHandler接口,实现 方法拦截
回调方式:调用invoke方法实现增强
使用场景:目标类有接口实现,但是能用jdk尽量用(没有的话用CGLIB动态代理的方式,暂时未学后续补上)
效率:1.8高于CGLIB代理机制