示例1:通过包路径及类名规则为应用增加切面
该示例是通过拦截所有com.dxz.web.aop包下的以Controller结尾的所有类的所有方法,在方法执行前后打印和记录日志到数据库。
新建一个springboot项目
1:首先定义maven
4.0.0 com.dxz.auth auth-demo1 0.0.1-SNAPSHOT jar auth-demo1 Demo project for Spring Boot org.springframework.boot spring-boot-starter-parent 1.5.9.RELEASE UTF-8 UTF-8 1.8 org.springframework.boot spring-boot-starter org.springframework spring-core org.springframework spring-context-support org.springframework spring-web org.springframework spring-context org.springframework spring-webmvc org.springframework spring-webmvc-portlet org.springframework spring-test org.springframework spring-beans org.springframework spring-aspects javax.servlet javax.servlet-api org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-maven-plugin
2:在拦截controller之前需要自定义一个注解,该注解是放在需要通过AOP织入系统日志的方法上。
package com.dxz.web.aop;import java.lang.annotation.*;@Target({ElementType.PARAMETER, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface SystemLog { String module() default ""; String methods() default "";}
3:定义记录日志的切面
package com.dxz.web.aop;import java.lang.reflect.Method;import java.util.Date;import javax.servlet.http.HttpServletRequest;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.Signature;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.aspectj.lang.reflect.MethodSignature;import org.springframework.stereotype.Component;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;@Component@Aspectpublic class LogAopAction { // 获取开始时间 private long BEGIN_TIME; // 获取结束时间 private long END_TIME; // 定义本次log实体 private LogModel logModel = new LogModel(); @Pointcut("execution(* com.dxz.web.aop.*Controller.*(..))") private void controllerMethodAspect() { } /** * 方法开始执行 */ @Before("controllerMethodAspect()") public void doBefore() { BEGIN_TIME = new Date().getTime(); System.out.println("aop--开始"); } /** * 方法结束执行 */ @After("controllerMethodAspect()") public void after() { END_TIME = new Date().getTime(); System.out.println("aop--结束"); } /** * 方法结束执行后的操作 */ @AfterReturning("controllerMethodAspect()") public void doAfter() { if (logModel.getState() == 1 || logModel.getState() == -1) { logModel.setActionTime(END_TIME - BEGIN_TIME); logModel.setGmtCreate(new Date(BEGIN_TIME)); System.out.println("aop--将logModel="+logModel +",存入到数据库"); } else { System.out.println(logModel); System.out.println("aop-->>>>>>>>不存入到数据库"); } } /** * 方法有异常时的操作 */ @AfterThrowing("controllerMethodAspect()") public void doAfterThrow() { System.out.println("aop--例外通知-----------------------------------"); } /** * 方法执行 * * @param pjp * @return * @throws Throwable */ @Around("controllerMethodAspect()") public Object around(ProceedingJoinPoint pjp) throws Throwable { // 日志实体对象 HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()) .getRequest(); // 获取当前登陆用户信息 String uid = request.getParameter("uid"); if (uid == null) { logModel.setLoginAccount("—— ——"); } else { logModel.setLoginAccount(uid); } // 拦截的实体类,就是当前正在执行的controller Object target = pjp.getTarget(); // 拦截的方法名称。当前正在执行的方法 String methodName = pjp.getSignature().getName(); // 拦截的方法参数 Object[] args = pjp.getArgs(); // 拦截的放参数类型 Signature sig = pjp.getSignature(); MethodSignature msig = null; if (!(sig instanceof MethodSignature)) { throw new IllegalArgumentException("该注解只能用于方法"); } msig = (MethodSignature) sig; Class[] parameterTypes = msig.getMethod().getParameterTypes(); Object object = null; Method method = null; try { method = target.getClass().getMethod(methodName, parameterTypes); } catch (NoSuchMethodException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } catch (SecurityException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } if (null != method) { // 判断是否包含自定义的注解,说明一下这里的SystemLog就是我自己自定义的注解 if (method.isAnnotationPresent(SystemLog.class)) { SystemLog systemlog = method.getAnnotation(SystemLog.class); logModel.setModule(systemlog.module()); logModel.setMethod(systemlog.methods()); logModel.setLoginIp(getIp(request)); logModel.setActionUrl(request.getRequestURI()); try { object = pjp.proceed(); logModel.setDescription("执行成功"); logModel.setState((short) 1); } catch (Throwable e) { // TODO Auto-generated catch block logModel.setDescription("执行失败"); logModel.setState((short) -1); } } else { // 没有包含注解 object = pjp.proceed(); logModel.setDescription("此操作不包含注解"); logModel.setState((short) 0); } } else { // 不需要拦截直接执行 object = pjp.proceed(); logModel.setDescription("不需要拦截直接执行"); logModel.setState((short) 0); } return object; } /** * 获取ip地址 * * @param request * @return */ private String getIp(HttpServletRequest request) { if (request.getHeader("x-forwarded-for") == null) { return request.getRemoteAddr(); } return request.getHeader("x-forwarded-for"); }}
其中我的LogModel实体类如下:
package com.dxz.web.aop;import java.util.Date;public class LogModel { /**日志id */ private Integer id; /** * 当前操作人id */ private String loginAccount; /**当前操作人ip */ private String loginIp; /**操作请求的链接 */ private String actionUrl; /**执行的模块 */ private String module; /**执行的方法 */ private String method; /**执行操作时间 */ private Long actionTime; /** 描述 */ private String description; /** 执行的时间 */ private Date gmtCreate; /** 该操作状态,1表示成功,-1表示失败! */ private Short state; //set()/get()}
4:业务controller中增加@SystemLog注解
package com.dxz.web.aop;import javax.servlet.http.HttpServletRequest;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("/user")public class LoginController { /** * 登录方法 * @param request * @return */ @RequestMapping(value="/toLogin", method = RequestMethod.GET, produces = {"application/json;charset=UTF-8"}) @SystemLog(methods="用户管理", module = "用户登录") public String toLogin(HttpServletRequest request) { System.out.println("biz--登录验证中"); return "login"; }}
5、springboot配置类
package com.dxz;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.boot.builder.SpringApplicationBuilder;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.EnableAspectJAutoProxy;@SpringBootApplication@EnableAspectJAutoProxy@ComponentScanpublic class AuthDemo1Application { public static void main(String[] args) { new SpringApplicationBuilder(AuthDemo1Application.class).web(true).run(args); }}
启动springboot后,
通过浏览器访问:后的结果如下:
二、@Pointcut("execution(* com.dxz.web.aop.*Controller.*(..))")是“com.dxz.web.aop”包下所有以Controller结尾的类的所有方法,为了验证,我再增加2个controller如下:
package com.dxz.web.aop;import javax.servlet.http.HttpServletRequest;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("/user")public class LogoutController { /** * 退出方法 * @param request * @return */ @RequestMapping(value="/logout", method = RequestMethod.GET, produces = {"application/json;charset=UTF-8"}) @SystemLog(methods="用户管理", module = "用户退出") public String logout(HttpServletRequest request) { System.out.println("biz--退出逻辑"); return "logout"; }}package com.dxz.web.aop;import javax.servlet.http.HttpServletRequest;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("/user")public class TestController2 { /** * test方法 * @param request * @return */ @RequestMapping(value="/test", method = RequestMethod.GET, produces = {"application/json;charset=UTF-8"}) @SystemLog(methods="用户管理", module = "用户测试") public String test(HttpServletRequest request) { System.out.println("biz--test"); return "test"; }}
结果:
示例2:通过within()指定所有@RestController注解类 + @annotation()指定方法上有@Auth注解
package com.dxz.web.aop.auth;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;import org.springframework.core.Ordered;import org.springframework.core.annotation.Order;@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@Order(Ordered.HIGHEST_PRECEDENCE) //优先级,暂定最高级public @interface Auth { boolean login() default false;}package com.dxz.web.aop.auth;import javax.servlet.http.HttpServletRequest;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.springframework.stereotype.Component;@Aspect@Componentpublic class AuthAopAction { @Before("within(@org.springframework.web.bind.annotation.RestController *) && @annotation(authParam)") public void requestLimit(JoinPoint joinPoint, Auth authParam) throws AuthException { HttpServletRequest request = null; try { Object[] args = joinPoint.getArgs(); for (int i = 0; i < args.length; i++) { if (args[i] instanceof HttpServletRequest) { request = (HttpServletRequest) args[i]; break; } } if (null == request) { System.out.println("auth handler error : target:[{}] no param : HttpServletRequest"); throw new AuthException("HttpServletRequest is null error."); } if (null != authParam && authParam.login()) { // 登录权限验证开启 //Object userId = request.getSession().getAttribute("uid"); Object userId = request.getParameter("uid"); if ("duanxz".equals(userId)) { System.out.println("账号正确,成功登录"); } else { System.out.println("账号不正确,需要重新登录"); throw new AuthException("NEED_LOGIN"); } } } catch (Exception e) { System.out.println("auth handler error : exception:{}" + e.getMessage()); throw e; } }}package com.dxz.web.aop.auth;public class AuthException extends Exception { private static final long serialVersionUID = -1341655401594111052L; public AuthException() { super(); } public AuthException(String message) { super(message); }}
下面的交易controller用于是否登录的权限验证
package com.dxz.web.aop;import javax.servlet.http.HttpServletRequest;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RestController;import com.dxz.web.aop.auth.Auth;@Controller@RestController@RequestMapping("/pay")public class PayController { @Auth(login = true) @RequestMapping(value="prePay", method = RequestMethod.GET, produces = {"application/json;charset=UTF-8"}) public String prePay(HttpServletRequest request) { String result = "预交易,uid=" + request.getParameter("uid"); System.out.println(result); return result; }}
浏览器访问:
及 的结果如下: