【设计模式】- 代理模式

本博客demo源码地址:
https://github.com/suchahaerkang/design-pattern.git

1 定义

1.1 代理模式:给一个目标对象创建一个代理对象,代理对象可以获得控制这个目标对象的权限。既可以通过代理对象访问目标对象,对目标对象的功能进行增强
1.2 代理模式的方式有三种:1)静态代理, 2)动态代理(又叫JDK代理或接口代理),3)Cglib代理(目标对象不需要实现接口,代理对象在内存中是动态生成的,有的教程把它归类于动态代理)
下面有个场景:现在有个TeacherDao的对象,它有个teach()的方法。现在我们就用代理模式的三种方式来给teach()方法增强。

2 静态代理

静态代理:代理对象和目标对象要么实现同一个接口,要么继承同一个类,并且目标对象要聚合到代理对象中去
类图:
在这里插入图片描述
代码实现:

/**
 * @author sukang
 * @date 2020/3/9 19:07
 * <p>
 */
public interface ITeacherDao {

    /**
     * @description:
     * @param name 老师名字
     * @return: void
     * @author: sukang
     * @date: 2020/3/9 19:08
     */
    void teach(String name);
}
/**
 * @description:
 * @author: sukang
 * @date: 2020-03-09 19:09
 */
public class TeacherDao implements ITeacherDao{

    @Override
    public void teach(String name) {
        System.out.println(name + "老师在讲课...");
    }
}
/**
 * @description:
 * @author: sukang
 * @date: 2020-03-09 19:10
 */
public class TeacherProxy implements ITeacherDao{
    //聚合TeacherDao对象
    private TeacherDao teacherDao;

    //构造函数将TeacherDao对象聚合进来
    public TeacherProxy(TeacherDao teacherDao) {
        this.teacherDao = teacherDao;
    }

    //代理对象对目标对象的方法增加
    @Override
    public void teach(String name) {
        System.out.println("执行teach()方法之前执行...");
        teacherDao.teach(name);
        System.out.println("执行teach()方法之后执行...");
    }
}
/**
 * @description:
 * @author: sukang
 * @date: 2020-03-09 19:15
 */
public class Client {

    public static void main(String[] args) {
        //创建一个目标对象
        TeacherDao teacherDao = new TeacherDao();
        //将目标对象聚合到代理对象中
        TeacherProxy teacherProxy = new TeacherProxy(teacherDao);
        //调用代理对象的teach()方法,然后用代理对象去调控目标对象的teach(),达到对teach()方法功能增加的效果
        teacherProxy.teach("张三");
    }
}

测试结果:
在这里插入图片描述
静态代理优缺点:
优点:在不修改目标对象的条件下,代理对象能对目标对象的功能增加
缺点:代理对象和目标对象必须实现同一个接口或则继承同一个父类,程序中会存在大量的代理对象;如果接口添加一个新的方法,代理对象和目标对象都需要跟着变化

3 动态代理JDK

JDK代理:又叫接口代理,目标对象必须实现一个接口,代理对象是由JDK的api动态生成的
类图:
在这里插入图片描述
代码实现:

/**
 * @author sukang
 * @date 2020/3/10 13:07
 * <p>
 */
public interface ITeacherDao {

    void teach(String name);
}
/**
 * @description:
 * @author: sukang
 * @date: 2020-03-10 13:08
 */
public class TeacherDao implements ITeacherDao{

    @Override
    public void teach(String name) {
        System.out.println(name + "老师正在授课...");
    }
}
/**
 * @description: 代理工厂
 * @author: sukang
 * @date: 2020-03-10 13:10
 */
public class ProxyFactory {

    //目标对象
    private Object target;

    //将目标对象注入到动态代理工厂中
    public ProxyFactory(Object target) {
        this.target = target;
    }

    /**
     * @description: 获取代理对象
     * @param 
     * @return: java.lang.Object
     * @author: sukang
     * @date: 2020/3/10 13:17
     * jdk的Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)方法创建代理对象
     * 参数的解释:
     * ClassLoader loader:表示当前目标类的类加载器
     * Class<?>[] interfaces: 表示当前目标类的接口类型
     * InvocationHandler h:事件处理器,当通过代理对象去调用目标对象的方法的时候,会触发InvocationHandler事件处理
     * 器,将目标对象的方法的信息通过参数的形式传给事件处理的invoke()方法。invoke()方法在处理目标方法前后可以做一些
     * 操作,比如打印日志。
     *
     */
    public Object getProxyInstance(){
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),
                new InvocationHandler(){
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("目标方法处理之前处理...");
                        Object obj = method.invoke(target,args);
                        System.out.println("目标方法处理之后处理...");
                        return obj;
                    }
                });
    }
}

/**
 * @description:
 * @author: sukang
 * @date: 2020-03-10 13:27
 */
public class Client {

    public static void main(String[] args) {
        //创建目标对象
        TeacherDao teacherDao = new TeacherDao();
        //创建代理对象
        ITeacherDao proxyInstance = (ITeacherDao) new ProxyFactory(teacherDao).getProxyInstance();
        //调用目标对象的teach()方法
        proxyInstance.teach("张三");
    }
}

运行结果
在这里插入图片描述
JDK代理相比静态代理来说,JDK代理是在内存中动态生成的代理对象,在开发中不需要创建很多代理对象,但是JDK代理规定目标函数必须要实现接口,不然的话,实现不了代理的效果

3 Cglib代理

Cglib代理:不需要目标对象实现一个接口,它是通过在内存中动态生成目标对象的子类来控制目标对象的方法的,所以也有教程称之为子类代理。Cglib是一个高性能的代码生成包,可以在代码运行期间动态的扩展java类和实现java接口。Cglib包的底层是有字节码处理框架ASM来转换字节码来生成新的类
Cglib代理在开发中用到很多,比如现在比较流行的Spring AOP通过Cglib代理拦截方法
所以要先实现Cglib代理的功能,首先要引入ASM框架的一些jar包。这里因为spring框架已经依赖了这些jar包,我们就直接引入spring的jar包。
先画个类图
在这里插入图片描述
代码实现

/**
 * @description:
 * @author: sukang
 * @date: 2020-03-10 14:15
 */
public class TeacherDao {

    public void teach(String name){
        System.out.println(name + "老师正在授课...");
    }
}
/**
 * @description:
 * @author: sukang
 * @date: 2020-03-10 14:16
 */
public class ProxyFactory implements MethodInterceptor {
    //目标对象
    private Object targert;

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

    //获取目标对象的代理对象
    public Object getProxyInstance(){
        //创建一个工具类
        Enhancer enhancer = new Enhancer();
        //指定目标对象为父类
        enhancer.setSuperclass(targert.getClass());
        //指定回调函数
        enhancer.setCallback(this);
        //创建代理对象
        return enhancer.create();
    }

    //代理对象调用目标对象的方法的时候,会回调重写的intercept()方法对目标对象的方法增强
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("调用目标方法之前操作....");
        Object object = method.invoke(targert, objects);
        System.out.println("调用目标方法之后操作....");
        return object;
    }
}
/**
 * @description:
 * @author: sukang
 * @date: 2020-03-10 14:50
 */
public class Client {

    public static void main(String[] args) {
        //创建目标对象
        TeacherDao teacherDao = new TeacherDao();
        //创建代理对象
        TeacherDao proxy  = (TeacherDao) new ProxyFactory(teacherDao).getProxyInstance();
        //代理对象调用目标对象的方法
        proxy.teach("李四");
    }
}

运行结果
在这里插入图片描述

发布了88 篇原创文章 · 获赞 34 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/suchahaerkang/article/details/104758223