Spring:六、ProxyFactory使用

这篇具有很好参考价值的文章主要介绍了Spring:六、ProxyFactory使用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Spring:六、ProxyFactory使用

1 前言

Spring提供了编程式AOP代理方式,而不依赖于Spring Ioc,即ProxyFactory。使用此种方式可以很方便的使用JDK动态代理或CGLIB代理,且支持Advisor chain的使用,可以通过advisor链,增强需要代理的对象方法。

文档描述:

It is easy to create AOP proxies programmatically with Spring. 
This lets you use Spring AOP without dependency on Spring IoC.

The interfaces implemented by the target object are automatically proxied. 
The following listing shows creation of a proxy for a target object, 
with one interceptor and one advisor:

实例代码:

ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl);
factory.addAdvice(myMethodInterceptor);
factory.addAdvisor(myAdvisor);
MyBusinessInterface tb = (MyBusinessInterface) factory.getProxy();

Spring文档参考:

https://docs.spring.io/spring-framework/docs/5.3.27/reference/html/core.html#aop-prog

2 使用

接口定义:

interface Fruit {
    void eat(String name);

    static void sale() {
        System.out.println("静态sale方法");
    }

    default void buy() {
        System.out.println("默认buy方法");
    }
}

实现类:

class Grape implements Fruit {

    @Override
    public void eat(String name) {
        System.out.println("eat:" + name);
    }

}

Spring底层源码可知,调用ProxyFactory的getProxy()方法,不论是cglib的实现ObjenesisCglibAopProxy,还是JDK代理的JdkDynamicAopProxy实现,MethodInvocation的实现都是使用ReflectiveMethodInvocation或其子类(子类也是调用super.proceed())。且它们实现都会调用ProxyFactory的父类AdvisedSupport的getInterceptorsAndDynamicInterceptionAdvice方法,用于获取advice的执行Chain。其中会使用DefaultAdvisorAdapterRegistry来调用getInterceptors方法注册所有的interceptors,而这个Registry类的适配器adaptors有3个,用于注册Spring自身提供的一些advice,即MethodBeforeAdviceAdapter,AfterReturningAdviceAdapter,ThrowsAdviceAdapter,只要它们的supportsAdvice方法返回true,则该adive也会被添加到执行的chain中,即advice是MethodBeforeAdvice,AfterReturningAdvice,ThrowsAdvice的子类实现即可。

由此我们可以自定义如下的增强逻辑:

前置增强:

class BeforeAdi implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("前置执行...");
    }
}

返回值后增强:

class AfterReturnAdi implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("返回值后, 执行...");
    }
}

而ThrowsAdvice的调用较为特殊,即必须含有public方法afterThrowing,参数这里取1个,因为只要是Throwable的子类实现即可,所以这里可以自定义业务异常,用于处理发生自定义业务异常的场景:

自定义业务异常:

class MyError extends RuntimeException{
    public MyError() {
    }

    public MyError(String message) {
        super(message);
    }

    public MyError(String message, Throwable cause) {
        super(message, cause);
    }

    public MyError(Throwable cause) {
        super(cause);
    }

