【设计模式】动态代理设计模式

本文介绍如何使用动态代理技术在不修改原有Dog类的情况下,实现追踪Dog类中sleep方法的执行时间。通过创建一个Animal接口,实现Dog类,并为其封装一个Advice接口用于记录sleep方法的执行时间。利用Proxy类动态生成代理对象,实现在调用sleep方法前后执行特定操作,从而达到追踪方法执行时间的目的。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

动态代理
假如有一个Dog类,该类有eat()和sleep()方法;由该类创建了一个dog对象,现在有一个需求,想知道sleep执行了多长时间,该如何实现这个需求呢?
可以使用动态代理来实现这个需求。
关于动态代理的两个重要的类:
1.Proxy:构造动态代理对象的方法Proxy:构造动态代理对象的方法
          public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
                               throws IllegalArgumentException
          loader - 定义代理类的类加载器
          interfaces - 代理类要实现的接口列表
          h - 指派方法调用的调用处理程序    
         
2.InvocationHandler:只有一个invoke方法:
          Object invoke(Object proxy,
                      Method method,
                      Object[] args)
                      throws Throwable
               proxy - 在其上调用方法的代理实例
               method - 对应于在代理实例上调用的接口方法的 Method 实例。
               args - 包含传入代理实例上方法调用的参数值的对象数组.

为了实现计算dog的sleep时间,需要抽象出一个Animal接口,代码为:
Animal.java
代码为:
public interface Animal {
     public void eat();
     public void sleep();
}

Dog类实现Animal接口:
Dog.java
代码为:
public class Dog implements Animal {
     public void eat() {
          System. out.println("狗狗正在吃东西。。" );
     }

     public void sleep() {
          System. out.println("狗狗正在睡觉觉。。" );
           try {
              Thread. sleep(1000);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
     }
}

封装了自己需求的Advice接口:
Advice.java:

public interface Advice {

     void beforeMethod(Method method);

     void afterMethod(Method method);
}


MyAdvice.java:

public class MyAdvice implements Advice {
     long beginTime=0;
     long endTime=0;
     public void afterMethod(Method method) {
           endTime=System. currentTimeMillis();
          System. out.println(method.getName()+"方法执行后。。共睡了:" +(endTime -beginTime )/1000+"秒" );
     }

     public void beforeMethod(Method method) {
           beginTime=System.currentTimeMillis(); 
          System. out.println(method.getName()+"方法执行前。。。" );
     }
}



抽象一个返回某个实例target的代理对象的方法:
public class ProxyDemo {

     /**
      * 获取某个target对象的代理对象,并加入自己想要操作的代码,我们称之为advice;
      *
      * @param target
      *            要去代理的对象;
      * @param advice
      *            封装了自己需求的对象;
      * @return target的代理对象;
      */
     public static Object getProxy(final Object target, final Advice advice) {
           return Proxy.newProxyInstance(
                   target.getClass().getClassLoader(),
                   target.getClass().getInterfaces(), 
                   new InvocationHandler() {
                         public Object invoke(Object proxy, Method method,
                                  Object[] args) throws Throwable {

                              if (method.getName().equals("sleep" )) {// 如果调用的方法是sleep方法;
                                  advice.beforeMethod(method); // 调用真正的方法前,执行一些操作
                                  Object retValue = method.invoke(target, args);
                                  advice.afterMethod(method); // 调用方法后,执行一些操作。
                                   return retValue;
                             } else {// 调用的是其他方法
                                  Object retValue = method.invoke(target, args);
                                   return retValue;
                             }

                        }

                   });

     }

