前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Java设计模式】之代理模式

【Java设计模式】之代理模式

作者头像
用户5640963
发布2019-07-26 14:12:11
3380
发布2019-07-26 14:12:11
举报
文章被收录于专栏:卯金刀GG

代理模式是Java常见的设计模式之一。所谓代理模式是指客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象。 为什么要采用这种间接的形式来调用对象呢?一般是因为客户端不想直接访问实际的对象,或者访问实际的对象存在困难,因此通过一个代理对象来完成间接的访问。

代理模式的UML图

从UML图中,可以看出代理类与真正实现的类都是继承了抽象的主题类,这样的好处在于代理类可以与实际的类有相同的方法,可以保证客户端使用的透明性。

代理模式的实现

代理模式可以有两种实现的方式,一种是静态代理类,另一种是各大框架都喜欢的动态代理。下面我们主要讲解一下这两种代理模式

1、静态代理

我们先看针对上面UML实现的例子,再看静态代理的特点。 Subject接口的实现

代码语言:javascript
复制
public interface Subject {
    void visit();
}

实现了Subject接口的两个类:

代码语言:javascript
复制
public class RealSubject implements Subject {

    private String name = "byhieg";
    @Override
    public void visit() {
        System.out.println(name);
    }
}
代码语言:javascript
复制
public class ProxySubject implements Subject{

    private Subject subject;

    public ProxySubject(Subject subject) {
        this.subject = subject;
    }

    @Override
    public void visit() {
        subject.visit();
    }
}

具体的调用如下:

代码语言:javascript
复制
public class Client {

    public static void main(String[] args) {
        ProxySubject subject = new ProxySubject(new RealSubject());
        subject.visit();
    }
}

通过上面的代理代码,我们可以看出代理模式的特点,代理类接受一个Subject接口的对象,任何实现该接口的对象,都可以通过代理类进行代理,增加了通用性。但是也有缺点,每一个代理类都必须实现一遍委托类(也就是realsubject)的接口,如果接口增加方法,则代理类也必须跟着修改。其次,代理类每一个接口对象对应一个委托对象,如果委托对象非常多,则静态代理类就非常臃肿,难以胜任。

2、动态代理(JDK)

动态代理有别于静态代理,是根据代理的对象,动态创建代理类。这样,就可以避免静态代理中代理类接口过多的问题。动态代理是实现方式,是通过反射来实现的,借助Java自带的java.lang.reflect.Proxy,通过固定的规则生成。 其步骤如下:

  1. 编写一个委托类的接口,即静态代理的(Subject接口)
  2. 实现一个真正的委托类,即静态代理的(RealSubject类)
  3. 创建一个动态代理类,实现InvocationHandler接口,并重写该invoke方法
  4. 在测试类中,生成动态代理的对象。

第一二步骤,和静态代理一样,不过说了。第三步,代码如下:

代码语言:javascript
复制
public class DynamicProxy implements InvocationHandler {
    private Object object;
    public DynamicProxy(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(object, args);
        return result;
    }
}

第四步,创建动态代理的对象

代码语言:javascript
复制
Subject realSubject = new RealSubject();
DynamicProxy proxy = new DynamicProxy(realSubject);
ClassLoader classLoader = realSubject.getClass().getClassLoader();
Subject subject = (Subject) Proxy.newProxyInstance(classLoader, new  Class[]{Subject.class}, proxy);
subject.visit();

创建动态代理的对象,需要借助Proxy.newProxyInstance。该方法的三个参数分别是:

  • ClassLoader loader表示当前使用到的appClassloader。
  • Class<?>[] interfaces表示目标对象实现的一组接口。
  • InvocationHandler h表示当前的InvocationHandler实现实例对象。

JDK代理模式

代码语言:javascript
复制
public class ProxyFactory {  
      
    private Object obj;  
      
      
    public ProxyFactory(Object obj) {  
        super();  
        this.obj = obj;  
    }  
  
    public Object getTransactionProxyInstance(){  
          
        Object proxy = Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() {  
            /** 
             * 三个参数:1、代理对象,2、目标对象的方法,3、目标对象的参数值列表 
             */  
            @Override  
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
                System.out.println("开启事务..."); //执行核心业务之前执行的内容  
                method.invoke(obj, args);      //执行目标对象方法,即核心业务  
                System.out.println("关闭事务..."); //执行核心业务之后执行的内容  
                return proxy;  
            }  
        });  
          
        return proxy;  
    }  
    
} 

3、动态代理(CGLIB)

JDK动态代理机制只能代理实现接口的类,一般没有实现接口的类不能进行代理。cglib就是针对类来实现代理的,它的原理是对指定目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。

使用cglib实现动态代理,完全不受代理类必须实现接口的限制,而且cglib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用java反射效率要高。

需要引入两个jar包:cglib.jar,asm.jar

定义了一个拦截器,在调用目标方法之前,cglib回调MethodInterceptor接口方法拦截,来实现自己的业务逻辑,类似

于JDK中的InvocationHandler接口。

代码语言:javascript
复制
@Override  
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable ;

proxy:为cglib动态生成的代理实例

method:为上文中实体类所调用的被代理的方法调用

args:为method参数数值列表

methodProxy:为生成代理类对方法的代理引用

返回:从代理实例方法调用返回的值

其中,methodProxy.invokeSuper(obj,arg):

调用代理类实例上的proxy方法的父类方法

UserDaoImpl.java

代码语言:javascript
复制
public class CglibProxyFactory {  
      
    private Object obj;  
  
    public CglibProxyFactory(Object obj) {  
        super();  
        this.obj = obj;  
    }  
      
    public Object getProxyFactory(){  
        //Enhancer类是cglib中的一个字节码增强器,它可以方便的为你所要处理的类进行扩展  
        Enhancer enhancer = new Enhancer();  
        enhancer.setSuperclass(obj.getClass());//将目标对象所在的类作为Enhaner类的父类  
        enhancer.setCallback(new MethodInterceptor() {  
            //通过实现MethodInterceptor实现方法回调  
            @Override  
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {  
                System.out.println("事务开启...");  
                method.invoke(obj, args);  
                System.out.println("事务结束...");  
                  
                return proxy;  
            }  
        });  
          
        return enhancer.create();//生成目标对象并返回  
    }  
}  

测试类

代码语言:javascript
复制
public class TestCglibProxy {  
      
    @Test  
    public void test1(){  
        UserDaoImpl userDao = new UserDaoImpl();  
        UserDaoImpl userDaoProxy = (UserDaoImpl) new CglibProxyFactory(userDao).getProxyFactory();  
        userDaoProxy.save();  
        System.out.println("目标对象类型:"+userDao.getClass());  
        System.out.println("代理对象类型:"+userDaoProxy.getClass());  
    }  
      
      
}  
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 代理模式的UML图
  • 代理模式的实现
    • 1、静态代理
    • 2、动态代理(JDK)
    • 3、动态代理(CGLIB)
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档