
一、证明
近期,心头萌生了做一个个人官网的愿望,来一场说走就走的编程之旅。说做就做,在项目框架构建(SpringMVC+mybatis+mysql)好了以后,直到考虑项目中日志的设计。经过考虑并结合网上的档案,决定采用注解的手段来记录访问日志。当然,当前的日记设计还不够完美,中期会在开发的过程中随之优化。
二、实现
2.1 关于AOP及相关注解
比较于AOP,有很偏向于使用拦截器来管理日记,这点要看个人的愿望了。那么怎么实现AOP拦截controller呢?由于默认的现象下,controller是交给jdk去代理的,即使,要想AOP能够拦截到controller,必须将其指定给cglib代理。
上面介绍一下,使用AOP拦截controller用到的注解(标红字段代表将要使用),一定,我们也可以使用配置文档的手段去定义,反而个人更喜欢将系统集中在一起,找配置文档真的很累~
@Target:注解的效果目标,即注解会对哪些对象造成效果。包括:
ElementType.TYPE接口、类、枚举、注解
ElementType.FIELD字段、枚举的常量

ElementType.METHOD方法
ElementType.PARAMETER方法里的取值
ElementType.CONSTRUCTOR构造函数
ElementType.LOCAL_VARIABLE局部变量
ElementType.ANNOTATION_TYPE注解
ElementType.PACKAGE包
@Retention:注解的保留位置,用来描述注解的生命周期,通俗的讲,@Retention注解负责定义该注解在多少范围内或条件下就会去造成效果。
RetentionPolicy.SOURCE注解仅存在于代码中,在class字节码文件中不包括
RetentionPolicy.CLASS默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得

RetentionPolicy.RUNTIME注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Document:证明该注解将被涵盖在javadoc中
以解,再加上@Inherited、@Repeatable注解,被称为java中的元注解。什么是元注解?
元注解的效果就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,这些被用于提供对其余 annotation类型作说明。
关于注解及元注解的表述springmvc日志处理,请点击这儿
@Aspect:当@Aspect声明与类上时,显示这个类将会成为一个切面,即切面类。此时容器就可以调用到这个类,但前提是开启了cglib代理。
@Component:将类声明为bean并注入到容器中,spring在启动时会扫描并装载。与在顶配文档中定义bean的作用相似。
@Pointcut:原理级别的注解,使用此注解后,可被这些办法引用。
与@Aspect、@Pointcut一起使用的还有5种通知型注解,也叫增强型注解:

@Before前置通知,在原理执行前执行
@After后置通知
@AfterReturning后置【try】通知,放置方法头上,使用returning来引用方式返回值
@AfterThrowing后置【catch】通知,放置方法头上,使用throwing来引用抛出的异常
@Around环绕通知,放置方法头上,这个原理要决定真实的原理是否执行,不过必须有返回值
2.2 配置cglib代理
在spring-mvc.xml文档中加入如下代码:
<aop:aspectj-autoproxy proxy-target-class="true" />
2.3 自定义注解,用来描述日志信息

