如果程序员返回一个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方法的提示?
答案 0 :(得分:1)
首先,对于List.class.isAssignableFrom(clazz)
以及List
,ArrayList
都是正确的,即您无法区分这两者。顺便说一下,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
返回类型,而其他方法声明“禁止”返回类型,例如ArrayList
,LinkedList
和{{ 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中它看起来像这样: