天天看点

设计模式-代理模式(Proxy Pattern)

推荐:​​Java设计模式汇总​​

代理模式

定义

代理模式又叫委托模式,是为某个对象提供一个代理对象,并且由代理对象控制对原对象的访问。代理模式通俗来讲就是我们生活中常见的中介。

类型

结构型。

例子

我们每次在​​

​Controller层​

​​中实现向数据库​

​insert数据​

​​,一般都是经过​

​Service层、Dao层,再到数据库​

​​,这里我们将​

​Service层​

​进行代理。

为了简单,有些地方并没有像实际开发一样严格。

Order类(订单类),实体类。

package com.kaven.design.pattern.structural.proxy;

public class Order {
    private Object orderInfo;
    private Integer userId;

    public Object getOrderInfo() {
        return orderInfo;
    }

    public void setOrderInfo(Object orderInfo) {
        this.orderInfo = orderInfo;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }
}      

IOrderDao接口(Dao层接口),这里没有分不同的包。

package com.kaven.design.pattern.structural.proxy;

public interface IOrderDao {
    int insert(Order order);
}      

OrderDaoImpl类(Dao层实现类)。

package com.kaven.design.pattern.structural.proxy;

public class OrderDaoImpl implements IOrderDao {
    public int insert(Order order) {
        System.out.println("Dao层添加Order成功");
        return 1;
    }
}      

IOrderService接口(Service层接口)。

package com.kaven.design.pattern.structural.proxy;

public interface IOrderService {
    int saveOrder(Order order);
}      

OrderServiceImpl类(Service层实现类)。

package com.kaven.design.pattern.structural.proxy;

public class OrderServiceImpl implements IOrderService {

    private IOrderDao iOrderDao;
    public int saveOrder(Order order) {
        // 使用Spring注解会自己注入,这里就直接new了
        iOrderDao = new OrderDaoImpl();
        System.out.println("Service层调用Dao层添加Order");
        return iOrderDao.insert(order);
    }
}      

静态代理

OrderServiceStaticProxy类(Service层的静态代理类)。

package com.kaven.design.pattern.structural.proxy.staticproxy;

import com.kaven.design.pattern.structural.proxy.IOrderService;
import com.kaven.design.pattern.structural.proxy.Order;
import com.kaven.design.pattern.structural.proxy.OrderServiceImpl;

public class OrderServiceStaticProxy {
    private IOrderService iOrderService;

    public int saveOrder(Order order){
        //方法增强,比如分库、校验、安全等处理
        beforeMethod(order);

        iOrderService = new OrderServiceImpl();
        int result = iOrderService.saveOrder(order);

        //方法增强,比如释放资源等处理
        afterMethod();
        return result;
    }

    private void beforeMethod(Order order){
        System.out.println("静态代理 before code");
        //根据用户ID % 2 的值进行分库
        int userId = order.getUserId();
        int dbRouter = userId % 2;
        System.out.println("静态代理分配到 【db"+dbRouter+"】处理数据");
        //TODO 分库操作
    }

    private void afterMethod(){
        System.out.println("静态代理 after code");
    }
}      

应用层代码:

package com.kaven.design.pattern.structural.proxy.staticproxy;

import com.kaven.design.pattern.structural.proxy.Order;

public class Test {
    public static void main(String[] args) {
        Order order = new Order();
        order.setUserId(2);

        OrderServiceStaticProxy orderServiceStaticProxy = new OrderServiceStaticProxy();
        orderServiceStaticProxy.saveOrder(order);
    }
}      

输出:

静态代理 before code
静态代理分配到 【db0】处理数据
Service层调用Dao层添加Order
Dao层添加Order成功
静态代理 after code      

JDK动态代理

OrderServiceDynamicProxy类(Service层的JDK动态代理类)。

从静态代理中可以发现,每个代理类只能为一个接口服务,这样程序开发中必然会产生许多的代理类。所以我们想办法通过一个代理类完成全部的代理功能,那么我们就需要用动态代理。

静态代理是在代码编译后就已经确定被代理的对象了。而动态代理是在代码运行时,通过反射机制实现动态代理,并且能够代理各种类型的对象。

在Java中要想实现动态代理机制,​​

​需要java.lang.reflect.InvocationHandler接口和java.lang.reflect.Proxy类的支持​

​​。

​​

​动态代理类只能代理接口(不支持抽象类),代理类都需要实现InvocationHandler接口,实现invoke方法​

