设计模式 - 代理模式

79 阅读7分钟

代理模式

  1. 静态代理
  2. 动态代理

image.png

静态代理

角色分析

  1. 抽象角色:一般用接口或抽象类来解决
  2. 真实角色:被代理的角色
  3. 代理角色:代理真实角色的角色,在代理成功后,会做一些附属操作
  4. 客户:找代理的角色

文件目录

image.png

代码步骤

1. 抽象角色

image.png

2.真实角色

image.png

3.代理角色

image.png

4.客户

image.png

优点

  1. 可以使真实角色做的事更纯粹,不用关注公共业务
  2. 公共业务可以交给代理角色,实现了业务的分工
  3. 公共业务的扩展很方便,便于集中管理
  4. 可以在不改变原有业务的前提下,实现业务的增强

缺点

  1. 一个真实角色就会对应一个代理角色,如果要新增一个真实角色,会使代码量成倍增加

优点的第四点扩展

需求:在不改动原有代码的前提下,新增操作日志打印 步骤:

  1. 把原有实现类转化为Impl类(proxy只能代理接口的实现类)
  2. 逆向新增一个Impl类的interface
  3. 新增一个Impl类的Proxy代理类,并继承implements interface(这一步是为了快速继承所有的原有方法)
  4. 在Proxy类中,设置Impl类为其属性,并复用其中的方法
  5. 最后在Proxy类中实现日志打印方法,并加载每个@override方法中
**
 * 需求:在不改动原有代码下,新增每个操作的日志打印
 * 解析:即代码增强
 * 解答:使用代理类
 */
public class UserServiceProxy implements UserService {
    private UserServiceImpl userServiceImpl;

    public void setUserServiceImpl(UserServiceImpl userServiceImpl) {
        this.userServiceImpl = userServiceImpl;
    }


    @Override
    public void add() {
        log("add");
        userServiceImpl.add();
    }

    @Override
    public void delete() {
        log("delete");
        userServiceImpl.delete();
    }

    @Override
    public void update() {
        log("update");
        userServiceImpl.update();
    }

    @Override
    public void query() {
        log("query");
        userServiceImpl.query();
    }

    public void log(String msg) {
        System.err.println("log " + msg);
    }
}

动态代理(通过反射机制实现代理)

  1. 动态代理和静态代理的角色是一样的
  2. 动态代理的代理类是动态生成的,不是我们直接写好的
  3. 动态代理分为两大类:基于接口的动态代理、基于类的动态代理
  4. 基于接口的动态代理:JDK动态代理
  5. 基于类的动态代理:cglib,利用字节码技术生成代理对象
  6. java字节码原理:javassist(放在jboss应用服务器,不是tomcat)

JDK自带的Proxy动态代理

代理接口的实现类

public interface UserService {
    void add();
    void delete();
    void update();
    void query();
}
public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        System.err.println("新增");
    }

    @Override
    public void delete() {
        System.err.println("删除");
    }

    @Override
    public void update() {
        System.err.println("修改");
    }

    @Override
    public void query() {
        System.err.println("查询");
    }
}

动态代理工具类的编写

/**
 * JDK自带的动态代理
 * 可编写为代理工具类,此代码基本上是不变的
 * InvocationHandler: 调用处理程序(被代理接口中的程序)并返回结果
 */
public class ProxyInvocationHandler implements InvocationHandler  {
    /**
     * 被代理的接口
     */
    private Object target;

    /**
     * 外部设置该接口
     *
     * @param target 被代理的接口
     */
    public void setTarget(Object target) {
        this.target = target;
    }

