设计模式:代理模式

代理模式定义

代理模式(proxy pattern)指为对象提供一层代理,以控制对象的访问,属于结构型设计模式。

当无法或不想直接引用某个对象或访问某个对象困难时,可以通过代理对象来间接访问。使用代理模式主要有两个目的:1.保护目标对象 2.增强目标对象。

通用实现(写法)

public interface ISubject {
    public void invokeMethod();
}

public class Subject implements ISubject{
    @Override
    public void invokeMethod() {
        System.out.println("Subject invokeMethod");
    }
}
//代理类
public class ProxySubject implements ISubject{

    private ISubject iSubject;

    ProxySubject(ISubject iSubject) {
        this.iSubject = iSubject;
    }

    @Override
    public void invokeMethod() {
        before();
        iSubject.invokeMethod();
        after();
    }

    private void before(){
        System.out.println("before");
    }
    private void after(){
        System.out.println("after");
    }
}

uml:

image-20201223224906641

测试:

    @Test
    public void test(){
        ProxySubject proxySubject = new ProxySubject(new Subject());
        proxySubject.invokeMethod();
    }

image-20201223225020698

代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。值得注意的是,代理类和被代理类应该共同实现一个接口,或者是共同继承某个类

上述写法是静态代理,下面介绍动态代理

Jdk动态代理

上述的ProxySubject是代理类,我们需要实现Isubject接口,并手动创建该代理对象,调用目标方法。

而在动态代理中,我们可以让程序在运行的时候自动在内存中创建一个实现 Isubject接口的代理,而不需要去定义 ProxySubject这个类。

目前普遍使用的是Jdk动态代理和Cglib动态代理。现介绍Jdk动态代理。

两种写法:

实现InvocationHandler接口

public interface IJdkSubject {
    public void invokeMethod();
}
public class JdkSubject implements IJdkSubject{
    @Override
    public void invokeMethod() {
        System.out.println("JdkSubject invokeMethod");
    }
}

public class JdkProxySubject1 implements InvocationHandler {

    private IJdkSubject jdkSubject;

    /**
     * 创建代理对象
     */
    public IJdkSubject createProxy(IJdkSubject jdkSubject){
        this.jdkSubject = jdkSubject;
        return (IJdkSubject)Proxy.newProxyInstance(jdkSubject.getClass().getClassLoader(), jdkSubject.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before");
        Object invoke = method.invoke(jdkSubject, args);
        System.out.println("after");
        return invoke;
    }
}

image-20201223231521182

实现匿名内部类

public class JdkProxySubject2 {

    public IJdkSubject createProxy(IJdkSubject jdkSubject){
        return (IJdkSubject)Proxy.newProxyInstance(jdkSubject.getClass().getClassLoader(), jdkSubject.getClass().getInterfaces(), (proxy, method, args) -> {
            System.out.println("before");
            Object invoke = method.invoke(jdkSubject, args);
            System.out.println("after");
            return invoke;
        });
    }
}

测试:

    @Test
    public void test(){
        JdkProxySubject1 jdkProxySubject1 = new JdkProxySubject1();
        IJdkSubject proxy = jdkProxySubject1.createProxy(new JdkSubject());
        proxy.invokeMethod();
    }

    @Test
    public void test2(){
        JdkProxySubject2 jdkProxySubject2 = new JdkProxySubject2();
        IJdkSubject proxy = jdkProxySubject2.createProxy(new JdkSubject());
        proxy.invokeMethod();
    }

两种方法本质是一种,都是创建动态代理对象执行目标方法,都会进入InvocationHandler的invoke回调方法,通过此方法,可以对目标方法进行增强。

对于jdk动态代理来说,我们需要被代理类实现接口,那么没有实现接口的类就不能使用jdk动态代理了。由此引入cglib动态代理。

Cglib动态代理

导入相关jar包(在spring-core自带cglib可直接测试,或者导入其他cglib依赖包)

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
class CglibSubject {
    void methodInvoke(){
        System.out.println("CglibSubject methodInvoke");
    }
}

public class CglibProxySubject implements MethodInterceptor {

    CglibSubject createProxy(Class<CglibSubject> clazz){
        //创建核心类,让它帮助我们创建代理对象
        Enhancer enhancer = new Enhancer();
        //设置父类
        enhancer.setSuperclass(clazz);
        //设置回调
        enhancer.setCallback(this);
        //创建代理
        return (CglibSubject)enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("before");
        Object invoke = proxy.invokeSuper(obj, args);
        System.out.println("after");
        return invoke;
    }
}

MethodInterceptor接口:

image-20201223234258392

测试:

public class CglibTest {
    @Test
    public void test(){
        CglibProxySubject cglibProxySubject = new CglibProxySubject();
        CglibSubject proxy = cglibProxySubject.createProxy(CglibSubject.class);
        proxy.methodInvoke();
    }
}

我们发现:cglib动态代理的目标对象不需要实现任何接口,它是通过动态继承目标对象实现动态代理的

原文地址:https://www.cnblogs.com/wwjj4811/p/14181840.html