我目前正在开发两个使用Spring-AOP的Spring应用程序。我有一个方面允许简单的性能记录,其定义如下:
@Aspect
final class PerformanceAdvice {
private Logger logger = LoggerFactory.getLogger("perfLogger");
public Object log(final ProceedingJoinPoint call) throws Throwable {
logger.info("Logging statistics.");
}
}
然后可以通过Spring AOP配置使用以下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并不像their docs中所述那样支持这一点:
在Spring AOP中,不可能将方面本身作为其他方面建议的目标。类上的@Aspect注释将其标记为方面,因此将其从自动代理中排除。
因此,我可能需要更强大的功能,例如AspectJ
。或者是否有可能让Spring知道这方面并仍然允许建议?从StackOverflow上的许多其他问题(与这个特定问题没有直接关系)我尝试制作方面@Configurable
,通过将它们定义为@Component
并使用它们来使它们具有Spring感知能力各种XML和插件设置,例如:
<context:spring-configured />
<context:annotation-config/>
<context:load-time-weaver/>
<aop:aspectj-autoproxy/>
我现在已经没想完了。我是否需要撰写成熟的AspectJ
方面?如果是这样,它可以使用相同的配置,如Spring,引用现有方面和定义新的切入点?这将非常有用,因此我不必为PerformanceAdvice
重新Project 1
,但仍会在Project 2
中引用并使用它。
关于this comment的修改 为了让自己更清楚,我有以下例子。
我在Project 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>
我希望在PerformanceAdvice
log()
方法上调用我的ValidationAdvice
validate()
方法。 doSomeComplexThings()
类的TargetSpringService
方法上的 NOT 。因为这只是一个额外的切入点。问题在于建议另一方面的方法。
答案 0 :(得分:0)
首先,Spring不使用aspectJ来实现AOP,而是使用JDK或cglib动态代理。这里的aspectJ样式只是指语法。
Aspectj方面是静态的,并且在编译或类加载时使用字节码注入。
然后spring只能在它管理的对象上应用代理,因为在依赖注入期间应用了动态代理。此外,如果您有2个不同的项目,则必须确保它们共享相同的弹簧上下文,否则它们将被隔离,并且将1个项目中的方面应用于第2个bean将无效。
是的,你可能不得不在这里使用真正的aspectJ方面。出于性能记录的目的,它更合适,因为不会对性能产生影响。
答案 1 :(得分:0)
所以(根据我的理解)你想在第二个项目中重复使用你的第一个项目的建议。但另外您还想为建议添加更多逻辑。这可以通过用a包装项目-1的建议来完成 项目-2中的自定义实现。 您可以通过包含其他建议(请参阅Advice ordering):
来实现 项目1 需要一些小修改(或者实施Ordered
,您可以使用@Order
注释/当然您也可以注入订单而不是硬编码) :
public class LoggingPerformanceAdvice implements org.springframework.core.Ordered {
Object log(final ProceedingJoinPoint pjp) throws Throwable {
// does the logging/statistics
...
Object result = pjp.proceed(pjp.getArgs());
...
return result;
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
在项目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>
首先调用Ordered.HIGHEST_PRECEDENCE
的方面。
答案 2 :(得分:0)
如果我理解你想对方面建议采取行动,我会做的就是这样(使用aspectj可以改为弹簧注释):
public abstract aspect AspectPerformanceLogging {
private static Logger LOG = LoggerFactory.getLogger(AspectPerformanceLogging.class);
before() : methodExecution() {
LOG.info("Loggin stat");
doBefore(thisJoinPoint);
LOG.info("Loggin stat end");
}
after() : methodExecution() {
LOG.info("Loggin stat");
doAfter(thisJoinPoint);
LOG.info("Loggin stat end");
}
Object around() : methodExecution() {
LOG.info("Loggin stat");
Object result = doAround((ProceedingJoinPoint)thisJoinPoint);
LOG.info("Loggin stat end");
return result;
}
protected abstract pointcut methodExecution();
protected abstract Object doBefore(JoinPoint joinPoint);
protected abstract Object doAfter(JoinPoint joinPoint);
protected abstract Object doAround(ProceedingJoinPoint joinPoint);
}
然后我创建了扩展这方面的其他方面。
答案 3 :(得分:0)
我已经找到了解决问题的两种方法。一个是实际建议的方面,另一个解决问题,但实际上更优雅。
在AspectJ
中,可以编织任何东西。借助AspectJ LTW documentation中所述的META-INF/aop.xml
文件,我可以参考方面并按以下方式定义新的切入点。
PerformanceAdvice
要允许AspectJ
定义新的切入点,建议必须为abstract
并且具有可以挂钩的抽象pointcut
方法。
@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.");
}
}
META-INF/aop.xml
aop.xml
文件定义了一个名为ConcretePerformanceAdvice
的新方面。它也扩展了AbstractPerformanceAdvice
,但定义了一个新的切入点。然后,在AspectJ
中 IS 可能(与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>
pom.xml
编织方面需要一些仪器。这需要依赖项和插件来执行它。至于依赖性,它如下:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument</artifactId>
<version>${org.springframework.version}</version>
</dependency>
目前,在测试期间,我通过surefire-plugin
进行检测。这需要以下几点:
<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上下文中。
<context:load-time-weaver/>
Spring-AOP
不允许方面建议其他方面。但它确实允许在Spring @Component
上运行建议。所以另一个解决方案是将建议中的验证移动到另一个Spring bean。然后将此Spring bean自动装入建议并执行,但PerformanceAdvice
在验证组件上有切入点,而不在验证方面。所以看起来如下:
无!
该建议自动装配Spring @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
。
<!-- 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>