一般我们在创建自定义注解时springmvc日志处理,使用@interface会使此类默认继承annotation。代码如下:
package com.t1heluosh1.system.log.annotion;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 日志controller层注解
*
* @author xuyong
*
*/
//作用于参数和方法上
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysControllerLog {
int logType() default 100; //日志类型,默认为100-系统
int module() default 100; //操作模块,默认为100-登录
String description() default ""; //操作描述
}
自定义注解创建后,我们需要将注解作为bean注入到容器中,在spring-mvc.xml文档中加入如下代码:
<context:component-scan base-package="com.t1heluosh1.system.log" >
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
2.4 定义切面类,实现记录日志的系统
这里没有什么可以多说的,直接看源码:
package com.t1heluosh1.system.log.aspect;
import java.lang.reflect.Method;
import java.util.Date;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.t1heluosh1.pullulate.biz.sysLog.service.SysLogService;
import com.t1heluosh1.pullulate.biz.sysLog.vo.SysLogVo;
import com.t1heluosh1.pullulate.biz.user.model.User;
import com.t1heluosh1.system.constant.SysParams;
import com.t1heluosh1.system.log.annotion.SysControllerLog;
import com.t1heluosh1.util.IPUtil;
/**
* 日志切点类即实现类
*
* @author xuyong
*
*/
@Aspect
@Component
public class SysLogAspect {
//本地异常日志记录对象
private static final Logger logger = Logger.getLogger(SysLogAspect.class);
@Resource
private SysLogService logService;
//切入点定义:controller
@Pointcut("@annotation(com.t1heluosh1.system.log.annotion.SysControllerLog)")
public void controllerAspect() {
System.out.println("---------------------controllerAspect for log start----------------------------");
}
/**
* controller切入点方法实现
*
* @param joinPoint
*/
@Before("controllerAspect()")
public void doBefore(JoinPoint joinPoint) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
.getRequestAttributes()).getRequest();
//获取登录用户的信息
User user = (User)request.getSession().getAttribute(SysParams.CURRENT_USER);
//获取请求IP地址
String ip = IPUtil.getRemoteHost(request);
try {
String methodDesc = getControllerMethodDescription(joinPoint);
System.out.println("request method : " + joinPoint.getTarget().getClass().getName()+"."+joinPoint.getSignature().getName()+"()");
System.out.println("method description : " + methodDesc);
System.out.println("request username : " + (user==null?"no login info found":user.getUserName()));
System.out.println("request ip address : "+ ip);
System.out.println("insert log infos into db start ...");
//获取相关日志参数
Object[] orgs = joinPoint.getArgs();
SysLogVo sysLogVo = null;
if (orgs != null && orgs.length > 0) {
for (Object obj:orgs) {
if (obj instanceof SysLogVo)
sysLogVo = (SysLogVo)obj;
}
}
if (sysLogVo == null) {
sysLogVo = new SysLogVo();
}
//执行日志入库操作
//获取注解的信息
MethodSignature ms = (MethodSignature)joinPoint.getSignature();
Method method = ms.getMethod();
SysControllerLog log = method.getAnnotation(SysControllerLog.class);
sysLogVo.setLogType(log.logType());
sysLogVo.setModule(log.module());
sysLogVo.setIpAddr(ip);
sysLogVo.setUrl(request.getRequestURI());
sysLogVo.setMethodName(joinPoint.getTarget().getClass().getName()+"."+joinPoint.getSignature().getName()+"()");
sysLogVo.setMethodDesc(methodDesc);
//TODO:remark可根据业务来进行改变,暂时为方法描述
sysLogVo.setRemark(log.description());
Date date = new Date();
sysLogVo.setAddTime(date);
sysLogVo.setAddUser(user==null?SysParams.ADMIN_ID:String.valueOf(user.getId()));
sysLogVo.setUpdateTime(date);
sysLogVo.setUpdateUser(user==null?SysParams.ADMIN_ID:String.valueOf(user.getId()));
logService.save(sysLogVo);
System.out.println("insert log infos into db successful.");
} catch (Exception e) {
logger.error("--------------controllerAspect for log fail-----------------------");
logger.error("exception info : ", e);
}
}
/**
* 获取方法的描述
*
* @param joinPoint
* @return
* @throws Exception
*/
@SuppressWarnings("rawtypes")
private String getControllerMethodDescription(JoinPoint joinPoint) throws Exception {
//获取目标类名
String targetName = joinPoint.getTarget().getClass().getName();
//获取方法名
String methodName = joinPoint.getSignature().getName();
//获取相关参数
Object[] arguments = joinPoint.getArgs();
//生成类对象
Class targetClass = Class.forName(targetName);
//获取该类中的方法
Method[] methods = targetClass.getMethods();
String description = "";
for(Method method : methods) {
if(!method.getName().equals(methodName)) {
continue;
}
Class[] clazzs = method.getParameterTypes();
if(clazzs.length != arguments.length) {
continue;
}
description = method.getAnnotation(SysControllerLog.class).description();
}
return description;
}
}
2.5 使用demo
具体使用的原理如下:
/**
* 跳转到登陆页面
*
* @param request
* @return
* @throws Exception
*/
@RequestMapping(value="login")
@SysControllerLog(description="跳转到登录页面",logType=100,module=100)
public ModelAndView gotoLogin(HttpServletRequest request) {
ModelAndView modelAndView = new ModelAndView("/show/login");
return modelAndView;
}
当用户刷新按钮时,控制台会打印相关的访问资料并将它们资料入库。当然,日记的应用需要按照项目来决定,每位方法前都加入注解,一是影响功能的型号,且导致访问日志的作用大打折扣;二是这样手段记录日志,对于代码还是有一定的侵入性的。最后看一下记录的资料:

以下就是java开发实战之springMVC使用AOP实现访问日志的管理的详实内容,更多请关注php中文网其它相关标题!
本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/jisuanjixue/article-119313-1.html
俺们成熟了
谢作诗这厮脑袋进水了吧