深入理解Java设计模式——代理模式

追求适度,才能走向成功;人在顶峰,迈步就是下坡;身在低谷,抬足既是登高;弦,绷得太紧会断;人,思虑过度会疯;水至清无鱼,人至真无友,山至高无树;适度,不是中庸,而是一种明智的生活态度。

导读:本篇文章讲解 深入理解Java设计模式——代理模式,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

一、什么是代理模式

为其他对象提供一种代理以控制对这个对象的访问。

二、为什么使用代理模式

中介隔离:在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。

开闭原则,增加功能代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。

三、代理模式实现原理

在这里插入图片描述
代理模式主要包含三个角色,即抽象主题角色(Subject)、委托类角色(被代理角色,Proxied)以及代理类角色(Proxy),如上图所示:
抽象主题角色:可以是接口,也可以是抽象类;
委托类角色:真实主题角色,业务逻辑的具体执行者;
代理类角色:内部含有对真实对象RealSubject的引用,负责对真实主题角色的调用,并在真实主题角色处理前后做预处理和后处理。

四、代理模式应用场景

SpringAop、日志收集、权限控制、过滤器、RPC远程调用

五、代理模式创建的方式

静态代理和动态代理

六、静态代理

静态代理是由程序员创建或工具生成代理类的源码,再编译代理类。
所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
一句话,自己手写代理类就是静态代理。

1. 基于接口实现方式

public interface OrderService {
    void order();
}
public class OrderServiceImpl implements OrderService {
    public void order() {
        System.out.println("用户下单操作..");
    }
}
public class OrderServiceProxy implements OrderService {
    /**
     * 代理对象
     */
    private OrderService proxiedOrderService;

    public OrderServiceProxy(OrderService orderService) {
      this.proxiedOrderService=orderService;
    }

    public void order() {
        System.out.println("日志收集开始..");
        proxiedOrderService.order();
        System.out.println("日志收集结束..");
    }
}
public class ClientTest {
    public static void main(String[] args) {
        OrderService orderService = new OrderServiceProxy(new OrderServiceImpl());
        orderService.order();
    }
}

2. 接口继承方式实现

public interface OrderService {
    void order();
}
public class OrderServiceImpl implements OrderService {
    public void order() {
        System.out.println("用户下单操作..");
    }
}
public class OrderServiceProxy extends OrderServiceImpl {

    public void order() {
        System.out.println("日志收集开始..");
        super.order();
        System.out.println("日志收集结束..");
    }
}
public class ClientTest {
    public static void main(String[] args) {
        OrderService orderService = new OrderServiceProxy();
        orderService.order();
    }
}

七、动态代理

动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。
动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成 。

1. JDK动态代理

JDK动态代理的一般步骤如下:
1.创建被代理的接口和类;
2.实现InvocationHandler接口,对目标接口中声明的所有方法进行统一处理;
3.调用Proxy的静态方法,创建代理类并生成相应的代理对象;

public interface OrderService {
    void order();
}
public class OrderServiceImpl implements OrderService {
    public void order() {
        System.out.println("修改数据库订单操作..");
    }
}
public class JdkInvocationHandler implements InvocationHandler {
    /**
     * 目标代理对象
     */
    public Object target;

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

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(">>>日志收集开始>>>>");
        // 执行代理对象方法
        Object reuslt = method.invoke(target, args);
        System.out.println(">>>日志收集结束>>>>");
        return reuslt;
    }

    /**
     * 获取代理对象接口
     *
     * @param <T>
     * @return
     */
    public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

}
public class ClientTest {
    public static void main(String[] args) {
		JdkInvocationHandler jdkInvocationHandler = new JdkInvocationHandler(new OrderServiceImpl());
		OrderService proxy = jdkInvocationHandler.getProxy();
		proxy.order();
    }
}

1.1 原理分析

  1. 获取代理的生成的class文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

在这里插入图片描述

  1. 使用反编译工具该Proxy0.class
    在这里插入图片描述
    在这里插入图片描述

注意:继承了Proxy类,实现了代理的接口,由于java不能多继承,这里已经继承了Proxy类了,不能再继承其他的类,所以JDK的动态代理不支持对实现类的代理,只支持接口的代理。

2. CGLIB动态代理

Cglib是一个强大的,高性能,高质量的代码生成类库。它可以在运行期扩展JAVA类与实现JAVA接口。其底层实现是通过ASM字节码处理框架来转换字节码并生成新的类。大部分功能实际上是ASM所提供的,Cglib只是封装了ASM,简化了ASM操作,实现了运行期生成新的class。

