我正在编写的代码需要拦截对对象#2(外部库的一部分)的调用,该对象由对象#1(也在外部库中)即时实例化/检索。
因此,我拦截对象#1,然后向对象#2 添加一个方面;这样我可以获得调用次数、抛出的异常、响应时间等
// My aspect intercepts object #1 that retrieves object #2 and adds the aspect
Object obj2 = joinPoint.proceed();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(obj2);
proxyFactory.addAspect(this);
现在,由于
obj2
将被检索数百万次,当一遍又一遍地调用(添加方面)时,proxyFactory.addAspect(this);
会随着时间的推移而降低应用程序的性能,或者这个调用是幂等的并且几乎没有副作用吗?
注意:我猜 obj2 只会被实例化几次,然后它会一遍又一遍地成为同一个对象,但我对此不能肯定,因为它是一个外部的、封闭的源码库。
就像我在评论中所说,只需使用本机 AspectJ,最好通过 加载时编织 (LTW),因为这样您还可以拦截第三方类,而无需繁琐的编译后二进制编织和现有 JAR 的重新打包。超级简单,超级高效。
FWIW,你的 Spring AOP 问题很有趣,值得考虑一下。如果您出于某种原因对重复和冗余地创建代理工厂、代理并将它们链接到方面时的性能不满意,只需将目标实例缓存在弱哈希映射中,使用原始实例作为键,使用代理作为值。它基本上看起来像这样:
package de.scrum_master.spring.q77417629;
public class MyPojo {
public void doPojoStuff() {}
}
package de.scrum_master.spring.q77417629;
import org.springframework.stereotype.Component;
@Component
public class MyComponent {
private final MyPojo myPojo;
public MyComponent() {
myPojo = new MyPojo();
}
public MyPojo doComponentStuff() {
return myPojo;
}
}
如您所见,组件始终返回相同的 POJO 实例,即它应该是可缓存的。
package de.scrum_master.spring.q77417629;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;
import org.springframework.stereotype.Component;
import java.util.WeakHashMap;
@Aspect
@Component
public class MyAspect {
private final WeakHashMap<Object, Object> proxyTargets = new WeakHashMap<>();
@Around("execution(* MyComponent.doComponentStuff())")
public Object interceptMyComponent(ProceedingJoinPoint joinPoint) throws Throwable {
Object result = joinPoint.proceed();
if (proxyTargets.containsKey(result)) {
System.out.println("Existing proxy target " + result);
return proxyTargets.get(result);
}
System.out.println("New proxy target " + result);
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(result);
proxyFactory.addAspect(this);
Object proxy = proxyFactory.getProxy();
proxyTargets.put(result, proxy);
return proxy;
}
@Before("within(de.scrum_master.spring.q77417629..*)")
public void log(JoinPoint joinPoint) {
System.out.println(joinPoint);
}
}
package de.scrum_master.spring.q77417629;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@SpringBootApplication
@Configuration
@EnableAspectJAutoProxy
public class DemoApplication {
public static void main(String[] args) {
try (ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args)) {
// The POJO is not proxied yet,i.e. the aspect should not fire
new MyPojo().doPojoStuff();
new MyPojo().doPojoStuff();
new MyPojo().doPojoStuff();
MyComponent component = context.getBean(MyComponent.class);
// The first time doComponentStuff() is intercepted by the aspect, the proxy factory
// should bind an aspect to its return value, a MyPojo instance. The subsequent
// doPojoStuff() call should immediately be intercepted by the second aspect advice.
component.doComponentStuff().doPojoStuff();
// All future doComponentStuff() is interceptions should use the cached MyPojo instance.
component.doComponentStuff().doPojoStuff();
component.doComponentStuff().doPojoStuff();
}
}
}
控制台日志显示其工作原理:
execution(MyPojo de.scrum_master.spring.q77417629.MyComponent.doComponentStuff())
New proxy target de.scrum_master.spring.q77417629.MyPojo@43e7f104
execution(void de.scrum_master.spring.q77417629.MyPojo.doPojoStuff())
execution(MyPojo de.scrum_master.spring.q77417629.MyComponent.doComponentStuff())
Existing proxy target de.scrum_master.spring.q77417629.MyPojo@43e7f104
execution(void de.scrum_master.spring.q77417629.MyPojo.doPojoStuff())
execution(MyPojo de.scrum_master.spring.q77417629.MyComponent.doComponentStuff())
Existing proxy target de.scrum_master.spring.q77417629.MyPojo@43e7f104
execution(void de.scrum_master.spring.q77417629.MyPojo.doPojoStuff())