动态代理模式

代理模式

    代理模式:给某一个对象提供一个代理,并由代理对象来控制对真实对象的访问。代理模式是一种结构型设计模式。
   代理模式角色分为 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();
     }
}

         通过静态代理,我们达到了功能增强的目的,而且没有侵入原代码,这是静态代理的一个优点。
         虽然静态代理实现简单,且不侵入原代码,但是,当场景稍微复杂一些的时候,静态代理的缺点也会暴露出来。

  1. 当需要代理多个类的时候,由于代理对象要实现与目标对象一致的接口,有两种方式:
  • 只维护一个代理类,由这个代理类实现多个接口,但是这样就导致代理类过于庞大
  • 新建多个代理类,每个目标对象对应一个代理类,但是这样会产生过多的代理类

     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()));
     }
}

测试类:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值