Spring5参考指南
  • 简介
  • 前言
    • 1. “Spring”代表什么
    • 2. Spring和Spring框架的历史
    • 3. 设计哲学
    • 4. 反馈和贡献
    • 5. 开始
  • 核心技术
    • 1. IoC容器
      • 1.1 IoC容器和Beans介绍
      • 1.2 IoC容器概述
      • 1.3 Bean概述
      • 1.4 依赖
      • 1.5 Bean作用域
      • 1.6 自定义Bean
      • 1.7 Bean的继承
      • 1.8 容器扩展点
      • 1.9 基于注解的容器配置
      • 1.10 类路径扫描和托管组件
      • 1.11 使用JSR 330标准注解
      • 1.12 基于Java的容器配置
      • 1.13 环境抽象
      • 1.14 注册LoadTimeWeaver
      • 1.15 ApplicationContext的其他功能
      • 1.16 BeanFactory
    • 2.资源
      • 2.1介绍
      • 2.2资源接口
      • 2.3内置资源实现
      • 2.4ResourceLoader
      • 2.5ResourceLoaderAware接口
      • 2.6资源作为依赖
      • 2.7应用程序上下文和资源路径
    • 3.验证,数据绑定,和类型转换
      • 3.1使用Spring Validator接口
      • 3.2将代码解析为错误消息
      • 3.3bean操作和BeanWrapper
      • 3.4Spring类型转换
      • 3.5Spring字段格式化
      • 3.6配置全局Date和Time格式
      • 3.7Spring验证
    • 4.SpEL Spring表达式语言
      • 4.1求值
      • 4.2bean定义中的表达式
      • 4.3语言引用
    • 5.Spring AOP
      • 5.1什么是AOP
      • 5.1Spring AOP的能力和目标
      • 5.3AOP代理
      • 5.4@AspectJ 支持
      • 5.5基于Schema的AOP支持
      • 5.6选择要使用的AOP声明样式
      • 5.7混合Aspect类型
      • 5.8代理机制
      • 5.9程序创建@AspectJ代理
      • 5.10在Spring应用程序中使用AspectJ
      • 5.11更多资源
    • 6.Spring AOP APIs
      • 6.1Pointcut API
      • 6.2Advice API
      • 6.3Advisor API
      • 6.4使用ProxyFactoryBean来创建AOP代理
      • 6.5简介的代理定义
      • 6.6使用ProxyFactory创建AOP代理
      • 6.7操作被通知的对象
      • 6.8使用auto-proxy功能
      • 6.9使用TargetSource的实现
      • 6.10定义新的Advice Types
    • 7.Null-safety
    • 8.数据缓存和解码器
    • 9.附录
      • 9.1XML Schemas
      • 9.2创建XML Schemas
  • 测试
    • 1.Spring测试介绍
    • 2.单元测试
      • 2.1Mock Objects
      • 2.2单元测试支持类
    • 3.集成测试
      • 3.1概览
      • 3.2集成测试的目的
      • 3.3JDBC测试支持
      • 3.4注解
      • 3.5Spring TestContext框架
      • 3.6Spring MVC测试框架
      • 3.7WebTestClient
    • 4.更多资源
  • 数据访问
    • 1.事务管理
    • 2.DAO支持
    • 3.JDBC
      • 3.1选择JDBC数据库访问方法
      • 3.2包层次结构
      • 3.3使用JDBC核心类控制基本JDBC处理和错误处理
      • 3.4控制数据库连接
      • 3.5JDBC批处理操作
      • 3.6使用SimpleJdbc
      • 3.7将JDBC操作建模为Java对象
      • 3.8参数和数据值处理的常见问题
      • 3.9嵌入式数据库支持
      • 3.10初始化数据源
    • 4.ORM
      • 4.1Spring ORM介绍
      • 4.2ORM集成的一般注意事项
      • 4.3Hibernate
      • 4.4JPA
    • 5.使用Object-XML映射封装XML
  • Web Servlet
    • 1. Spring Web MVC
      • 1.1 DispatcherServlet
      • 1.2 Filters
      • 1.3 Controllers注解
      • 1.4 URI链接
      • 1.5 异步请求
      • 1.6 CORS
      • 1.7 Web Security
      • 1.8 HTTP Caching
      • 1.9 View技术
      • 1.10 MVC配置
      • 1.11 HTTP/2
    • 2. REST客户端
    • 3. 测试
    • 4. WebSockets
      • 4.1 WebSocket介绍
      • 4.2 WebSocket API
      • 4.3 SockJS Fallback
      • 4.4 STOMP
  • Web Reactive
    • 1.Spring WebFlux
      • 1.1 Overview
      • 1.2 Reactive Core
      • 1.3 DispatcherHandler
      • 1.4 Annotated Controllers
      • 1.5 Functional Endpoints
      • 1.6 URI Links
      • 1.7 CORS
      • 1.8 Web Security
      • 1.9 View Technologies
      • 1.10 HTTP Caching
      • 1.11 WebFlux Config
      • 1.12 HTTP/2
    • 2.WebClient
    • 3.WebSockets
    • 4.测试
    • 5.Reactive库