     public static void main(String[] args) {
          Animal dog = new Dog();
          Advice advice = new MyAdvice();
          Animal proxyDog = (Animal) getProxy(dog, advice);

          proxyDog.eat();
          proxyDog.sleep();
           /*
           * 每次调用proxyDog对象的方法,都会去执行invoke方法,因此可以在invoke方法里对方法的执行实现控制,
           * 如如果调用的是sleep方法,那么就加入自己的一些想法,是其他方法则放行,相当于起到了过滤器的左右,这比过滤器实现了更细微程度的控制。
           */
     }
}

================================
例子二.动态代理更为简单的实现:
     //动态代理,针对的是一个已经存在的对象,需要对这个对象的特定的一些方法做改造;例如,改造狗叫的方法;
           final Dog dog = new Dog();//Dog有吃和叫这两个方法;
          Animal proxy = (Animal) Proxy.newProxyInstance(dog.getClass().getClassLoader(), dog.getClass().getInterfaces()
              , new InvocationHandler(){

                    public Object invoke(Object proxy, Method method, Object[] args)
                              throws Throwable {
                         if("叫" .equals(method.getName())){
                             System. out.println("代理狗哈哈哈哈的叫。。。。。" );
                              return null ;
                        } else{
                              return method.invoke(dog, args);
                        }
                   }
          });
          
          proxy.吃();
          proxy.叫();

====================================
动态代理例子三
使用hibernate进行操作数据库,保存数据时,需要开启事务,现将开启事务的操作交由动态代理对象来做。 

创建一个对象的代理对象,需要三个参数:
          public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
自己写一个handler,在handler的invoke方法中,实现开启事务和提交事务。

Classes为要保存到数据库的实体类。

完整代码:
public class HibernateUtils {
     public static SessionFactory sessionFactory;
     static{
          Configuration configuration = new Configuration();
          configuration.configure();
           sessionFactory = configuration.buildSessionFactory();
     }
}
============================================
public class MyTransaction extends HibernateUtils{
     private Transaction transaction;
     public void beginTransaction(){
           this.transaction = sessionFactory.getCurrentSession().beginTransaction();
     }
     public void commit(){
           this.transaction .commit ();
     }
}
============================================
public interface ClassesDao {
     public void saveClasses(Classes classes);
     
     public void updateClasses(Classes classes);
     
     public List<Classes> getClasses();
}
============================================
public class ClassesDaoImpl extends HibernateUtils implements ClassesDao{

     @Override
     public void saveClasses(Classes classes) {
           sessionFactory.getCurrentSession().save(classes);
     }

     @Override
     public List<Classes> getClasses() {
           return sessionFactory .getCurrentSession().createQuery("from Classes").list();
     }

     @Override
     public void updateClasses(Classes classes) {
           sessionFactory.getCurrentSession().update(classes);
     }

}
============================================
/**
 * 自己定义一个handler来完成事务操作。
 * 1、引入目标类
 * 2、引入事务
 * 3、完成invoke方法
 */
public class ClassesDaoInterceptor implements InvocationHandler{
     
     private ClassesDao target;
     
     private MyTransaction myTransaction;
     
     public ClassesDaoInterceptor(ClassesDao target,MyTransaction myTransaction){
           this.target = target;
           this.myTransaction = myTransaction;
     }

     @Override
     public Object invoke(Object proxy, Method method, Object[] args)
               throws Throwable {
           /**
           * 1、开启事务
           * 2、调用目标类的方法
           * 3、提交事务
           */
           if(method.getName().equals("saveClasses" )||
                   method.getName().equals( "updateClasses")){
               //开启事务,调用方法,提交事务
               this.myTransaction .beginTransaction();//通知
              method.invoke( this.target , args);//目标方法
               this.myTransaction .commit();
              
          } else{
              method.invoke( this.target , args);
          }
           return null ;
     }

}
============================================
/**
 * 注意的点
 *    1、代理对象的方法体的内容就是拦截器 中invoke方法体的内容
 *    2、在客户端,用代理对象调用方法的时候进去了invoke方法
 */
public class ClassesDaoTest {
     @Test
     public void testSaveClasses(){
           //目标类
          ClassesDao target = new ClassesDaoImpl();
          MyTransaction myTransaction = new MyTransaction();
          ClassesDaoInterceptor interceptor = new ClassesDaoInterceptor(target, myTransaction);
           /**
           * 获取动态代理对象
           * 1、目标类的类加载器
           * 2、目标类实现的所有的接口
           * 3、拦截器
           */
          ClassesDao targetProxy = (ClassesDao) Proxy.newProxyInstance(
                   target.getClass().getClassLoader(),
                   target.getClass().getInterfaces(),
                   interceptor);
          
          Classes classes = new Classes();
          classes.setCname( "多对多");
          
          targetProxy.saveClasses(classes);
     }
}
============================================



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值