Spring 框架文档之核心技术—— AOP

简介: Spring AOP 提供声明式企业服务(如声明式事务管理)。简化和补充 OOP。基于动态代理(JDK 动态代理或 CGLIB 代理)实现。

AOP

AOP 提供一种新的思考程序结构的方法来补充 OOP。OOP 中模块的关键是类,而 AOP 中模块的关键是切面。切面支持跨多个类型和对象的模块化(如事务管理)。

AOP 概念

  • 切面 —— 在切入点进行通知操作的过程(包含通知和切人点的类 @Aspect)
  • 连接点 —— 所有可能被织入通知的候选点(具体业务逻辑方法)
  • 切入点 —— 满足匹配规则的连接点(@Pointcut)
  • 目标对象 —— 被一到多个切面通知的对象
  • AOP 代理 —— AOP 框架基于切面规则创建的对象
  • 织入 —— 创建通知对象关联切面和其他应用
  • 通知 —— 对切入点进行的操作

    • 前置通知 —— 在连接点之前执行的通知(@Before)
    • 后置通知 —— 在连接点正常完成后执行的通知(@AfterReturning)
    • 环绕通知 —— 在方法调用前后执行的通知(@Around)
    • 异常通知 —— 方法抛出异常时执行的通知(@AfterThrowing)
    • 最终通知 —— 从连接点退出后执行的通知(@After)

Spring AOP 目标

  • 纯 Java 实现。不需要特殊的编译过程。不需要控制类加载器层次结构,适合在 Servlet 容器或应用程序服务器中使用。
  • 只支持方法执行连接点。如果需要字段拦截和更新连接点,考虑 AspectJ 之类的语言。
  • Spring AOP 不是提供最完整的 AOP 实现。其目标是提供 AOP 实现和 Spring IoC 之间的紧密集成。

AOP 代理

  • Spring AOP 默认使用标准 JDK 动态代理实现 AOP 代理。
  • Spring AOP 还可以使用CGLIB代理。

@Aspect 风格

  • 激活 @AspectJ 支持 —— 通过自动代理,Spring 自动为被通知的 Bean 生成代理来拦截方法调用,确保按需执行通知
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {

}
  • 定义一个切面 —— 在应用程序上下文中使用 @AspectJ 类定义 Bean 会被 Spring 自动检测到,并用于配置 Spring AOP
@Aspect
public class NotVeryUsefulAspect {

}
  • 定义一个切入点 —— 确定感兴趣的连接点,从而使我们能够控制何时执行通知。
@Pointcut("execution(* transfer(..))")// 切入点表达式
private void anyOldTransfer() {}
  • execution —— 用于匹配方法执行连接点
  • within —— 将匹配限制为特定类型中的连接点
  • this —— 将匹配限制为 Bean 引用(AOP 代理)是给定类型实例的连接点
  • target —— 将匹配限制为目标对象是给定类型实例的连接点
  • args —— 将匹配限制为参数是给定类型实例的连接点
  • @target —— 将匹配限制为执行对象的类具有给定类型注解的连接点
  • @args —— 将匹配限制为传递的实际参数的运行时类型具有给定类型注解的连接点
  • @within —— 将匹配限制为与具有给定注解类型的连接点
  • @annotation —— 将匹配限制为连接点的主题具有给定类型注释的连接点
@Aspect
public class SystemArchitecture {
    @Pointcut("within(com.xyz.someapp.web..*)")
    public void inWebLayer() {}
    
    @Pointcut("within(com.xyz.someapp.service..*)")
    public void inServiceLayer() {}
    
    @Pointcut("within(com.xyz.someapp.dao..*)")
    public void inDataAccessLayer() {}
    
    @Pointcut("execution(* com.xyz.someapp..service.*.*(..))")
    public void businessService() {}
    
    @Pointcut("execution(* com.xyz.someapp.dao.*.*(..))")
    public void dataAccessOperation() {}
}

execution(修饰符? 返回类型 包名?方法名称(参数类型) 异常类型?)

//拦截任何公共方法的执行
execution(public * *(..))

//拦截任何以 set 开头的方法的执行
execution(* set*(..))

//拦截任何定义在 AccountService 接口中的方法的执行
execution(* com.xyz.service.AccountService.*(..))

//拦截任何定义在 service 包中的方法的执行
execution(* com.xyz.service.*.*(..))

//拦截任何定义在 service 包或者其一个子包中的方法的执行
execution(* com.xyz.service..*.*(..))

//拦截 service 包中的连接点
within(com.xyz.service.*)

//拦截 service 包或者其一个子包中的连接点
within(com.xyz.service..*)

//拦截实现 AccountService 接口的代理的连接点
this(com.xyz.service.AccountService)

//拦截实现 AccountService 接口的目标对象的连接点
target(com.xyz.service.AccountService)