由 GitBook 提供支持
在本页
  • 6.2.1 Advice生命周期
  • 6.2.2 Advice类型

这有帮助吗?

  1. 核心技术
  2. 6.Spring AOP APIs

6.2Advice API

下面我们看一下Spring AOP怎么处理advice。

6.2.1 Advice生命周期

每个Advice都是一个Spring bean。Advice实例可以在所有Advice对象之间共享,也可以对每个Advice对象都是唯一的。这对应于每个类或每个实例的通知。

Per-class advice最常用。它适用于一般的advice,如事务advisors。这些不依赖于代理对象的状态或添加新状态。他们只根据方法和参数行事。

Per-instance的advice都适用于introductions,以支持混合。在这种情况下,建议将状态添加到代理对象。

你可以在同一个AOP代理中混合使用共享和基于实例的建议。

6.2.2 Advice类型

Spring提供了几种Advice类型,并且可以扩展以支持任意Advice类型。本节介绍基本概念和标准Advice类型。

环绕拦截Advice

Spring最基本的建议类型是围绕建议进行拦截。

Spring与AOP联盟接口兼容,以获取使用方法拦截的 around advice。实现MethodInterceptor和实现around advice的类还应实现以下接口:

public interface MethodInterceptor extends Interceptor {

    Object invoke(MethodInvocation invocation) throws Throwable;
}

invoke()方法的methodInvocation参数公开了被调用的方法、目标连接点、AOP代理以及该方法的参数。invoke()方法应返回调用的结果:连接点的返回值。

下面的示例显示了一个简单的SystemInterceptor实现:

public class DebugInterceptor implements MethodInterceptor {

    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("Before: invocation=[" + invocation + "]");
        Object rval = invocation.proceed();
        System.out.println("Invocation returned");
        return rval;
    }
}

注意对MethodInvocation的proceed()方法的调用。这将沿着拦截器链向连接点前进。大多数拦截器调用此方法并返回其返回值。但是,与任何around通知一样,MethodIntercetor可以返回不同的值或引发异常,而不是调用继续方法。但是,如果没有充分的理由,你不想这样做。

MethodIntercetor实现提供与其他AOP联盟兼容的AOP实现的互操作性。本节其余部分讨论的其他建议类型以特定于Spring的方式实现了常见的AOP概念。虽然在使用最具体的通知类型方面有一个优势,但是如果你可能希望在另一个AOP框架中运行该方面,那么请坚持使用围绕通知的MethodInterceptor。注意,切入点目前在框架之间不可互操作,AOP联盟目前没有定义切入点接口。

Before Advice

更简单的建议类型是before建议。这不需要MethodInvocation对象,因为它只在进入方法之前被调用。

before通知的主要优点是不需要调用proceed()方法,因此,不可能无意中中断拦截器链。

下面的列表显示了MethodBeforeAdvice接口:

public interface MethodBeforeAdvice extends BeforeAdvice {

    void before(Method m, Object[] args, Object target) throws Throwable;
}

(Spring的API设计将允许在建议之前提供字段,尽管通常的对象适用于字段拦截,但是Spring不太可能实现它。)

请注意,返回类型为void。before advice可以在连接点执行之前插入自定义行为,但不能更改返回值。如果before通知抛出异常,它将中止拦截器链的进一步执行。异常向上传播到拦截器链。如果它未选中或在被调用方法的签名上,则直接传递给客户机。否则,AOP代理会将其包装在unchecked exception中。