    public MyError(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

异常增强(ThrowsAdvice会先执行增强逻辑,并且抛出异常):

public class ThrowErrorHandler implements ThrowsAdvice {
    // 因为底层源码使用getMethods,故而此 afterThrowing方法必须是throwsAdvice的public方法
    // 另外源码反射执行该方法,没有对该method:afterThrowing调用setAccessible,故而非public class也会反射调用失败
    // 故而该类最好不要写在同1个java文件中,即不要是非public class
    public void afterThrowing(MyError myError){
        System.out.println("发生未知自定义异常:" + myError.getMessage());
    }
}

源码实现即ThrowsAdviceInterceptor,因为适配器ThrowsAdviceAdapter的getInterceptor方法,就是直接返回new ThrowsAdviceInterceptor(advisor.getAdvice())拦截器。源码中的invoke逻辑:

@Nullable
public Object invoke(MethodInvocation mi) throws Throwable {
    try {
        return mi.proceed();
    } catch (Throwable var4) {
        Method handlerMethod = this.getExceptionHandler(var4);
        if (handlerMethod != null) {
            this.invokeHandlerMethod(mi, var4, handlerMethod);
        }

        throw var4;
    }
}

可知即递归调用ReflectiveMethodInvocation的proceed方法(MethodBeforeAdviceAdapter,AfterReturningAdviceAdapter亦是如此),如果抛出Throwable,则从预先的exceptionHandlerMap中取出对应的Throwable的Class的value,即Method。而这个Class,就是我们自定义的afterThrowing方法的Throwable参数的class,故而支持自定义业务异常的拦截增强。

因为该Method在this.invokeHandlerMethod(mi, var4, handlerMethod)方法中,是直接使用method.invoke执行该方法,此前Spring并未执行setAccessible的操作,也未执行过Spring-core包下的ReflectionUtils.makeAccessible(Method method)方法,故而此afterThrowing方法必须是public方法,且类本身的作用范围同样影响到Method方法的反射执行(Reflection下执行ensureMemberAccess,verifyMemberAccess方法返回false的场景,均会抛出IllegalAccessException),故而此类定义为public类为优,同时便于拓展。

亦或可以使用以下方式,自定义继承ThrowsAdvice的接口,然后自行实现该接口,因为接口方法默认是public,且重写方法时,作用域无法小于原重写方法的作用域,即不能为protected、private等,只能以public重写,只是重写时,若接口方法是Object,重写方法的返回值可以是其子类,即存在继承关系的类的重写,故而这里返回Boolean值。当然实际源码也未使用返回值,只是执行afterThrowing方法后,抛出异常而已:

public class ThrowErrorHandler implements ThrowsAdviceHandle<MyError> {
    // 因为底层源码使用getMethods,故而此 afterThrowing方法必须是throwsAdvice的public方法
    // 另外源码反射执行该方法,没有对该method:afterThrowing调用setAccessible,故而非public class也会反射调用失败
    // 故而该类最好不要写在同1个java文件中,即不要是非public class
    @Override
    public Boolean afterThrowing(MyError myError) {
        System.out.println("发生未知自定义异常:" + myError.getMessage());
        return Boolean.TRUE;
    }
}

interface ThrowsAdviceHandle<T extends Throwable> extends ThrowsAdvice {
    Object afterThrowing(T myError);
}

除了上述3种特殊的advice,当然自定义的MethodInterceptor也会添加到advisor chain中:

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

class MyAdvice implements MethodInterceptor {
    @Nullable
    @Override
    public Object invoke(@Nonnull MethodInvocation invocation) throws Throwable {
        if (invocation instanceof ReflectiveMethodInvocation) {
            System.out.println("***************advice1");
            return 123;
        }
        throw new RuntimeException("未知异常");
    }
}
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

class MyAdvice2 implements MethodInterceptor {
    @Nullable
    @Override
    public Object invoke(@Nonnull MethodInvocation invocation) throws Throwable {
        if (invocation instanceof ReflectiveMethodInvocation) {
            System.out.println("***************advice2");
            return 456;
        }
        return null;
    }
}

定义了MethodInterceptor 后,可以使用ProxyFactory增强,使用代理后的对象调用增强的Fruit.class接口的实例方法或default方法,均可以获得增强:

ProxyFactory factory = new ProxyFactory();

factory.setInterfaces(Fruit.class);
factory.setTarget(new Grape());
//  Advice must not be null
factory.addAdvice(new BeforeAdi());
factory.addAdvice(new AfterReturnAdi());
factory.addAdvice(new ThrowErrorHandler());
factory.addAdvice(new MyAdvice());
factory.addAdvice(new MyAdvice2());

Fruit fruit = (Fruit) factory.getProxy();
fruit.eat("葡萄");
fruit.buy();

执行结果如下:

前置执行...
***************advice1
返回值后, 执行...
前置执行...
***************advice1
返回值后, 执行...

修改MyAdvice,使其反射调用实例方法:

class MyAdvice implements MethodInterceptor {
    @Nullable
    @Override
    public Object invoke(@Nonnull MethodInvocation invocation) throws Throwable {
        if (invocation instanceof ReflectiveMethodInvocation) {
            System.out.println("***************advice1");
            ReflectiveMethodInvocation reflectiveMethodInvocation = (ReflectiveMethodInvocation) invocation;
            Object target = reflectiveMethodInvocation.getThis();
            return invocation.getMethod().invoke(target, invocation.getArguments());
        }
        throw new RuntimeException("未知异常");
    }
}

再次执行:

前置执行...
***************advice1
eat:葡萄
返回值后, 执行...
前置执行...
***************advice1
默认buy方法
返回值后, 执行...

之所以factory.addAdvice,添加了MyAdvice和MyAdvice2,但只执行了MyAdvice,是因为MyAdvice直接反射调用了该实例方法,即invocation.getMethod().invoke(target, invocation.getArguments()),所以链式执行advice chain时,因为已经return了,所以后续的MyAdvice2不会再次执行(Spring源码可知,该Chain的执行,instanceof MethodInterceptor 的增强,或adapter.supportsAdvice(advice),是按照list的顺序执行的,即addAdvice的添加顺序,所以这里先添加的特殊的3种增强,避免被自定义的MethodInterceptor 直接return,导致这三个不生效),若希望所有的advice都执行完,改为调用invocation.proceed(),即递归执行ReflectiveMethodInvocation的proceed方法即可,这也是Spring代理增强的设计中,非常出彩的逻辑(责任链设计模式中,即采用递归链式方式执行)。

再次修改MyAdvice、MyAdvice2的增强执行:

class MyAdvice implements MethodInterceptor {
    @Nullable
    @Override
    public Object invoke(@Nonnull MethodInvocation invocation) throws Throwable {
        if (invocation instanceof ReflectiveMethodInvocation) {
            System.out.println("***************advice1");
            return invocation.proceed();
        }
        throw new RuntimeException("未知异常");
    }
}
class MyAdvice2 implements MethodInterceptor {
    @Nullable
    @Override
    public Object invoke(@Nonnull MethodInvocation invocation) throws Throwable {
        if (invocation instanceof ReflectiveMethodInvocation) {
            ReflectiveMethodInvocation reflectiveMethodInvocation = (ReflectiveMethodInvocation) invocation;
            System.out.println("***************advice2");
            return invocation.proceed();
        }
        return null;
    }
}

再次执行,结果如下,可见全部的advice均已执行,在这些advice增强通过invoke方法执行完后,源码中会调用return this.invokeJoinpoint();,即反射执行该实例方法:

前置执行...
***************advice1
***************advice2
eat:葡萄
返回值后, 执行...
前置执行...
***************advice1
***************advice2
默认buy方法
返回值后, 执行...

如下为部分源码示例,因为Spring源码底层在链式执行全部增强后,判断if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1),则会反射调用该被增强的实例方法,故而一般只需要自定义增强逻辑即可,无需再手动反射调用该实例方法并return(当然,举例来说,若原本接口的返回值为Object,而重写方法比如返回为其子类Integer,而该方法执行比较耗时,希望增强是返回CompletableFuture对象,能够获取增强后的CompletableFuture对象并收集所有对象异步执行,提升效率,那么此时可以添加自定义实现的MethodInterceptor,直接return 返回自定义对实例方法包装后的CompletableFuture对象即可,也就不会执行invokeJoinpoint方法了,这也是Spring该编程式AOP实现方式非常方便的地方。另外异步执行通过代理实现的这个思想,也可以从Spring的@Async实现方式中参考,与该思想类似):

@Nullable
protected Object invokeJoinpoint() throws Throwable {
    return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
}

按照如上逻辑,再次修改eat方法实现:

class Grape implements Fruit {

