JAVA设计模式——代理模式

代理模式是一种结构型设计模式。代理模式的思想是:给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用

代理模式涉及3个角色:

  • 抽象对象(Subject):代理和目标的共同接口。共同的接口使得任何可以使用目标对象的地方都可以使用代理对象。
  • 目标对象(Target):要被代理的对象,也称为委托者
  • 代理对象(Proxy):内部包含了一个目标对象的引用,从而代替目标对象为客户提供服务。


静态代理

静态代理模式可以在不修改目标对象的功能前提下,对目标功能扩展。结构图如下:
这里写图片描述

代码实现:

/* 抽象角色 */
public interface Subject {
    void method1();
}

/* 目标(委托类) */
public class Target implements Subject {
    @Override
    public void method1() {
        System.out.println("method 1");
    }
}

/* 代理 */
public class Proxy implements Subject {

    Target target; // 包装一个目标类型对象

    public Proxy(Target target) {
        this.target = target;
    }

    @Override
    public void method1() {
        System.out.println("===start==="); // 目标方法执行前的操作
        target.method1();
        System.out.println("=== end ==="); // 目标方法执行后的操作
    }
}

// 测试
class ProxyTest {
    public static void main(String[] args) {
        Subject subject = new Proxy(new Target());
        subject.method1();
    }
}

运行结果:

===start===
method 1
=== end ===

可以看到,静态代理模式的缺点是很明显的:代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理;另外,如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法,代码维护的复杂度很大。


JDK动态代理

动态代理类的原理是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和目标类(委托类)的关系是在程序运行时确定。

java.lang.reflect.Proxy是 Java 动态代理机制生成的所有动态代理类的父类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。

java.lang.reflect.ProxynewProxyInstance()方法要求传入3个参数,其中最后一个参数是一个调用处理器对象,即实现了java.lang.reflect.InvocationHandler这个接口的类的实例。InvocationHandler定义了一个invoke() 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对目标类(委托类)的代理访问。每次生成动态代理类对象时都要指定一个对应的调用处理器对象。

代码实现:

/* 抽象角色 */
public interface Subject {
    void method1();
}

/* 目标类(委托类) */
public class Target implements Subject {
    @Override
    public void method1() {
        System.out.println("method 1");
    }
}

/* 动态代理生成类 */
public class ProxyFactory {
    // 封装一个目标对象
    private Object target;

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

    // 获取代理对象的方法
    public Object getProxy() {
        return java.lang.reflect.Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                (proxy, method, args) -> {
                    System.out.println("===start==="); // 目标方法执行前的操作
                    Object result = method.invoke(target, args);
                    System.out.println("=== end ==="); // 目标方法执行后的操作
                    return result; // 目标方法的返回值
                }
        );
    }
}

// 测试
class ProxyTest {
    public static void main(String[] args) {
        Subject target = new Target();
        Subject proxy = (Subject) new ProxyFactory(target).getProxy();
        proxy.method1();
    }
}

运行结果:

===start===
method 1
=== end ===

结构图:
这里写图片描述

可以看到,代理工厂和目标是完全松耦合的,根据传入的目标的类型不同,代理工厂可以生成不同类型的代理。这解决了静态代理模式代理类和目标耦合性强的问题。

另外java.lang.reflect.Proxy.newProxyInstance()方法的第二个参数是目标对象实现的接口的类型,所以,使用JDK代理,目标类必须实现了接口。而且,代理的类型应该是接口的类型,像上面的测试代码,如果改成下面这样就会出错,因为代理对象无法强转成Target类型。

public static void main(String[] args) {
    Target target = new Target();
    Target proxy = (Target) new ProxyFactory(target).getProxy(); // 类型转换异常
    proxy.method1();
}


Cglib动态代理

Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展。

JDK的动态代理有一个限制,就是使用动态代理的对象必须至少实现一个接口,如果想代理没有实现接口的类,就可以使用Cglib实现。

Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口。它广泛的被许多AOP的框架使用,例如Spring AOP

Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类。因此,要使用Cglib,必须引入cglib.jarasm.jar。另外Spring也集成了cglib功能,使用Spring框架抢建的项目中可以直接使用Cglib的功能。

代码实现:

/* 目标,注意这里不再需要实现接口了 */
public class Target{
    public void method1() {
        System.out.println("method 1");
    }
}

/* 代理工厂,注意和JDK代理工厂的区别 */
public class ProxyFactory implements MethodInterceptor {
    // 封装一个目标对象
    Object target;

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

    // 实现MethodInterceptor接口中的拦截器方法,实现对目标的操作
    @Override
    public Object intercept(Object object, Method method,
                            Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("===start==="); // 目标方法执行前的操作
        Object result = method.invoke(target, args);
        System.out.println("=== end ==="); // 目标方法执行后的操作
        return result; // 目标方法的返回值
    }

    // 获取代理对象的方法
    public Object getProxy() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass()); // 将目标类(委托类)设为父类
        enhancer.setCallback(this); // 设置回调
        return enhancer.create(); // 生成目标类(委托类)的子类对象(即代表对象)并返回
    }

}

// 测试
class ProxyTest {
    public static void main(String[] args) {
        Target target = new Target(); // 注意这里的类型不再是接口的类型了,因为并不存在接口
        Target proxy = (Target) new ProxyFactory(target).getProxy();
        proxy.method1();
    }
}

运行结果:

===start===
method 1
=== end ===

结构图:
这里写图片描述
可以看到,跟JDK动态代理模式一样,Cglib动态代理模式与目标也是松耦合的,而且这里更简化了,目标并不须要实现任何接口。


代理模式的使用场景

  • 当我们想要隐藏某个类时,可以为其提供代理类。
  • 当一个类需要对不同的调用者提供不同的调用权限时,可以使用代理类来实现(代理类不一定只有一个,我们可以建立多个代理类来实现,也可以在一个代理类中金进行权限判断来进行不同权限的功能调用)。
  • 当我们要扩展某个类的某个功能时,可以使用代理模式,在代理类中进行简单扩展(只针对简单扩展,可在引用委托类的语句之前与之后进行)。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值