下面的示例显示了Spring中的一个before通知,它对所有方法调用进行计数:

public class CountingBeforeAdvice implements MethodBeforeAdvice {

    private int count;

    public void before(Method m, Object[] args, Object target) throws Throwable {
        ++count;
    }

    public int getCount() {
        return count;
    }
}

Before advice可以用在任意pointcut中。

Throws Advice

如果连接点引发异常,则在返回连接点后调用throws通知。Spring提供 typed throws advice。注意,这意味着org.springframework.aop.ThrowsAdvice接口不包含任何方法。它是一个标记接口,标识给定对象实现一个或多个类型化的throw advice方法。应采用以下形式:

afterThrowing([Method, args, target], subclassOfThrowable)

只需要最后一个参数。方法签名可以有一个或四个参数,这取决于advice方法对方法和参数是否感兴趣。下面的两个列表显示了属于throw建议示例的类。

如果抛出了RemoteException(包括子类),将调用以下建议:

public class RemoteThrowsAdvice implements ThrowsAdvice {

    public void afterThrowing(RemoteException ex) throws Throwable {
        // Do something with remote exception
    }
}

与前面的建议不同,下一个示例声明了四个参数,这样它就可以访问被调用的方法、方法参数和目标对象。如果抛出servletexception,将调用以下通知:

public class ServletThrowsAdviceWithArguments implements ThrowsAdvice {

    public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {
        // Do something with all arguments
    }
}

最后一个示例说明了这两个方法如何在处理RemoteException和ServletException的单个类中使用。可以在单个类中组合任意数量的throw advice方法。下面的列表显示了最后一个示例:

public static class CombinedThrowsAdvice implements ThrowsAdvice {

    public void afterThrowing(RemoteException ex) throws Throwable {
        // Do something with remote exception
    }

    public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {
        // Do something with all arguments
    }
}

如果throws advice方法本身引发异常,它将重写原始异常(即,它将更改引发给用户的异常)。重写异常通常是RuntimeException,它与任何方法签名都兼容。但是,如果throw advice方法抛出一个选中的异常,它必须与目标方法的声明异常匹配,因此在某种程度上与特定的目标方法签名耦合。不要抛出与目标方法的签名不兼容的未声明的已检查异常!

Throws advice可以在任何pointcut使用。

After Returning Advice

在Spring中after returning advice,必须实现org.springframework.aop.AfterReturningAdvice接口,如下所示:

public interface AfterReturningAdvice extends Advice {

    void afterReturning(Object returnValue, Method m, Object[] args, Object target)
            throws Throwable;
}

after returning advice可以访问返回值(它不能修改)、调用的方法、方法的参数和目标。

返回通知后,以下内容将统计所有未引发异常的成功方法调用:

public class CountingAfterReturningAdvice implements AfterReturningAdvice {

    private int count;

    public void afterReturning(Object returnValue, Method m, Object[] args, Object target)
            throws Throwable {
        ++count;
    }

    public int getCount() {
        return count;
    }
}

此建议不会更改执行路径。如果它抛出一个异常,它将被抛出拦截器链而不是返回值。

After returning advice可以用在任何pointcut。

Introduction Advice

Spring将Introduction Advice视为一种特殊的拦截Advice。

Introduction需要一个IntroductionAdvisor和一个实现以下接口的IntroductionInterceptor:

public interface IntroductionInterceptor extends MethodInterceptor {

    boolean implementsInterface(Class intf);
}

从AOP Alliance MethodIntercetor接口继承的invoke()方法必须实现introduction。也就是说,如果被调用的方法在引入的接口上,则引入拦截器负责处理方法调用-它不能调用proceed()。

Introduction advice不能与任何切入点一起使用,因为它只适用于类,而不是方法级别。你只能将 introduction advice 与IntroductionAdvisor一起使用,其方法如下:

public interface IntroductionAdvisor extends Advisor, IntroductionInfo {

    ClassFilter getClassFilter();

    void validateInterfaces() throws IllegalArgumentException;
}

public interface IntroductionInfo {

    Class[] getInterfaces();
}