​。invoke方法就是调用被代理接口的所有方法时需要调用的,返回的值是被代理接口的一个实现类。

package com.kaven.design.pattern.structural.proxy.jdk;

import com.kaven.design.pattern.structural.proxy.Order;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class OrderServiceJDKDynamicProxy implements InvocationHandler {
    private Object target;

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

    public Object bind(){
        Class cls = target.getClass();
        return Proxy.newProxyInstance(cls.getClassLoader() , cls.getInterfaces(), this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object argObject = args[0];
        beforeMethod(argObject);
        Object object = method.invoke(target,args);
        afterMethod();
        return object;
    }

    private void beforeMethod(Object object){
        System.out.println("JDK动态代理 before code");
        if(object instanceof Order){
            Order order = (Order)object;
            int userId = order.getUserId();
            int dbRouter = userId % 2;
            System.out.println("JDK动态代理分配到 【db"+dbRouter+"】处理数据");
            //TODO 分库操作
        }
        //TODO 其他类型处理
    }

    private void afterMethod(){
        System.out.println("JDK动态代理 after code");
    }
}      

看不太懂的话,可以先去看一看JDK动态代理相关的博客。

应用层代码:

package com.kaven.design.pattern.structural.proxy.jdk;

import com.kaven.design.pattern.structural.proxy.IOrderService;
import com.kaven.design.pattern.structural.proxy.Order;
import com.kaven.design.pattern.structural.proxy.OrderServiceImpl;

public class Test {
    public static void main(String[] args) {
        Order order = new Order();
        order.setUserId(1);

        IOrderService orderServiceJDKDynamicProxy = (IOrderService) new OrderServiceJDKDynamicProxy(new OrderServiceImpl()).bind();
        orderServiceJDKDynamicProxy.saveOrder(order);
    }
}      

输出:

JDK动态代理 before code
JDK动态代理分配到 【db1】处理数据
Service层调用Dao层添加Order
Dao层添加Order成功
JDK动态代理 after code      

CGLib动态代理

JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了。CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。

OrderServiceCGLibDynamicProxy类(Service层的CGLib动态代理类)。

package com.kaven.design.pattern.structural.proxy.cglib;

import com.kaven.design.pattern.structural.proxy.Order;
import com.kaven.design.pattern.structural.proxy.OrderServiceImpl;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class OrderServiceCGLibDynamicProxy implements MethodInterceptor {

    public OrderServiceImpl bind(){

        Enhancer enhancer = new Enhancer();//生成代理对象
        enhancer.setSuperclass(OrderServiceImpl.class);//设置对谁进行代理
        enhancer.setCallback(this);//代理要做什么
        OrderServiceImpl orderService = (OrderServiceImpl) enhancer.create();//创建代理对象

        return orderService;
    }

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

    private void beforeMethod(Object object){
        System.out.println("CGLib动态代理 before code");
        if(object instanceof Order){
            Order order = (Order)object;
            int userId = order.getUserId();
            int dbRouter = userId % 2;
            System.out.println("CGLib动态代理分配到 【db"+dbRouter+"】处理数据");
            //TODO 分库操作
        }
        //TODO 其他类型处理
    }

    private void afterMethod(){
        System.out.println("CGLib动态代理 after code");
    }
}      

应用层代码:

package com.kaven.design.pattern.structural.proxy.cglib;

import com.kaven.design.pattern.structural.proxy.IOrderService;
import com.kaven.design.pattern.structural.proxy.Order;

public class Test {
    public static void main(String[] args) {
        Order order = new Order();
        order.setUserId(1);

        OrderServiceCGLibDynamicProxy orderServiceCGLibDynamicProxy =
                new OrderServiceCGLibDynamicProxy();
        IOrderService iOrderService = orderServiceCGLibDynamicProxy.bind();
        iOrderService.saveOrder(order);
    }
}      

输出:

CGLib动态代理 before code
CGLib动态代理分配到 【db1】处理数据
Service层调用Dao层添加Order
Dao层添加Order成功
CGLib动态代理 after code      

应用场景

  • 应用远程代理控制访问远程对象。
  • 虚拟代理控制访问创建开销大的对象。
  • 保护代理基于权限控制对资源的访问。

优点

  • 实现了访问者与访问对象之间的解耦。
  • 代理模式在应用层与对象之间起到中介作用,保护了对对象的访问。
  • 代理模式可以在访问过程中增加逻辑,如Spring框架的AOP。
  • 增加代理会使程序请求处理变慢。
  • 类的数量变多,系统更加复杂。