//拦截参数只有一个 Serializable 的连接点
args(java.io.Serializable)

//拦截目标对象有 @Transactional 注解的连接点
@target(org.springframework.transaction.annotation.Transactional)

//拦截目标对象的声明类型有 @Transactional 注解的连接点
@within(org.springframework.transaction.annotation.Transactional)

//拦截执行方法有 @Transactional 注解的连接点
@annotation(org.springframework.transaction.annotation.Transactional)

//拦截参数只有一个 @Classified 注解的连接点
@args(com.xyz.security.Classified)
  • 定义通知 —— 与切入点表达式相关联,并在匹配的方法执行之前、之后或前后运行
@Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
public void doAccessCheck() {
  // ...
}

@AfterReturning("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
public void doAccessCheck() {
  // ...
}

@AfterReturning(
  pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
  returning="retVal")
public void doAccessCheck(Object retVal) {
  // ...
}

@AfterThrowing("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
public void doRecoveryActions() {
  // ...
}

@AfterThrowing(
  pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
  throwing="ex")
public void doRecoveryActions(DataAccessException ex) {
  // ...
}

@After("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
public void doReleaseLock() {
  // ...
}

@Around("com.xyz.myapp.SystemArchitecture.businessService()")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
  // start stopwatch
  Object retVal = pjp.proceed();
  // stop stopwatch
  return retVal;
}
  • JoinPoint 参数 —— ProceedingJoinPoint 是 JoinPoint 的子类

    • getArgs(): 返回方法参数
    • getThis(): 返回代理对象
    • getTarget(): 返回目标对象
    • getSignature(): 返回被通知方法的描述
    • toString(): 打印被通知方法的描述
  • 传递参数给通知
@Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation() && args(account,..)")
public void validateAccount(Account account) {
    // ...
}
@Pointcut("com.xyz.myapp.SystemArchitecture.dataAccessOperation() && args(account,..)")
private void accountDataAccessOperation(Account account) {}

@Before("accountDataAccessOperation(account)")
public void validateAccount(Account account) {
    // ...
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Auditable {
    AuditCode value();
}

@Before("com.xyz.lib.Pointcuts.anyPublicMethod() && @annotation(auditable)")
public void audit(Auditable auditable) {
    AuditCode code = auditable.value();
    // ...
}
public interface Sample<T> {
    void sampleGenericMethod(T param);
    void sampleGenericCollectionMethod(Collection<T> param);
}

@Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)")
public void beforeSampleMethod(MyType param) {
    // Advice implementation
}

@Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)")
public void beforeSampleMethod(Collection<MyType> param) {
    // Advice implementation
}
@Before(value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)",
        argNames="bean,auditable")
public void audit(Object bean, Auditable auditable) {
    AuditCode code = auditable.value();
    // ... use code and bean
}

//第一参数是 JoinPoint, ProceedingJoinPoint, 或者 JoinPoint.StaticPart 类型,argNames 中可以省略
@Before(value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)",
        argNames="bean,auditable")
public void audit(JoinPoint jp, Object bean, Auditable auditable) {
    AuditCode code = auditable.value();
    // ... use code, bean, and jp
}

@Before("com.xyz.lib.Pointcuts.anyPublicMethod()")
public void audit(JoinPoint jp) {
    // ... use jp
}
@Around("execution(List<Account> find*(..)) && " +
        "com.xyz.myapp.SystemArchitecture.inDataAccessLayer() && " +
        "args(accountHolderNamePattern)")
public Object preProcessQueryPattern(ProceedingJoinPoint pjp,
        String accountHolderNamePattern) throws Throwable {
    String newPattern = preProcess(accountHolderNamePattern);
    return pjp.proceed(new Object[] {newPattern});
}

代理机制

  • Spring AOP 使用 JDK 动态代理或 CGLIB 为给定的目标对象创建代理。
  • 如目标对象实现至少一个接口,则使用 JDK 动态代理。如目标对象没有实现任何接口,则创建一个 CGLIB 代理。
public class SimplePojo implements Pojo {

    public void foo() {
        // this next method invocation is a direct call on the 'this' reference
        this.bar();
    }

    public void bar() {
        // some logic...
    }
}

public class Main {

    public static void main(String[] args) {
        ProxyFactory factory = new ProxyFactory(new SimplePojo());
        factory.addInterface(Pojo.class);
        factory.addAdvice(new RetryAdvice());

        Pojo pojo = (Pojo) factory.getProxy();
        // this is a method call on the proxy!
        pojo.foo();
    }
}

API

Pointcut

public interface Pointcut {

    ClassFilter getClassFilter();

    MethodMatcher getMethodMatcher();

}

public interface ClassFilter {

    boolean matches(Class clazz);
}

public interface MethodMatcher {

    boolean matches(Method m, Class targetClass);

