代理模式

141 阅读4分钟
  • 定义:为其他对象提供代理,以控制对这个对象的访问,代理对象在客户端和目标对象之间起到中介的作用。
  • 使用场景:
  1. 保护目标对象,客户端并不知道具体调用了哪一个目标对象。
  2. 增强目标对象
  • 优点
  1. 代理模式能够将代理对象与真实被调用的目标对象分离
  2. 一定程度上降低了系统的耦合度,扩展性好(待进一步分析)
  • 缺点
  1. 使用代理模式需要添加代理类,会导致系统设计中类数目的增加
  2. 在客户端和目标对象之间增加了一个代理对象,会造成请求处理速度变慢
  3. 增加了系统的复杂度

代理模式分类: 静态代理、jdk动态代理、cglib代理类。

静态代理:在代码中显示定义一个业务实现类进行代理,对目标对象的方法进行进一步的封装,只能代理某一个固定的目标对象。
下面的uml类图中,代理类OrderServiceStaticService中包含被代理对象OrderService,在代理类中对被代理对象的被代理方法进行增强,客户端直接调用代理类的createOrder方法(被代理的方法)即可。

jdk动态代理:jdk动态代理无法代理类,只能代码接口(即目标对象必须实现接口),代理类时运行时由jvm根据业务实现类对象和方法名动态地创建一个代理类的class文件,此class文件被字节码引擎执行,通过代理类对象进行方法调用。
jdk动态代理的使用:

/**
代理得业务逻辑类,必须实现InvocationHandler接口并重写invoke()方法,  
代理类方法的调用都会映射到此类的invoke()方法,所以这是一个实际调用的方法。
*/
public class OrderServiceDynamicProxy implements InvocationHandler {

    private Object target;

    public OrderServiceDynamicProxy(Object target) {
        this.target = target;
    }

    /**
     * 获取代理类
     * @return
     */
    public Object bind(){
        Class cls = target.getClass();
        Object obj = Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),this);
        return obj;
    }

    /**
     *
     * @param proxy
     * @param method 需要被增强的method方法
     * @param args 被增强方法的参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         beforeMethod(args[0]);
         Object obj = method.invoke(target,args);
         afterMethod();
         return obj;
    }

    private void beforeMethod(Object obj){
        System.out.println("动态代理 before code");
        // 动态代理在一个业务逻辑类中处理不同的被代理对象
        if (obj instanceof Order){
            Order order = (Order)obj;
            int userId = order.getUserId();
            // 根据userId获取db路由
            int dbRouter = userId % 2;
            System.out.println("动态理分配到[db" + dbRouter + "]处理数据");
        }else {

        }
    }

    private void afterMethod(){
        System.out.println("动态代理 after code");
    }
}
/*  
客户端类
*/
public class Test {

    public static void main(String[] args){
        // ============动态代理测试=================================
        // OrderServiceImpl是需要被代理的类,实现了IOrderService接口,createOrder()是需要备注增强的方法。
        IOrderService dynamicProxy = (IOrderService) new OrderServiceDynamicProxy(new OrderServiceImpl()).bind();
        dynamicProxy.createOrder(order);
    }
}

cglib代理类:可以代理类(没有实现接口),它针对类实现进行代理。cglib的实现原理很简单,它会生成一个被代理类的子类,覆盖其中的方法,也就是通过继承和重写。(注意被代理类和被代理方法都不能) cglib动态代理的使用

/*
cglib业务逻辑处理类,必须实现methodInterceptor接口,
并重写intercept()方法,在方法中对目标方法进行增强。
*/
public class OrderServiceCglibDynamicProxy implements MethodInterceptor {

    /**
     *创建CGLIB代理对象
     * @return
     */
    public Object createProxyObject(){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(OrderServiceImpl.class);
        enhancer.setCallback(new OrderServiceCglibDynamicProxy());
        return enhancer.create();
    }


    @Override
    public Object intercept(Object target, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        beforeMethod(objects[0]);
        Object result =  methodProxy.invokeSuper(target,objects);
        afterMethod();
        return result;
    }

    /**
     * 被代理方法执行前的处理
     * @param obj
     */
    private void beforeMethod(Object obj){
        System.out.println("CGLIB动态代理 before method");
        if (obj instanceof Order){
            Order order = (Order) obj;
            int userId = order.getUserId();
            int dbRouter = userId%2;
            System.out.println("动态代理分配到db" + dbRouter+"上");
        }else {
            // 参数不是Order对象,做其它处理
        }

    }

    /**
     *被代理方法执行后的处理
     */
    private void afterMethod(){
        System.out.println("CGLIB动态代理 after method");
    }

}

客户端调用:
public class Test {
    public static void main(String[] args){
        Order order = new Order();
        order.setUserId(1);
        // ============CGLIB动态代理测试===========================
        OrderServiceCglibDynamicProxy orderServiceCglibDynamicProxy = new OrderServiceCglibDynamicProxy();
        OrderServiceImpl orderService = (OrderServiceImpl) orderServiceCglibDynamicProxy.createProxyObject();
        orderService.createOrder(order);
    }

控制台输出结果:
CGLIB动态代理 before method
动态代理分配到db1上
service层调用dao层
数据库订单创建成功
CGLIB动态代理 after method
  • spring的默认代理选择
  1. bean没有实现接口->cglib
  2. bean实现接口->jdk动态代理
  • 代理速度对比
  1. cglib动态代理底层是采用asm字节码0生成的,这种方要式比使用java反射效率要高,jdk动态代理的速度相对要比cglib的速度要快。

参考资料:geely老师的教学,本文仅做个人笔记使用。