springAOP自定义注解方式实现日志管理

王朝学院·作者佚名  2016-08-27
窄屏简体版  字體: 小  |  中  |  大  |  超大  

今天继续实现AOP,到这里我个人认为是最灵活,可扩展的方式了,就拿日志管理来说,用SPRing AOP 自定义注解形式实现日志管理。废话不多说,直接开始!!!

关于配置我还是的再说一遍。

在applicationContext-mvc.xml中要添加的

<mvc:annotation-driven />

<!-- 激活组件扫描功能,在包com.gcx及其子包下面自动扫描通过注解配置的组件 -->

<context:component-scan base-package="com.gcx" />

<!-- 启动对@aspectJ注解的支持 -->

<!-- proxy-target-class等于true是强制使用cglib代理,proxy-target-class默认是false,如果你的类实现了接口 就走JDK代理,如果没有,走cglib代理 -->

<!-- 注:对于单利模式建议使用cglib代理,虽然JDK动态代理比cglib代理速度快,但性能不如cglib -->

<!--如果不写proxy-target-class="true"这句话也没问题-->

<aop:aspectj-autoproxy proxy-target-class="true"/>

<!--切面-->

<bean id="systemLogAspect" class="com.gcx.annotation.SystemLogAspect"></bean>

接下来开始编写代码。

创建日志类实体

1publicclassSystemLog {2privateString id;34privateString description;56privateString method;78privateLong logType;910privateString requestIp;1112privateString exceptioncode;1314privateString exceptionDetail;1516privateString params;1718privateString createBy;1920privateDate createDate;2122publicString getId() {23returnid;24}2526publicvoidsetId(String id) {27this.id = id ==null?null: id.trim();28}2930publicString getDescription() {31returndescription;32}3334publicvoidsetDescription(String description) {35this.description = description ==null?null: description.trim();36}3738publicString getMethod() {39returnmethod;40}4142publicvoidsetMethod(String method) {43this.method = method ==null?null: method.trim();44}4546publicLong getLogType() {47returnlogType;48}4950publicvoidsetLogType(Long logType) {51this.logType =logType;52}5354publicString getRequestIp() {55returnrequestIp;56}5758publicvoidsetRequestIp(String requestIp) {59this.requestIp = requestIp ==null?null: requestIp.trim();60}6162publicString getExceptioncode() {63returnexceptioncode;64}6566publicvoidsetExceptioncode(String exceptioncode) {67this.exceptioncode = exceptioncode ==null?null: exceptioncode.trim();68}6970publicString getExceptionDetail() {71returnexceptionDetail;72}7374publicvoidsetExceptionDetail(String exceptionDetail) {75this.exceptionDetail = exceptionDetail ==null?null: exceptionDetail.trim();76}7778publicString getParams() {79returnparams;80}8182publicvoidsetParams(String params) {83this.params = params ==null?null: params.trim();84}8586publicString getCreateBy() {87returncreateBy;88}8990publicvoidsetCreateBy(String createBy) {91this.createBy = createBy ==null?null: createBy.trim();92}9394publicDate getCreateDate() {95returncreateDate;96}9798publicvoidsetCreateDate(Date createDate) {99this.createDate =createDate;100}101}

View Code

编写dao接口

1packagecom.gcx.dao;23importcom.gcx.entity.SystemLog;45publicinterfaceSystemLogMapper {6intdeleteByPrimaryKey(String id);78intinsert(SystemLog record);910intinsertSelective(SystemLog record);1112SystemLog selectByPrimaryKey(String id);1314intupdateByPrimaryKeySelective(SystemLog record);1516intupdateByPrimaryKey(SystemLog record);17}

View Code

编写service层

1packagecom.gcx.service;23importcom.gcx.entity.SystemLog;45publicinterfaceSystemLogService {67intdeleteSystemLog(String id);89intinsert(SystemLog record);1011intinsertTest(SystemLog record);1213SystemLog selectSystemLog(String id);1415intupdateSystemLog(SystemLog record);16}

View Code

编写service实现类serviceImpl

1packagecom.gcx.service.impl;23importjavax.annotation.Resource;45importorg.springframework.stereotype.Service;67importcom.gcx.annotation.Log;8importcom.gcx.dao.SystemLogMapper;9importcom.gcx.entity.SystemLog;10importcom.gcx.service.SystemLogService;1112@Service("systemLogService")13publicclassSystemLogServiceImplimplementsSystemLogService {1415@Resource16privateSystemLogMapper systemLogMapper;1718@Override19publicintdeleteSystemLog(String id) {2021returnsystemLogMapper.deleteByPrimaryKey(id);22}2324@Override2526publicintinsert(SystemLog record) {2728returnsystemLogMapper.insertSelective(record);29}3031@Override32publicSystemLog selectSystemLog(String id) {3334returnsystemLogMapper.selectByPrimaryKey(id);35}3637@Override38publicintupdateSystemLog(SystemLog record) {3940returnsystemLogMapper.updateByPrimaryKeySelective(record);41}4243@Override44publicintinsertTest(SystemLog record) {4546returnsystemLogMapper.insert(record);47}4849}

