结构型设计模式:代理模式

时间:2022-07-23
本文章向大家介绍结构型设计模式:代理模式,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

代理模式主要作用是不改变原始类的情况下,通过代理类来访问原始类方法,从而对原始类方法进行增强。当然,把增强功能直接放入原始类也是可以的,但是这样不符合开闭原则,而且容易把业务代码和非业务代码耦合。代理模式的实现有2种方式

1.代理类和原始类实现同一个接口,代码如下:

public interface UserManager {
    //新增用户抽象方法
    void addUser(String userName, String password);
    //删除用户抽象方法
    void delUser(String userName);
}

public class UserManagerImpl implements UserManager{

    @Override
    public void addUser(String userName, String password) {
        System.out.println("传入参数为 userName: "+userName+" password: "+password);
    }
    
    @Override
    public void delUser(String userName) {
        System.out.println("传入参数为 userName: "+userName);
    }
}

public class UserManagerProxy implements UserManager{

    private UserManager userManager = new UserManagerImpl();

    @Override
    public void addUser(String userName, String password) {
        System.out.println("proxy addUser");
        userManager.addUser("zhangsan", "123456");
    }

    @Override
    public void delUser(String userName) {
        System.out.println("proxy deleteUser");
        userManager.delUser("zhangsan");
    }
}

先定义了一个UserManager接口, UserManagerImpl实现了这个接口,做具体的操作。同时,UserManagerProxy也实现了这个接口,但是UserManagerProxy自己不做具体的业务,而是组合了UserManagerImpl,用UserManagerImpl来做业务操作。UserManagerProxy自身做一些增强操作,比如记录日志等。

2.如果原始类没有实现接口,这种情况下代理模式就得用继承的方式来实现了。如下代码:

public class UserManagerProxy1 extends UserManagerImpl{

    @Override
    public void addUser(String userName, String password) {
        System.out.println("proxy addUser");
        super.addUser("zhangsan", "123456");
    }

    @Override
    public void delUser(String userName) {
        System.out.println("proxy deleteUser");
        super.delUser("zhangsan");
    }
}

上面的都是静态代理,静态代理的问题是针对每一个被代理类都需要生成一个代理类,而且代理类里面需要重新实现被代理类的那一套代码逻辑。使用动态代理可以解决这个问题,下面我们看一下动态代理的实现。

3.jdk中的动态代理

public class JdkProxy implements InvocationHandler{

    private Object target ;//需要代理的目标对象

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("JDK动态代理,监听开始!");
        Object result = method.invoke(target, args);
        System.out.println("JDK动态代理,监听结束!");
        return result;
    }
    //定义获取代理对象方法
    private Object getJDKProxy(Object targetObject){
        //为目标对象target赋值
        this.target = targetObject;
        //JDK动态代理只能针对实现了接口的类进行代理,newProxyInstance 函数所需参数就可看出
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
    }

    public static void main(String[] args) {
        JdkProxy jdkProxy = new JdkProxy();//实例化JDKProxy对象
        UserManager user = (UserManager) jdkProxy.getJDKProxy(new UserManagerImpl());//获取代理对象
        user.addUser("admin", "123123");//执行新增方法
    }
}

jdk只能对实现了接口的类做代理,下面看一下cglib代理

4.cglib代理

cglib代理是基于代理类生成了一个子类,就像上面2的实现一样,代码如下:

public class CglibProxy implements MethodInterceptor {
    private Object target;//需要代理的目标对象

    //重写拦截方法
    @Override
    public Object intercept(Object obj, Method method, Object[] arr, MethodProxy proxy) throws Throwable {
        System.out.println("Cglib动态代理,监听开始!");
        Object invoke = method.invoke(target, arr);//方法执行,参数:target 目标对象 arr参数数组
        System.out.println("Cglib动态代理,监听结束!");
        return invoke;
    }
    //定义获取代理对象方法
    public Object getCglibProxy(Object objectTarget){
        //为目标对象target赋值
        this.target = objectTarget;
        Enhancer enhancer = new Enhancer();
        //设置父类,因为Cglib是针对指定的类生成一个子类,所以需要指定父类
        enhancer.setSuperclass(objectTarget.getClass());
        enhancer.setCallback(this);// 设置回调
        Object result = enhancer.create();//创建并返回代理对象
        return result;
    }

    public static void main(String[] args) {
        CglibProxy cglib = new CglibProxy();//实例化CglibProxy对象
        UserManager user =  (UserManager) cglib.getCglibProxy(new UserManagerImpl());//获取代理对象
        user.delUser("admin");//执行删除方法
    }

}

5.动态代理的使用

动态代理的使用场景非常广泛,在我们日常开发中,spring的事务管理就是用动态代理实现的。除此之外,比如对于缓存,我们可以在代理类中查询缓存,而在被代理类中查询数据库,代理类只有缓存中没有数据时,才会访问数据库。再比如RPC框架,我们可以通过代理类将缓存细节隐藏起来,只关注调用。