    /**
     * 通过反射动态获取接口的代理类
     * Proxy类:生成动态代理实例
     *
     * @return 代理类
     */
    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    /**
     * 执行接口中的方法
     *
     * @param proxy 代理类
     * @param method 被代理的接口
     * @param args 参数
     * @return 结果
     * @throws Throwable 异常信息
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log(method.getName());
        Object result = method.invoke(target, args);
        System.err.println(new SimpleDateFormat("yyyyMMdd HH:mm:ss").format(new Date()));
        System.err.println("-------------");
        return result;
    }

    private void log(String msg) {
        System.err.println("正在执行" + msg + "方法");
    }
}

测试类

public class Test {
    public static void main(String[] args) {
        // 真实角色
        UserServiceImpl userServiceImpl = new UserServiceImpl();

        // 动态代理角色,此时代理角色还不存在
        ProxyInvocationHandler pih = new ProxyInvocationHandler();

        // 设置需要代理的对象
        pih.setTarget(userServiceImpl);

        // 动态生成代理角色
        UserService proxy = (UserService) pih.getProxy();

        // 执行代理行为
        proxy.add();
        proxy.delete();
        proxy.update();
        proxy.query();
    }

动态代理完成,此时猜想是否可以代理“非”接口的实现类

public class Car implements CarService {
    private String brand;
    private String color;
    private boolean ifNow;
    private long price;

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public boolean isIfNow() {
        return ifNow;
    }

    public void setIfNow(boolean ifNow) {
        this.ifNow = ifNow;
    }

    public long getPrice() {
        return price;
    }

    public void setPrice(long price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Car{" +
                "brand='" + brand + ''' +
                ", color='" + color + ''' +
                ", ifNow=" + ifNow +
                ", price=" + price +
                '}';
    }
}

测试类

public class Test {
    public static void main(String[] args) {


        Car car = new Car();
        car.setBrand("Porsche");
        car.setColor("DarkGreen");
        car.setIfNow(true);
        car.setPrice(100000L);

        ProxyInvocationHandler pih2 = new ProxyInvocationHandler();
        pih2.setTarget(car);
        Car carProxy = (Car) pih2.getProxy();
        System.err.println(carProxy.toString());

    }
}

结果会报错

Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to com.shinefriends.juc.proxy.activeProxy.jdkProxy.Car at com.shinefriends.juc.proxy.activeProxy.jdkProxy.Test.main(Test.java:15)

把Car改为接口的实现类


public interface CarService{

    String toString();

}
public class Car implements CarService {
    private String brand;
    private String color;
    private boolean ifNow;
    private long price;

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public boolean isIfNow() {
        return ifNow;
    }

    public void setIfNow(boolean ifNow) {
        this.ifNow = ifNow;
    }

    public long getPrice() {
        return price;
    }

    public void setPrice(long price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Car{" +
                "brand='" + brand + ''' +
                ", color='" + color + ''' +
                ", ifNow=" + ifNow +
                ", price=" + price +
                '}';
    }

}

改写测试类,成功执行

public class Test {
    public static void main(String[] args) {


        Car car = new Car();
        car.setBrand("Porsche");
        car.setColor("DarkGreen");
        car.setIfNow(true);
        car.setPrice(100000L);

        ProxyInvocationHandler pih2 = new ProxyInvocationHandler();
        pih2.setTarget(car);
        CarService carProxy = (CarService) pih2.getProxy();
        System.err.println(carProxy.toString());

    }
}

此时我还想通过代理类对被代理类的属性进行设置, 比如 carProxy.setColor("blue");会发现这个方法并不允许调用。因此尝试到CarService 接口中定义一个 void setCarColor(String color); 并实现

public interface CarService{

    String toString();

    void setCarColor(String color);
}

Car实现类中

@Override
public void setCarColor(String color) {
    this.color = color;
}

修改测试类,成功执行

ProxyInvocationHandler pih2 = new ProxyInvocationHandler();
pih2.setTarget(car);
Car carProxy = (Car) pih2.getProxy();
System.err.println(carProxy.toString());

System.err.println("-------");
carProxy.setCarColor("blue"); // 该方法为接口中的方法
System.err.println(carProxy.toString());

总结:proxy代理方式只能代理接口的实现类,且只能调用接口中定义的方法

如果只能代理接口的实现类,一定会改动很多代码,由此引申出“基于类的动态代理”->"cglib"

cglib

首先引入cglib的依赖

implementation 'cglib:cglib:3.3.0'

对象类

public class Car {
    private String brand;
    private String color;
    private boolean ifNow;
    private long price;

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public boolean isIfNow() {
        return ifNow;
    }

    public void setIfNow(boolean ifNow) {
        this.ifNow = ifNow;
    }

    public long getPrice() {
        return price;
    }

    public void setPrice(long price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Car{" +
                "brand='" + brand + ''' +
                ", color='" + color + ''' +
                ", ifNow=" + ifNow +
                ", price=" + price +
                '}';
    }
}

代理类(包含泛型的写法、Object的写法)

/**
 * 基于类的代理
 * 通过继承,实现方法的覆盖并增强,因此不支持final方法
 * @param <T> 也可以使用Object
 */
public class CglibProxy<T> implements MethodInterceptor { // 泛型的写发