    @Override
    public void eat(String name) {
        System.out.println("eat:" + name);
        throw new MyError("【默认方式异常】");
    }

}

再次执行,可见ThrowErrorHandler能够正确处理该异常(实际业务逻辑可以是失败DB插入错误日志、日志打印等等),自定义增强处理完后,则抛出异常:

前置执行...
***************advice1
***************advice2
eat:葡萄
发生未知自定义异常:【默认方式异常】
Exception in thread "main" action.MyError: 【默认方式异常】

最后,对于上述提到的返回值为Object的接口,使其返回值为其子类的实现,能够通过代理增强为CompletableFuture异步执行的对象方式,还可以通过自定义注解,配合SpringBoot的拓展BeanFactoryPostProcessor、BeanPostProcessor来实现对Bean的Field字段,自动代理为异步对象,由该异步代理对象,来达到方法异步执行的效果。降低耦合的同时,也不失为一种很好的编程思路。文章来源地址https://www.toymoban.com/news/detail-463352.html

到了这里,关于Spring:六、ProxyFactory使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring系列2 -- Spring的创建和使用

    Spring 就是⼀个包含了众多工具方法的 IOC容器。既然是容器那么它就具备两个最基本的功能: 将对象存储到容器(Spring)中; 从容器中将对象取出来。  在Java中对象也叫做Bean,后续我们就把对象称之为Bean; 目录 1. 创建Spring项目(Maven创建) 1.1 创建一个Maven项目  1.2 添加

