Java代理模式实例详解【静态代理与动态代理】

 更新时间:2019年09月29日 11:47:51   作者:hu_beliefs  
这篇文章主要介绍了Java代理模式,结合实例形式详细分析了java静态代理与动态代理模式相关概念、原理、操作技巧与注意事项,需要的朋友可以参考下

本文实例讲述了Java代理模式。分享给大家供大家参考,具体如下:

即Proxy Pattern,23种java常用设计模式之一。代理模式的定义:对其他对象提供一种代理以控制对这个对象的访问。

Java的代理模式是Java中比较常用的设计模式,分为2中代理:静态代理与动态代理(JDK动态代理和cglib动态代理)

优点:

  • 职责清晰 真实角色只需关注业务逻辑的实现,非业务逻辑部分,后期通过代理类完成即可。
  • 高扩展性 不管真实角色如何变化,由于接口是固定的,代理类无需做任何改动。

缺点:

  • 很明显的一点就是反射机制,没有高安全性,性能也相对来讲低一些。

代理模式这种设计模式是一种使用代理对象来执行目标对象的方法并在代理对象中增强目标对象方法的一种设计模式。代理对象代为执行目标对象的方法,并在此基础上进行相应的扩展。看起来是有点拗口,首先介绍一个原则:开闭原则(对扩展开放,对修改关闭)。一种好的设计模式甚至是架构,都是在不修改原有形态的基础上扩展出新的功能。

事例场景:

小张是一个普普通通的码农,每天勤勤恳恳地码代码。某天中午小张刚要去吃饭,一个电话打到了他的手机上。“是XX公司的小张吗?我是YY公司的王AA”。“哦,是王总啊,有什么事情吗?”。沟通过后,小张弄明白了,原来客户有个需求,刚好负责这方面开发的是小张,客户就直接找到了他。不过小张却没有答应客户的请求,而是让客户找产品经理小李沟通。

是小张着急去吃面而甩锅吗?并不是,只是为了使故事可以套到代理模式上。我们先看一下代理模式的定义: * 为其他对象提供一种代理,以控制对这个对象的访问。(Provide a surrogate or placeholder for another object to control access to it)

对照定义,码农小张可以映射为其他对象,产品经理小李为小张的代理。我们通过JAVA代码,表述上面事例。

一、静态代理

什么是静态代理:静态代理就是在程序运行前就已经确定代理类与代理对象的代理模式

静态代理模式就是如上图所示,构造三个类实现他们的关系。

首先会思考的一点就是为什么需要实现同一个接口,如果不实现同一个接口,一样可以“代理”功能,所以为什么非要实现同一个接口。我个人认为不实现统一接口的话应该叫做聚合而不是代理;然后,实现统一接口能够使代理类与被代理类之间的联系,提高代码的复用性又能解耦。

package staticProxy;
/**
*接口
*/
public interface DAOInterface {
  public void add();
  public void delete();
  public void update();
  public void query();
}
package staticProxy;
/**
*被代理类
*/
public class UserDao implements DAOInterface{
  @Override
  public void add() {
    System.out.println("在目标对象中执行add");
  }
  @Override
  public void delete() {
    System.out.println("在目标对象中执行delete");
  }
  @Override
  public void update() {
    System.out.println("在目标对象中执行update");
  }
  @Override
  public void query() {
    System.out.println("在目标对象中执行query");
  }
}
package staticProxy;
/**
 * 代理类
 *
 */
public class UserDaoProxy implements DAOInterface{
  UserDao userDao = null;
  public UserDaoProxy(UserDao userDao){
    this.userDao = userDao;
  }
  @Override
  public void add() {
    userDao.add();
    System.out.println("记录日志add");
  }
  @Override
  public void delete() {
    userDao.delete();
    System.out.println("记录日志delete");
  }
  @Override
  public void update() {
    userDao.update();
    System.out.println("记录日志update");
  }
  @Override
  public void query() {
    userDao.query();
    System.out.println("记录日志query");
  }
}