    private T target;

    public T getProxy(T target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return (T) enhancer.create();
    }

//    public class CglibProxy implements MethodInterceptor { // Object的写法
//    private Object target;
//
//    public void setTarget(Object target) {
//        this.target = target;
//    }
//
//    public Object getProxy() {
//        Enhancer enhancer = new Enhancer();
//        enhancer.setSuperclass(target.getClass());
//        enhancer.setCallback(this);
//        return enhancer.create();
//    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.err.println("cglib proxy method name: " + method.getName());
        Object result = proxy.invoke(target, args);
        System.err.println("cglib proxy finish");
        return result;
    }
}

测试类

public class Test {
    public static void main(String[] args) {
        Car car = new Car();
        car.setBrand("Porsche");
        car.setColor("DarkGreen");
        car.setIfNow(true);
        car.setPrice(100000L);

        // 泛型的写法
        Car carProxy = new CglibProxy<Car>().getProxy(car);
        System.err.println(carProxy.toString());
        car.setColor("red");
        System.err.println(carProxy.toString());

//        // Object的写法
//        CglibProxy cglibProxy = new CglibProxy();
//        cglibProxy.setTarget(car);
//        Car carProxy = (Car) cglibProxy.getProxy();
//
//        System.err.println(carProxy.toString());
//        car.setColor("red");
//        System.err.println(carProxy.toString());
    }
}

总结,cglib可以直接代理类,并且可以使用类中的方法

JDK Proxy动态代理与cglib动态代理的对比

JDK动态代理的优点:

  1. JDK动态代理是Java标准库的一部分,因此它不需要引入任何外部依赖。
  2. JDK动态代理只需要实现接口即可生成代理对象,不需要改变原有类的结构。
  3. 由于JDK动态代理是基于接口实现的,因此它更适合用于代理接口实现类的场景。

JDK动态代理的缺点:

  1. JDK动态代理只能代理实现了接口的类,无法代理没有实现接口的类。
  2. JDK动态代理在生成代理对象时,需要使用反射机制,因此它的效率相对较低。

CGLIB代理的优点:

  1. CGLIB代理是基于字节码技术实现的,因此它的效率比JDK动态代理更高。
  2. CGLIB代理可以代理没有实现接口的类。

CGLIB代理的缺点:

  1. CGLIB代理需要引入外部依赖。
  2. CGLIB代理在生成代理对象时,需要改变原有类的结构,因此它可能会引起一些问题,例如无法代理final类或final方法等问题。

综上所述,JDK动态代理适用于代理接口实现类的场景,而CGLIB代理适用于代理没有实现接口的类的场景。如果你需要代理接口实现类,而且不想引入额外的依赖,那么JDK动态代理是一个不错的选择;如果你需要代理没有实现接口的类,那么CGLIB代理可能更适合你的需求。

JAVA动态字节技术之Javassist

待完善