Java 方面建议其他方面
我目前正在开发两个利用SpringAOP的Spring应用程序。我有一个方面允许简单的性能记录,定义如下:Java 方面建议其他方面,java,spring,aop,spring-aop,Java,Spring,Aop,Spring Aop,我目前正在开发两个利用SpringAOP的Spring应用程序。我有一个方面允许简单的性能记录,定义如下: @Aspect final class PerformanceAdvice { private Logger logger = LoggerFactory.getLogger("perfLogger"); public Object log(final ProceedingJoinPoint call) throws Throwable { logger.
@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配置创建此建议:
<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>
我现在没有主意了。我是否需要编写成熟的AspectJ
aspects?如果是这样,它是否可以使用相同的配置(如Spring),引用现有方面并定义新的切入点?这将非常有用,因此我不必为项目1
重新使用性能设备
,但仍然在项目2中引用和使用它
@Service
public class TargetSpringServiceImpl implements TargetSpringService {
@Override
public String doSomeComplexThings(String parameter) {
return "Complex stuff";
}
}
编辑相关信息:
为了让自己更清楚,我举了下面的例子
我在项目2中有一项服务
@Service
public class TargetSpringServiceImpl implements TargetSpringService {
@Override
public String doSomeComplexThings(String parameter) {
return "Complex stuff";
}
}
当调用此方法时,我有一个方面可以进行一些验证
@Aspect
public class ValidationAdvice {
@Autowired
ValidationService validationService
public void validate(JoinPoint jp) throws Throwable {
//Calls the validationService to validate the parameters
}
}
执行以下切入点:
<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>
我希望在ValidationAdvice
的validate()
方法上调用我的PerformanceDevice
的log()
方法NOT关于TargetSpringService
类的doSomeComplexThings()
方法。因为这只是一个额外的切入点。问题在于建议另一个方面的方法。首先,Spring不使用aspectJ实现AOP,而是使用JDK或cglib动态代理。这里的aspectJ风格只是指语法
<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>
Aspectj方面是静态的,在编译或类加载时使用字节码注入
然后,spring只能在其管理的对象上应用代理,因为动态代理是在依赖项注入期间应用的。此外,如果您有两个不同的项目,您必须确保它们共享相同的spring上下文,否则它们将被隔离,并且将方面从1个项目应用到第2个项目的bean将不起作用
是的,您可能需要在这里使用真正的aspectJ方面。出于性能记录的目的,它更合适,因为不会对性能造成影响。因此(据我所知),您希望在第二个项目中重复使用第一个项目的建议。但另外,您还希望向advice添加进一步的逻辑。这可以通过使用
project-2中的自定义实现。
您可以通过包装其他建议来实现这一点(请参阅):
项目1需要一些小的修改(除了实现Ordered
之外,您还可以使用@Order
注释/当然,您也可以插入排序,而不是硬编码):
在您的项目2中创建自定义建议建议实施
public class AdditionalPerformanceAdvice implements Ordered {
Object log(final ProceedingJoinPoint pjp) throws Throwable {
...
Object result = pjp.proceed(pjp.getArgs());
...
return result;
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
将组件连接在一起:
<!-- component of project 1 -->
<bean id="loggingPerformanceAdvice" class="com.acme.project1.LoggingPerformanceAdvice"/>
<!-- component of project 2 -->
<bean id="additionalPerformanceAdvice" class="com.acme.project2.AdditionalPerformanceAdvice"/>
<aop:config>
<aop:aspect ref="loggingPerformanceAdvice">
<aop:around pointcut="execution(* com.acme..*(..))" method="log"/>
</aop:aspect>
<aop:aspect ref="additionalPerformanceAdvice">
<aop:around pointcut="execution(* com.acme..*(..))" method="log"/>
</aop:aspect>
</aop:config>
排序为的Aspect。首先调用最高优先级的。如果我理解您不想对Aspect建议执行操作,我会这样做(使用aspectj可以更改为spring注释):
然后我创建了扩展此方面的其他方面。我已经找到了解决问题的两种可能的方法。一个实际上是建议方面,另一个围绕问题工作,但实际上更优雅
解决方案1:建议方面
在AspectJ
中,几乎可以编织任何东西。在中所述的META-INF/aop.xml
文件的帮助下,我可以引用方面并以以下方式定义新的切入点
对项目1的修改
性能设备
要允许AspectJ
定义一个新的切入点,建议必须是abstract
,并且有一个可以连接到的abstractpointcut
方法
@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
文件定义了一个名为concretePerformanceDevice
的新方面。它还扩展了AbstractPerformanceDevice
,但定义了一个新的切入点。然后,在AspectJ
中可以定义指向另一个方面的切入点(与Spring AOP不同)
<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>
目前,在测试过程中,我通过surefire插件
进行检测。这需要以下位:
<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上下文中,可以在@组件
上定义切入点,而验证广告
自动连接@组件
<!-- 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>
你为什么不建议一个方面?你可以只做方面本身的工作。或者,您可以创建另一个方面,建议与第一个方面相同的方法/类/etcone@JEY我想建议另一个方面,以避免代码重复。我可以在每个其他方面编写相同的性能日志逻辑我希望有一些性能日志,但似乎有一个已经存在的方面为我做,而不是复制粘贴逻辑要整洁得多。继承呢?你不需要这个方面来连接多个切入点吗?这就是你需要的吗?@JEY那么performancedevice
不应该是我的其他方面的超类,因为performancedevice
应该在方面的方法上“发挥其魔力”。e、 g.与其记录RandomService
类的doIt()
方法,不如记录doIt()时触发的doBefore()
方法
被调用。我感谢您的详尽回答,如果我需要PerformanceDevice
的扩展,您将介绍一个很好的实现。不过,我恐怕我说得不够清楚。我不希望扩展PerformanceDevice
日志记录的功能。我希望在另一个方面的方法上执行PerformanceDevice
的log()
方法。我会
<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>
<context:load-time-weaver/>
@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
}
}
<!-- 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>