简单的回答是:AOP并不是为了研究您的方法,因为方法一直都在被重构,应该被视为黑盒。
因此,无论是Spring AOP还是AspectJ,都不能满足您的期望。AOP的思想是实现横切关注点。日志记录只是其中一个问题。如果您认为需要方法内日志记录,仍然可以手动进行。但是,如果干净的代码对您有任何意义,您可以重构代码,使其更易于维护(也更便于日志记录)。方法应该简短,没有太多的输入参数,也没有太多的复杂性。
因此,您可以将复杂的意大利面代码方法分解为一组较小的方法,甚至可以提取新的类并从方法中使用这些类。我这样做是为了你的代码(见下文)。此外,返回0或-1或其他任何值而不是引发异常不是OOP,而是C风格的编程。因此,与其根据返回值记录问题,不如根据抛出的异常记录问题,并根据应用程序逻辑处理这些异常(或者让它们升级,如果出现致命错误)。我的示例代码也显示了这一点。
示例代码,迭代1
这个示例可以很好地与AspectJ配合使用,因为AspectJ不是基于委托动态代理的,因此在自调用方面没有问题,例如类内的内部方法调用。
package de.scrum_master.app;
public class UnexpectedResultException extends Exception {
private static final long serialVersionUID = 1L;
public UnexpectedResultException(String message) {
super(message);
}
}
如您所见,我从您复杂的方法中提取了一些方法。为了向您展示更多的日志输出,我甚至在
doSomething(..)
再次调用该方法,在for循环中多次执行复杂的操作。
package de.scrum_master.app;
import java.util.Random;
public class Application {
public void doSomething(String name, Object someObj) {
int result = new Random().nextInt(100);
for (int counter = 0; counter < 5; counter++) {
try {
result = doComplexThing(result + 1);
} catch (UnexpectedResultException unexpectedResultException) {
result = 4;
}
}
result = doSomeMoreWork(result);
otherBusinessCode(result);
}
public int doComplexThing(int input) throws UnexpectedResultException {
if (input % 2 == 0)
throw new UnexpectedResultException("uh-oh");
return input % 5;
}
public int doSomeMoreWork(int input) {
return input * input;
}
public void otherBusinessCode(int input) {}
public static void main(String[] args) {
Application application = new Application();
application.doSomething("John Doe", new Integer(11));
}
}
日志方面可能如下所示:
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class LoggingAspect {
@Pointcut("within (de.scrum_master.app..*) && execution(* *(..))")
private void loggingTargets() {}
@Before("loggingTargets()")
public void logEnterMethod(JoinPoint thisJoinPoint) {
System.out.println("ENTER " + thisJoinPoint);
}
@AfterReturning(pointcut = "loggingTargets()", returning = "result")
public void logExitMethod(JoinPoint thisJoinPoint, Object result) {
System.out.println("EXIT " + thisJoinPoint + " -> return value = " + result);
}
@AfterThrowing(pointcut = "loggingTargets()", throwing = "exception")
public void logException(JoinPoint thisJoinPoint, Exception exception) {
System.out.println("ERROR " + thisJoinPoint + " -> exception = " + exception);
}
}
控制台日志如下所示:
ENTER execution(void de.scrum_master.app.Application.main(String[]))
ENTER execution(void de.scrum_master.app.Application.doSomething(String, Object))
ENTER execution(int de.scrum_master.app.Application.doComplexThing(int))
ERROR execution(int de.scrum_master.app.Application.doComplexThing(int)) -> exception = de.scrum_master.app.UnexpectedResultException: uh-oh
ENTER execution(int de.scrum_master.app.Application.doComplexThing(int))
EXIT execution(int de.scrum_master.app.Application.doComplexThing(int)) -> return value = 0
ENTER execution(int de.scrum_master.app.Application.doComplexThing(int))
EXIT execution(int de.scrum_master.app.Application.doComplexThing(int)) -> return value = 1
ENTER execution(int de.scrum_master.app.Application.doComplexThing(int))
ERROR execution(int de.scrum_master.app.Application.doComplexThing(int)) -> exception = de.scrum_master.app.UnexpectedResultException: uh-oh
ENTER execution(int de.scrum_master.app.Application.doComplexThing(int))
EXIT execution(int de.scrum_master.app.Application.doComplexThing(int)) -> return value = 0
ENTER execution(int de.scrum_master.app.Application.doSomeMoreWork(int))
EXIT execution(int de.scrum_master.app.Application.doSomeMoreWork(int)) -> return value = 0
ENTER execution(void de.scrum_master.app.Application.otherBusinessCode(int))
EXIT execution(void de.scrum_master.app.Application.otherBusinessCode(int)) -> return value = null
EXIT execution(void de.scrum_master.app.Application.doSomething(String, Object)) -> return value = null
EXIT execution(void de.scrum_master.app.Application.main(String[])) -> return value = null
正如您所看到的,基于新的、更模块化的方法结构,您可以在最初的问题中获得您想要的所有日志记录。旧方法变得更具可读性,新方法更简单,因为它们专注于完成您提取它们的目的。
请注意:
此示例代码是使用AspectJ运行的,而不是使用“AOP lite”框架Spring AOP。因此,在春季AOP中,它不会像这样工作,因为:
-
Spring AOP不能处理内部方法调用(自调用),正如我前面所说的,并在
Spring manual
.
-
Spring AOP也不会记录静态方法,例如
main
因为它只能截获公共的、非静态的接口方法(当使用Java动态代理时)或其他受保护的和包作用域的方法(当使用CGLIB代理时)。
因此,如果您考虑将代码重构为我建议的内容,并考虑将一些助手方法设置为私有,但仍希望将它们记录下来,那么除了将Spring配置为使用full
AspectJ via LTW (load-time weaving)
以使用AOP的全部功能。
示例代码,迭代2
如果您希望坚持使用Spring AOP及其代理,但仍然需要通过AOP记录的那些内部调用的方法,那么您需要在重构过程中更进一步,将这三个新方法提取到一个额外的Spring组件/bean中,并将其连接到您的应用程序中。然后,方法调用将不再是内部的,而是跨越组件/bean边界,从而被Spring AOP日志方面拦截。
这个
Application
worker methods类将按如下方式提取和调用:
package de.scrum_master.app;
// Make this into a @Component
public class MyWorker {
public int doComplexThing(int input) throws UnexpectedResultException {
if (input % 2 == 0)
throw new UnexpectedResultException("uh-oh");
return input % 5;
}
public int doSomeMoreWork(int input) {
return input * input;
}
public void otherBusinessCode(int input) {}
}
package de.scrum_master.app;
import java.util.Random;
public class Application {
// In a Spring context this would be injected via configuration
private MyWorker worker = new MyWorker();
public void doSomething(String name, Object someObj) {
int result = new Random().nextInt(100);
for (int counter = 0; counter < 5; counter++) {
try {
result = worker.doComplexThing(result + 1);
} catch (UnexpectedResultException unexpectedResultException) {
result = 4;
}
}
result = worker.doSomeMoreWork(result);
worker.otherBusinessCode(result);
}
public static void main(String[] args) {
Application application = new Application();
application.doSomething("John Doe", new Integer(11));
}
}
该方面可以保持不变。
日志输出更改如下:
ENTER execution(void de.scrum_master.app.Application.main(String[]))
ENTER execution(void de.scrum_master.app.Application.doSomething(String, Object))
ENTER execution(int de.scrum_master.app.MyWorker.doComplexThing(int))
EXIT execution(int de.scrum_master.app.MyWorker.doComplexThing(int)) -> return value = 2
ENTER execution(int de.scrum_master.app.MyWorker.doComplexThing(int))
EXIT execution(int de.scrum_master.app.MyWorker.doComplexThing(int)) -> return value = 3
ENTER execution(int de.scrum_master.app.MyWorker.doComplexThing(int))
ERROR execution(int de.scrum_master.app.MyWorker.doComplexThing(int)) -> exception = de.scrum_master.app.UnexpectedResultException: uh-oh
ENTER execution(int de.scrum_master.app.MyWorker.doComplexThing(int))
EXIT execution(int de.scrum_master.app.MyWorker.doComplexThing(int)) -> return value = 0
ENTER execution(int de.scrum_master.app.MyWorker.doComplexThing(int))
EXIT execution(int de.scrum_master.app.MyWorker.doComplexThing(int)) -> return value = 1
ENTER execution(int de.scrum_master.app.MyWorker.doSomeMoreWork(int))
EXIT execution(int de.scrum_master.app.MyWorker.doSomeMoreWork(int)) -> return value = 1
ENTER execution(void de.scrum_master.app.MyWorker.otherBusinessCode(int))
EXIT execution(void de.scrum_master.app.MyWorker.otherBusinessCode(int)) -> return value = null
EXIT execution(void de.scrum_master.app.Application.doSomething(String, Object)) -> return value = null
EXIT execution(void de.scrum_master.app.Application.main(String[])) -> return value = null