Java架构算法设计模式和编程理论Java 杂谈

方法的增强——代理模式

2019-05-14  本文已影响0人  RunAlgorithm

1. 定义

代理模式:给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。

代理对象会表现出和目标对象一样的行为,同时对目标对象进行增强,完成了一些无法完成的业务。

现实的例子:

代理模式-效果图

2. 设计

代理模式两大角色:

类图如下:

代理模式-类图

根据业务模型,代理可以进行分类。

根据我的理解,从对目标对象侵入程度的影响,可以分成两大类:

代理模式的实现,有个关键的地方就是 代理与目标需要有一致的行为

Java 有这几种方式:

2.1. 静态代理

目标对象创建接口:

public interface ISubject {
    void request();
}

实现类:

public class RealSubject implements ISubject {
    public void request() {
        System.out.println("Hello world!");
    }
}

代理对象也实现该接口,并且持有目标对象的引用:

public class Proxy implements ISubject {
   
   public ISubject target;

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

    public void request() {
        System.out.println("begin");
        target.request();
        System.out.println("end");
    }

    public static ISubject wrap(ISubject target) {
        return new Proxy(target);
    }
}

使用者面向接口编程:

public class TestStaticProxy {

    public static void main(String[] args) {

        ISubject subject = new RealSubject();

        System.out.println("代理前:");
        subject.request();

        System.out.println("代理后:");
        subject = Proxy.wrap(subject);
        subject.request();
    }
}

确实实现了代理。

静态代理实现简单,但有以下弊端:

2.2. JDK 动态代理

无需静态创建代理类,解决了静态代理类膨胀的问题。

使用 JDK reflect 包的工具,核心类有:

两种的关系为:把目标类的接口传递给 Proxy 生成代理对象,在调用接口的方法时,会触发 InvocationHandler 回调,在回调中实现代理业务。

还是上面的例子,这里代理对象实现 InvocationHandler:

public class JDKProxy implements InvocationHandler {

    public Object target;

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

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("begin");
        Object result = method.invoke(target, args);
        System.out.println("end");
        return result;
    }

    @SuppressWarnings("unchecked")
    public static <T> T wrap(Object target) {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new JDKProxy(target));
    }
}

使用:

public class TestJDKProxy {

    public static void main(String[] args) {
        ISubject subject = new RealSubject();

        System.out.println("代理前:");
        subject.request();

        System.out.println("代理后:");
        subject = JDKProxy.wrap(subject);
        subject.request();
    }
}

因为动态去创建代理对象,所以 SimpleProxy 可以应用到任务实现接口的目标类型中,而不仅仅是 ISubject。

JDK 动态代理虽强大,但也存在几个问题:

假如我们的目标类就没有实现接口,该怎么做?

2.3. Cglib 动态代理

静态代理和 JDK 动态代理都需要实现接口,但 Cglib 不需要。

Cglib 的底层使用了 ASM 修改字节码,动态创建代理类并加载。

虽然没有实现同一套接口,但与目标类属于继承关系,所以 final 类型的类是无法代理的。

核心类:

使用方式和 JDK 动态代理类似:

public class CglibProxy implements MethodInterceptor {

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("begin");
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("end");
        return result;
    }

    @SuppressWarnings("unchecked")
    public static <T> T wrap(Class<T> targetCls) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetCls);
        enhancer.setCallback(new CglibProxy());
        return (T) enhancer.create();
    }
}

及时目标对象没有实现接口,也可以进行代理。

3. 应用

在实际开发中,代理模式有着很广的应用,也是很多框架的基础。

具体的应用有:

3.1. Spring:AopProxy

AOP 编程,在我理解就是把通用的规则、规律或者公共行为统一在统一的地方搞定,然后通知到各个要处理的分类对象中(对象没有继承关系)。这样就可以实现非侵入式代码,而且便于维护和修改规则代码。

Spring AOP 编程中,来指定切入点(point cut),获取连接点(join point),编写通知(advice),内聚到一个切面类(Apect)中并使用 @Aspect 注解。

框架会去识别到这个切面类。

然后 Bean 的创建后,内部会使用 AopProxy,结合我们定义好的规则,创建代理对象,找到每个代理对象的连接点,织入增强代码。

AopProxy 的定义如下:

public interface AopProxy {

    /**
     * Create a new proxy object.
     * <p>Uses the AopProxy's default class loader (if necessary for proxy creation):
     * usually, the thread context class loader.
     * @return the new proxy object (never {@code null})
     * @see Thread#getContextClassLoader()
     */
    Object getProxy();

    /**
     * Create a new proxy object.
     * <p>Uses the given class loader (if necessary for proxy creation).
     * {@code null} will simply be passed down and thus lead to the low-level
     * proxy facility's default, which is usually different from the default chosen
     * by the AopProxy implementation's {@link #getProxy()} method.
     * @param classLoader the class loader to create the proxy with
     * (or {@code null} for the low-level proxy facility's default)
     * @return the new proxy object (never {@code null})
     */
    Object getProxy(ClassLoader classLoader);
}

AopProxy 的具体实现有:

4. 特点

4.1. 优势

4.2. 缺点

4.3. 注意事项

上一篇 下一篇

猜你喜欢

热点阅读