design pattern · 2020-02-09 0

Java设计模式-代理模式

一、概述

代理模式(Proxy):为一个对象提供一个替身,以控制对这个对象的访问,即通过代理对象访问目标对象,这样做的好处是,可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能

代理模式有不同的形式,主要有三种:静态代理、动态代理(JDK代理、接口代理)、Cglib代理

二、角色

抽象角色:为真实对象和代理对象提供一个共同的接口,一般是抽象类或者接口

代理角色:代理角色内部包含对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能够代替真实对象。

真实角色:最终引用的对象

三、静态代理

静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口

1.抽象角色

public interface ITeacherDao {
    void teach();
}

2.真实角色

public class TeacherDao implements ITeacherDao {

    @Override
    public void teach() {
        System.out.println("TeacherDao teach");
    }
}

3.代理角色

public class TeacherDaoProxy implements ITeacherDao {
    private ITeacherDao target;

    public TeacherDaoProxy(ITeacherDao target) {
        this.target = target;
    }

    @Override
    public void teach() {
        System.out.println("static proxy begin...");
        target.teach();
        System.out.println("static proxy end...");
    }
}

4.测试

@Test
public void testStaticProxy() {
    // 创建目标对象(被代理对象)
    ITeacherDao teacherDao = new TeacherDao();
    // 创建代理对象,同时将被代理对象传递给代理对象
    ITeacherDao proxy = new TeacherDaoProxy(teacherDao);
    proxy.teach();
}

四、动态代理

代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理,利用JDK的API,动态的在内存中构建代理对象

1.抽象角色

public interface ITeacherDao {
    void teach();
}

2.真实角色

public class TeacherDao implements ITeacherDao {

    @Override
    public void teach() {
        System.out.println("TeacherDao teach");
    }
}

3.代理角色

JDK实现代理只需要使用newProxyInstance方法,static Obkect newProxtInstance(ClassLoader loader, Class<?>[] interface, InvocationHandler h)

ClassLoader loader: 指定当前目标对象使用的类加载器,获取加载器的方法固定
Class<?> interfaces: 目标对象实现的接口类型,使用泛型方法确认类型
InvocationHandler h: 事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行的目标对象方法作为参数传入

public class ProxyFactory {
    // 维护一个目标对象
    private Object target;

    // 构造器,对target进行初始化
    public ProxyFactory(Object target) {
        this.target = target;
    }

    // 给目标对象 生成一个代理对象
    public Object getProxyInstance() {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), new MyInvocationHandler());
    }

    class MyInvocationHandler implements InvocationHandler {

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

4.测试

@Test
public void testJDKProxy() {
    // 创建目标对象(被代理对象)
    ITeacherDao teacherDao = new TeacherDao();
    // 创建代理对象,同时将被代理对象传递给代理对象
    ITeacherDao proxyInstance = (ITeacherDao) new ProxyFactory(teacherDao).getProxyInstance();
    proxyInstance.teach();
    System.out.println(proxyInstance.getClass());
}

五、Cglib代理

静态代理和JDK代理都要求目标对象是实现一个接口,Cglib代理不要求目标对象实现一个接口

1) 在内存中动态构建子类,注意代理的类不能为final,否则报错java.lang.IllegalArgumentException
2) 目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法

添加maven依赖

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

1.真实角色

public class TeacherDao {

    public void teach() {
        System.out.println("TeacherDao teach");
    }

    public void say() {
        System.out.println("TeacherDao say");
    }
}

2.代理角色

public class ProxyFactory {

    // 维护一个目标对象
    private Object target;

    // 构造器传入一个被代理对象
    public ProxyFactory(Object target) {
        this.target = target;
    }

    // 返回一个代理对象,是target对象的代理对象
    public Object getProxyInstance() {
        // 1.创建一个工具类
        Enhancer enhancer = new Enhancer();
        // 2.设置父类
        enhancer.setSuperclass(target.getClass());
        // 3.设置回调函数
        Callback[] callbacks = new Callback[2];
        callbacks[0] = new MyMethodInterceptor1();
        callbacks[1] = new MyMethodInterceptor2();
        enhancer.setCallbacks(callbacks);
        // 4.设置Filter
        enhancer.setCallbackFilter(new MyCallbackFilter());
        // 2. 创建子类对象,即代理对象
        return enhancer.create();
    }

    class MyCallbackFilter implements CallbackFilter {

        @Override
        public int accept(Method method) {
            if ("teach".equals(method.getName())) {
                return 0;
            }
            if ("say".equals(method.getName())) {
                return 1;
            }
            return 0;
        }
    }

    class MyMethodInterceptor1 implements MethodInterceptor {

        // 重写intercept方法,会调用目标对象的方法
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("cglib proxy begin 1...");
            Object result = method.invoke(target, args);
            System.out.println("cglib proxy end 1...");
            return result;
        }
    }

    class MyMethodInterceptor2 implements MethodInterceptor {

        // 重写intercept方法,会调用目标对象的方法
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("cglib proxy begin 2...");
            Object result = method.invoke(target, args);
            System.out.println("cglib proxy end 2...");
            return result;
        }
    }
}

3.测试

@Test
public void testCglib() {
    TeacherDao teacherDao = new TeacherDao();
    TeacherDao proxyInstance = (TeacherDao) new ProxyFactory(teacherDao).getProxyInstance();
    proxyInstance.teach();
    proxyInstance.say();
}