2.1 CGLIB原理

运行时动态的生成一个被代理类的子类(通过ASM字节码处理框架实现),子类重写了被代理类中所有非final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势植入横切逻辑。

2.2 CGLIB优缺点

优点:JDK动态代理要求被代理的类必须实现接口,当需要代理的类没有实现接口时Cglib代理是一个很好的选择。另一个优点是Cglib动态代理比使用java反射的JDK动态代理要快
缺点:对于被代理类中的final方法,无法进行代理,因为子类中无法重写final函数

2.4 CGLIB 代理实现

实现MethodInterceptor接口的intercept方法后,所有生成的代理方法都调用这个方法。
intercept方法的具体参数有

  1. obj 目标类的实例
  2. method 目标方法实例(通过反射获取的目标方法实例)
  3. args 目标方法的参数
  4. proxy 代理类的实例
    该方法的返回值就是目标方法的返回值。
public interface OrderService {
    void order();
}
public class OrderServiceImpl implements OrderService {
    public void order() {
        System.out.println("用户下单操作...");
    }
}
public class CglibMethodInterceptor implements MethodInterceptor {
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("<<<<<日志收集开始...>>>>>>>");
        Object reuslt = proxy.invokeSuper(obj, args);
        System.out.println("<<<<<日志收集结束...>>>>>>>");
        return reuslt;
    }
}

public class ClientTest {
    public static void main(String[] args) {
		System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
		CglibMethodInterceptor cglibMethodInterceptor = new CglibMethodInterceptor();
		Enhancer enhancer = new Enhancer();
		// 设置代理类的父类
		enhancer.setSuperclass(OrderServiceImpl.class);
		// 设置回调对象
		enhancer.setCallback(cglibMethodInterceptor);
		// 创建代理对象
		OrderServiceImpl orderServiceImpl = (OrderServiceImpl) enhancer.create();
		orderServiceImpl.order();
    }
}

2.5 Maven依赖

<dependencies>

    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.2.12</version>
    </dependency>
</dependencies>

3. 静态代理与动态代理区别

静态代理需要自己写代理类,而动态代理不需要写代理类。

4. JDK动态代理与CGLIB实现区别

JDK动态代理底层实现:
JDK的动态代理使用Java的反射技术生成动态代理类,只能代理实现了接口的类, 没有实现接口的类不能实现动态代理。

CGLIB动态代理底层实现:
运行时动态的生成一个被代理类的子类(通过ASM字节码处理框架实现),子类重写了被代理类中所有非final的方法,在子类中采用方法拦截的技术拦截所有父类方法的调用,不需要被代理类对象实现接口,从而CGLIB动态代理效率比Jdk动态代理反射技术效率要高。

5. 使用AOP拦截Controller所有请求日志

@Aspect
@Component
@Slf4j
public class AopLogAspect {


    // 申明一个切点 里面是 execution表达式
    @Pointcut("execution(* com.demo.controller.*.*(..))")
    private void serviceAspect() {
    }

    // 请求method前打印内容
    @Before(value = "serviceAspect()")
    public void methodBefore(JoinPoint joinPoint) {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();

        // 打印请求内容
        log.info("===============请求内容===============");
        log.info("请求地址:" + request.getRequestURL().toString());
        log.info("请求方式:" + request.getMethod());
        log.info("请求类方法:" + joinPoint.getSignature());
        log.info("请求类方法参数:" + Arrays.toString(joinPoint.getArgs()));
        log.info("===============请求内容===============");


    }

    // 在方法执行完结后打印返回内容
    @AfterReturning(returning = "o", pointcut = "serviceAspect()")
    public void methodAfterReturing(Object o) {
        log.info("--------------返回内容----------------");
        log.info("Response内容:" + o.toString());
        log.info("--------------返回内容----------------");


    }

}

6. Maven依赖信息

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.1.RELEASE</version>
</parent>
<dependencies>

    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.2.12</version>
    </dependency>
    <!-- sprinboot web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.10</version>
    </dependency>
    <dependency>
        <groupId>commons-lang</groupId>
        <artifactId>commons-lang</artifactId>
        <version>2.6</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
</dependencies>

八、纯手写JDK动态代理

