方面建议其他方面 [英] Aspect advising other aspects

查看:27
本文介绍了方面建议其他方面的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在开发两个使用 Spring-AOP 的 Spring 应用程序.我有一个方面允许简单的性能日志记录,其定义如下:

I am currently developing two Spring applications that makes use of Spring-AOP. I have an aspect that allows simple performance logging which is defined as such:

@Aspect
final class PerformanceAdvice {
    private Logger logger = LoggerFactory.getLogger("perfLogger");

    public Object log(final ProceedingJoinPoint call) throws Throwable {
        logger.info("Logging statistics.");
    }
}

然后可以使用以下 XML 通过 Spring AOP 配置创建此建议:

This advice can then be created through Spring AOP configuration with the following XML:

<bean id="performanceAdvice" class="com.acme.PerformanceAdvice" />

<aop:config>
    <aop:aspect ref="performanceAdvice">
        <aop:around pointcut="execution(* com.acme..*(..))" method="log"/>
    </aop:aspect>
</aop:config>

这适用于 Spring 创建的类,例如用 @Service 注释的类.但我希望这方面也能在次要项目中为其他方面提供建议.我知道 Spring 不支持 他们的文档:

This works fine for classes that are created by Spring such as classes annotated with @Service. But I'd like this aspect to also advise other aspects in the secondary project. I'm aware Spring doesn't support this as noted in their docs:

在 Spring AOP 中,不可能让方面本身成为来自其他方面的建议的目标.类上的 @Aspect 注释将其标记为方面,因此将其排除在自动代理之外.

In Spring AOP, it is not possible to have aspects themselves be the target of advice from other aspects. The @Aspect annotation on a class marks it as an aspect, and hence excludes it from auto-proxying.

因此我可能需要更强大的东西,例如 AspectJ.或者是否有可能让 Spring 意识到这个方面并仍然允许提供建议?从 StackOverflow 上的许多其他问题(与这个特定问题没有直接关系),我尝试制作方面 @Configurable,通过将它们定义为 @Component 使它们具有 Spring 感知能力code> 并使用各种 XML 和插件设置,例如:

Thus I'm probably in need of something more powerful such as AspectJ. Or is it possible to make Spring aware of the aspect and still allow advising? From numerous other questions (that aren't directly related to this specific problem) on StackOverflow I have tried making aspects @Configurable, making them Spring-aware by defining them as a @Component and played around with various XML and plugin settings such as:

<context:spring-configured />
<context:annotation-config/>
<context:load-time-weaver/>
<aop:aspectj-autoproxy/>

我现在没有想法了.我是否需要编写成熟的AspectJ 方面?如果是这样,它是否可以使用与 Spring 相同的配置,引用现有方面并定义新的切入点?这将非常有用,因此我不必为 Project 1 重新编写 PerformanceAdvice,但仍然在 Project 2 中引用和使用它.

I'm running out of ideas now. Will I be needing to write fully-fledged AspectJ aspects? If so, can it use the same configuration such as Spring, referencing the existing aspect and defining a new pointcut? This would be useful so I don't have to re-work the PerformanceAdvice for Project 1 but still reference and use it in Project 2.

编辑关于此评论:为了让自己更清楚,我有以下示例.

edit regarding this comment: To make myself more clear, I have the following example.

我在 Project 2 中有一项服务.

I have a Service in Project 2.

@Service
public class TargetSpringServiceImpl implements TargetSpringService {
    @Override
    public String doSomeComplexThings(String parameter) {
        return "Complex stuff";
    }
}

当这个方法被调用时,我有一个方面做一些验证.

When this method gets called, I have an aspect that does some validation.

@Aspect
public class ValidationAdvice {
    @Autowired
    ValidationService validationService

    public void validate(JoinPoint jp) throws Throwable {
        //Calls the validationService to validate the parameters
    }
}

使用以下切入点作为执行:

With the following pointcut as execution:

<bean id="validationAdvice" class="com.acme.advice.ValidationAdvice" />

<aop:config>
    <aop:aspect ref="validationAdvice">
        <aop:before pointcut="execution(* com.acme.service..*(..))" method="validate"/>
    </aop:aspect>
</aop:config>

我希望在 ValidationAdvicevalidate 上调用我的 PerformanceAdvicelog() 方法() 方法.NOTTargetSpringService 类的 doSomeComplexThings() 方法上.因为这只是一个额外的切入点.问题在于建议另一个方面的方法.

I'd like to have my PerformanceAdvice's log() method to be invoked on the ValidationAdvice's validate() method. NOT on the doSomeComplexThings() method of the TargetSpringService class. As this is just an additional pointcut. The problem lies with advising the method of another aspect.

推荐答案

我想出了两种可能的解决方案来解决我的问题.一个实际上是建议方面,另一个解决问题,但实际上更优雅.

I have figured out two possible solutions to my problem. One is actually advising the aspect, the other works around the problem but is actually more elegant.

AspectJ 中,几乎可以编织任何东西.借助 AspectJ LTW 文档,我可以通过以下方式引用方面并定义新的切入点.

In AspectJ it's possible to weave just about anything. With the help of a META-INF/aop.xml file as stated in the AspectJ LTW documentation, I could reference the aspect and define a new pointcut in the following way.

