停止使用Aspectj捕获Jparepository方法 [英] Stop catching Jparepository methods with aspectj

查看:116
本文介绍了停止使用Aspectj捕获Jparepository方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to generate a warning if the programmer returns an Arraylist instead of a list. I use 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,但是如果我运行以下代码行,则方面仍会触发:

looks like spring data (jpa repository) is returning an Arraylist. I dont want to catch methods from the jpa repository, i excluded the org.springframework but the aspect is still triggered if i run something like this line:

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

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

Any hints that will stop trigger the aspect from calling spring jparepository methods ?

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

推荐答案

首先,List.class.isAssignableFrom(clazz)对于ListArrayList是正确的,即,您不能像这样在两者之间进行区分.顺便说一句,returnValue.getClass()永远不会 评估为List,因为这是一种接口类型.它将总是求值到实际的实现类,例如ArrayList.因此,您尝试通过反射找出想要了解的东西的方法总是注定要失败的.

First of all, List.class.isAssignableFrom(clazz) is true for List as well as for ArrayList, i.e. you cannot differentiate between the two like this. And by the way, returnValue.getClass() will never evaluate to List because that is an interface type. It will always evaluate to the actual implementation class such as ArrayList. So your contrived way of trying to find out what you want to know via reflection is doomed anyway.

好消息是:AspectJ使您能够执行自己想要的事情.您似乎通过加载或编译时编织使用了真正的AspectJ,否则您将无法使用call()切入点,因为它在Spring AOP中不可用. 编辑:是的,您的Gradle构建显示您正在使用编译时编织.但是,为什么在当前版本1.8.4中使用编译器,而在过时的1.5.4中使用AspectJ运行时?您应该统一两者,并在版本1.8.4中使用 aspectjrt .

The good news is: AspectJ enables you to do what you want. You seem to use real AspectJ via load or compile time weaving, otherwise you could not use the call() pointcut because it is unavailable in Spring AOP. Yes, your Gradle build shows you are using compile time weaving. But hey, why are you using the compiler in the current version 1.8.4 and the AspectJ runtime in a massively outdated 1.5.4? You should harmonise the two and also use aspectjrt in version 1.8.4.

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

Now let us deconstruct your pointcut:

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

这意味着:拦截对de.fhb或其子包中定义的任何方法(..*表示法)的调用,但前提是该调用也必须由de.fhb中定义的类进行,但 not 在子包中(.*表示法). !within(org.springframework.*)部分是多余的,因为在de.fhb.*中定义的代码永远不能同时来自org.springframework.*.没道理.

It means: Intercept calls to any methods defined in de.fhb or its subpackages (..* notation), but only if the calls are also made from classes defined in de.fhb but not in subpackages (.* notation). The !within(org.springframework.*) part is redundant because code defined in de.fhb.* can never be also come from org.springframework.* at the same time. It makes no sense.

您可能真正想要的是找出在您自己的程序包中是否有返回具体类型的方法,例如ArrayList而不是接口类型List.正确的?我认为切入点应该看起来像这样:

What you probably really want is find out if in your own packages there are methods returning a concrete type like ArrayList instead of the interface type List. Correct? I think the pointcut should rather look like this:

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

表示:对于de.fhb或其子包中的所有类,请根据其方法签名截获所有返回List+的方法(+表示:List或其子类型).因为这也将与返回父类型List的方法匹配,并且只需要子类型,所以必须通过!execution(java.util.List *(..))在切入点的第三部分中排除该类型.

It means: For all classes within de.fhb or its subpackages, intercept all methods returning a List+ (the + means: a List or its subtypes) according to its method signature. Because this would also match methods returning the parent type List and you only want the subtypes, you must exclude that type in the third part of the pointcut via !execution(java.util.List *(..)).

我匹配的是方法执行而不是调用,因为这样做效率更高.如果从包中的100个位置调用了一个方法,则call()将把方面代码编织到100个调用连接点中,而execution()实际上只是将实际定义该方法的位置编织在一起.

I am matching method executions rather than calls because that is more efficient. If a method is called from 100 places in your package, call() would weave aspect code into 100 calling joinpoints while execution() really just weaves place where the method is actually defined.

以下是一些示例代码:

驱动程序应用程序:

您看到有一种方法可以根据您的意愿正确地声明一个List返回类型,而其他方法则声明了禁止"返回类型,例如ArrayListLinkedListVector.

You see that there is one method correctly declaring (according to your wish) a List return type while others declare "forbidden" return types such as ArrayList, LinkedList and Vector.

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();
    }
}

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

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);
    }
}

控制台输出:

execution(ArrayList de.fhb.app.Application.methodReturningArrayList())
execution(LinkedList de.fhb.app.Application.methodReturningLinkedList())
execution(Vector de.fhb.app.Application.methodReturningVector())

如您所见,您完全可以截获根据您的定义有问题的那些方法.

As you can see, you get exactly those methods intercepted which are problematic according to your definition.

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

但是AspectJ可以做更多的事情.为什么不在编译期间(即,甚至在打包和部署软件之前)发出警告甚至错误?您的开发团队可以在错误进入生产代码之前对其进行修复.为此,请使用@DeclareWarning@DeclareError:

But AspectJ can do more. Why not throw a warning or even an error during compilation, i.e. before the software is even packaged and deployed? Your development team can fix the bugs before they get into the production code. In order to do that, use @DeclareWarning or @DeclareError:

package de.fhb.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareError;

@Aspect
public class ReturnTypeChecker {
    @DeclareError("within(de.fhb..*) && execution(java.util.List+ *(..)) && !execution(java.util.List *(..))")
    private static final String typeWarning = "Please do not declare methods returning concrete subclasses of List";
}

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

Now you will get compilation errors on the console. In Eclipse it looks like this:

这篇关于停止使用Aspectj捕获Jparepository方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