静态代理就是写死了在代理对象中执行这个方法前后执行添加功能的形式,每次要在接口中添加一个新方法,则需要在目标对象中实现这个方法,并且在代理对象中实现相应的代理方法;利用Java的反射机制,动态的调用生成代理对象,就能完成动态代理的需求。

二、动态代理

1、JDK动态代理

在代理类管理器的新建代理实例方法中,必须要获得类的加载器、类所实现的接口、还有一个拦截方法的句柄。

在句柄的invoke中如果不调用method.invoke则方法不会执行。在invoke前后添加通知,就是对原有类进行功能扩展了。创建好代理对象之后,proxy可以调用接口中定义的所有方法,因为它们实现了同一个接口,并且接口的方法实现类的加载器已经被反射框架获取到了。

package JDKAgency;
/**
 * DAO接口
 */
public interface DAO {
  void add();
  void update();
  void delete();
  void query();
}

package JDKAgency;
/**
 * DAO的实现类
 */
public class DAOImpl implements DAO {
  @Override
  public void add() {
    System.out.println("添加的方法");
  }
  @Override
  public void update() {
    System.out.println("更新的方法");
  }
  @Override
  public void delete() {
    System.out.println("删除的方法");
  }
  @Override
  public void query() {
    System.out.println("查询的方法");
  }
}

package JDKAgency;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * 代理类管理器
 */
public class ProxyManager implements InvocationHandler {//动态代理的核心处理器接口
  private Object object;
  public ProxyManager(Object object) {
    this.object = object;
  }
  public Object createNewInstance() {
    Object o = Proxy.newProxyInstance(object.getClass().getClassLoader(),//真实对象的类加载器
        object.getClass().getInterfaces(),//真实对象的所有接口
        this);//代理对象
    return o;
  }
  @Override        //代理对象   执行业务的方法   参数
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.println("before......权限检测");//前置通知
    Object invoke = method.invoke(object, args);//动态调用执行方法
    System.out.println("after......日志监控");//后置通知
    return invoke;
  }
}

package JDKAgency;
/**
 * 测试
 */
public class JTest {
  public static void main(String[] args) {
    //创建真实对象
    DAO dao = new DAOImpl();
    DAO o =(DAO) manager.createNewInstance();
//JDK动态代理是代理的接口,因此强制转换应该转换为接口,而不是实现类,若强制转换实现类就会抛出ClassCastException,好比ArrayList与LinkedList实现统一接口List,两者也不能相互转换,但都可以向上转型。
    o.add();
    o.query();
  }
}

注意:JDK动态代理是代理的接口,因此强制转换应该转换为接口,而不是实现类,若强制转换实现类就会抛出ClassCastException,好比ArrayList与LinkedList实现统一接口List,两者也不能相互转换,但都可以向上转型。

补充:

JavaJDK动态代理报错java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to *。

javaJDK动态代理是Java原生代理模式。

注意:JDK动态代理是代理的接口,因此强制转换应该转换为接口,而不是实现类,若强制转换实现类就会抛出ClassCastException,好比ArrayList与LinkedList实现统一接口List,两者也不能相互转换,但都可以向上转型。

正确的转型方案:

//创建处理器对象
ProxyManager manager = new ProxyManager(dao);
//生成动态代理对象
// DAO o = (DAO) Proxy.newProxyInstance(dao.getClass().getClassLoader(), dao.getClass().getInterfaces(), manager);

2、cglib动态代理

cglib动态代理是web应用框架常用的一种动态代理方式。cglib是动态生成被代理类的子类,注意:是子类。

他需要先引入asm与cglib的jar包。如下图:

接着废话不多说,看代码分析:

package CGlibAgency;
/**
 * 被代理类
 */
public class User {
  public void saveUser(){
    System.out.println("保存对象。");
  }
  public void updateUser(){
    System.out.println("修改对象。");
  }
}

package CGlibAgency;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
 * 代理类管理器
 */
