【问题标题】:Why my Aspect is not detected for Jersey controller (using custom annotation)?为什么没有为 Jersey 控制器检测到我的 Aspect(使用自定义注释)?
【发布时间】:2014-01-21 23:51:27
【问题描述】:

我想在 Jersey 控制器上创建一个 Aspect 来测量执行服务需要多长时间。我正在与我的切入点作斗争,因为它没有被检测到并且我的方面永远不会启动。

我尝试过使用很多切入点,例如:

execution(@Monitor * *.*(..))
execution(public * *(..))
change the order of @Aspect and @Component

Added a pointcut like this:
@Pointcut("execution(@Monitor * *.*(..))")
public void monitorRequestTargets(){}
@Around("monitorRequestTargets()")

Tried using AOP and CGLIB
<aop:aspectj-autoproxy proxy-target-class="true"/>

Also tried changing the order of configuration in context.xml

Eclipse 检测到我的方法正在由我的方面提供建议,但它没有在运行时执行。你能给我任何关于为什么没有创建方面或没有启动切入点的提示吗?

我的代码如下。

Context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <!-- Enables the Spring MVC @Controller -->
    <annotation-driven />

    <!-- Enables AspectJ -->
    <aop:aspectj-autoproxy />

    <!-- .....more definitions -->

    <context:component-scan base-package="com.mypackage" />

</beans:beans>

我的MonitorAspect

@Component
@Aspect
public class MonitorAspect
{
    private static final Logger logger = LoggerFactory.getLogger(MonitorAspect.class);

    @Around("@annotation(com.mypackage.Monitor)")
    public void logTimeUsage(ProceedingJoinPoint joinPoint) throws Throwable 
    {
        // Store executing method
        String method = joinPoint.getSignature().getName();

        // Track time
        long startTime = System.currentTimeMillis();
        joinPoint.proceed();
        long endTime = System.currentTimeMillis();

        long duration = endTime - startTime;

        // Log time consumed by executing method
        logger.info(method + ": " + duration);

    }
}

我的 Monitor 自定义注解是

