Java代理模式

代理模式

代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。

简言之,代理模式就是设置一个中间代理来控制访问原目标对象,以达到增强原对象的功能和简化访问方式。
在这里插入图片描述

静态代理

静态代理目录结构
在这里插入图片描述
主题接口 IUserDao

public interface IUserDao {
    void saveUser();
}

主题接口实现类(被代理类) UserDao

public class UserDao implements IUserDao{
    @Override
    public void saveUser() {
        System.out.println("保存用户数据");
    }
}

代理类 UserDaoProxy

public class UserDaoProxy implements IUserDao {
    
    //目标对象
    private IUserDao targer;

    public UserDaoProxy(IUserDao iUserDao){
        this.targer = iUserDao;
    }

    @Override
    public void saveUser() {
        System.out.println("开启事务");
        targer.saveUser();
        System.out.println("提交事务");
    }
}

测试类 ProxyTest

public class ProxyTest {
    @Test
    void testStaticProxy(){
        //目标对象
        IUserDao target = new UserDao();
        //代理对象
        UserDaoProxy proxy = new UserDaoProxy(target);
        proxy.saveUser();
    }
}
动态代理

要求真实对象必须有实现接口
说明:

  • Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。
//返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。
public static Object newProxyInstance(ClassLoader loader,//类加载器<?>[] interfaces,//代理类实现的接口列表
                                      InvocationHandler h//调度方法调用的调用处理函数
                                      ) throws IllegalArgumentException                                  

InvocationHandler 是由代理实例的调用处理程序实现的接口

Object invoke(Object proxy,   //调用该方法的代理实例
              方法 method,  //所述方法对应于调用代理实例上的接口方法的实例
              Object[] args) //接口方法参数
       throws Throwable

IUserDao、UserDao同上
UserDaoProxy

public class ProxyFactory {

    //目标对象
    private Object targer;

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

    public Object getProxyInstance() {
        return Proxy.newProxyInstance(targer.getClass().getClassLoader(), targer.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println(proxy.getClass());
                System.out.println("开启事务");
                method.invoke(targer, args);
                System.out.println("提交事务");
                return null;
            }
        });
    }
}

一般可以改成如下
说明:
代理类继承InvocationHandler

public class ProxyFactory2 implements InvocationHandler{

    //目标对象
    private Object targer;

    public ProxyFactory2(Object targer){
        this.targer = targer;
    }

    public <T> T getProxyInstance() {
        return (T) Proxy.newProxyInstance(targer.getClass().getClassLoader(),
                targer.getClass().getInterfaces(),
                this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("开启事务");
        Object invoke = method.invoke(targer, args);
        System.out.println("提交事务");
        return invoke;
    }
}

动态代理测试

public class ProxyTest {
    @Test
    void testStaticProxy(){
        //目标对象
        IUserDao target = new UserDao();
        //代理对象
//        IUserDao proxyInstance = (IUserDao) new UserDaoProxy(target).getProxyInstance();
        IUserDao proxyInstance = new UserDaoProxy2(target).getProxyInstance();//将强转放到了代理类中
        proxyInstance.saveUser();
    }
}

注意

  • 若在invoke中调用 proxy.toString()会出现栈溢出,原因是改方法会继续调用invoke方法,出现循环调用,最终栈溢出。
  • 关于invoke方法中proxy参数的作用:只有proxy 实例在InvocationHandler 实现类里加载才能产生第二个参数method (静态代码块是虚拟机加载类的时候执行的,而且只执行一次)
cglib代理

引入依赖

compile "cglib:cglib:3.2.5"

UserDao

public class UserDao {
    public void saveUser() {
        System.out.println("保存用户数据");
    }
}

ProxyFactory

public class ProxyFactory implements MethodInterceptor {
    //目标对象
    private Object target;

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

    public Object getProxyInstance() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("开启事务");
        Object invoke = method.invoke(target, args);
        System.out.println("提交事务");
        return invoke;
    }
}

ProxyTest类

public class ProxyTest {
    @Test
    public void testcglibProxy(){
        //目标对象
        UserDao target = new UserDao();
        //代理对象
        UserDao proxy = (UserDao) new ProxyFactory(target).getProxyInstance();
        proxy.saveUser();
    }
}
代理方式实现优点缺点特点
JDK静态代理代理类与委托类实现同一接口,并且在代理类中需要硬编码接口实现简单,容易理解代理类需要硬编码接口,在实际应用中可能会导致重复编码,浪费存储空间并且效率很低
JDK动态代理代理类与委托类实现同一接口,主要是通过代理类实现InvocationHandler并重写invoke方法来进行动态代理的,在invoke方法中将对方法进行增强处理不需要硬编码接口,代码复用率高只能够代理实现了接口的委托类底层使用反射机制进行方法的调用
CGLIB动态代理代理类将委托类作为自己的父类并为其中的非final委托方法创建两个方法,一个是与委托方法签名相同的方法,它在方法中会通过super调用委托方法;另一个是代理类独有的方法。在代理方法中,它会判断是否存在实现了MethodInterceptor接口的对象,若存在则将调用intercept方法对委托方法进行代理可以在运行时对类或者是接口进行增强操作,且委托类无需实现接口不能对final类以及final方法进行代理底层将方法全部存入一个数组中,通过数组索引直接进行方法调用

参考:
https://segmentfault.com/a/1190000011291179#articleHeader3
https://www.jianshu.com/p/9a61af393e41?from=timeline&isappinstalled=0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值