    2024年02月12日
    浏览(27)
  • Spring系列一:spring的安装与使用

    进入官网: https://spring.io/ 这里的版本是Spring5 (Spring框架就是一系列jar包. 即引入jar包, 就可以使用spring) 进入Spring5的github(Spring本身也是GitHub的开源项目) 下拉找到Access to Binaries, 进入Spring Framework Artifacts 进入到Spring的仓库(这里有Spring的各个版本的jar包) 具体路径 snapshot-org-spri

    2024年02月16日
    浏览(28)
  • 如果你需要使用重试机制,请使用Spring官方的Spring Retry

    Spring Retry 是 Spring Framework 中的一个模块,提供了一种简单的方式来在应用程序中实现重试机制。 在应用程序中,如果遇到了一些不可避免的错误,比如网络连接失败、数据库连接失败等,我们通常需要对这些错误进行重试,以尝试解决这些问题。 Spring Retry 提供了一个可插拔

    2024年02月13日
    浏览(39)
  • MyBatis的使用、Spring AOP、Spring事务

    1.1、建库建表 1.2、安装插件  1.3、插件的使用 1.3.1、已存在的项目中使用 右键点击出现 Generate 在 SQL中选择相应的依赖:  创建好后会显示: 1.3.2、新项目中使用 创建 Spring项目时要注意添加的依赖  创建好后会显示:  1.4、数据库的配置 PS:注意: 如果使⽤ mysql-connector-

    2024年02月08日
    浏览(54)
  • Spring系列学习九、Spring MVC的使用

    本章我们将与大家一起探讨Spring框架中的Web模块中的一颗璀璨明珠——Spring MVC。在这个章节中,我们将对MVC设计模式、Spring MVC的工作原理、HandlerMapping和ViewResolver的作用,以及如何处理表单、文件上传和异常处理等内容有基本的了解。别担心,我将以通俗易懂、幽默风趣的方

    2024年02月01日
    浏览(44)
  • 什么是Spring Cache?Spring项目如何使用?

    目前Spring Cloud微服务在Web项目中占据了主流地位,如果去面试关于Spring Cloud的岗位时,面试官一般都会提问你的项目是如何优化的,从哪些方面入手去优化。而缓存技术绝对是项目必不可少的,所以我们必须掌握好Java项目的缓存技术。 目前,在 Java 中,常见的缓存有以下几

    2024年02月05日
    浏览(77)
  • 【Spring实战】26 使用Spring Security 保护 Spring Boot Admin

    Spring Boot Admin 是一个用于监控和管理 Spring Boot 应用程序的工具,而 Spring Security 是一个用于提供身份验证和授权的强大框架。本文们将探讨如何将 Spring Boot Admin 与 Spring Security 集成,以确保管理端的安全性。 Spring Boot Admin: Spring Boot Admin 是一个基于Web的用户界面,用于集中监

    2024年01月25日
    浏览(49)
  • 【Spring框架】Spring事务的介绍与使用方法

    ⚠️ 再提醒一次:Spring 本身并不实现事务,Spring事务 的本质还是底层数据库对事务的支持。你的程序是否支持事务首先取决于数据库 ,比如使用 MySQL 的话,如果你选择的是 innodb 引擎,那么恭喜你,是可以支持事务的。但是,如果你的 MySQL 数据库使用的是 myisam 引擎的话,

    2024年02月11日
    浏览(45)
  • 使用Spring Initializr方式构建Spring Boot项目

    除了可以使用Maven方式构建Spring Boot项目外,还可以通过Spring Initializr方式快速构建Spring Boot项目。从本质上说,Spring lnitializr是一个Web应用,它提供了一个基本的项目结构,能够帮助我们快速构建一个基础的Spring Boot项目。下面讲解如何使用Spring Initializr方式构建Spring Boot项目,

    2024年02月12日
    浏览(56)
  • 如何在Spring Boot中使用Spring MVC

    目录 1.MVC 2.Spring MVC 3.Spring Boot中使用Spring MVC 3.1.配置 3.1.1.文件配置 3.1.2.代码配置 3.2.使用 3.2.1.映射处理器 3.2.2.传参 3.2.3.参数转换 3.2.4.数据校验 3.2.5.数据模型 3.2.6.视图和解析器 3.2.7.拦截器 MVC 是一种常见的软件设计模式,用于分离应用程序的不同部分以实现松散耦合和高内

    2023年04月15日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包