View Code

到这里基本程序编写完毕

下面开始自定义注解

1packagecom.gcx.annotation;23importjava.lang.annotation.*;45@Target({ElementType.PARAMETER, ElementType.METHOD})6@Retention(RetentionPolicy.RUNTIME)7@Documented8public@interfaceLog {910/**要执行的操作类型比如:add操作 **/11publicStringOperationType()default"";1213/**要执行的具体操作比如:添加用户 **/14publicString operationName()default"";15}

下面编写切面

1packagecom.gcx.annotation;23importjava.lang.reflect.Method;4importjava.util.Date;5importjava.util.UUID;67importjavax.annotation.Resource;8importjavax.servlet.http.HttpServletRequest;9importjavax.servlet.http.Httpsession;1011importorg.aspectj.lang.JoinPoint;12importorg.aspectj.lang.ProceedingJoinPoint;13importorg.aspectj.lang.annotation.After;14importorg.aspectj.lang.annotation.AfterReturning;15importorg.aspectj.lang.annotation.AfterThrowing;16importorg.aspectj.lang.annotation.Around;17importorg.aspectj.lang.annotation.Aspect;18importorg.aspectj.lang.annotation.Before;19importorg.aspectj.lang.annotation.Pointcut;20importorg.slf4j.Logger;21importorg.slf4j.LoggerFactory;22importorg.springframework.stereotype.Component;2324importcom.gcx.entity.SystemLog;25importcom.gcx.entity.User;26importcom.gcx.service.SystemLogService;27importcom.gcx.util.JsonUtil;2829/**30*@author杨建31* @E-mail: email32*@version创建时间:2015-10-19 下午4:29:0533* @desc 切点类34*/3536@Aspect37@Component38publicclassSystemLogAspect {3940//注入Service用于把日志保存数据库41@Resource//这里我用resource注解,一般用的是@Autowired,他们的区别如有时间我会在后面的博客中来写42privateSystemLogService systemLogService;4344privatestaticfinalLogger logger = LoggerFactory.getLogger(SystemLogAspect.class);4546//Controller层切点47@Pointcut("execution (* com.gcx.controller..*.*(..))")48publicvoidcontrollerAspect() {49}5051/**52* 前置通知 用于拦截Controller层记录用户的操作53*54*@paramjoinPoint 切点55*/56@Before("controllerAspect()")57publicvoiddoBefore(JoinPoint joinPoint) {58System.out.println("==========执行controller前置通知===============");59if(logger.isInfoEnabled()){60logger.info("before " +joinPoint);61}62}6364//配置controller环绕通知,使用在方法aspect()上注册的切入点65@Around("controllerAspect()")66publicvoidaround(JoinPoint joinPoint){67System.out.println("==========开始执行controller环绕通知===============");68longstart =System.currentTimeMillis();69try{70((ProceedingJoinPoint) joinPoint).proceed();71longend =System.currentTimeMillis();72if(logger.isInfoEnabled()){73logger.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms!");74}75System.out.println("==========结束执行controller环绕通知===============");76}catch(Throwable e) {77longend =System.currentTimeMillis();78if(logger.isInfoEnabled()){79logger.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms with exception : " +e.getMessage());80}81}82}8384/**85* 后置通知 用于拦截Controller层记录用户的操作86*87*@paramjoinPoint 切点88*/89@After("controllerAspect()")90publicvoidafter(JoinPoint joinPoint) {9192/*HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();93HttpSession session = request.getSession();*/94//读取session中的用户95//User user = (User) session.getAttribute("user");96//请求的IP97//String ip = request.getRemoteAddr();98User user =newUser();99user.setId(1);100user.setName("张三");101String ip = "127.0.0.1";102try{103104String targetName =joinPoint.getTarget().getClass().getName();105String methodName =joinPoint.getSignature().getName();106Object[] arguments =joinPoint.getArgs();107Class targetClass =Class.forName(targetName);108Method[] methods =targetClass.getMethods();109String operationType = "";110String operationName = "";111for(Method method : methods) {112if(method.getName().equals(methodName)) {113Class[] clazzs =method.getParameterTypes();114if(clazzs.length ==arguments.length) {115operationType = method.getAnnotation(Log.class).operationType();116operationName = method.getAnnotation(Log.class).operationName();117

break;118}119}120}121//*========控制台输出=========*//122System.out.println("=====controller后置通知开始=====");123System.out.println("请求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")+"."+operationType);124System.out.println("方法描述:" +operationName);125System.out.println("请求人:" +user.getName());126System.out.println("请求IP:" +ip);127//*========数据库日志=========*//128SystemLog log =newSystemLog();129log.setId(UUID.randomUUID().toString());130log.setDescription(operationName);131log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")+"."+operationType);132log.setLogType((long)0);133log.setRequestIp(ip);134log.setExceptioncode(null);135log.setExceptionDetail(null);136log.setParams(null);137log.setCreateBy(user.getName());138log.setCreateDate(newDate());139//保存数据库140systemLogService.insert(log);141System.out.println("=====controller后置通知结束=====");142}catch(Exception e) {143//记录本地异常日志144logger.error("==后置通知异常==");145logger.error("异常信息:{}", e.getMessage());146}147}148149//配置后置返回通知,使用在方法aspect()上注册的切入点150@AfterReturning("controllerAspect()")151publicvoidafterReturn(JoinPoint joinPoint){152System.out.println("=====执行controller后置返回通知=====");153if(logger.isInfoEnabled()){154logger.info("afterReturn " +joinPoint);155}156}157158/**159* 异常通知 用于拦截记录异常日志160*161*@paramjoinPoint162*@parame163*/164@AfterThrowing(pointcut = "controllerAspect()", throwing="e")165publicvoiddoAfterThrowing(JoinPoint joinPoint, Throwable e) {166/*HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();167HttpSession session = request.getSession();168//读取session中的用户169User user = (User) session.getAttribute(WebConstants.CURRENT_USER);170//获取请求ip171String ip = request.getRemoteAddr();*/172//获取用户请求方法的参数并序列化为JSON格式字符串173174User user =newUser();175user.setId(1);176user.setName("张三");177String ip = "127.0.0.1";178179String params = "";180if(joinPoint.getArgs() !=null&& joinPoint.getArgs().length > 0) {181for(inti = 0; i < joinPoint.getArgs().length; i++) {182params += JsonUtil.getJsonStr(joinPoint.getArgs()[i]) + ";";183}184}185try{186187String targetName =joinPoint.getTarget().getClass().getName();188String methodName =joinPoint.getSignature().getName();189Object[] arguments =joinPoint.getArgs();190Class targetClass =Class.forName(targetName);191Method[] methods =targetClass.getMethods();192String operationType = "";193String operationName = "";194for(Method method : methods) {195if(method.getName().equals(methodName)) {196Class[] clazzs =method.getParameterTypes();197if(clazzs.length ==arguments.length) {198operationType = method.getAnnotation(Log.class).operationType();199operationName = method.getAnnotation(Log.class).operationName();200

