前言:曾一直想拥有自己的的博客,将自己对java的感悟记录下来,由于时间原因一直没有行动,这是我的第一次随笔,好的话关注一下,谢谢。
创建@Logger注解的作用
1.封装一些常用的日志记录以及统计功能
2.可以自定义记录方法,解耦,脱离传统的业务方法里面记录日志
@Logger注解自定义之前我们需要用到以下技术:
1.java反射机制 2.动态代理 3.Spring AOP(实际aop的实现可以归结动态代理)
对以下技术做一个简单的概括:
**(1).java反射:
反射是运行中的程序检查自己和软件运行环境的能力,它可以根据它发现的进行改变。通俗的讲就是反射可以在运行时根据指定的类名获得类的信息。
(2)动态代理:动态代理的实现也是根据反射实现,可以说优秀的代码设计,都离不开反射。(3)Spring Aop:spring框架提供的其中之一模块就是Aop,它是对OOP的一个补充,OOP是面向对象,它则是面向切面。
Aop的应用场景:
流量统计 日志记录 程序监控 归结一点:一切和业务逻辑无关的,都可以用AOP来完成。**
说了这么多,接下来我们具体来使用自定义注解,将以上的类容全部运用一下:
(该注解目前只实现了一些最基本的记录,后面会继续完善)
首先定义一个@Logger注解:
package com.ph.licenceserver.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 日志注解类,提供简单的日志操作功能
* @author LT
*
*/
@Target({PARAMETER,METHOD})
@Retention(RUNTIME)
@Documented
public @interface Logger {
String value() default "";//设置打印信息
String type() default Constant.INFO;//设置打印类型
boolean isPrintTimeStamp() default true;//是否答应timestamp类型的时间,主要用于性能测试
boolean EnableIpAddress() default true;//是否开启ip记录功能
boolean EnableURL() default true;//是否开启url记录功能
boolean EnableRequestParmas() default true;//请求参数记录
boolean EnableSigar() default false;//开启监控功能
}
创建一个注解的处理类(Handle):
package com.ph.licenceserver.AnnotationHandle;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.JoinPoint;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.ph.licenceserver.Annotation.Logger;
/**
* 注解处理类
*
* @author LT
*
*/
public class LoggerHandles {
String value = "";
String type = "INFO";
boolean timestampe;
boolean address;
boolean url;
boolean sigar;
boolean requestParmas;
JoinPoint joinPoint;
org.apache.log4j.Logger logger;
public void setLoggerInfo(Class> clazz,JoinPoint joinPoint) {
this.joinPoint=joinPoint;
logger = org.apache.log4j.Logger.getLogger(clazz);//获取日志对象
Method[] methods = clazz.getMethods();获取所有切入点所在类的所有方法
for (Method method : methods) {//遍历切入方法所在类的所有成员方法,是否有Logger.class类型的注解并获取它的值
if (method.isAnnotationPresent(Logger.class)) {
value = method.getAnnotation(Logger.class).value();
type = method.getAnnotation(Logger.class).type();
timestampe = method.getAnnotation(Logger.class)
.isPrintTimeStamp();
address = method.getAnnotation(Logger.class).EnableIpAddress();
url = method.getAnnotation(Logger.class).EnableURL();
sigar = method.getAnnotation(Logger.class).EnableSigar();
requestParmas = method.getAnnotation(Logger.class)
.EnableRequestParmas();
}
}
}
public void LoggerHandle() {
if ("ERROR".equals(type)) {
logger.error(value);
setLoggerMessage(type,joinPoint);
} else if ("INFO".equals(type))
logger.info(value);
setLoggerMessage(type,joinPoint);
}
public void setLoggerMessage(String value,JoinPoint joinPoint) {
System.out.println(value);
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
Method method;
/**
利用反射,实现方法的动态获取,如果这里不用method.invoke()方法,logger的info和error方法就要进行大量的判断
*/
try {
method = this.logger.getClass().getMethod(value.toLowerCase(),Object.class);
method.invoke(this.logger, value);
if (timestampe) {
method.invoke(this.logger,"当前时间戳:" + System.currentTimeMillis());
}
if (address) {
method.invoke(this.logger,"IpAddress:" + request.getRemoteAddr());
}
if (url) {
method.invoke(this.logger,"URL:" + request.getRequestURL().toString());
}
if (requestParmas) {
method.invoke(this.logger,"HTTP_METHOD:"+request.getMethod());
method.invoke(this.logger,"CLASS_METHOD : "
+ joinPoint.getSignature().getDeclaringTypeName() + "."
+ joinPoint.getSignature().getName());
@SuppressWarnings("unchecked")
Enumeration enu = request.getParameterNames();
String message = "";
while (enu.hasMoreElements()) {
String paraName = (String) enu.nextElement();
message = (paraName + ": " + request.getParameter(paraName));
}
method.invoke(this.logger,message);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();//这里就直接打印,不进行处理了,明白就行
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
/*
* if(timestampe){ logger.error("当前时间戳:"+System.currentTimeMillis()); }
* if(address){ logger.error("IpAddress:"+request.getRemoteAddr()); }
* if(url){ logger.error("URL:"+request.getRequestURL().toString()); }
* if(requestParmas){
*
* @SuppressWarnings("unchecked") Enumeration enu =
* request.getParameterNames(); String message=""; while
* (enu.hasMoreElements()) { String paraName = (String)
* enu.nextElement(); message=(paraName + ": " +
* request.getParameter(paraName)); } logger.error(message); }
*/
}
public String DateNow(){
return new SimpleDateFormat("yyyy-MM-dd").format(new Date());
}
}
创建spring Aop:
package com.ph.licenceserver.Aop;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Enumeration;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.ph.licenceserver.AnnotationHandle.LoggerHandles;
@Aspect
@Component
public class LoggingAspect {
private Logger logger = Logger.getLogger(this.getClass());
// 对以下com.web.controller.Controller类中的所有方法进行切入
public static Long starttime = null;
@Pointcut("execution(public * com.ph.licenceserver.web.*.*(..))")//利用表达式定义需要切入的点
private void method() {
}
@After("method()")
//在切入方法执行后执行该方法
public void recordLog(JoinPoint joinPoint) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String targetName = joinPoint.getTarget().getClass().getName();//获取连接点躲在目标对象的类信息的类名
String methodName = joinPoint.getSignature().getName();//获取连接点的方法签名对象的方法名
Class targetClass;
try {
targetClass = Class.forName(targetName);//获得类名对应的类
Object[] arguments = joinPoint.getArgs();//或得连接点方法传入的参数
Method[] methods = targetClass.getMethods();//得到方法名
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Class[] clazzs = method.getParameterTypes();
if (clazzs.length == arguments.length) {
try {
LoggerHandles handle=LoggerHandles.class.newInstance();
handle.setLoggerInfo(joinPoint.getTarget().getClass(),joinPoint);
handle.LoggerHandle();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
break;
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
@AfterReturning("method()")
public void doAfterReturning(JoinPoint joinPoint) {
Long time = System.currentTimeMillis() - starttime;
logger.info("用时:" + time.toString() + "毫秒");
}
}
使用自定义注解运用在之前定义的controller类上(以下涉及到隐私内容所以我将方法体中的内容去掉了,不影响日志记录结果)
@RequestMapping("/all.do")
@ResponseBody
@com.ph.licenceserver.Annotation.Logger(value="执行了logger注解",type="INFO")
public PageUtil getCountAll(@RequestBody Query query,HttpServletRequest request){
}
来查看一下crontroller接受请求之后有@Logger注解有没有输出相应类容(刷新之后):
以上就是使用创建注解的全过程,可能有人要问了,为什么要使用这个注解,直接利用spring AOP来定义切面,在@Before注解的方法或@Around注解所在方法中直接实现日志记录不是也可以吗?
确实可以,但是使用注解的方式我们可以配置我们想配置的方法,只需要使用@Pointcut定义切入点,在切入点所在方法之内配置@Logger注解,该注解就会生效。利用xml配置的形式配置aop更方便,不用操作具体的类
可能也有人会问你所用实现的注解,注解的实现,是利用sping aop的动态代理,为何不自己脱离spring AOP自己利用动态代理来实现注解的功能呢,这样岂不解耦吗?
是的,脱离spring AOP来自己实现注解功能更好,所以在下一篇文章中,我将会分析一下spring AOP底层动态代理的实现,以及如何脱离AOP来实现该注解。
本文中有什么不妥的地方希望大家指出
QQ:1336576735 欢迎大家加我,共同学习