代理模式
代理模式:给某一个对象提供一个代理,并由代理对象来控制对真实对象的访问。代理模式是一种结构型设计模式。
代理模式角色分为 3种:
- Subject(抽象主题角色):定义代理类和真实主题的公共对外方法,通常被设计成接口;
- RealSubject(真实主题角色):真正实现业务逻辑的类;
- Proxy(代理主题角色):用来代理和封装真实主题;
代理模式的结构比较简单,其核心是代理类,为了让客户端能够一致性地对待真实对象和代理对象,在代理模式中引入了抽象层。
如果根据字节码的创建时机来分类,可以分为静态代理和动态代理:
- 所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和真实主题角色的关系在运行前就确定了。
- 而动态代理的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以在运行前并不存在代理类的字节码文件
静态代理
假设有 UserService接口及其实现类 UserServiceImpl,我们需要在不改变实现类代码的基础上,增加日志记录的功能。
UserService:
public interface UserService {
public void select();
public void update();
}
UserService:
public class UserServiceImpl implements UserService{
public void select(){
System.out.println("查询 selectById");
}
public void update(){
System.out.println("更新 update");
}
}
我们将通过静态代理对UserServiceImpl 进行功能增强,在调用 select 和 update 之前记录一些
日志。
静态代理通过 UserServiceProxy实现代理类同时也需要实现 UserService 接口
Proxy:
public class UserServiceProxy implements UserService
private Userservice target;// 被代理的对象
public UserServiceProxy(UserService target){
this.target =target;
}
public void select(){
before();
target.select(); // 这里才实际调用真实主题角色的方法
after();
}
public void update(){
before();
target.update(); //这里才实际调用真实主题角色的方法
after();
}
private void before(){ // 在执行方法之前执行
System.out.println(String.format("log start time [%s]",new Date()));
}
private void after(){ //在执行方法之后执行
System.out.println(String.format("log end time [%s]",new Date()));
}
}
测试:
public class Test{
public static void main(String[]args){
UserService userServiceImpl=new UserServiceImpl();
UserService proxy =new UserServiceProxy(userServiceImpl);
proxy.select();
proxy.update();
}
}
通过静态代理,我们达到了功能增强的目的,而且没有侵入原代码,这是静态代理的一个优点。
虽然静态代理实现简单,且不侵入原代码,但是,当场景稍微复杂一些的时候,静态代理的缺点也会暴露出来。
- 当需要代理多个类的时候,由于代理对象要实现与目标对象一致的接口,有两种方式:
- 只维护一个代理类,由这个代理类实现多个接口,但是这样就导致代理类过于庞大
- 新建多个代理类,每个目标对象对应一个代理类,但是这样会产生过多的代理类
2.当接口需要增加、删除、修改方法的时候,目标对象与代理类都要同时修改,不易维护。
动态代理
Java中两种常见的动态代理方式: JDK 原生动态代理和CGLIB 动态代理(第三方开源类库)
JDK动态代理主要涉及两个类:java.lang.reflect.Proxy和 java.lang.reflect.InvocationHandler 。我们通过编写一个调用逻辑处理器LogHandler 类案例来提供日志增强功能,并实现InvocationHandler 接口;在LogHandler 中维护一个目标对象,这个对象是被代理的对象(真实主题角色);
在 invoke()方法中编写方法调用的逻辑处理。
//动态代理的处理类
public class LogHandler implements InvocationHandler{
0bject target; // 被代理的对象,实际的方法执行者
public LogHandler(Object target){
this.target = target;
}
@0verride
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable{
before();
Object result =method.invoke(target,args);// 调用 target 的 method 方法
after();
return result; // 返回方法的执行结果
}
//调用invoke方法之前执行
private void before(){
System.out.println(String.format("log start time [%s]",new Date()));
}
//调用invoke方法之后执行
private void after(){
System.out.println(String.format("log end time [%s]",new Date()));
}
}
测试类: