浅入java架构-Aspect-日志处理
很多的架构师,或者高级程序员在面试新人的时候,都会在架构方面问一个非常常见且十分经典的知识:那就是怎样设计系统日志输出程序。毫无疑问,面试官想了解面试者的spring中aspect的编程思想及在程序的运用,从而了解面试者在以前的项目当中所担任的项目角色和所做的事情范围。
闲话少说,今天笔者就spring中aspect,也就是面向切面编程,与广大java爱好者一起探讨下,在项目中日志打印的设计;
六个步骤即可完成日志打印:
一、定义注解(annocation):AutoLogMethod
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
@Component
public @interface AutoLogMethod {
String value() default "";
}
二、定义注解(annocation):MethodParam
@Retention(RetentionPolicy.RUNTIME)
@Target({java.lang.annotation.ElementType.PARAMETER})
@Documented
@Component
public @interface MethodParam{
String value() default "param->";
}
三、创建日志输入工具类:LogUtil
public class LogUtil {
private static final Logger logger = LoggerFactory.getLogger(LogUtil.class);
public static void error(final String message) {
if (logger != null) {
logger.error(message);
} else {
System.err.printf("ERROR: %s\n", message);
}
}
public static void error(final String message, final Throwable t) {
if (logger != null) {
logger.error(message, t);
} else {
String err = message + "\r\n" + ExceptionUtils.getRootCauseMessage(t);
System.err.printf("ERROR: %s\n", err);
}
}
public static void error(String message, Throwable t, Object... obj) {
String err = String.format(message, obj) ;
error(err,t);
}
public static void info(final String message) {
if (logger != null) {
logger.info(message);
} else {
System.err.printf("INFO: %s\n", message);
}
}
public static void info(String message, Object... obj) {
info(String.format(message, obj));
}
}
四、创建切面(Aspect):LogAspect
(1)、引入Aspectj所需相关jar支持
pom.xml
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.12</version>
</dependency>
(2)、spring管理启动Aspectj,这只自动启动
<aop:aspectj-autoproxy proxy-target-class="true"/>
(3)、创建切面(Aspect):LogAspect
注意:因为是日志打印处理,所以给该切面的优先级设置最高,即@Order(-1)
@Aspect
@Component
@Order(-1)
public class LogAspect {
@Pointcut("@annotation(com.jd.jr.hd.jsf.common.annocation.AutoLogMethod)")
public void autoLog() {
}
@Around(value = "autoLog()")
public Object aroundLog(ProceedingJoinPoint pjp) throws Throwable {
Field filed = pjp.getClass().getDeclaredField("methodInvocation");
filed.setAccessible(true);
Object methodInvocation = (Object) filed.get(pjp);
Method method = methodInvocation.getClass().getMethod("getMethod");
Method m = (Method) method.invoke(methodInvocation);
String mn = pjp.getTarget().getClass().getName() + '.' + m.getName();
AutoLogMethod autoLogMethod = m.getAnnotation(AutoLogMethod.class);
StringBuffer sb = new StringBuffer();
if (autoLogMethod != null) {
Annotation annotation[][] = m.getParameterAnnotations();
for (int i = 0; i < pjp.getArgs().length; i++) {
String paramName = null;
for (Annotation a : annotation[i]) {
if (a.annotationType() == MethodParam.class) {
paramName = ((MethodParam) a).value();
break;
}
}
if (paramName != null) {
sb.append('[').append(paramName);
sb.append(':');
sb.append(pjp.getArgs()[i] == null ? "" : pjp.getArgs()[i].toString()).append(']');
}
}
LogUtil.info("autoLog %s --> %s请求 %s", mn, autoLogMethod == null ? "" : autoLogMethod.value(), sb.toString());
}
try {
Object retVal = pjp.proceed();
LogUtil.info("autoLog %s --> %s响应 %s", mn, autoLogMethod == null ? "" : autoLogMethod.value(), retVal);
return retVal;
} catch (Throwable t) {
LogUtil.error(String.format("autoLog %s --> %s异常 %s", mn, autoLogMethod == null ? "" : autoLogMethod.value(), sb.toString()), t);
throw t;
}
}
}
五、创建一个服务供测试调用:LogService
@Service
public class LogService {
@AutoLogMethod(value = "autoLogTest")
public void autoLogTest(@MethodParam("参数") LoginReq req) throws ServiceException {
System.out.println("----------------------autoLogTest打印成功!-----------------------");
}
public String logTest(String name) {
System.out.println("----------------------logTest打印成功!-----------------------");
return name+"返回了";
}
}
六、创建JUnit测试启动类:LogTest.autoLogtest()
@Service
public class LogService {
@AutoLogMethod(value = "autoLogTest")
public void autoLogTest(@MethodParam("参数") LoginReq req) throws ServiceException {
if("".equals(req.getUserId())) {
throw new ServiceException("-1", "用户名不能为空");
}
if("".equals(req.getPassword())) {
throw new ServiceException("-2", "密码不能为空");
}
System.out.println("----------------------autoLogTest打印成功!-----------------------");
}
public String logTest(String name) {
System.out.println("----------------------logTest打印成功!-----------------------");
return name+"返回了";
}
}