【问题标题】:Stop catching Jparepository methods with aspectj停止使用 aspectj 捕获 Jparepository 方法
【发布时间】:2014-12-02 13:08:19
【问题描述】:

如果程序员返回 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

【问题讨论】:

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


    【解决方案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 返回类型,而其他方法则声明“禁止”返回类型,例如 ArrayListLinkedListVector

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

    如您所见,您得到的正是那些根据您的定义有问题的方法被截获。

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

    但 AspectJ 可以做得更多。为什么不在编译期间(即在软件甚至打包和部署之前)发出警告甚至错误?您的开发团队可以在错误进入生产代码之前修复它们。为此,请使用@DeclareWarning@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 中它看起来像这样:

    【讨论】:

    • 谢谢你的详细解答,没想到
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-02
    • 2020-04-02
    • 1970-01-01
    • 1970-01-01
    • 2016-05-09
    • 1970-01-01
    相关资源
    最近更新 更多