设计模式之——代理模式

一、基本介绍

代理模式:给某些对象提供一个代理对象,以控制对这些对象的访问,或对这些对象进行与业务无关的增强操作。

二、包含角色

1.抽象主题类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。

2.真实主题类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,即被代理对象。

3.代理类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。

三、案例及UML类图

案例说明:

           当对数据库进行操作时,要求打印日志,即对数据库的操作进行增强。

UML类图:

类DatabaseOperate:

public interface DatabaseOperate {

    void select();

    void insert();

    void update();

    void delete();
}

说明:数据库接口定义,抽象主题类,定义公共接口,主要定义数据库的增删改查操作。

类MysqlOperate:

public class MysqlOperate implements DatabaseOperate {

    @Override
    public void select() {
        System.out.println("mysql查询操作");
    }

    @Override
    public void insert() {
        System.out.println("mysql插入操作");
    }

    @Override
    public void update() {
        System.out.println("mysql更新操作");
    }

    @Override
    public void delete() {
        System.out.println("mysql删除操作");
    }
}

说明:Mysql增删改查操作类,真实主题类,实现对应的逻辑代码。

类LogProxy:

public class LogProxy implements DatabaseOperate {

    private DatabaseOperate databaseOperate;

    public LogProxy(DatabaseOperate databaseOperate) {
        this.databaseOperate = databaseOperate;
    }

    @Override
    public void select() {
        System.out.println("select操作记录");
        databaseOperate.select();
    }

    @Override
    public void insert() {
        System.out.println("insert操作记录");
        databaseOperate.insert();
    }

    @Override
    public void update() {
        System.out.println("update操作记录");
        databaseOperate.update();
    }

    @Override
    public void delete() {
        System.out.println("删除操作记录");
        databaseOperate.delete();
    }
}

说明:日志代理类,代理类,对数据库操作进行增加,增加日志的记录功能。

类ProxyTest1:

public class ProxyTest1 {


    public static void main(String[] args) {
        DatabaseOperate proxy = new LogProxy(new MysqlOperate());
        proxy.select();
        proxy.update();
        proxy.insert();
        proxy.delete();
    }
}

说明:测试及客户端,使用代理类包装具体操作类就可以进行增强操作。

 

四、静态代理优缺点

优:能对真实对象进行增强操作。

缺:静态代理只能对一个接口进行增强,如果新增了其它的接口,则要再写一个代理类接口。

 

五、动态代理

根据上述内容可知,静态代理只能对一个接口进行代理,一般在开发中不使用。

在开发中都是使用动态代理,但是动态代理在编译时UML类图并不符合上述UML类图,因为代理类是运行时期才生成,即在运行时才能符合上述UML类图。

在java中动态代理主要分为两类:

        1.基于JDK需要实现接口的动态代理。

        2.基于cglib的不需要实现和继承动态代理。

JDK动态代理:

 类LogHandler:

public class LogHandler implements InvocationHandler {

    /**
     * 真实对象,需要传递进来
     */
    private Object realObject;

    public LogHandler(Object realObject) {
        this.realObject = realObject;
    }

