代理模式01-初体验

定义

Proxy Pattern 是一种结构模式,指为其他对象提供一种代理以控制对这个对象的访问。

类图

image

代理模式一般包含三种角色:

Subject:
定义的RealSubject和Proxy的共有方法,这个类可以是接口,也可以是抽象类。这样所有使用RealSubject的地方都可以用Proxy代替。

RealSubject:
被代理类,此类定义了真实的Subject实现。

Proxy:
代理类一般要持有一个被代理的对象的引用,并可能负责创建和删除被代理对象。

静态代理和动态代理

代理模式的实现大体上可以分为两种,一种是静态代理,一种是动态代理。两种代理从JVM加载类的角度来讲,本质上都是一样的。

静态代理

上面的类图就是传统的静态代理,代码不贴了。(然而相关代码接下来的demo里有用到。)

优点:

  • 实现简单。

缺点:

  • 静态代理只能通过手动完成被代理对象的注入。
  • 如果被代理类增加了新的方法,代理类需要同步增加,违背了开闭原则。
  • 一个代理类只能处理一个被代理类。
动态代理

JAVA的SDK中已经有了实现方法,Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h);
使用JDK动态代理需要注意,目标类必须实现的某个接口,如果某个类没有实现接口则不能生成代理对象。

优点:

  • 动态代理扩展性好。
    • 动态代理采用在运行时动态生成字节码的方式,取消了被代理类的扩展限制,遵循开闭原则。
  • 被代理对象自动注入。

缺点:

  • 执行效率相对要低,每次都要反射动态调用控。
代码示例
public static class SubjectInvocationHandler implements InvocationHandler {

    private Object object;

    public SubjectInvocationHandler(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before 动态代理...");
        System.out.println(proxy.getClass().getName());
        System.out.println(this.object.getClass().getName());
        return method.invoke(object, args);
    }
}

public static void main(String[] args) {
    Subject o = (Subject) Proxy.newProxyInstance(Main.class.getClassLoader(), new Class[]{Subject.class}, new SubjectInvocationHandler(new RealSubject()));
    o.request();
}
Cglib动态代理
添加Maven依赖
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib-nodep</artifactId>
    <version>2.2</version>
</dependency>
代码示例
public static void main(String[] args) {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(RealSubject.class);
    enhancer.setCallback((MethodInterceptor) (obj, method, args1, proxy) -> {
        System.out.println("before method run...");
        Object result = proxy.invokeSuper(obj, args1);
        System.out.println("after method run...");
        return result;
    });
    RealSubject subject = (RealSubject) enhancer.create();
    subject.request();
}

Cglib原理是针对目标类生成一个子类,覆盖其中的所有方法,所以目标类和方法不能声明为final类型。
Cglib采用ASM框架写Class字节码,实现比JDK复杂,所以生产代理类比JDK动态代理效率低。
JDK是采用反射机制调用的,而CGLib通过FastClass机制直接调用方法,所以只需效率更高。

FastClass

FastClass不使用反射类(Constructor或Method)来调用委托类方法,而是动态生成一个新的类(继承FastClass),向类中写入委托类实例直接调用方法的语句,用模板方式解决Java语法不支持问题,同时改善Java反射性能。

动态类为委托类方法调用语句建立索引,使用者根据方法签名(方法名+参数类型)得到索引值,再通过索引值进入相应的方法调用语句,得到调用结果。

Spring中的代理选择原则
  • 当Bean有实现接口时,Spring就会调用JDK动态代理。
  • 当Bean没有实现接口时,Spring会选择CGLib代理。
  • Spring可以通过配置强制使用CGLib代理。

补充阅读

警惕动态代理导致的Metaspace内存泄漏问题

https://blog.csdn.net/xyghehehehe/article/details/78820135

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值