在一篇名为"AOP Fundamentals"的文章中,我要求King的英文解释AOP是什么,以及它做了什么.我收到了一些非常有用的答案和链接到文章,这些文章帮助我了解了所有理论.
但是现在AOP得到了我的全部关注,所有这些文章和章节摘录都很棒,但在每一个案例中,它们都包含了崇高的理论,模糊的UML模型,以及抽象的顺序,这些都是我喜欢的.
这是我对AOP理论的理解,只是为了澄清,所以如果你看到一些看起来不对的东西,请告诉我!
诸如日志记录,身份验证,同步,验证,异常处理等交叉问题在非AOP系统中变得高度耦合,因为它们几乎被代码库中的每个组件/模块使用.
AOP定义了使用连接点,建议和切入点来抽象这些横切关注点的方面(类/方法).
一个.建议 - 实现跨领域关注(即进行实际记录,验证,验证等)的实际代码(方面的方法,可能?)
湾 加入点 - 在非AOP代码中触发的事件,导致特定方面的建议被执行("编织"到非AOP代码中)
C.切入点 - 基本上是连接点(触发事件)到建议执行的映射
所有方面都模块化(LoggingAspect,AuthenticationAspect,ValidationAspect等)到组件中并使用AspectWeaver注册.当非AOP/POJO代码遇到连接点时,AspectWeaver会围绕非AOP代码"编织"(集成)映射的建议:
public class LoggingAspect { // ... public void log(String msg) { ... } } public class ExceptionHandlingAspect { // .. public void handle(Exception exc) { ... } } public class NonAOPCode { // ... @LoggingAspect @ExceptionHandlingAspect public void foo() { // do some stuff... } } // Now in the driver public static int main void(String[] args) { NonAOPCode nonAOP = new NonAOPCode(); nonAOP.foo(); } // The AspectWeaver *magically* might weave in method calls so main now becomes: { NonAOPCode nonAOP = new NonAOPCode(); log(someMsg); nonAOP.foo(); handle(someExc); }
64,000美元的问题:我对基于Java的AOP的理解是针对目标还是关闭,为什么?如何正确使用注释来实现方面,建议,连接点,切入点和这个所谓的方面编织器?
gab*_*uzo 63
假设您想要使用@LogExecTime
注释记录一些带注释方法所花费的时间.
我首先创建一个注释LogExecTime
:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogExecTime {
}
Run Code Online (Sandbox Code Playgroud)
然后我定义了一个方面:
@Component // For Spring AOP
@Aspect
public class LogTimeAspect {
@Around(value = "@annotation(annotation)")
public Object LogExecutionTime(final ProceedingJoinPoint joinPoint, final LogExecTime annotation) throws Throwable {
final long startMillis = System.currentTimeMillis();
try {
System.out.println("Starting timed operation");
final Object retVal = joinPoint.proceed();
return retVal;
} finally {
final long duration = System.currentTimeMillis() - startMillis;
System.out.println("Call to " + joinPoint.getSignature() + " took " + duration + " ms");
}
}
}
Run Code Online (Sandbox Code Playgroud)
我创建了一个带有LogExecTime
以下内容的类:
@Component
public class Operator {
@LogExecTime
public void operate() throws InterruptedException {
System.out.println("Performing operation");
Thread.sleep(1000);
}
}
Run Code Online (Sandbox Code Playgroud)
主要使用Spring AOP:
public class SpringMain {
public static void main(String[] args) throws InterruptedException {
ApplicationContext context = new GenericXmlApplicationContext("applicationContext.xml");
final Operator bean = context.getBean(Operator.class);
bean.operate();
}
}
Run Code Online (Sandbox Code Playgroud)
如果我运行这个类,我在stdout上得到以下输出:
Starting timed operation
Performing operation
Call to void testaop.Operator.Operate() took 1044 ms
Run Code Online (Sandbox Code Playgroud)
现在带着魔力.正如我使用Spring AOP而不是AspectJ weaver,魔术是在运行时使用proxy-ish机制发生的.所以这些.class
文件保持不变.例如,如果我调试此程序并在其中放置一个断点,operate
您将看到Spring如何执行魔术:
由于Spring AOP实现是非侵入式的并且使用Spring机制,因此您需要添加@Component
注释并使用Spring上下文而不是plain来创建对象new
.
另一方面,AspectJ将更改.class
文件.我用AspectJ尝试了这个项目,用jad反编译了Operator类.导致:
public void operate()
throws InterruptedException
{
JoinPoint joinpoint = Factory.makeJP(ajc$tjp_0, this, this);
operate_aroundBody1$advice(this, joinpoint, LogTimeAspect.aspectOf(), (ProceedingJoinPoint)joinpoint, (LogExecTime)(ajc$anno$0 == null && (ajc$anno$0 = testaop/Operator.getDeclaredMethod("operate", new Class[0]).getAnnotation(testaop/LogExecTime)) == null ? ajc$anno$0 : ajc$anno$0));
}
private static final void operate_aroundBody0(Operator ajc$this, JoinPoint joinpoint)
{
System.out.println("Performing operation");
Thread.sleep(1000L);
}
private static final Object operate_aroundBody1$advice(Operator ajc$this, JoinPoint thisJoinPoint, LogTimeAspect ajc$aspectInstance, ProceedingJoinPoint joinPoint, LogExecTime annotation)
{
long startMillis = System.currentTimeMillis();
Object obj;
System.out.println("Starting timed operation");
ProceedingJoinPoint proceedingjoinpoint = joinPoint;
operate_aroundBody0(ajc$this, proceedingjoinpoint);
Object retVal = null;
obj = retVal;
long duration = System.currentTimeMillis() - startMillis;
System.out.println((new StringBuilder("Call to ")).append(joinPoint.getSignature()).append(" took ").append(duration).append(" ms").toString());
return obj;
Exception exception;
exception;
long duration = System.currentTimeMillis() - startMillis;
System.out.println((new StringBuilder("Call to ")).append(joinPoint.getSignature()).append(" took ").append(duration).append(" ms").toString());
throw exception;
}
private static void ajc$preClinit()
{
Factory factory = new Factory("Operator.java", testaop/Operator);
ajc$tjp_0 = factory.makeSJP("method-execution", factory.makeMethodSig("1", "operate", "testaop.Operator", "", "", "java.lang.InterruptedException", "void"), 5);
}
private static final org.aspectj.lang.JoinPoint.StaticPart ajc$tjp_0; /* synthetic field */
private static Annotation ajc$anno$0; /* synthetic field */
static
{
ajc$preClinit();
}
Run Code Online (Sandbox Code Playgroud)
Car*_*bés 12
几个月前,我写了一篇文章,其中有一个例子,说明我如何实现将Aspect/J方面与Java注释相结合的实际案例,您可能会发现它很有用:
http://technomilk.wordpress.com/2010/11/06/combining-annotations-and-aspects-part-1/
我相信应用于注释的方面是一个很好的组合,因为它们使代码中的方面更加明确,但是以一种干净的方式,您可以在注释中使用参数以获得进一步的灵活性.
顺便说一句,Aspect/J的工作方式是在编译时修改类,而不是在运行时修改类.您可以通过Aspect/J编译器运行源和方面,并创建修改后的类文件.
据我所知,Spring AOP以不同的方式编织(操作类文件以包含方面处理),通过创建代理对象,我相信在实例化时(但不要相信我的话) .
经过多次挖掘和肘部油脂后,我自己找到了答案......
是的,AOP应该是基于注释的Java世界,但是您无法处理与常规(元数据)注释等方面相关的注释.要在它之前/之后拦截标记的方法调用和"编织"建议方法,您需要一些非常漂亮的以AOP为中心的引擎(如AspectJ)的帮助.@Christopher McCann在另一个与注释相关的主题中提供了一个非常好的解决方案,他建议将AOP联盟与Google Guice结合使用.在阅读了Guice关于AOP支持的文档之后,这正是我正在寻找的:一个易于理解的框架,用于编织交叉问题的"建议"(方法调用),例如日志记录,验证,缓存,等等
这个是个笨蛋.
更改评论
// The AspectWeaver *magically* might weave in method calls so main now becomes
Run Code Online (Sandbox Code Playgroud)
到
// The AspectWeaver *magically* might weave in method calls so main now
// becomes effectively (the .class file is not changed)
Run Code Online (Sandbox Code Playgroud)
我喜欢 AOP 的 Spring 文章。查看第 7 章