break;201}202}203}204/*========控制台输出=========*/205System.out.println("=====异常通知开始=====");206System.out.println("异常代码:" +e.getClass().getName());207System.out.println("异常信息:" +e.getMessage());208System.out.println("异常方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")+"."+operationType);209System.out.println("方法描述:" +operationName);210System.out.println("请求人:" +user.getName());211System.out.println("请求IP:" +ip);212System.out.println("请求参数:" +params);213/*==========数据库日志=========*/214SystemLog log =newSystemLog();215log.setId(UUID.randomUUID().toString());216log.setDescription(operationName);217log.setExceptioncode(e.getClass().getName());218log.setLogType((long)1);219log.setExceptionDetail(e.getMessage());220log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));221log.setParams(params);222log.setCreateBy(user.getName());223log.setCreateDate(newDate());224log.setRequestIp(ip);225//保存数据库226systemLogService.insert(log);227System.out.println("=====异常通知结束=====");228}catch(Exception ex) {229//记录本地异常日志230logger.error("==异常通知异常==");231logger.error("异常信息:{}", ex.getMessage());232}233/*==========记录本地异常日志==========*/234logger.error("异常方法:{}异常代码:{}异常信息:{}参数:{}", joinPoint.getTarget().getClass().getName() +joinPoint.getSignature().getName(), e.getClass().getName(), e.getMessage(), params);235236}237238}

我这里写的比较全,前置通知,环绕通知,后置通知,异常通知,后置饭后通知,都写上了,在我们实际编写中不写全也没事,我习惯上把记录日志的逻辑写在后置通知里面,我看网上也有些在前置通知里面的,但我感觉写在后置通知里比较好。

下面开始在controller中加入自定义的注解!!

1packagecom.gcx.controller;23importorg.springframework.beans.factory.annotation.Autowired;4importorg.springframework.stereotype.Controller;5importorg.springframework.web.bind.annotation.RequestMapping;67importcom.gcx.annotation.Log;8importcom.gcx.service.UserService;910@Controller11@RequestMapping("userController")12publicclassUserController {1314@Autowired15privateUserService userService;1617@RequestMapping("testAOP")18@Log(operationType="add操作:",operationName="添加用户")19publicvoidtestAOP(String userName,String passWord){20userService.addUser(userName, password);21}22}

下面编写测试类

1@Test2publicvoidtestAOP1(){3//启动Spring容器4ApplicationContext ctx =newClassPathXmlApplicationContext(newString[]{"classpath:applicationContext-mvc.xml","classpath:applicationContext-dataSource.xml"});5//获取service或controller组件6UserController userController = (UserController) ctx.getBean("userController");7userController.testAOP("zhangsan", "123456");8}9

数据库数据:

我原本想写两个切点,一个是service层,一个是controller层,service层是用来记录异常信息的日志,而controller层的是用来记录功能的日志,运行结果如下。

这样做的话不知道在实际的项目中运行效率好不好,在这里请看到博客的大牛给点建议!!

 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有 導航