前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java设计模式(6)-代理模式(必看的springAOP原理)

java设计模式(6)-代理模式(必看的springAOP原理)

作者头像
爱敲代码的猫
发布2019-10-17 01:38:39
6260
发布2019-10-17 01:38:39
举报
文章被收录于专栏:爱敲代码的猫

代理模式:

代理模式 最典型的应用就是AOP,本文结合主要讲解了代理模式的几种实现方式:静态代理和动态代理,这里动态代理又可以分为jdk代理和Cglib代理

代理模式的角色:

1.Source:代理者与被代理者共同实现的接口,可以理解为需要代理的行为; 2.SourceImpl:被代理者,其为具有某种特定行为的实现者; 3.Proxy:代理者,其会全权代理SourceImpl所具有的功能,在实现其功能的基础上做一些额外的工作; 4.Client:客户端,客户端访问代理者与访问被代理者具有类似的效果,其无法区分访问的是代理者还是被代理者。

静态代理:

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

被代理类(真实类):

代码语言:javascript
复制
public class SourceImpl implements Source{
    
    @Override
    public void SourceMethod(){
        System.out.println("this is Source Method!")
    }
}

代理类:

代码语言:javascript
复制
public class Proxy implements Source{
    private Source sourceImpl;
    
    public Proxy(Source sourceImpl){
        this.sourceImpl = sourceImpl;
    }
    
    @Override
    public void SourceMethod(){
        System.out.println("before proxy!")
        sourceImpl.SourceMethod();
        System.out.println("after proxy!")
    }
}

测试类:

代码语言:javascript
复制
public class Client {
  @Test
  public void testProxy() {
    Source source = new SourceImpl();
    Source proxy = new SubjectProxy(source);
    proxy.SourceMethod();
  }
}

输出:

代码语言:javascript
复制
before proxy!
this is Source Method!
after proxy!

小结:

1.客户端获取的是一个实现Source接口的实例,其在调用的SourceMethod()方法实际上是代理对象的SourceMethod()方法。这种代理方式称为静态代理,并且这种代理方式也是效率最高的一种方式,因为所有的类都是已经编写完成的,客户端只需要取得代理对象并且执行即可---和装饰类相似 2.缺陷:代理类和被代理类都是实现同一个接口,如果有多个被代理类(真实类),客户端想要实现业务逻辑,那么就需要不断的创建代理类的对象,造成代码重复,加重维护工作

动态代理:

jdk 代理:

所谓的jdk代理指的是借助jdk所提供的相关类来实现代理模式,其主要有两个类:InvocationHandler和Proxy。在实现代理模式时,只需要实现InvocationHandler接口即可

代码语言:javascript
复制
//用户管理接口
public interface UserManager {
    //新增用户抽象方法
    void addUser(String userName,String password);
    //删除用户抽象方法
    void delUser(String userName);
}
代码语言:javascript
复制
//用户管理实现类,实现用户管理接口
public class UserManagerImpl implements UserManager{
    //重写新增用户方法
    @Override
    public void addUser(String userName, String password) {
        System.out.println("调用了新增的方法!");
        System.out.println("传入参数为 userName: "+userName+",password: "+password);
    }
    //重写删除用户方法
    @Override
    public void delUser(String userName) {
        System.out.println("调用了删除的方法!");
        System.out.println("传入参数为 userName: "+userName);
    }
    
}

jdk代理实现:

代码语言:javascript
复制
public class JdkProxy implements InvocationHandler {
  private Object target;

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

  //定义获取代理对象方法
    private Object getJDKProxy(Object targetObject){
        //为目标对象target赋值
        this.target = targetObject;
        //JDK动态代理只能针对实现了接口的类进行代理,newProxyInstance 函数所需参数就可看出,
        //interfaces参数是该动态类所继承的所有接口
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
    }
    
  @Override
  public Object invoke(Object proxy, Method method,       Object[] args) throws Throwable {
    System.out.println("before proxy!");
    Object result = method.invoke(source, args);
    System.out.println("after proxy!");
    return result;
  }
}
代码语言:javascript
复制
public class Client {
    
  @Test
  public void testDynamicProxy() {
    //实例化JDKProxy对象
    JdkProxy jdkProxy = new JdkProxy();
    //获取代理对象
    UserManager proxy = (UserManager) jdkProxy.getJDKProxy(new UserManagerImpl());
    proxy.addUser("Leemus", "MShan");
  }
}

before proxy! 调用了新增的方法! 传入参数为 userName: Leemus,password: MShan after proxy!

jdk代理解决了静态代理需要为每个业务接口创建一个代理类的问题,虽然使用反射创建代理对象效率比静态代理稍低,但其在现代高速jvm中也是可以接受的,在Spring的AOP代理中默认就是使用的jdk代理实现的。这里jdk代理的限制也是比较明显的,即其需要被代理的对象必须实现一个接口。这里如果被代理对象没有实现任何接口,或者被代理的业务方法没有相应的接口,我们则可以使用另一种方式来实现,即Cglib代理。

Cglib 代理:

Cglib代理是功能最为强大的一种代理方式,因为其不仅解决了静态代理需要创建多个代理类的问题,还解决了jdk代理需要被代理对象实现某个接口的问题。对于需要代理的类,如果能为其创建一个子类,并且在子类中编写相关的代理逻辑,因为“子类 instanceof 父类”,因而在进行调用时直接调用子类对象的实例,也可以达到代理的效果。Cglib代理的原理实际上是动态生成被代理类的子类字节码,由于其字节码都是按照jvm编译后的class文件的规范编写的,因而其可以被jvm正常加载并运行。这也就是Cglib代理为什么不需要为每个被代理类编写代理逻辑的原因。这里需要注意的是,根据Cglib实现原理,由于其是通过创建子类字节码的形式来实现代理的,如果被代理类的方法被声明final类型,那么Cglib代理是无法正常工作的,因为final类型方法不能被重写.

Cglib代理实现:

代码语言:javascript
复制
/**
 * 被代理类
 */
public class Source {
  public void SourceMethod() {
    System.out.println("without implement any interface!");
  }
}
代码语言:javascript
复制
/**
 * 代理类
 */
public class SafetyCheckCallback implements MethodInterceptor {
    private Object target;//需要代理的目标对象

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before proxy!");
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("after proxy!");
        return result;
      }
    
    //定义获取代理对象方法
    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;
    }
}

客户端调用:

代码语言:javascript
复制
public class Client {
  @Test
  public void testCglibProxy() {
    //实例化CglibProxy对象
    CglibProxy cglib = new CglibProxy();
    //获取代理对象
    UserManager user =  (UserManager) cglib.getCglibProxy(new UserManagerImpl());
    user.delUser("Leemus");
  }
}

before proxy! 调用了删除的方法! 传入参数为 userName: Leemus after proxy!

客户端代码中首先创建了一个Enhancer对象,并且设置了父类及代理回调类对象。该Enhancer对象会为目标类创建相关的子类字节码,并且将代理代码植入该子类字节码中

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-03-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 爱敲代码的猫 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 静态代理:
    • 被代理类(真实类):
      • 代理类:
        • 输出:
          • 小结:
          • 动态代理:
            • jdk 代理:
              • jdk代理实现:
                • Cglib 代理:
                  • Cglib代理实现:
                    • 客户端调用:
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档