1. JDK动态代理原理分析

  1. 在使用jdk动态代理的时候,必须要实现InvocationHandler接口,invoke方法
    Invoke 方法中该三个参数分别表示为: 代理对象、被代理执行的方法、参数

  2. 使用jdk动态代理获取代理类对象(JDK自动生成代理类) $Proxy0.class

2. 纯手写动态代理分析

  1. 创建代理类$Proxy0源代码文件实现被代理的接口。
public final class $Proxy0 extends java.lang.reflect.Proxy implements com.demo.service.OrderService
  1. 使用JavaCompiler技术编译该

    P

    r

    o

    x

    y

    0

    Proxy0文件获取到

    Proxy0Proxy0.class

  2. 使用ClassLoader将该$Proxy0.class加入到当前JVM内存中

ClassLoader 顾名思义就是类加载器,ClassLoader 作用:
负责将 Class 加载到 JVM 中
审查每个类由谁加载(父优先的等级加载机制)
将 Class 字节码重新解析成 JVM 统一要求的对象格式

3. JDK动态代理的简单实现

public interface MyExtInvocationHandler {
    /**
     * 纯手写模拟 jdk动态模拟 InvocationHandler接口
     *
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException;
}

public class ExtJdkInvocationHandler implements MyExtInvocationHandler {
    /**
     * 目标执行对象 (被代理的对象)
     */
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
        System.out.println("纯手写JDK动态代理日志拦截开始>>>>>");
        Object result = method.invoke(target, args);// 执行代理对象的方法
        System.out.println("纯手写JDK动态代理日志拦截结束>>>>>");
        return result;
    }

    //    /**
//     * 获取代理对象
//     *
//     * @param <T>
//     * @return
//     */
    public <T> T getProxy() {
        return (T) new $Proxy0(this);
    }
}

public class $Proxy0 implements OrderService {
    private MyExtInvocationHandler h;

    /**
     * 使用构造函数传递myExtInvocationHandler
     *
     * @param myExtInvocationHandler
     */
    public $Proxy0(MyExtInvocationHandler myExtInvocationHandler) {
        this.h = myExtInvocationHandler;
    }

    @Override
    public void order() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //传递被代理对象的方法
        Method orderMethod = OrderService.class.getMethod("order", new Class[]{});
        this.h.invoke(this, orderMethod, null);
    }
}

ExtJdkInvocationHandler extJdkInvocationHandler = new ExtJdkInvocationHandler(new OrderServiceImpl());
OrderService proxy = extJdkInvocationHandler.getProxy();
proxy.order();


4. 完全逼真模拟JDK动态代理

public interface MyExtInvocationHandler {
    /**
     * 纯手写模拟 jdk动态模拟 InvocationHandler接口
     *
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException;
}

public class JavaClassLoader extends ClassLoader {

    private File classPathFile;

    public JavaClassLoader(){
//        String classPath=JavaClassLoader.class.getResource("").getPath();
        String classPath="D:\\code";
        this.classPathFile=new File(classPath);
    }

    @Override
    public Class<?> findClass(String name) throws ClassNotFoundException {
        String className= JavaClassLoader.class.getPackage().getName()+"."+name;
        if(classPathFile!=null){
          File classFile=new File(classPathFile,name.replaceAll("\\.","/")+".class");
          if(classFile.exists()){
              FileInputStream in=null;
              ByteArrayOutputStream out=null;
              try {
                  in=new FileInputStream(classFile);
                  out=new ByteArrayOutputStream();
                  byte[] buff=new byte[1024];
                  int len;
                  while ((len=in.read(buff))!=-1){
                     out.write(buff,0,len);
                  }
                  return defineClass(className,out.toByteArray(),0,out.size());
              }catch (Exception e){
                  e.printStackTrace();
              }finally {
                  if(in!=null){
                      try {
                          in.close();
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
                  }
                  if(out!=null){
                      try {
                          out.close();
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
                  }
              }
          }
        }
        return null;
    }
}

public class ExtJdkInvocationHandler implements MyExtInvocationHandler {
    /**
     * 目标执行对象 (被代理的对象)
     */
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
        System.out.println("纯手写JDK动态代理日志拦截开始>>>>>");
        Object result = method.invoke(target, args);// 执行代理对象的方法
        System.out.println("纯手写JDK动态代理日志拦截结束>>>>>");
        return result;
    }

    //    /**
//     * 获取代理对象
//     *
//     * @param <T>
//     * @return
//     */
    public <T> T getProxy() {
        return (T) MyProxy.newProxyInstance(new JavaClassLoader(), OrderService.class, this);
    }
}

