Spring AOP 不适用于具有泛型类型 Return 值的方法
Spring AOP Doesn't Apply on Methods with Generic Type Return Values
编辑:已解决,因为开头有双星号,我从书中复制了它:Spring 微服务实战。
我正在尝试建议一些控制器方法。
这是相位class:
@Aspect
public class PBLogger {
@Pointcut("execution(** com.sunwell.product..*.*(..))")
public void standardMethod() {
}
@Around("standardMethod()")
public Object log(ProceedingJoinPoint jp) {
...
}
...
}
这是目标方法,我确定包名是正确的。
这个不行(不调用advice方法):
@RequestMapping(value = "resources/items", method = RequestMethod.GET,
produces = "application/json"
)
public ResponseEntity<Map<String,Object>> getItems(
@RequestHeader(value="Authorization", required = false) String _auth, @RequestParam(value="systemId", required = false) Integer _i ) throws Exception
{ ... }
没有通用 return 类型,它可以工作:
@RequestMapping(value = "resources/items", method = RequestMethod.GET,
produces = "application/json"
)
public void getItems(@RequestHeader(value="Authorization", required = false) String _auth, @RequestParam(value="systemId", required = false) Integer _i ) throws Exception
{ ... }
它也可以使用带注释的切入点而不是执行
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Example
{
public String msg();
}
@Aspect
public class PBLogger {
@Pointcut("@annotation(Example)")
public void standardMethod() {
}
@Around("standardMethod()")
public Object log(ProceedingJoinPoint jp) {
...
}
...
}
//target method:
@RequestMapping(value = "resources/items", method = RequestMethod.GET,
produces = "application/json"
)
@Example
public ResponseEntity<Map<String,Object>> getItems(
@RequestHeader(value="Authorization", required = false) String _auth, @RequestParam(value="systemId", required = false) Integer _ ) throws Exception
{ ... }
目标方法签名不是问题,泛型与否。看看我的MCVE:
目标 classes:
请注意,您不能将两种方法放在同一个 class 中,因为它们将具有相同的类型擦除。编译器会抱怨这个。
package de.scrum_master.spring.q60474362;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Map;
@Component
public class MyTargetClass {
@RequestMapping(
value = "resources/items", method = RequestMethod.GET,
produces = "application/json"
)
public ResponseEntity<Map<String, Object>> getItems(
@RequestHeader(value = "Authorization", required = false) String _auth,
@RequestParam(value = "systemId", required = false) Integer _i
) throws Exception
{
System.out.println("MyTargetClass.getItems");
return null;
}
}
package de.scrum_master.spring.q60474362;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
@Component
public class MyOtherTargetClass {
@RequestMapping(
value = "resources/items", method = RequestMethod.GET,
produces = "application/json"
)
public void getItems(
@RequestHeader(value = "Authorization", required = false) String _auth,
@RequestParam(value = "systemId", required = false) Integer _i
) throws Exception
{
System.out.println("MyOtherTargetClass.getItems");
}
}
看点:
请注意,示例切入点中的 **
没有意义,我将其更改为使用 *
。如果 **
有效,那么你很幸运,语法检查并不严格,但我的 IDE 实际上会抱怨。此外,您实际上并不需要 my.package..*.*(..)
,如果 class 和方法名称都无关紧要,my.package..*(..)
就足够了。
package de.scrum_master.spring.q60474362;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class PBLogger {
@Pointcut("execution(* de.scrum_master.spring..*(..))")
public void standardMethod() {}
@Around("standardMethod()")
public Object log(ProceedingJoinPoint jp) throws Throwable {
System.out.println(jp);
return jp.proceed();
}
}
Spring 驱动申请及配置:
package de.scrum_master.spring.q60474362;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@SpringBootApplication
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class Application {
public static void main(String[] args) throws Exception {
try (ConfigurableApplicationContext appContext = SpringApplication.run(Application.class, args)) {
doStuff(appContext);
}
}
private static void doStuff(ConfigurableApplicationContext appContext) throws Exception {
MyTargetClass myTargetClass = appContext.getBean(MyTargetClass.class);
myTargetClass.getItems("x", 11);
MyOtherTargetClass myOtherTargetClass = appContext.getBean(MyOtherTargetClass.class);
myOtherTargetClass.getItems("y", 22);
}
}
控制台日志:
. ____ _ __ _ _
/\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.5.2.RELEASE)
(...)
2020-03-02 09:00:49.712 INFO 24028 --- [ main] d.s.spring.q60474362.Application : Started Application in 3.554 seconds (JVM running for 5.157)
execution(ResponseEntity de.scrum_master.spring.q60474362.MyTargetClass.getItems(String,Integer))
MyTargetClass.getItems
execution(void de.scrum_master.spring.q60474362.MyOtherTargetClass.getItems(String,Integer))
MyOtherTargetClass.getItems
(...)
如果它对您不起作用,那么您还有其他问题,例如组件扫描未拾取有问题的目标 class,例如由于忘记了 @Component
注释或其他原因。
编辑:已解决,因为开头有双星号,我从书中复制了它:Spring 微服务实战。
我正在尝试建议一些控制器方法。
这是相位class:
@Aspect
public class PBLogger {
@Pointcut("execution(** com.sunwell.product..*.*(..))")
public void standardMethod() {
}
@Around("standardMethod()")
public Object log(ProceedingJoinPoint jp) {
...
}
...
}
这是目标方法,我确定包名是正确的。
这个不行(不调用advice方法):
@RequestMapping(value = "resources/items", method = RequestMethod.GET,
produces = "application/json"
)
public ResponseEntity<Map<String,Object>> getItems(
@RequestHeader(value="Authorization", required = false) String _auth, @RequestParam(value="systemId", required = false) Integer _i ) throws Exception
{ ... }
没有通用 return 类型,它可以工作:
@RequestMapping(value = "resources/items", method = RequestMethod.GET,
produces = "application/json"
)
public void getItems(@RequestHeader(value="Authorization", required = false) String _auth, @RequestParam(value="systemId", required = false) Integer _i ) throws Exception
{ ... }
它也可以使用带注释的切入点而不是执行
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Example
{
public String msg();
}
@Aspect
public class PBLogger {
@Pointcut("@annotation(Example)")
public void standardMethod() {
}
@Around("standardMethod()")
public Object log(ProceedingJoinPoint jp) {
...
}
...
}
//target method:
@RequestMapping(value = "resources/items", method = RequestMethod.GET,
produces = "application/json"
)
@Example
public ResponseEntity<Map<String,Object>> getItems(
@RequestHeader(value="Authorization", required = false) String _auth, @RequestParam(value="systemId", required = false) Integer _ ) throws Exception
{ ... }
目标方法签名不是问题,泛型与否。看看我的MCVE:
目标 classes:
请注意,您不能将两种方法放在同一个 class 中,因为它们将具有相同的类型擦除。编译器会抱怨这个。
package de.scrum_master.spring.q60474362;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Map;
@Component
public class MyTargetClass {
@RequestMapping(
value = "resources/items", method = RequestMethod.GET,
produces = "application/json"
)
public ResponseEntity<Map<String, Object>> getItems(
@RequestHeader(value = "Authorization", required = false) String _auth,
@RequestParam(value = "systemId", required = false) Integer _i
) throws Exception
{
System.out.println("MyTargetClass.getItems");
return null;
}
}
package de.scrum_master.spring.q60474362;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
@Component
public class MyOtherTargetClass {
@RequestMapping(
value = "resources/items", method = RequestMethod.GET,
produces = "application/json"
)
public void getItems(
@RequestHeader(value = "Authorization", required = false) String _auth,
@RequestParam(value = "systemId", required = false) Integer _i
) throws Exception
{
System.out.println("MyOtherTargetClass.getItems");
}
}
看点:
请注意,示例切入点中的 **
没有意义,我将其更改为使用 *
。如果 **
有效,那么你很幸运,语法检查并不严格,但我的 IDE 实际上会抱怨。此外,您实际上并不需要 my.package..*.*(..)
,如果 class 和方法名称都无关紧要,my.package..*(..)
就足够了。
package de.scrum_master.spring.q60474362;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class PBLogger {
@Pointcut("execution(* de.scrum_master.spring..*(..))")
public void standardMethod() {}
@Around("standardMethod()")
public Object log(ProceedingJoinPoint jp) throws Throwable {
System.out.println(jp);
return jp.proceed();
}
}
Spring 驱动申请及配置:
package de.scrum_master.spring.q60474362;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@SpringBootApplication
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class Application {
public static void main(String[] args) throws Exception {
try (ConfigurableApplicationContext appContext = SpringApplication.run(Application.class, args)) {
doStuff(appContext);
}
}
private static void doStuff(ConfigurableApplicationContext appContext) throws Exception {
MyTargetClass myTargetClass = appContext.getBean(MyTargetClass.class);
myTargetClass.getItems("x", 11);
MyOtherTargetClass myOtherTargetClass = appContext.getBean(MyOtherTargetClass.class);
myOtherTargetClass.getItems("y", 22);
}
}
控制台日志:
. ____ _ __ _ _
/\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.5.2.RELEASE)
(...)
2020-03-02 09:00:49.712 INFO 24028 --- [ main] d.s.spring.q60474362.Application : Started Application in 3.554 seconds (JVM running for 5.157)
execution(ResponseEntity de.scrum_master.spring.q60474362.MyTargetClass.getItems(String,Integer))
MyTargetClass.getItems
execution(void de.scrum_master.spring.q60474362.MyOtherTargetClass.getItems(String,Integer))
MyOtherTargetClass.getItems
(...)
如果它对您不起作用,那么您还有其他问题,例如组件扫描未拾取有问题的目标 class,例如由于忘记了 @Component
注释或其他原因。