@Target(value = {ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Monitor
{

}

我想要使用方面的控制器:

    @Monitor
    @POST
    @Consumes("application/json")
    @Produces("application/json")
    @Path("/{tkn}/test/")
    public Response test(
            @Context HttpServletRequest httpReq,
            @Context UriInfo uri,
                     String enrollReqJson
            ) {
          Thread.sleep(1000); // Implementation is not important
    }

我的 pom.xml

<!-- Spring -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>${org.springframework-version}</version>
    <exclusions>
        <!-- Exclude Commons Logging in favor of SLF4j -->
        <exclusion>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-orm</artifactId>
    <version>${org.springframework-version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>${org.springframework-version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>${org.springframework-version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-orm</artifactId>
    <version>${org.springframework-version}</version>
</dependency>
  <!-- Jersey / Spring -->
<dependency>
    <groupId>com.sun.jersey.contribs</groupId>
    <artifactId>jersey-spring</artifactId>
    <version>1.14</version>
    <exclusions>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>com.sun.jersey</groupId>
    <artifactId>jersey-bundle</artifactId>
    <version>1.8</version>
</dependency>
        <dependency>
    <groupId>com.sun.jersey.contribs</groupId>
    <artifactId>jersey-multipart</artifactId>
    <version>1.8</version>
</dependency>
        <!-- Spring AOP / AspectJ -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>${org.aspectj-version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>${org.springframework-version}</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>${org.aspectj-version}</version>
</dependency>

【问题讨论】:

  • 你的控制器在 com.mypackage 包中吗?
  • Kyle 提到的任何内容以及 @Around(value = "@annotation(com.mypackage.Monitor)")。试试这个。在组件扫描期间是否也扫描了您的 Aspect?
  • 另外,一个 around Advice 应该返回一个 Object,它不能是 void
  • 让我们看看你的完整控制器类。
  • @Hrishikesh,如何检查我的方面是否在组件扫描时被扫描?

标签: java jersey aop aspectj spring-aop


【解决方案1】:

看起来和我遇到的问题一样。根据您提供的内容,您似乎在不是由 Spring 创建的对象上使用 Spring AOP(大概是 Jersey 是创建您的控制器类实例的对象),因此没有应用建议。 Eclipse 说它被建议了,但这仅适用于由 Spring 创建的控制器实例。不建议您实际使用的那些,由 Jersey(或您使用的任何提供商)创建的那些。

我花了很多时间试图让 Jersey 使用 Spring 来创建控制器实例(包括几个声称能够做到这一点的插件),但始终无法让它工作。我能够通过直接使用 AspectJ 而不是 Spring AOP 来使其工作。除了弄清楚如何让 spring 注入方面之外,这并不难。

添加:

注释保持不变。您只需要从 spring.xml 中删除 AOP 内容,并在 META-INF 中创建一个 aop.xml 文件。这是我的样子:

<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
    <aspects>
        <aspect name="com.ancestry.academy.api.ServiceApiAspect" />
        <aspect name="com.ancestry.academy.manager.ManagerApiAspect" />
    </aspects>
</aspectj>

您可以为弹簧加载的类保留 Spring AOP。您只需为在 spring 之外创建的类(如 jax 的控制器)执行此操作。

有一点我不太明白的黑魔法。如果你需要使用 spring 注入你的切面本身,那么你需要在 spring.xml 中声明它,如下所示:

<bean id="serviceApiAspect" class="com.ancestry.academy.api.ServiceApiAspect" factory-method="aspectOf" />

<bean id="managerApiAspect" class="com.ancestry.academy.manager.ManagerApiAspect" factory-method="aspectOf" />

请注意,我的方面没有名为 aspectOf 的方法。显然这是由 AspectJ 添加的。这就是它的全部内容。

【讨论】:

  • 感谢您的回答。我理解你的观点,但不明白如果我用@Component 注释它,为什么 spring 不处理这个方面。顺便说一句,如果你是对的,你可以添加一个使用 AspectJ 而不是 SpringAOP 的例子吗?非常感谢
  • 这与谁创建它有关。如果它用@Component 注释,那么spring 将创建一个它可以使用的实例。如果 Jersey 创建实例,则 spring 没有机会代理它。不幸的是,您真正关心的实例是 Jersey 创建的实例。
  • 感谢您的评论。你知道我怎么能用春天做到这一点吗?还是一种创建切面以到达 Jersey 对象的方法?
  • 非常感谢您的回答,绝对+1。我会试试你的代码。一旦我测试它,我会告诉你,如果它有效,我会选择你的答案是正确的。
  • 我已经尝试了您的建议等等,但无法成功。我尝试匹配每个公共方法并加载了方面,但从未进入我的 Jersey 控制器。
【解决方案2】:

我发现了为什么我的方面不能与 Jersey 一起工作,因为我必须在这种特殊情况下使用编织器。

在配置 aspectj-maven-plugin 以编织我的方面后,一切正常。

可以在此答案中找到配置编织器的完整详细信息: Spring + AspectJ weaving for java 8 using aspectj-maven-plugin

【讨论】:

    【解决方案3】:

    添加

    compile 'org.aspectj:aspectjtools:1.8.1'
    compile 'org.aspectj:aspectjrt:1.8.1'
    

    做注释

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface ParmsAudit
    {
      String action();
    }
    

    方面类

    @Aspect
    public class AuditAspect {
    
    @Before(value="execution(@uk.co.billcomer.audit.ParmsAudit * *(..)) &&   @annotation(parmsAudit)", argNames="parmsAudit")
    public void logTheAuditActivity(JoinPoint aPoint, ParmsAudit parmsAudit) {
     String userName = getUserName();
     mlogger.info("Parms Auditing User Name: " + userName);
     mlogger.info("auditType:" + parmsAudit.auditType().getDescription());
    
     String arguments = getArgs(aPoint.getArgs());
    
     if (arguments.length() > 0) {
     mlogger.info("args-" + arguments);
     }
    }
    
    private String getArgs(Object[] args) {
     String arguments = "";
     int argCount = 0;
    
     for (Object object : args) {
       if (argCount > 0) {
         arguments += ", ";
       }
       arguments += "arg[" + ++argCount + "]=" + "[" + object + "]";
     }
     return arguments;
    }
    
    private String getUserName() {
     try {
       return SecurityContextHolder.getContext().getAuthentication().getName();
     } catch (NullPointerException npe) {
       return "Unknown User";
     }
    }
    }
    

    这个链接很好http://billcomer.blogspot.in/2011/07/auditing-using-aop-and-annotations-in.html

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-11-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多