java - 如何为抽象方法创建切入点

标签 java spring spring-aop

我试图为子类中实现的抽象方法创建切入点,但 AOP 永远不会被调用。

这是我的最小 Java 代码:

package com.example;

public class Service {
    private ParentAbstractClass clazz;

    public Service(ParentAbstractClass clazz) {
        this.clazz = clazz;
    }

    public void process() {
        clazz.method();
    }
}

这是服务类,它具有要注入(inject)的类的抽象,并调用一个方法。

我的抽象类具有一些通用逻辑和一个抽象方法的特定于实现的代码。

package com.example;

import java.util.List;

public abstract class ParentAbstractClass {
    public void method() {
        abstractMethod(List.of("test"));
    }

    public abstract void abstractMethod(List<String> names);
}

这个类提供了抽象方法的实现。

package com.example;

import java.util.List;

public class ConcreteClass extends ParentAbstractClass {
    @Override
    public void abstractMethod(List<String> names) {
        System.out.println("Look up! AOP should have executed");
    }
}

通过此设置,我使用 spring XML 来配置我的 bean。

<bean id = "clazz" class="com.example.ConcreteClass"/>

<bean id="myservice" class="com.example.Service">
    <constructor-arg ref="clazz"/>
</bean>

<bean id = "aspect" class="com.exmple.TxAspect"/>

<aop:config>
    <aop:aspect id="mergeEnableAspect" ref="aspect">
        <aop:pointcut id="mergeServicePointCut"
                      expression="execution(* com.example.ConcreteClass.abstractMethod(..))"/>
        <aop:around pointcut-ref="mergeServicePointCut" method="test" arg-names="pjp"/>
    </aop:aspect>
</aop:config>

最后是 AOP 类:

import org.aspectj.lang.ProceedingJoinPoint;

public class TxAspect {
    public void test(ProceedingJoinPoint pjp) {

        System.out.println("I am not going to do anything");

    }
}

在我的abstractMethod中,我正在做一些本质上是事务性的事情,并且我有手动控制事务的业务需求,但是我的方面类永远不会被调用。有人可以帮我弄清楚我犯了什么错误吗?

谢谢。

最佳答案

该问题是由 Spring AOP 实现的限制引起的。这是来自 Spring documentation 的引用:

Due to the proxy-based nature of Spring’s AOP framework, calls within the target object are, by definition, not intercepted. For JDK proxies, only public interface method calls on the proxy can be intercepted. With CGLIB, public and protected method calls on the proxy are intercepted (and even package-visible methods, if necessary). However, common interactions through proxies should always be designed through public signatures.

Note that pointcut definitions are generally matched against any intercepted method. If a pointcut is strictly meant to be public-only, even in a CGLIB proxy scenario with potential non-public interactions through proxies, it needs to be defined accordingly.

If your interception needs include method calls or even constructors within the target class, consider the use of Spring-driven native AspectJ weaving instead of Spring’s proxy-based AOP framework. This constitutes a different mode of AOP usage with different characteristics, so be sure to make yourself familiar with weaving before making a decision.

所以你有两种可能的方法来解决这个问题:

  1. 仅在其他 bean 调用的方法上使用您的方面(无自调用)
  2. 使用AspectJ AOP实现。我准备了一个使用 AspectJ 编译时编织的简单示例,代码托管在 github

AspectJ 方法简而言之:

修改事务方面如下:

package com.example;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class TxAspect {

    @Around("methodsToBeProfiled()")
    public void test(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("I am not going to do anything");
        pjp.proceed();
    }

    @Pointcut("execution(* com.example.ConcreteClass.abstractMethod(..))")
    public void methodsToBeProfiled(){}
} 

减少 XML 配置如下:

  <bean id="clazz" class="com.example.ConcreteClass"/>

  <bean id="myservice" class="com.example.Service">
    <constructor-arg ref="clazz"/>
  </bean>

使用以下 Maven 插件编译应用程序:

  <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.11</version>
    <dependencies>
      <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjtools</artifactId>
        <version>${ascpectj.version}</version>
      </dependency>
    </dependencies>
    <configuration>
      <source>1.8</source>
      <target>1.8</target>
      <complianceLevel>1.8</complianceLevel>
    </configuration>
    <executions>
      <execution>
        <goals>
          <goal>compile</goal>
          <goal>test-compile</goal>
        </goals>
      </execution>
    </executions>
  </plugin>

关于java - 如何为抽象方法创建切入点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54536039/

相关文章:

java - 为什么我的第二个 GUI 按钮不起作用?

java - MQ 最大连接数问题

java - APACHE POI 4.1 : Set cell background color from hex code

java - 如何使用spring的分层架构仍然遵循面向对象的结构?

java - Spring @Around > 参数的行为

java - 检查 SparseBooleanArray 中是否至少有一个 true 值

java - 为什么jetpack compose中没有热重载?理论上他们将来是否有可能在 compose 中添加类似热重载的 flutter 功能?

spring - Spring AOP 中抛出异常

java - 使用 AspectJ spring-aop 改变返回值的类型

java - BigDecimal 符号/括号