    /**
     * proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0,一般在此处作用不大
     * method:我们所要调用某个对象真实的方法的Method对象
     * args:指代代理对象方法传递的参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //日志增强操作
        String methodName = method.getName();
        System.out.println(methodName+"操作记录");
        return  method.invoke(realObject,args);
    }
}

说明:基于JDK动态代理需要新增一个处理类,实现InvocationHandler接口,即对真实主题对象的增强操作处理。

类DatabaseOperate:

public interface DatabaseOperate {

    void select();

    void insert();

    void update();

    void delete();
}

说明:数据库接口定义,抽象主题类,定义公共接口,主要定义数据库的增删改查操作。

类MysqlOperate:

public class MysqlOperate implements DatabaseOperate {

    @Override
    public void select() {
        System.out.println("mysql查询操作");
    }

    @Override
    public void insert() {
        System.out.println("mysql插入操作");
    }

    @Override
    public void update() {
        System.out.println("mysql更新操作");
    }

    @Override
    public void delete() {
        System.out.println("mysql删除操作");
    }
}

说明:Mysql增删改查操作类,真实主题类,实现对应的逻辑代码。

类ProxyTest2:

public class ProxyTest2 {

    public static void main(String[] args) {
        //第一个参数,定义代理类的类加载器
        ClassLoader classLoader = DatabaseOperate.class.getClassLoader();
        //第二个参数,代理类要实现的接口数组class
        Class[] clazzs = new Class[]{ DatabaseOperate.class };
        //第三个参数,对真实对象进行增强的InvocationHandler实现类
        InvocationHandler invocationHandler = new LogHandler(new MysqlOperate());
        //此方法结果返回接口的代理对象
        DatabaseOperate proxy = (DatabaseOperate) Proxy.newProxyInstance(classLoader,clazzs,invocationHandler);
        proxy.select();
        proxy.update();
        proxy.insert();
        proxy.delete();
    }
}

说明:测试类,需要通过Proxy类的静态方法newProxyInstance,传递对应的参数获取对应的代理对象。

注意:使用jdk动态方式获取代理对象时,可以传递多个接口,但是具体使用哪个接口需要自己去强制转换。

CgLib动态代理:

类LogHandler:

public class LogHandler implements MethodInterceptor {

    /**
     * 真实对象,需要传递进来
     */
    private Object realObject;


    /**
     * 创建一个代理对象并返回
     * @param target 真实对象
     * @return 代理对象
     */
    public Object getInstance(Object target) {
        this.realObject = target;
        //固定写法,照抄即可,cglib生成一个代理对象的步骤
        Enhancer enhancer = new Enhancer();
        //设置父类
        enhancer.setSuperclass(this.realObject.getClass());
        //设置进行增强操作的对象
        enhancer.setCallback(this);
        return enhancer.create();
    }

    /**
     *增强操作
     * @param target 此真实对象(被代理对象)是cglib通过class反射创建出来的对象,并没有设置属性值等,一般不用。
     * @param method 目标对象的方法
     * @param args 参数
     * @param methodProxy 代理的方法
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        //日志增强操作
        String methodName = method.getName();
        System.out.println(methodName+"操作记录");
        //通过代理的方法进行调用,也可以通过method调用,一般使用methodProxy调用。
        return methodProxy.invoke(this.realObject,args);
    }
}

说明:基于cglib的动态代理需要新增一个处理类,实现MethodInterceptor接口,即对真实主题对象的增强操作处理。

 

类MysqlOperate:

public class MysqlOperate{

    public void select() {
        System.out.println("mysql查询操作");
    }

    public void insert() {
        System.out.println("数据库插入操作");
    }

    public void update() {
        System.out.println("数据库更新操作");
    }

    public void delete() {
        System.out.println("数据库删除操作");
    }

}

说明:Mysql增删改查操作类,真实主题类,实现对应的逻辑代码。

类ProxyTest3:

public class ProxyTest3 {


    public static void main(String[] args) {
        LogHandler logProxy = new LogHandler();
        //获取一个代理对象
        MysqlOperate proxy = (MysqlOperate) logProxy.getInstance(new MysqlOperate());
        proxy.select();
        proxy.update();
        proxy.insert();
        proxy.delete();
    }
}

说明:测试类,获取代理代理对象,然后进行操作。

注意:

     1.cglib是用字节码技术在运行时新增一个类去继承真实主题对象,所以抽象主题类可有可无,一般使用cglib则真实主题类是没有实现接口的,否则使用jdk动态代理即可。cglib的获取代理对象的方法,按照上面固定步骤即可。

     2.使用cglib动态代理需要导入额外的jar包,请去maven搜索查找即可。

CgLib动态代理和JDK动态代理对比:

    1.性能:1.8版本后的jdk动态代理效率比cglib高

    2.适用范围:jdk动态代理只能使用在实现了接口的地方,cglib的限制条件是该类不能被final修饰,因为cglib是通过继承这个类来实现代理的,final修饰的类不能被继承。

    3.选择:如果有实现接口,则用jdk动态代理,否则使用cglib动态代理。

 

六、适用场景

1.需要对某些对象进行与业务无关的增强操作时。

2.需要对真实对象进行访问控制时

3.懒加载

4.未知具体类,但需要为客户提供接口时

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值