public class MyProxy {
    static String rt = "\r\t";

    /**
     * @param classInfo 被代理实现的接口信息 <br>
     * @param h
     * @return
     */
    public static Object newProxyInstance(JavaClassLoader javaClassLoader, Class classInfo, MyExtInvocationHandler h) {
        try {
            // 1.创建代理类java源码文件,写入到硬盘中..
            Method[] methods = classInfo.getMethods();
            String proxyClass = "package com.demo.ext.jdk.proxy;" + rt
                    + "import java.lang.reflect.Method;" + rt
                    + "import com.demo.ext.jdk.proxy.MyExtInvocationHandler;" + rt
                    + "public class $Proxy0 implements " + classInfo.getName() + "{" + rt
                    + "MyExtInvocationHandler h;" + rt
                    + "public $Proxy0(MyExtInvocationHandler h)" + "{" + rt
                    + "this.h= h;" + rt + "}"
                    + getMethodString(methods, classInfo) + rt + "}";
            // 2. 将代理类源码文件写入硬盘中
            String filename = "d:/code/$Proxy0.java";
            File f = new File(filename);
            FileWriter fw = new FileWriter(f);
            fw.write(proxyClass);
            fw.flush();
            fw.close();

            // 3.使用JavaJavaCompiler 编译该$Proxy0源代码 获取class文件
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
            Iterable units = fileMgr.getJavaFileObjects(filename);
            JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
            t.call();
            fileMgr.close();

            //4.使用classClassLoader 将$Proxy0.class读取到内存中...
            Class proxy0Class = javaClassLoader.findClass("$Proxy0");
            //5.使用java反射机制给函数中赋值
            Constructor m = proxy0Class.getConstructor(MyExtInvocationHandler.class);
            Object o = m.newInstance(h);
            return o;
        } catch (Exception e) {
            e.printStackTrace();
            ;
        }
        return null;
    }


    public static String getMethodString(Method[] methods, Class intf) {
        String proxyMe = "";
        for (Method method : methods) {
            proxyMe += "public void " + method.getName() + "() throws Throwable {" + rt
                    + "Method md= " + intf.getName() + ".class.getMethod(\"" + method.getName()
                    + "\",new Class[]{});" + rt
                    + "this.h.invoke(this,md,null);" + rt + "}" + rt;

        }
        return proxyMe;
    }
}

九、纯手写Spring事务框架

1. 基于JDK动态代理方式实现

public class MyInvocationHandler implements InvocationHandler {
    private MyBatisTransaction myBatisTransaction;
    private Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
        myBatisTransaction = SpringUtils.getBean("myBatisTransaction", MyBatisTransaction.class);

    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        TransactionStatus transactionStatus = null;
        try {
            transactionStatus = myBatisTransaction.begin();
            Object result = method.invoke(target, args);
            myBatisTransaction.commit(transactionStatus);
            return result;
        } catch (Exception e) {
            myBatisTransaction.rollBack(transactionStatus);
            return "系统出现异常!";
        }

    }

    /**
     * 获取代理对象接口
     *
     * @param <T>
     * @return
     */
    public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

}

@Component
public class MyBatisTransaction {
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;

    /**
     * 手动开启
     */
    public TransactionStatus begin() {
        TransactionStatus transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute());
        return transaction;
    }


    /**
     * 手动提交
     */
    public void commit(TransactionStatus transaction) {
        if (transaction != null) {
            dataSourceTransactionManager.commit(transaction);
        }

    }

    /**
     * 手动回滚
     */
    public void rollBack(TransactionStatus transaction) {
        if (transaction != null) {
            dataSourceTransactionManager.rollback(transaction);
        }

    }

}




2. 基于AOP方式实现

@Component
@Aspect
public class AopTransaction {
    @Autowired
    private MyBatisTransaction myBatisTransaction;
    // // 异常通知
    @AfterThrowing("execution(* com.demo.service.impl.OrderServiceImpl.addOrder(..))")
    public void afterThrowing() {
        System.out.println("程序已经回滚");
        // 获取程序当前事务 进行回滚
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
    // 环绕通知
    @Around("execution(* com.demo.service.impl.OrderServiceImpl.addOrder(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("开启事务");
        TransactionStatus begin = myBatisTransaction.begin();
        proceedingJoinPoint.proceed();
        myBatisTransaction.commit(begin);
        System.out.println("提交事务");
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/131287.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!