1、application.yml加入声明
spring: aop: proxy-target-class: true auto: true
2.pom文件加入
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
3.自定义注解@ControllerLog
package com.ps.uzkefu.util; /** * Created by Administrator on 2018/6/22 0022. */ import java.lang.annotation.*; /** * Created by zhangmy on 2017/7/24. */ @Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface ControllerLog { /** * 操作描述 业务名称business * * @return */ String description() default ""; /** * 操作模块 * * @return */ OperateModule module(); /** * 操作类型 create modify delete * * @return */ OperateType opType(); /** * 主键入参参数名称,入参中的哪个参数为主键 * * @return */ String primaryKeyName() default ""; /** * 主键在参数中的顺序,从0开始,默认0 */ int primaryKeySort() default 0; /** * 主键所属类 * * @return */ Class<?> primaryKeyBelongClass(); }
4. 定义模块名称枚举
package com.ps.uzkefu.util; /** * Created by Administrator on 2018/6/22 0022. */ /** * Created by zhangmy on 2017/7/26. */ public enum OperateModule { SightMerchant("商家管理"), AdminUser("用户管理"), Tour("行程管理"), UserTour("用户行程"); private String text; OperateModule(String text) { this.text = text; } public String getText() { return text; } }
5.定义操作类型枚举
package com.ps.uzkefu.util; /** * Created by Administrator on 2018/6/22 0022. */ public enum OperateType { /** * 当操作类型为created时,要求方法返回格式为:return ok("具体操作信息", new MapBean("此处为实体主键属性名称", primaryKeyValue)); */ create, modify, delete }
6. 切面
package com.ps.uzkefu.common; /** * Created by Administrator on 2018/6/22 0022. */ import com.ps.uzkefu.util.ControllerLog; import com.ps.uzkefu.util.OperateType; import org.apache.commons.lang3.StringUtils; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.CodeSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.validation.BindingResult; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.Map; /** * Created by zhangmy on 2017/7/25. */ @Aspect @Component public class WebLogAspect { private Logger logger = LoggerFactory.getLogger(this.getClass()); /** * 获取@controllerLog 注解上信息 * * @param joinPoint * @return map * @throws Exception */ public static Map<String, Object> getControllerAnnotationValue(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(); Map<String, Object> map = new HashMap<>(); for (Method method : methods) { if (method.getName().equals(methodName)) { Class[] classes = method.getParameterTypes(); if (classes.length == arguments.length) { //取入参数据 String description = method.getAnnotation(ControllerLog.class).description(); String module = method.getAnnotation(ControllerLog.class).module().name(); String opType = method.getAnnotation(ControllerLog.class).opType().name(); String primaryKeyName = method.getAnnotation(ControllerLog.class).primaryKeyName(); int primaryKeySort = method.getAnnotation(ControllerLog.class).primaryKeySort(); Class<?> clazz = method.getAnnotation(ControllerLog.class).primaryKeyBelongClass(); map.put("module", module); map.put("opType", opType); map.put("business", description); map.put("primaryKeyName", primaryKeyName); map.put("primaryKeySort", primaryKeySort); map.put("primaryKeyBelongClass", clazz); break; } } } return map; } /** * 定义一个切入点. * ("execution(public * com.kfit.*.web..*.*(..))") * 解释下: * 第一个 * 代表任意修饰符及任意返回值. * 第二个 * 任意包名 * 第三个 * 代表任意方法. * 第四个 * 定义在web包或者子包 * 第五个 * 任意方法 * .. 匹配任意数量的参数. */ @Pointcut(" @annotation(com.ps.uzkefu.util.ControllerLog)") public void webLog() { } @Around("webLog()") public Object round(ProceedingJoinPoint joinPoint) throws Throwable { logger.info("环绕通知开始........"); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); //入参 value Object[] args = joinPoint.getArgs(); //入参名称 String[] paramNames = ((CodeSignature) joinPoint.getSignature()).getParameterNames(); Map<String, Object> params = new HashMap<>(); //获取所有参数对象 for (int i = 0; i < args.length; i++) { if (null != args[i]) { if (args[i] instanceof BindingResult) { params.put(paramNames[i], "bindingResult"); } else { params.put(paramNames[i], args[i]); } } else { params.put(paramNames[i], "无"); } } Map<String, Object> values = getControllerAnnotationValue(joinPoint); String opType = values.get("opType").toString(); String module = values.get("module").toString(); String business = values.get("business").toString(); String primaryKeyName = values.get("primaryKeyName").toString(); int primaryKeySort = Integer.parseInt(values.get("primaryKeySort").toString()); Class<?> primaryKeyBelongClass = (Class<?>) values.get("primaryKeyBelongClass"); Object primaryKeyValue = null; if (StringUtils.isNotBlank(primaryKeyName) && OperateType.valueOf(opType) != OperateType.create) { primaryKeyValue = args[primaryKeySort]; } //切面返回值 // 一切正常的情况下,继续执行被拦截的方法 // 一切正常的情况下,继续执行被拦截的方法 // result = joinPoint.proceed(); Object returnValue = joinPoint.proceed(); if (OperateType.valueOf(opType) == OperateType.create) { // 主要目的是为了获取方法保存成功的返回数据格式,以获取保存成功的数据id // 此处要限制新增返回成功的格式,return ok("具体操作信息", new MapBean("此处为实体主键属性名称", primaryKeyValue)); // 不然自己也可定义格式,进行拆分获取主键 } return returnValue; } @AfterReturning("webLog()") public void doAfterReturning(JoinPoint joinPoint) { // 处理完请求,返回内容 logger.info("WebLogAspect.doAfterReturning()"); } }
7.Cotroller中引用自定义注解,从而对该方法进行切面处理
package com.ps.uzkefu.apps; import com.ps.uzkefu.apps.oms.user.entity.Menu; import com.ps.uzkefu.base.BaseController; import com.ps.uzkefu.util.ControllerLog; import com.ps.uzkefu.util.OperateModule; import com.ps.uzkefu.util.OperateType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpServletRequest; /** * Author:WuZhiWei * Date:2018/6/20 */ @Controller @RequestMapping public class CorpIndexController extends BaseController { @RequestMapping("/") @ControllerLog(description = "修改商家状态", module = OperateModule.SightMerchant, opType = OperateType.modify, primaryKeyName = "id", primaryKeySort = 1, primaryKeyBelongClass = Menu.class) public String index(HttpServletRequest request) { return "/start/index"; } }
完整切面如下
package com.wdletu.log.aspect; import com.wdletu.core.exception.ServiceException; import com.wdletu.core.util.MapBean; import com.wdletu.core.util.StringUtil; import com.wdletu.log.annotation.ControllerLog; import com.wdletu.log.constant.OperateType; import com.wdletu.log.entity.OperateLog; import com.wdletu.log.service.OperateLogService; import com.wdletu.security.JwtService; import com.wdletu.travel.admin.entity.AdminUser; import com.wdletu.travel.admin.service.AdminUserService; import net.sf.json.JSONObject; import org.apache.commons.lang3.StringUtils; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.CodeSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.validation.BindingResult; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.persistence.Id; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.Map; /** * Created by zhangmy on 2017/7/25. */ @Aspect @Component public class WebLogAspect { @Autowired private OperateLogService operateLogService; @Autowired private JwtService jwtService; @Autowired private AdminUserService adminUserService; @Value("${jwt.header}") private String tokenHeader; private Logger logger = LoggerFactory.getLogger(this.getClass()); /** * 获取class的主键字段名 主键值 */ public static Object getPrimaryKeyName(Class<?> clazz) throws Exception { Object param = null; //递归获取父子类所有的field Class tempClass = clazz; //当父类为null的时候说明到达了最上层的父类(Object类 while (tempClass != null && !StringUtils.equals(tempClass.getName().toLowerCase(), "java.lang.object")) { Field[] fields = tempClass.getDeclaredFields(); for (Field field : fields) { String fieldName = field.getName(); //boolean类型不必判断,因实体里包含boolean类型的属性,getter方法是以is开头,不是get开头 if (field.getType().equals(Boolean.class) || field.getType().getName().equals("boolean")) { continue; } if ((field.getModifiers() & Modifier.FINAL) == Modifier.FINAL) { continue; } String getterMethod = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); Method method = tempClass.getDeclaredMethod(getterMethod); //字段上是否存在@Id注解 Id primaryAnnotation = field.getAnnotation(Id.class); //getter方法上是否存在@Id注解 if (primaryAnnotation == null) { primaryAnnotation = method.getAnnotation(Id.class); } //存在@Id注解,则说明该字段为主键 if (primaryAnnotation != null) { /*String primaryKeyName = field.getName();*/ param = field.getName(); break; } } if (param != null && StringUtil.isNotBlank(param.toString())) { break; } //得到父类赋值给tempClass tempClass = tempClass.getSuperclass(); } if (param == null) { throw new ServiceException(clazz.getName() + "实体,未设置主键"); } return param; } /** * 获取@controllerLog 注解上信息 * * @param joinPoint * @return map * @throws Exception */ public static Map<String, Object> getControllerAnnotationValue(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(); Map<String, Object> map = new HashMap<>(); for (Method method : methods) { if (method.getName().equals(methodName)) { Class[] classes = method.getParameterTypes(); if (classes.length == arguments.length) { //取入参数据 String description = method.getAnnotation(ControllerLog.class).description(); String module = method.getAnnotation(ControllerLog.class).module().name(); String opType = method.getAnnotation(ControllerLog.class).opType().name(); String primaryKeyName = method.getAnnotation(ControllerLog.class).primaryKeyName(); int primaryKeySort = method.getAnnotation(ControllerLog.class).primaryKeySort(); Class<?> clazz = method.getAnnotation(ControllerLog.class).primaryKeyBelongClass(); map.put("module", module); map.put("opType", opType); map.put("business", description); map.put("primaryKeyName", primaryKeyName); map.put("primaryKeySort", primaryKeySort); map.put("primaryKeyBelongClass", clazz); break; } } } return map; } /** * 定义一个切入点. * ("execution(public * com.kfit.*.web..*.*(..))") * 解释下: * 第一个 * 代表任意修饰符及任意返回值. * 第二个 * 任意包名 * 第三个 * 代表任意方法. * 第四个 * 定义在web包或者子包 * 第五个 * 任意方法 * .. 匹配任意数量的参数. */ @Pointcut("execution(public * com.wdletu..*.controller..*.*(..)) && @annotation(com.wdletu.log.annotation.ControllerLog)") public void webLog() { } @Around("webLog()") public Object round(ProceedingJoinPoint joinPoint) throws Throwable { logger.info("环绕通知开始........"); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); /*try {*/ //读取用户 String token = request.getHeader(tokenHeader); Long userId = jwtService.getUserIdFromToken(token); AdminUser adminUser = adminUserService.findByUserId(userId); String username = adminUser.getName(); //入参 value Object[] args = joinPoint.getArgs(); //入参名称 String[] paramNames = ((CodeSignature) joinPoint.getSignature()).getParameterNames(); Map<String, Object> params = new HashMap<>(); //获取所有参数对象 for (int i = 0; i < args.length; i++) { if (null != args[i]) { if (args[i] instanceof BindingResult) { params.put(paramNames[i], "bindingResult"); } else { params.put(paramNames[i], args[i]); } } else { params.put(paramNames[i], "无"); } } Map<String, Object> values = getControllerAnnotationValue(joinPoint); String opType = values.get("opType").toString(); String module = values.get("module").toString(); String business = values.get("business").toString(); String primaryKeyName = values.get("primaryKeyName").toString(); int primaryKeySort = Integer.parseInt(values.get("primaryKeySort").toString()); Class<?> primaryKeyBelongClass = (Class<?>) values.get("primaryKeyBelongClass"); Object primaryKeyValue = null; if (StringUtils.isNotBlank(primaryKeyName) && OperateType.valueOf(opType) != OperateType.create) { primaryKeyValue = args[primaryKeySort]; } //切面返回值 Object returnValue = joinPoint.proceed(); if (OperateType.valueOf(opType) == OperateType.create) { // 主要目的是为了获取方法保存成功的返回数据格式,以获取保存成功的数据id // 此处要限制新增返回成功的格式,return ok("具体操作信息", new MapBean("此处为实体主键属性名称", primaryKeyValue)); // 不然自己也可定义格式,进行拆分获取主键 primaryKeyName = getPrimaryKeyName(primaryKeyBelongClass).toString(); if (returnValue instanceof ResponseEntity<?>) { Object entity = ((ResponseEntity<?>) returnValue).getBody(); if (entity instanceof MapBean) { MapBean mapBean = (MapBean) entity; Boolean success = mapBean.get("success"); if (success) { MapBean content = mapBean.get("content"); if (content != null) { primaryKeyValue = content.get(primaryKeyName); } } } } } OperateLog operateLog = new OperateLog(); operateLog.setParams(JSONObject.fromObject(params).toString()); operateLog.setUserId(userId); operateLog.setUserName(username); operateLog.setModule(module); operateLog.setOpType(opType); operateLog.setBusiness(business); operateLog.setRecordId(primaryKeyValue == null ? null : Long.parseLong(primaryKeyValue.toString())); operateLogService.save(operateLog); /*} catch (Throwable throwable) { throwable.printStackTrace(); logger.error("日志切面异常", throwable.getMessage()); }*/ return returnValue; } @AfterReturning("webLog()") public void doAfterReturning(JoinPoint joinPoint) { // 处理完请求,返回内容 logger.info("WebLogAspect.doAfterReturning()"); } }