今天继续实现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>
接下来开始编写代码。
创建日志类实体
public class SystemLog { private String id; private String description; private String method; private Long logType; private String requestIp; private String exceptioncode; private String exceptionDetail; private String params; private String createBy; private Date createDate; public String getId() { return id; } public void setId(String id) { this.id = id == null ? null : id.trim(); } public String getDescription() { return description; } public void setDescription(String description) { this.description = description == null ? null : description.trim(); } public String getMethod() { return method; } public void setMethod(String method) { this.method = method == null ? null : method.trim(); } public Long getLogType() { return logType; } public void setLogType(Long logType) { this.logType = logType; } public String getRequestIp() { return requestIp; } public void setRequestIp(String requestIp) { this.requestIp = requestIp == null ? null : requestIp.trim(); } public String getExceptioncode() { return exceptioncode; } public void setExceptioncode(String exceptioncode) { this.exceptioncode = exceptioncode == null ? null : exceptioncode.trim(); } public String getExceptionDetail() { return exceptionDetail; } public void setExceptionDetail(String exceptionDetail) { this.exceptionDetail = exceptionDetail == null ? null : exceptionDetail.trim(); } public String getParams() { return params; } public void setParams(String params) { this.params = params == null ? null : params.trim(); } public String getCreateBy() { return createBy; } public void setCreateBy(String createBy) { this.createBy = createBy == null ? null : createBy.trim(); } public Date getCreateDate() { return createDate; } public void setCreateDate(Date createDate) { this.createDate = createDate; } }
编写dao接口
package com.gcx.dao; import com.gcx.entity.SystemLog; public interface SystemLogMapper { int deleteByPrimaryKey(String id); int insert(SystemLog record); int insertSelective(SystemLog record); SystemLog selectByPrimaryKey(String id); int updateByPrimaryKeySelective(SystemLog record); int updateByPrimaryKey(SystemLog record); }
编写service层
package com.gcx.service; import com.gcx.entity.SystemLog; public interface SystemLogService { int deleteSystemLog(String id); int insert(SystemLog record); int insertTest(SystemLog record); SystemLog selectSystemLog(String id); int updateSystemLog(SystemLog record); }
编写service实现类serviceImpl
package com.gcx.service.impl; import javax.annotation.Resource; import org.springframework.stereotype.Service; import com.gcx.annotation.Log; import com.gcx.dao.SystemLogMapper; import com.gcx.entity.SystemLog; import com.gcx.service.SystemLogService; @Service("systemLogService") public class SystemLogServiceImpl implements SystemLogService { @Resource private SystemLogMapper systemLogMapper; @Override public int deleteSystemLog(String id) { return systemLogMapper.deleteByPrimaryKey(id); } @Override public int insert(SystemLog record) { return systemLogMapper.insertSelective(record); } @Override public SystemLog selectSystemLog(String id) { return systemLogMapper.selectByPrimaryKey(id); } @Override public int updateSystemLog(SystemLog record) { return systemLogMapper.updateByPrimaryKeySelective(record); } @Override public int insertTest(SystemLog record) { return systemLogMapper.insert(record); } }
到这里基本程序编写完毕
下面开始自定义注解
package com.gcx.annotation; import java.lang.annotation.*; @Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Log { /** 要执行的操作类型比如:add操作 **/ public String operationType() default ""; /** 要执行的具体操作比如:添加用户 **/ public String operationName() default ""; }
下面编写切面
package com.gcx.annotation; import java.lang.reflect.Method; import java.util.Date; import java.util.UUID; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import com.gcx.entity.SystemLog; import com.gcx.entity.User; import com.gcx.service.SystemLogService; import com.gcx.util.JsonUtil; /** * @author 杨建 * @E-mail: email * @version 创建时间:2015-10-19 下午4:29:05 * @desc 切点类 */ @Aspect @Component public class SystemLogAspect { //注入Service用于把日志保存数据库 @Resource //这里我用resource注解,一般用的是@Autowired,他们的区别如有时间我会在后面的博客中来写 private SystemLogService systemLogService; private static final Logger logger = LoggerFactory.getLogger(SystemLogAspect. class); //Controller层切点 @Pointcut("execution (* com.gcx.controller..*.*(..))") public void controllerAspect() { } /** * 前置通知 用于拦截Controller层记录用户的操作 * * @param joinPoint 切点 */ @Before("controllerAspect()") public void doBefore(JoinPoint joinPoint) { System.out.println("==========执行controller前置通知==============="); if(logger.isInfoEnabled()){ logger.info("before " + joinPoint); } } //配置controller环绕通知,使用在方法aspect()上注册的切入点 @Around("controllerAspect()") public void around(JoinPoint joinPoint){ System.out.println("==========开始执行controller环绕通知==============="); long start = System.currentTimeMillis(); try { ((ProceedingJoinPoint) joinPoint).proceed(); long end = System.currentTimeMillis(); if(logger.isInfoEnabled()){ logger.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms!"); } System.out.println("==========结束执行controller环绕通知==============="); } catch (Throwable e) { long end = System.currentTimeMillis(); if(logger.isInfoEnabled()){ logger.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage()); } } } /** * 后置通知 用于拦截Controller层记录用户的操作 * * @param joinPoint 切点 */ @After("controllerAspect()") public void after(JoinPoint joinPoint) { /* HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); HttpSession session = request.getSession(); */ //读取session中的用户 // User user = (User) session.getAttribute("user"); //请求的IP //String ip = request.getRemoteAddr(); User user = new User(); user.setId(1); user.setName("张三"); String ip = "127.0.0.1"; try { 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 operationType = ""; String operationName = ""; for (Method method : methods) { if (method.getName().equals(methodName)) { Class[] clazzs = method.getParameterTypes(); if (clazzs.length == arguments.length) { operationType = method.getAnnotation(Log.class).operationType(); operationName = method.getAnnotation(Log.class).operationName(); break; } } } //*========控制台输出=========*// System.out.println("=====controller后置通知开始====="); System.out.println("请求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")+"."+operationType); System.out.println("方法描述:" + operationName); System.out.println("请求人:" + user.getName()); System.out.println("请求IP:" + ip); //*========数据库日志=========*// SystemLog log = new SystemLog(); log.setId(UUID.randomUUID().toString()); log.setDescription(operationName); log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")+"."+operationType); log.setLogType((long)0); log.setRequestIp(ip); log.setExceptioncode( null); log.setExceptionDetail( null); log.setParams( null); log.setCreateBy(user.getName()); log.setCreateDate(new Date()); //保存数据库 systemLogService.insert(log); System.out.println("=====controller后置通知结束====="); } catch (Exception e) { //记录本地异常日志 logger.error("==后置通知异常=="); logger.error("异常信息:{}", e.getMessage()); } } //配置后置返回通知,使用在方法aspect()上注册的切入点 @AfterReturning("controllerAspect()") public void afterReturn(JoinPoint joinPoint){ System.out.println("=====执行controller后置返回通知====="); if(logger.isInfoEnabled()){ logger.info("afterReturn " + joinPoint); } } /** * 异常通知 用于拦截记录异常日志 * * @param joinPoint * @param e */ @AfterThrowing(pointcut = "controllerAspect()", throwing="e") public void doAfterThrowing(JoinPoint joinPoint, Throwable e) { /*HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); HttpSession session = request.getSession(); //读取session中的用户 User user = (User) session.getAttribute(WebConstants.CURRENT_USER); //获取请求ip String ip = request.getRemoteAddr(); */ //获取用户请求方法的参数并序列化为JSON格式字符串 User user = new User(); user.setId(1); user.setName("张三"); String ip = "127.0.0.1"; String params = ""; if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) { for ( int i = 0; i < joinPoint.getArgs().length; i++) { params += JsonUtil.getJsonStr(joinPoint.getArgs()[i]) + ";"; } } try { 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 operationType = ""; String operationName = ""; for (Method method : methods) { if (method.getName().equals(methodName)) { Class[] clazzs = method.getParameterTypes(); if (clazzs.length == arguments.length) { operationType = method.getAnnotation(Log.class).operationType(); operationName = method.getAnnotation(Log.class).operationName(); break; } } } /*========控制台输出=========*/ System.out.println("=====异常通知开始====="); System.out.println("异常代码:" + e.getClass().getName()); System.out.println("异常信息:" + e.getMessage()); System.out.println("异常方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")+"."+operationType); System.out.println("方法描述:" + operationName); System.out.println("请求人:" + user.getName()); System.out.println("请求IP:" + ip); System.out.println("请求参数:" + params); /*==========数据库日志=========*/ SystemLog log = new SystemLog(); log.setId(UUID.randomUUID().toString()); log.setDescription(operationName); log.setExceptioncode(e.getClass().getName()); log.setLogType((long)1); log.setExceptionDetail(e.getMessage()); log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")); log.setParams(params); log.setCreateBy(user.getName()); log.setCreateDate(new Date()); log.setRequestIp(ip); //保存数据库 systemLogService.insert(log); System.out.println("=====异常通知结束====="); } catch (Exception ex) { //记录本地异常日志 logger.error("==异常通知异常=="); logger.error("异常信息:{}", ex.getMessage()); } /*==========记录本地异常日志==========*/ logger.error("异常方法:{}异常代码:{}异常信息:{}参数:{}", joinPoint.getTarget().getClass().getName() + joinPoint.getSignature().getName(), e.getClass().getName(), e.getMessage(), params); } }
我这里写的比较全,前置通知,环绕通知,后置通知,异常通知,后置饭后通知,都写上了,在我们实际编写中不写全也没事,我习惯上把记录日志的逻辑写在后置通知里面,我看网上也有些在前置通知里面的,但我感觉写在后置通知里比较好。
下面开始在controller中加入自定义的注解!!
package com.gcx.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import com.gcx.annotation.Log; import com.gcx.service.UserService; @Controller @RequestMapping("userController") public class UserController { @Autowired private UserService userService; @RequestMapping("testAOP") @Log(operationType="add操作:",operationName="添加用户") public void testAOP(String userName,String password){ userService.addUser(userName, password); } }
下面编写测试类
@Test public void testAOP1(){ //启动Spring容器 ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"classpath:applicationContext-mvc.xml","classpath:applicationContext-dataSource.xml"}); //获取service或controller组件 UserController userController = (UserController) ctx.getBean("userController"); userController.testAOP("zhangsan", "123456"); }
数据库数据:
我原本想写两个切点,一个是service层,一个是controller层,service层是用来记录异常信息的日志,而controller层的是用来记录功能的日志,运行结果如下。
这样做的话不知道在实际的项目中运行效率好不好,在这里请看到博客的大牛给点建议!!
以上这篇spring AOP自定义注解方式实现日志管理的实例讲解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。