spring AOP 之五:Spring MVC通过AOP切面编程来拦截controller
示例1:通过包路径及类名规则为应用增加切面
该示例是通过拦截所有com.dxz.web.aop包下的以Controller结尾的所有类的所有方法,在方法执行前后打印和记录日志到数据库。
新建一个springboot项目
1:首先定义maven
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com.dxz.auth</groupId>
<artifactId>auth-demo1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging> <name>auth-demo1</name>
<description>Demo project for Spring Boot</description> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc-portlet</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </project>
2:在拦截controller之前需要自定义一个注解,该注解是放在需要通过AOP织入系统日志的方法上。
package com.dxz.web.aop; import java.lang.annotation.*; @Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @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
@Aspect
public 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
@ComponentScan
public class AuthDemo1Application { public static void main(String[] args) {
new SpringApplicationBuilder(AuthDemo1Application.class).web(true).run(args);
}
}
启动springboot后,
通过浏览器访问:http://localhost:8080/user/toLogin?uid=duanxz后的结果如下:
二、@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
@Component
public 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;
}
}
浏览器访问:
http://localhost:8080/pay/prePay?uid=duanxz
及 http://localhost:8080/pay/prePay?uid=duanxz2的结果如下:
spring AOP 之五:Spring MVC通过AOP切面编程来拦截controller的更多相关文章
- Spring MVC通过AOP切面编程 来拦截controller 实现日志的写入
首选需要参考的是:[参考]http://www.cnblogs.com/guokai870510826/p/5977948.html http://www.cnblogs.com/guokai8 ...
- Java实战之03Spring-03Spring的核心之AOP(Aspect Oriented Programming 面向切面编程)
三.Spring的核心之AOP(Aspect Oriented Programming 面向切面编程) 1.AOP概念及原理 1.1.什么是AOP OOP:Object Oriented Progra ...
- Spring学习手札(二)面向切面编程AOP
AOP理解 Aspect Oriented Program面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术. 但是,这种说法有些片面,因为在软件工程中,AOP的价值体现的并 ...
- Spring之控制反转——IoC、面向切面编程——AOP
控制反转——IoC 提出IoC的目的 为了解决对象之间的耦合度过高的问题,提出了IoC理论,用来实现对象之间的解耦. 什么是IoC IoC是Inversion of Control的缩写,译为控制 ...
- 04 Spring:01.Spring框架简介&&02.程序间耦合&&03.Spring的 IOC 和 DI&&08.面向切面编程 AOP&&10.Spring中事务控制
spring共四天 第一天:spring框架的概述以及spring中基于XML的IOC配置 第二天:spring中基于注解的IOC和ioc的案例 第三天:spring中的aop和基于XML以及注解的A ...
- spring入门(四)【面向切面编程】
开发过程中很多时候会用到日志.事务等操作,这些操作如果要写在业务代码中会相当麻烦,这时就会用到面向切面编程(AOP),AOP作为一种编程思想,和OOP有着不同的侧重点,面向对象侧重于万事万物皆对象,而 ...
- Java AOP (2) runtime weaving 【Java 切面编程 (2) 运行时织入】
接上一篇 Java AOP (1) compile time weaving [Java 切面编程 (1) 编译期织入] Dynamic proxy 动态代理 Befor talking abou ...
- Spring详解(五)------面向切面编程
.AOP 什么? AOP(Aspect Oriented Programming),通常称为面向切面编程.它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的 ...
- AOP 面向切面编程、拦截器
AOP(Aspect-Oriented Programming,面向切面的编程),它是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术.它是一种新的方法论, ...
随机推荐
- Composer 安装东西遇到github需要token怎么办
安装yii2遇到这样的提示: Could not fetch https://api.github.com/repos/jquery/sizzle/contents/bower.json?ref=91 ...
- iOS Xcode全面剖析
链接:http://www.jianshu.com/p/3c8527898e84 一.创建新一个工程二.Xcode界面详细介绍三.Xcode菜单详解四.Xcode快捷键介绍五.结语六.参考资料 前言 ...
- EasyNVR网页/微信播放RTSP摄像机HLS/RTMP播放时出现起播等待问题的优化过程
EasyNVR 项目中, 我们需要在网页/微信中嵌入 HLS 播放器, 实现直播效果. 开发过程中, 我们调研了很多HLS播放器, 包括 百度cyberplayer, ckplayer, flowpl ...
- [置顶]
针对 CoordinatorLayout 及 Behavior 的一次细节较真
我认真不是为了输赢,我就是认真.– 罗永浩 我一直对 Material Design 很感兴趣,每次在官网上阅读它的相关文档时,我总会有更进一步的体会.当然,Material Design 并不是仅仅 ...
- RxJava 1.x 笔记:过滤型操作符
我真的是奇怪,上下班的路上看书.看文章学习的劲头特别大,到了周末有大把的学习时间,反而不珍惜,总想打游戏,睡前才踏踏实实地写了篇文章,真是服了自己! 本文内容为 RxJava 官方文档 学习笔记 作者 ...
- BeautifulSoup的安装和使用
Python用做数据处理还是相当不错的,如果你想要做爬虫,python是很好的选择,它有很多已经写好的类包,只要调用,即可完成很多复杂的功能,此文中所有的功能都是基于BeautifulSoup这个包. ...
- Encode Adjacent Letters
Encode a string by counting the consecutive letter. (i.e., "aaaabbxxxyyz" might become &qu ...
- html(对php也有效)页面自动刷新和跳转(简单版本)
<html> <head><title>html页面自动刷新和跳转</title><meta http-equiv="Refres ...
- 微信网页登录Tips
http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html 以这篇文章为例,一般都是用户在第三方app中登录时,由第三方去申请资源服务器的登录权限等.即 ...
- windows主机与virtualbox虚拟机下的Linux共享网络
环境: 主机:windows7 虚拟机:virtualbox 4.2 虚拟系统:CentOS6.2 需求: 1.虚拟机linux可以共享主机网络上互联网 2.主机.虚拟机互通讯,组成一个虚拟的局域网, ...