    boolean isRuntime();

    boolean matches(Method m, Class targetClass, Object[] args);
}
相关文章
|
19天前
|
XML 安全 Java
|
22天前
|
缓存 NoSQL Java
什么是缓存?如何在 Spring Boot 中使用缓存框架
什么是缓存?如何在 Spring Boot 中使用缓存框架
29 0
|
4天前
|
IDE Java 测试技术
互联网应用主流框架整合之Spring Boot开发
通过本文的介绍,我们详细探讨了Spring Boot开发的核心概念和实践方法,包括项目结构、数据访问层、服务层、控制层、配置管理、单元测试以及部署与运行。Spring Boot通过简化配置和强大的生态系统,使得互联网应用的开发更加高效和可靠。希望本文能够帮助开发者快速掌握Spring Boot,并在实际项目中灵活应用。
23 5
|
15天前
|
缓存 Java 数据库连接
Spring框架中的事件机制:深入理解与实践
Spring框架是一个广泛使用的Java企业级应用框架,提供了依赖注入、面向切面编程(AOP)、事务管理、Web应用程序开发等一系列功能。在Spring框架中,事件机制是一种重要的通信方式,它允许不同组件之间进行松耦合的通信,提高了应用程序的可维护性和可扩展性。本文将深入探讨Spring框架中的事件机制,包括不同类型的事件、底层原理、应用实践以及优缺点。
46 8
|
25天前
|
存储 Java 关系型数据库
在Spring Boot中整合Seata框架实现分布式事务
可以在 Spring Boot 中成功整合 Seata 框架,实现分布式事务的管理和处理。在实际应用中,还需要根据具体的业务需求和技术架构进行进一步的优化和调整。同时,要注意处理各种可能出现的问题,以保障分布式事务的顺利执行。
45 6
|
1月前
|
Java 数据库连接 数据库
不可不知道的Spring 框架七大模块
Spring框架是一个全面的Java企业级应用开发框架,其核心容器模块为其他模块提供基础支持,包括Beans、Core、Context和SpEL四大子模块;数据访问及集成模块支持数据库操作,涵盖JDBC、ORM、OXM、JMS和Transactions;Web模块则专注于Web应用,提供Servlet、WebSocket等功能;此外,还包括AOP、Aspects、Instrumentation、Messaging和Test等辅助模块,共同构建强大的企业级应用解决方案。
52 2
|
29天前
|
Java Kotlin 索引
学习Spring框架特性及jiar包下载
Spring 5作为最新版本,更新了JDK基线至8,修订了核心框架,增强了反射和接口功能,支持响应式编程及Kotlin语言,引入了函数式Web框架,并提升了测试功能。Spring框架可在其官网下载,包括文档、jar包和XML Schema文档,适用于Java SE和Java EE项目。
32 0
|
3月前
Micronaut AOP与代理机制:实现应用功能增强,无需侵入式编程的秘诀
AOP(面向切面编程)能够帮助我们在不修改现有代码的前提下,为应用程序添加新的功能或行为。Micronaut框架中的AOP模块通过动态代理机制实现了这一目标。AOP将横切关注点(如日志记录、事务管理等)从业务逻辑中分离出来,提高模块化程度。在Micronaut中,带有特定注解的类会在启动时生成代理对象,在运行时拦截方法调用并执行额外逻辑。例如,可以通过创建切面类并在目标类上添加注解来记录方法调用信息,从而在不侵入原有代码的情况下增强应用功能,提高代码的可维护性和可扩展性。
73 1
|
1月前
|
安全 Java 编译器
什么是AOP面向切面编程?怎么简单理解?
本文介绍了面向切面编程(AOP)的基本概念和原理,解释了如何通过分离横切关注点(如日志、事务管理等)来增强代码的模块化和可维护性。AOP的核心概念包括切面、连接点、切入点、通知和织入。文章还提供了一个使用Spring AOP的简单示例,展示了如何定义和应用切面。
125 1
什么是AOP面向切面编程?怎么简单理解?
|
1月前
|
XML Java 开发者
论面向方面的编程技术及其应用(AOP)
【11月更文挑战第2天】随着软件系统的规模和复杂度不断增加,传统的面向过程编程和面向对象编程(OOP)在应对横切关注点(如日志记录、事务管理、安全性检查等)时显得力不从心。面向方面的编程(Aspect-Oriented Programming,简称AOP)作为一种新的编程范式,通过将横切关注点与业务逻辑分离,提高了代码的可维护性、可重用性和可读性。本文首先概述了AOP的基本概念和技术原理,然后结合一个实际项目,详细阐述了在项目实践中使用AOP技术开发的具体步骤,最后分析了使用AOP的原因、开发过程中存在的问题及所使用的技术带来的实际应用效果。
67 5
下一篇
DataWorks