没有methodMatcher,因此没有与 introduction advice相关联的切入点。只有类筛选是合乎逻辑的。

getInterfaces()方法返回此advisor引入的接口。

validateInterfaces()方法在内部用于查看所引入的接口是否可以由配置的IntroductionInterceptor实现。

考虑一下Spring测试套件中的一个示例,假设我们要将以下接口引入一个或多个对象:

public interface Lockable {
    void lock();
    void unlock();
    boolean locked();
}

这里展示一个mixin。我们希望能够将建议的对象强制转换为Lockable的对象,无论其类型如何,并调用锁定和解锁方法。如果调用lock()方法,我们希望所有setter方法都抛出lockedException。因此,我们可以添加一个方面,使对象在不了解它的情况下保持不变:AOP的一个很好的例子。

首先,我们需要一个IntroductionInterceptor来完成这项工作。在本例中,我们扩展了org.springframework.aop.support.DelegatingIntroductionInterceptor便利类。我们可以直接实现IntroductionInterceptor,但对于大多数情况,使用DelegatingIntroductionInterceptor是最好的。

DelegingIntroductionInterceptor设计用于将introduction委托给所引入接口的实际实现,从而隐藏使用拦截来完成此操作。可以使用构造函数参数将委托设置为任何对象。默认委托(使用无参数构造函数时)是this。

因此,在下一个示例中,委托是DelegatingIntroductionInterceptorr的lockMixin子类。对于委托(默认情况下,它本身),DelegatingIntroductionInterceptor实例查找委托实现的所有接口(而不是IntroductionInterceptor),并支持针对其中任何一个进行introductions。LockMixin等子类可以调用SuppressInterface(Class Intf)方法来抑制不应公开的接口。然而,不管一个IntroductionInterceptor准备支持多少个接口,IntroductionAdvisor被用来控制哪些接口实际上被公开。引入的接口隐藏了目标对同一接口的任何实现。

因此,lockmixin扩展了DelegatingIntroductionInterceptor并实现了Lockable接口。超类会自动选取可以支持introduction的Lockable,所以我们不需要指定它。我们可以用这种方式引入任意数量的接口。

注意使用locked的实例变量。这有效地为目标对象中保存的状态添加了额外的状态。

下面的示例显示了lockmixin类的示例:

public class LockMixin extends DelegatingIntroductionInterceptor implements Lockable {

    private boolean locked;

    public void lock() {
        this.locked = true;
    }

    public void unlock() {
        this.locked = false;
    }

    public boolean locked() {
        return this.locked;
    }

    public Object invoke(MethodInvocation invocation) throws Throwable {
        if (locked() && invocation.getMethod().getName().indexOf("set") == 0) {
            throw new LockedException();
        }
        return super.invoke(invocation);
    }

}

通常,你不需要重写invoke()方法。DelegatingIntroductionInterceptor实现(如果引入了该方法,则调用delegate方法,否则将进入连接点)通常就足够了。

在本例中,我们需要添加一个检查:如果处于锁定模式,则不能调用setter方法。

所需的introduction只需要保存一个不同的lockmixin实例并指定引入的接口(在本例中,仅Lockable)。一个更复杂的例子可能会引用介绍拦截器(将被定义为原型)。在这种情况下,没有与LockMixin相关的配置,因此我们使用new创建它。下面的示例显示了我们的LockMixinAdvisor类:

public class LockMixinAdvisor extends DefaultIntroductionAdvisor {

    public LockMixinAdvisor() {
        super(new LockMixin(), Lockable.class);
    }
}

我们可以非常简单地应用这个advisor,因为它不需要配置。(然而,在没有IntroductionAdvisor的情况下,不可能使用IntroductionInterceptor。)与通常的introductions一样,顾问必须是有状态的。对于每个建议的对象,我们需要一个不同的LockMixinAdvisor实例,因此需要LockMixin。advisor是被advised对象状态的一部分。

我们可以通过在XML配置中使用advised.addAdvisor()方法或(推荐的方法)以编程方式应用此Advisor,就像任何其他Advisor一样。下面讨论的所有代理创建选项,包括“auto-proxy creators”,正确处理introductions 和 stateful 的混合。

上一页6.1Pointcut API下一页6.3Advisor API

最后更新于3年前

这有帮助吗?