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

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

问题描述

我目前正在开发两个使用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意识,并尝试使用各种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中有一个服务.

@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()方法. TargetSpringService类的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中,可以 IS (不同于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上下文中,可以在ValidationAdvice自动装配@Component的同时在@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天全站免登陆