public class Interceptor implements MethodInterceptor {//cgLib需要实现MethodInterceptor接口,cgLib是基于类的,动态的生成代理类(被代理类的子类)
  private Enhancer enhancer = new Enhancer();//Enhancer是Cglib代理的重要对象
  public Object createProxy(Class superClass){
    enhancer.setSuperclass(superClass);//获取父类的Class
    enhancer.setCallback(this);//设置方法的回调,类似于JDK动态代理中的Proxy与InvocationHandler实现类的绑定回调
    return enhancer.create();//返回代理类的对象
  }
  @Override
  public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    System.out.println("'权限验证'");
    Object aSuper = methodProxy.invokeSuper(o, objects);//通过虚拟的代理对象的代理方法调用父类的方法,参数中一个是父类,一个是所有的子类对象数组(??)
    System.out.println("'日志收集'");
    return aSuper;
  }
}

package CGlibAgency;
/**
 * 测试类
 */
public class JTest {
  public static void main(String[] args) {
    Interceptor interceptor = new Interceptor();//创建代理管理对象
    Object proxy = interceptor.createProxy(User.class);//创建一个代理类
    System.out.println(User.class.getTypeName());//查看代理类的类型
    User user = (User) proxy;//转型,子类自动向上转型
    user.saveUser();
    user.updateUser();
  }
}

总结:

代理模式不仅可以降低模块儿之间的耦合,还能做到高复用,简化代码等。spring的AOP模块就是最直观的代理模式,使用了动态代理来实现,在spring中的许多模块中都具有动态代理的影子。

更多java相关内容感兴趣的读者可查看本站专题:《Java面向对象程序设计入门与进阶教程》、《Java数据结构与算法教程》、《Java操作DOM节点技巧总结》、《Java文件与目录操作技巧汇总》和《Java缓存操作技巧汇总

希望本文所述对大家java程序设计有所帮助。

相关文章

  • Kafka中Producer和Consumer的作用详解

    Kafka中Producer和Consumer的作用详解

    这篇文章主要介绍了Kafka中Producer和Consumer的作用详解,Kafka是一个分布式的流处理平台,它的核心是消息系统,Producer是Kafka中用来将消息发送到Broker的组件之一,它将消息发布到主题,并且负责按照指定的分区策略将消息分配到对应的分区中,需要的朋友可以参考下
    2023-12-12
  • 一文搞懂java反射基本API

    一文搞懂java反射基本API

    这篇文章主要为大家介绍了一文搞懂java反射基本API,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11
  • Java RandomAccessFile 指定位置实现文件读取与写入

    Java RandomAccessFile 指定位置实现文件读取与写入

    这篇文章主要介绍了Java RandomAccessFile 指定位置实现文件读取与写入的相关资料,需要的朋友可以参考下
    2017-01-01
  • JAVA版排序算法之快速排序示例

    JAVA版排序算法之快速排序示例

    这篇文章主要介绍了JAVA版排序算法之快速排序,结合实例形式分析了基于java版的遍历、递归实现快速排序功能的具体步骤与操作技巧,需要的朋友可以参考下
    2017-01-01
  • Spring boot RedisTemplate 序列化服务化配置方式

    Spring boot RedisTemplate 序列化服务化配置方式

    这篇文章主要介绍了Springboot RedisTemplate序列化服务化配置方式,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-07-07
  • Jmeter测试时遇到的各种乱码问题及解决

    Jmeter测试时遇到的各种乱码问题及解决

    这篇文章主要介绍了Jmeter测试时遇到的各种乱码问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • java背包问题动态规划算法分析

    java背包问题动态规划算法分析

    这篇文章主要介绍了java背包问题动态规划算法分析,想了解算法的同学一定要看一下
    2021-04-04
  • Java多输入框查询需求实现方法详解

    Java多输入框查询需求实现方法详解

    这篇文章主要给大家介绍了Java多输入框查询需求实现的相关资料,文中通过代码以及图文介绍的非常详细,对大家学习或者使用Java具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-10-10
  • Spring通过c3p0配置bean连接数据库

    Spring通过c3p0配置bean连接数据库

    这篇文章主要为大家详细介绍了Spring通过c3p0配置bean连接数据库,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-08-08
  • SpringBoot下载文件的实现及速度对比

    SpringBoot下载文件的实现及速度对比

    这篇文章主要介绍了SpringBoot下载文件的实现及速度对比,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12

最新评论