为了允许 AspectJ 定义一个新的切入点,通知必须是 abstract 并且有一个抽象的 pointcut 方法可以被挂钩到.

To allow AspectJ to define a new pointcut, the advice has to be abstract and have an abstract pointcut method that can be hooked into.

@Aspect
final class PerformanceAdvice extends AbstractPerformanceAdvice {
    @Override
    void externalPointcut(){}
}

@Aspect
public abstract class AbstractPerformanceAdvice {
    private Logger logger = LoggerFactory.getLogger("perfLogger");

    @Pointcut
    abstract void externalPointcut();

    @Around("externalPointcut()")
    public Object log(final ProceedingJoinPoint call) throws Throwable {
        logger.info("Logging statistics.");
    }
}

对项目 2 的更改

META-INF/aop.xml

aop.xml 文件定义了一个名为 ConcretePerformanceAdvice 的新方面.它也扩展了 AbstractPerformanceAdvice,但定义了一个新的切入点.然后,在 AspectJ 中,可能(与 Spring-AOP 不同) 定义指向另一个方面的切入点.

Changes to project 2

The META-INF/aop.xml

The aop.xml file defines a new aspect called ConcretePerformanceAdvice. It extends of the AbstractPerformanceAdvice as well but defines a new pointcut. Then, in AspectJ it IS possible (unlike in Spring-AOP) to define a pointcut to another aspect.

<aspectj>
    <aspects>
        <concrete-aspect name="com.example.project2.ConcretePerformanceAdvice" extends="com.example.project1.AbstractPerformanceAdvice">
            <pointcut name="externalPointcut" expression="execution(* com.example.project2.ValidationAdvice.validate(..))"/>
        </concrete-aspect>
    </aspects>    
    <weaver options="-verbose"/>
</aspectj>

pom.xml

编织方面需要一些工具.这需要一个依赖项和一个插件来执行它.至于依赖,是这样的:

The pom.xml

Weaving the aspect requires some instrumentation. This requires both a dependency and a plugin to execute it. As for the dependency, it is the following:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-instrument</artifactId>
    <version>${org.springframework.version}</version>
</dependency>

目前,在测试期间,我通过 surefire-plugin 进行检测.这需要以下位:

At the moment, during testing, I do the instrumentation through the surefire-plugin. That requires the following bit:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.8</version>
            <configuration>
                <forkMode>once</forkMode>
                <argLine>
                    -javaagent:"${settings.localRepository}/org/springframework/spring-instrument/${org.springframework.version}/spring-instrument-${org.springframework.version}.jar"
                </argLine>
                <useSystemClassloader>true</useSystemClassloader>
            </configuration>
        </plugin>
    </plugins>
</build>

Spring 上下文

要启用加载时编织,还需要激活编织.因此必须将以下内容添加到 Spring 上下文中.

The Spring context

To enable the load-time weaving, it's also necessary to activate the weaving. So the following has to be added to the Spring context.

<context:load-time-weaver/>

解决方案 2:委托给 Spring bean

Spring-AOP 不允许方面建议其他方面.当然,它确实允许在 Spring @Component 上运行通知.因此,另一种解决方案是将通知中完成的验证移至另一个 Spring bean.然后这个 Spring bean 被自动装配到通知中并执行,但是 PerformanceAdvice 在验证 组件 上有它的切入点,而不是在验证 方面.所以它看起来像下面这样:

Solution 2: Delegate to a Spring bean

Spring-AOP does not allow aspects to advise other aspects. But it does allow advice to be run on a Spring @Component of course. So the other solution would be to move the validation done in the advice, to another Spring bean. This Spring bean is then autowired into the advice and executed, but the PerformanceAdvice has its pointcut on the validation component and not on the validation aspect. So it would look like the following:

没有!

通知自动装配 Spring @Component 并将其逻辑委托给组件.

The advice autowires the Spring @Component and delegates its logic to the component.

@Aspect
public class ValidationAdvice {
    @Autowired
    private ValidatorDefault validatorDefault;

    public void validate(JoinPoint jp) throws Throwable {
        validatorDefault.validate(jp);
    }
}

@Component
public class ValidatorDefault {
    @Autowired
    ValidationService validationService

    public void validate(JoinPoint jp) throws Throwable {
        //Calls the validationService to validate the parameters
    }
}

然后在 Spring 上下文中,可以在 @Component 上定义切入点,而 ValidationAdvice 自动装配 @Component.

Then in the Spring context it's possible to define the pointcut on the @Component while the ValidationAdvice autowires the @Component.

<!-- Scan the package to find the ValidatorDefault component for autowiring -->
<context:component-scan base-package="com.example.project2" />

<bean id="validationAdvice" class="com.example.project2.ValidationAdvice" />
<bean id="performanceAdvice" class="com.example.project1.PerformanceAdvice" />

<aop:config>
    <aop:aspect ref="validationAdvice">
        <aop:before pointcut="execution(* com.acme.service..*.*(..))" method="validate"/>
    </aop:aspect>
    <aop:aspect ref="performanceAdvice">
        <aop:around pointcut="execution(* com.example.project2.ValidatorDefault.validate(..))" method="log"/>
    </aop:aspect>
</aop:config>

这篇关于方面建议其他方面的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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