停止使用aspectj捕获Jparepository方法

时间:2014-12-02 13:08:19

标签: java spring-data aspectj spring-data-jpa

如果程序员返回一个Arraylist而不是一个列表,我试图生成一个警告。我使用Spring Boot,Spring Data JPA。

示例Pojo

@Entity
public class Box {

    @Id
    @GeneratedValue
    private long id;

    private long prio;


    public long getPrio() {
        return prio;
    }

    public void setPrio(long prio) {
        this.prio = prio;
    }

    public long getId() {
        return id;
    }   

    public void setId(long id) {
        this.id = id;
    }

}

我的存储库:

@Repository
public interface BoxRepository extends JpaRepository<Box, Long>{

    public List findByPrio(long prio);  
}

现在我的看点:

@Aspect
@Component
public class ReturnList {

    @AfterReturning(value = "within(de.fhb.*) && !within(org.springframework.*) && call(* de.fhb..*(..))", returning = "returnValue")
    public void logServiceAccess(JoinPoint joinPoint, Object returnValue) {

        if (returnValue != null) {
            if (returnValue.getClass() != null) {

                Class<?> clazz = returnValue.getClass();

                if (java.util.List.class.isAssignableFrom(clazz)) {
                    System.out
                            .println("Please use List instead of a concrete implementation ( "+ returnValue.getClass() + " ) for method: "
                                + joinPoint.getSignature().getName() + ".");
                }

            }
        }

    }

}

我的问题

看起来像spring数据(jpa存储库)正在返回一个Arraylist。我不想从jpa存储库中捕获方法,我排除了org.springframework但是如果我运行类似这样的行,方面仍然被触发:

System.out.println(boxRepository.findByPrio(1));

任何会停止触发方面调用spring jparepository方法的提示?

完整代码:https://github.com/svenhornberg/MDSD

1 个答案:

答案 0 :(得分:1)

首先,对于List.class.isAssignableFrom(clazz)以及ListArrayList都是正确的,即您无法区分这两者。顺便说一下,returnValue.getClass() 永远不会评估为List,因为这是一种接口类型。它将始终评估到实际的实现类,例如ArrayList。因此,无论如何,你试图通过反思找出你想知道的东西的人为的方法注定要失败。

好消息是:AspectJ可以让你做你想做的事。您似乎通过加载或编译时编织使用真正的AspectJ,否则您无法使用call()切入点,因为它在Spring AOP中不可用。 编辑:是的,您的Gradle构建显示您正在使用编译时编织。但是,嘿,为什么你在当前版本1.8.4中使用编译器而在大规模过时的1.5.4中使用AspectJ运行时?您应该协调这两者,并在1.8.4版本中使用 aspectjrt

现在让我们解构你的切入点:

within(de.fhb.*) &&
!within(org.springframework.*) &&
call(* de.fhb..*(..))

这意味着:拦截对de.fhb或其子包(..*表示法)中定义的任何方法的调用,但仅当调用也来自de.fhb中定义的类但不在子包中(.*表示法)。 !within(org.springframework.*)部分是多余的,因为de.fhb.*中定义的代码永远不会同时来自org.springframework.*。这毫无意义。

您可能真正想要的是找出在您自己的包中是否有方法返回类似ArrayList的具体类型而不是接口类型List。正确?我认为切入点应该是这样的:

within(de.fhb..*) &&
execution(java.util.List+ *(..)) &&
!execution(java.util.List *(..))

这意味着:对于de.fhb或其子包中的所有类,拦截所有返回List++表示:List或其子类型)的方法。它的方法签名。因为这也会匹配返回父类型List的方法而您只需要子类型,所以必须通过!execution(java.util.List *(..))在切入点的第三部分中排除该类型。

我匹配方法执行而不是调用,因为它更有效。如果从包中的100个位置调用方法,call()会将方面代码编织到100个调用连接点中,而execution()实际上只是编织方法实际定义的位置。

以下是一些示例代码:

驱动程序应用程序:

您看到有一种方法正确声明(根据您的意愿)List返回类型,而其他方法声明“禁止”返回类型,例如ArrayListLinkedList和{{ 1}}。

Vector

类型检查方面,变体A(运行时检查):

package de.fhb.app;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;

public class Application {
    public static List<String> methodReturningList() {
        return new ArrayList<String>();
    }

    public static ArrayList<String> methodReturningArrayList() {
        return new ArrayList<String>();
    }

    public static LinkedList<String> methodReturningLinkedList() {
        return new LinkedList<String>();
    }

    public static Vector<String> methodReturningVector() {
        return new Vector<String>();
    }

    public static void main(String[] args) {
        methodReturningList();
        methodReturningArrayList();
        methodReturningLinkedList();
        methodReturningVector();
    }
}

控制台输出:

package de.fhb.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class ReturnTypeChecker {
    @AfterReturning("within(de.fhb..*) && execution(java.util.List+ *(..)) && !execution(java.util.List *(..))")
    public void logServiceAccess(JoinPoint thisJoinPoint) {
        System.out.println(thisJoinPoint);
    }
}

正如您所看到的,根据您的定义,您可以获得截获的那些有问题的方法。

类型检查方面,变体B(编译时检查):

但AspectJ可以做得更多。为什么不在编译期间发出警告甚至错误,即在软件打包和部署之前?您的开发团队可以在进入生产代码之前修复错误。为此,请使用execution(ArrayList de.fhb.app.Application.methodReturningArrayList()) execution(LinkedList de.fhb.app.Application.methodReturningLinkedList()) execution(Vector de.fhb.app.Application.methodReturningVector()) @DeclareWarning

@DeclareError

现在您将在控制台上收到编译错误。在Eclipse中它看起来像这样:

Eclipse compilation errors