zoukankan      html  css  js  c++  java
  • 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的结果如下:

  • 相关阅读:
    某耳机厂商微信公众平台建设方案
    微信公众号订单好评红包返现系统
    LNMP的配置与优化
    微信公众号用户OpenID同步导出系统
    微信公众号无限定时群发系统
    小程序想要什么
    微信支付默认推荐关注规则
    微信小程序(应用号)资源汇总整理
    微信公众平台小程序开发教程
    微信应用号开发教程
  • 原文地址:https://www.cnblogs.com/duanxz/p/5226304.html
Copyright © 2011-2022 走看看