栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

代理模式(Proxy)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

代理模式(Proxy)

术语

Proxy:代理

如图:

分类

静态代理
    角色
             接口
                 ITeacherDao
             目标对象
                 TeacherDAO 
             代理类
                 TeacherDAOProxy 
         细节
             代理对象与目标对象要实现相同的接口
             调用的时候通过调用代理对象的方法来调用目标对象

案例: 

package com.javatf.design.proxy.staticproxy;
  
  
  public class TeacherDAO implements ITeacherDao {
      public void teach() {
          System.out.println("老师传授知识");
      }
  }
  
  //目标接口
  interface ITeacherDao {
      void teach();
  }
//代理类
  class TeacherDAOProxy implements ITeacherDao {
      private ITeacherDao teacherDAO;
  
      public TeacherDAOProxy(ITeacherDao teacherDAO) {
          this.teacherDAO = teacherDAO;
      }
  
      @Override
      public void teach() {
          System.out.println("老师正式授课前的准备工作,如学生全部签到...");
          teacherDAO.teach();
          System.out.println("老师结束授课,如下课铃声响起...");
      }
  }
public class Client {
      public static void main(String[] args) {
          TeacherDAOProxy proxy = new TeacherDAOProxy(new TeacherDAO());
          proxy.teach();
      }
  }

动态代理

jdk代理
      ​        角色
      ​            接口
      ​                ITeacherDao
      ​            目标对象
      ​                TeacherDAO 
      ​            代理类
      ​                TeacherDAOProxy 
      ​        细节
      ​            不需要实现接口,但是**目标对象要实现接口**,否则不能用动态代理
      ​            代理对象的生成,是利用 JDK 的 API,动态的在内存中构建代理对象
      ​            代理类所在包:java.lang.reflect.Proxy

 案例:

 package com.javatf.design.proxy.dynamic.jdk;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    
    interface ITeacherDao {
        String teach();
    
        ITeacherDao sleep(int minutes);
    }
    
    class TeacherDao implements ITeacherDao{
        @Override
        public String teach() {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            return sdf.format(new Date())+":老师传授知识";
        }
    
        @Override
        public ITeacherDao sleep(int minutes) {
            System.out.println("老师睡了" + minutes + "分钟");
            return this;
        }
    
    }
//真实代理类的外衣
    class TeacherDaoProxy{
        private ITeacherDao target;
    
        public TeacherDaoProxy(ITeacherDao target) {
            this.target = target;
        }
    
        public Object xxx(){
            return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(),
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            Object obj = null;
                            String methodName = method.getName();
                            System.out.println("目标方法" + methodName + ":jdk代理开始...");
                            System.out.println("真实代理对象:"+proxy.getClass());
                            System.out.println("目标对象:"+target.getClass());
                            if("sleep".equals(methodName)){
    //                            method.invoke(target, args);
    //                            obj = proxy;
                                obj = method.invoke(target, args);
                            }else {
    //                        proxy是真实代理类,method是目标方法,args是目标方法携带的参数
                                obj = method.invoke(target, args);
                            }
                            System.out.println("目标方法" + methodName + ":jdk代理结束...");
                            return obj;
                        }
                    });
        }
    }
public class Client {
        public static void main(String[] args) {
            TeacherDaoProxy proxy = new TeacherDaoProxy(new TeacherDao());
            ITeacherDao ins = (ITeacherDao) proxy.xxx();
            System.out.println("===========代理类实例被使用   begin=============");
            System.out.println(ins);
            System.out.println("===========代理类实例被使用   end=============");
            System.out.println(ins.teach());
    //        System.out.println(proxy.execute());
            System.out.println("===========代理类实例被使用   begin=============");
            ins.sleep(10);
            System.out.println("===========代理类实例被使用   end=============");
            ins.sleep(20).sleep(60);
        }
    }

注意:java.lang.reflect.InvocationHandler.invoke第一个参数proxy的使用场景,链式编程中会用到;

 Cglib代理

角色
      ​            接口
      ​                ITeacherDao
      ​            目标对象
      ​                TeacherDAO 
      ​            代理类
      ​                TeacherDAOProxy 

细节
      ​            目标对象与代理对象都不需要实现接口
      ​            Cglib 代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展
      ​            Cglib 是一个强大的高性能的代码生成包,它可以在运行期扩展 java 类与实现 java 接口.它广泛的被许多 AOP 的框架使用
      ​            Cglib 包的底层是通过使用字节码处理框架 ASM 来转换字节码并生成新的类

注意:
      1)需要引入 cglib 的 jar 文件
      2)在内存中动态构建子类,注意代理的类不能为 final,否则报错java.lang.IllegalArgumentException:
      3)目标对象的方法如果为 final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法. 

 案例:

package com.javatf.design.proxy.dynamic.cglib;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.SimpleDateFormat;
import java.util.Date;


class TeacherDao {
    public String teach() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        return sdf.format(new Date()) + ":老师传授知识";
    }

    public TeacherDao sleep(int minutes) {
        System.out.println("老师睡了" + minutes + "分钟");
        return this;
    }

}
//真实代理类的外衣
class TeacherDaoProxy implements MethodInterceptor {
    private Object target;

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

    //返回一个代理对象:	是 target  对象的代理对象
    public Object getProxyInstance() {
        //1. 创建一个工具类
        Enhancer enhancer = new Enhancer();
        //2. 设置父类
        enhancer.setSuperclass(target.getClass());
        //3. 设置回调函数
        enhancer.setCallback(this);
        //4. 创建子类对象,即代理对象
        return enhancer.create();
    }
    @Override
    public Object intercept(Object proxyIns, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        String methodName = method.getName();
        Object res;
        System.out.println("目标方法" + methodName + ":cglib代理开始...");
        System.out.println("真实代理对象:" + proxyIns.getClass());
        System.out.println("目标对象:" + target.getClass());
        if ("sleep".equals(methodName)) {
//                            method.invoke(target, args);
//                            obj = proxy;
            res = method.invoke(target, args);
//            res = methodProxy.invokeSuper(proxyIns,args);
            res = proxyIns;
        } else {
//                        proxy是真实代理类,method是目标方法,args是目标方法携带的参数
            res = method.invoke(target, args);
        }
        System.out.println("目标方法" + methodName + ":cglib代理结束...");
        return res;
    }
}

public class Client {
    public static void main(String[] args) {
        TeacherDao proxy = (TeacherDao) new TeacherDaoProxy(new TeacherDao()).getProxyInstance();
        proxy.sleep(111).sleep(222);
    }
}

jdk代理与Cglib代理比较:

     JDK中所要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中具有一定的局限性,而且使用反射的效率也并不是很高 

​     使用CGLib实现动态代理,完全不受代理类必须实现接口的限制,而且CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。 
 

应用场景:

Spring框架的AOP:Cglib代理的体现

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/831938.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号