SpringBoot之集成Spring AOP

在开始之前,我们先把需要的jar包添加到工程里。新增Maven依赖如下:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

接下来,我们进入正题。这里的涉及的通知类型有:前置通知、后置最终通知、后置返回通知、后置异常通知、环绕通知,下面我们就具体的来看一下怎么在SpringBoot中添加这些通知。

首先我们先创建一个Aspect切面类:

@Component
@Aspect
public class WebControllerAop {

}

指定切点:

    //匹配com.zkn.learnspringboot.web.controller包及其子包下的所有类的所有方法
    @Pointcut("execution(* com.zkn.learnspringboot.web.controller..*.*(..))")
    public void executeService(){

    }

接着我们再创建一个Controller请求处理类:

package com.zkn.learnspringboot.web.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * Created by zkn on 2016/11/19.
 */
@RestController
@RequestMapping("/aop")
public class AopTestController {

}

前置通知

配置前置通知:

    /**
     * 前置通知,方法调用前被调用
     * @param joinPoint
     */
    @Before("executeService()")
    public void doBeforeAdvice(JoinPoint joinPoint){
        System.out.println("我是前置通知!!!");
        //获取目标方法的参数信息
        Object[] obj = joinPoint.getArgs();
        //AOP代理类的信息
        joinPoint.getThis();
        //代理的目标对象
        joinPoint.getTarget();
        //用的最多 通知的签名
        Signature signature = joinPoint.getSignature();
        //代理的是哪一个方法
        System.out.println(signature.getName());
        //AOP代理类的名字
        System.out.println(signature.getDeclaringTypeName());
        //AOP代理类的类(class)信息
        signature.getDeclaringType();
        //获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        //从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
        //如果要获取Session信息的话,可以这样写:
        //HttpSession session = (HttpSession) requestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION);
        Enumeration<String> enumeration = request.getParameterNames();
        Map<String,String> parameterMap = Maps.newHashMap();
        while (enumeration.hasMoreElements()){
            String parameter = enumeration.nextElement();
            parameterMap.put(parameter,request.getParameter(parameter));
        }
        String str = JSON.toJSONString(parameterMap);
        if(obj.length > 0) {
            System.out.println("请求的参数信息为:"+str);
        }
    }

注意:这里用到了JoinPoint和RequestContextHolder。通过JoinPoint可以获得通知的签名信息,如目标方法名、目标方法参数信息等。通过RequestContextHolder来获取请求信息,Session信息。

接下来我们在Controller类里添加一个请求处理方法来测试一下前置通知:

    @RequestMapping("/testBeforeService.do")
    public String testBeforeService(String key,String value){

        return "key="+key+"  value="+value;
    }

前置通知拦截结果如下所示:


后置返回通知

配置后置返回通知的代码如下:

    /**
     * 后置返回通知
     * 这里需要注意的是:
     *      如果参数中的第一个参数为JoinPoint,则第二个参数为返回值的信息
     *      如果参数中的第一个参数不为JoinPoint,则第一个参数为returning中对应的参数
     * returning 限定了只有目标方法返回值与通知方法相应参数类型时才能执行后置返回通知,否则不执行,对于returning对应的通知方法参数为Object类型将匹配任何目标返回值
     * @param joinPoint
     * @param keys
     */
    @AfterReturning(value = "execution(* com.zkn.learnspringboot.web.controller..*.*(..))",returning = "keys")
    public void doAfterReturningAdvice1(JoinPoint joinPoint,Object keys){

        System.out.println("第一个后置返回通知的返回值:"+keys);
    }

    @AfterReturning(value = "execution(* com.zkn.learnspringboot.web.controller..*.*(..))",returning = "keys",argNames = "keys")
    public void doAfterReturningAdvice2(String keys){

        System.out.println("第二个后置返回通知的返回值:"+keys);
    }

Controller里添加响应的请求处理信息来测试后置返回通知:

    @RequestMapping("/testAfterReturning.do")
    public String testAfterReturning(String key){

        return "key=: "+key;
    }
    @RequestMapping("/testAfterReturning01.do")
    public Integer testAfterReturning01(Integer key){

        return key;
    }

当发送请求为:http://localhost:8001/aop/testAfterReturning.do?key=testsss&value=855sss时,处理结果如图所示:

当发送请求为:http://localhost:8001/aop/testAfterReturning01.do?key=55553&value=855sss时,处理结果如图所示:


后置异常通知

后置异常通知的配置方式如下:

    /**
     * 后置异常通知
     *  定义一个名字,该名字用于匹配通知实现方法的一个参数名,当目标方法抛出异常返回后,将把目标方法抛出的异常传给通知方法;
     *  throwing 限定了只有目标方法抛出的异常与通知方法相应参数异常类型时才能执行后置异常通知,否则不执行,
     *      对于throwing对应的通知方法参数为Throwable类型将匹配任何异常。
     * @param joinPoint
     * @param exception
     */
    @AfterThrowing(value = "executeService()",throwing = "exception")
    public void doAfterThrowingAdvice(JoinPoint joinPoint,Throwable exception){
        //目标方法名:
        System.out.println(joinPoint.getSignature().getName());
        if(exception instanceof NullPointerException){
            System.out.println("发生了空指针异常!!!!!");
        }
    }

Controller里配置响应的请求处理类:

    @RequestMapping("/testAfterThrowing.do")
    public String testAfterThrowing(String key){

        throw new NullPointerException();
    }

后置异常通知方法的处理结果如下所示:


后置最终通知

后置最终通知的配置方式如下:

    /**
     * 后置最终通知(目标方法只要执行完了就会执行后置通知方法)
     * @param joinPoint
     */
    @After("executeService()")
    public void doAfterAdvice(JoinPoint joinPoint){

        System.out.println("后置通知执行了!!!!");
    }

Controller类配置相应的请求处理类:

    @RequestMapping("/testAfter.do")
    public String testAfter(String key){

        throw new NullPointerException();
    }
    @RequestMapping("/testAfter02.do")
    public String testAfter02(String key){

        return key;
    }

当发送请求为:http://localhost:8001/aop/testAfter.do?key=55553&value=855sss


当发送请求为:http://localhost:8001/aop/testAfter02.do?key=55553&value=855sss

环绕通知

环绕通知的配置方式如下:

    /**
     * 环绕通知:
     *   环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值。
     *   环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型
     */
    @Around("execution(* com.zkn.learnspringboot.web.controller..*.testAround*(..))")
    public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
        System.out.println("环绕通知的目标方法名:"+proceedingJoinPoint.getSignature().getName());
        try {
            Object obj = proceedingJoinPoint.proceed();
            return obj;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }

Controller对应的请求处理类如下:

    @RequestMapping("/testAroundService.do")
    public String testAroundService(String key){

        return "环绕通知:"+key;
    }

当发送请求为:http://localhost:8001/aop/testAroundService.do?key=55553

当发送请求为:http://localhost:8001/aop/testAfter02.do?key=55553&value=855sss时,不符合环绕通知的切入规则,所以环绕通知不会 执行。

完整的AOP配置代码如下:

package com.zkn.learnspringboot.aop;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Enumeration;
import java.util.Map;

/**
 * Created by zkn on 2016/11/18.
 */
@Component
@Aspect
public class WebControllerAop {

    //匹配com.zkn.learnspringboot.web.controller包及其子包下的所有类的所有方法
    @Pointcut("execution(* com.zkn.learnspringboot.web.controller..*.*(..))")
    public void executeService(){

    }

    /**
     * 前置通知,方法调用前被调用
     * @param joinPoint
     */
    @Before("executeService()")
    public void doBeforeAdvice(JoinPoint joinPoint){
        System.out.println("我是前置通知!!!");
        //获取目标方法的参数信息
        Object[] obj = joinPoint.getArgs();
        //AOP代理类的信息
        joinPoint.getThis();
        //代理的目标对象
        joinPoint.getTarget();
        //用的最多 通知的签名
        Signature signature = joinPoint.getSignature();
        //代理的是哪一个方法
        System.out.println(signature.getName());
        //AOP代理类的名字
        System.out.println(signature.getDeclaringTypeName());
        //AOP代理类的类(class)信息
        signature.getDeclaringType();
        //获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        //从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
        //如果要获取Session信息的话,可以这样写:
        //HttpSession session = (HttpSession) requestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION);
        Enumeration<String> enumeration = request.getParameterNames();
        Map<String,String> parameterMap = Maps.newHashMap();
        while (enumeration.hasMoreElements()){
            String parameter = enumeration.nextElement();
            parameterMap.put(parameter,request.getParameter(parameter));
        }
        String str = JSON.toJSONString(parameterMap);
        if(obj.length > 0) {
            System.out.println("请求的参数信息为:"+str);
        }
    }

    /**
     * 后置返回通知
     * 这里需要注意的是:
     *      如果参数中的第一个参数为JoinPoint,则第二个参数为返回值的信息
     *      如果参数中的第一个参数不为JoinPoint,则第一个参数为returning中对应的参数
     * returning 限定了只有目标方法返回值与通知方法相应参数类型时才能执行后置返回通知,否则不执行,对于returning对应的通知方法参数为Object类型将匹配任何目标返回值
     * @param joinPoint
     * @param keys
     */
    @AfterReturning(value = "execution(* com.zkn.learnspringboot.web.controller..*.*(..))",returning = "keys")
    public void doAfterReturningAdvice1(JoinPoint joinPoint,Object keys){

        System.out.println("第一个后置返回通知的返回值:"+keys);
    }

    @AfterReturning(value = "execution(* com.zkn.learnspringboot.web.controller..*.*(..))",returning = "keys",argNames = "keys")
    public void doAfterReturningAdvice2(String keys){

        System.out.println("第二个后置返回通知的返回值:"+keys);
    }

    /**
     * 后置异常通知
     *  定义一个名字,该名字用于匹配通知实现方法的一个参数名,当目标方法抛出异常返回后,将把目标方法抛出的异常传给通知方法;
     *  throwing 限定了只有目标方法抛出的异常与通知方法相应参数异常类型时才能执行后置异常通知,否则不执行,
     *      对于throwing对应的通知方法参数为Throwable类型将匹配任何异常。
     * @param joinPoint
     * @param exception
     */
    @AfterThrowing(value = "executeService()",throwing = "exception")
    public void doAfterThrowingAdvice(JoinPoint joinPoint,Throwable exception){
        //目标方法名:
        System.out.println(joinPoint.getSignature().getName());
        if(exception instanceof NullPointerException){
            System.out.println("发生了空指针异常!!!!!");
        }
    }

    /**
     * 后置最终通知(目标方法只要执行完了就会执行后置通知方法)
     * @param joinPoint
     */
    @After("executeService()")
    public void doAfterAdvice(JoinPoint joinPoint){

        System.out.println("后置通知执行了!!!!");
    }

    /**
     * 环绕通知:
     *   环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值。
     *   环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型
     */
    @Around("execution(* com.zkn.learnspringboot.web.controller..*.testAround*(..))")
    public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
        System.out.println("环绕通知的目标方法名:"+proceedingJoinPoint.getSignature().getName());
        try {//obj之前可以写目标方法执行前的逻辑
            Object obj = proceedingJoinPoint.proceed();//调用执行目标方法
            return obj;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }
}

完整的Controller类代码如下:

package com.zkn.learnspringboot.web.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * Created by zkn on 2016/11/19.
 */
@RestController
@RequestMapping("/aop")
public class AopTestController {

    @RequestMapping("/testBeforeService.do")
    public String testBeforeService(String key,String value){

        return "key="+key+"  value="+value;
    }
    @RequestMapping("/testAfterReturning.do")
    public String testAfterReturning(String key){

        return "key=: "+key;
    }
    @RequestMapping("/testAfterReturning01.do")
    public Integer testAfterReturning01(Integer key){

        return key;
    }
    @RequestMapping("/testAfterThrowing.do")
    public String testAfterThrowing(String key){

        throw new NullPointerException();
    }
    @RequestMapping("/testAfter.do")
    public String testAfter(String key){

        throw new NullPointerException();
    }
    @RequestMapping("/testAfter02.do")
    public String testAfter02(String key){

        return key;
    }
    @RequestMapping("/testAroundService.do")
    public String testAroundService(String key){

        return "环绕通知:"+key;
    }
}
时间: 2024-10-03 21:53:14

SpringBoot之集成Spring AOP的相关文章

SSH集成框架下真正实现Spring AOP拦截功能

问题的提出: 在Struts1框架下,有三种类型的Action控制器,分别是MappingDispatchAction.DispatchAction和Action,他们是依次继承,最终执行的execute方法.但MappingDispatchAction.DispatchAction的子类中没有execute方法,只有参数指定的具体方法,而这些方法是被MappingDispatchAction.DispatchAction本类的execute方法调用执行,特别注意的是它是通过反射机制来做的(大家

Spring Boot中集成Spring Security 专题

if语句中条件判断就是检查当前的url请求是否是logout-url的配置值,接下来,获取用户的authentication,并循环调用处理器链中各个处理器的logout()函数,前面在parse阶段说过,处理器链中有两个实例,处理会话的SecurityContextLogoutHandler及remember-me服务,我们来一一看看它们的logout函数实现: 2.1.0 SecurityContextLogoutHandler public void logout(HttpServletR

J2EE中使用Spring AOP框架和EJB组件

j2ee 快速发展的开发人员社区.对各种后端技术(包括JMS.JTA.JDO.Hibernate.iBATIS等等)的支持,以及(更为重要的)非侵入性的轻量级IoC容器和内置的AOP运行时,这些因素使得Spring Framework对于J2EE应用程序开发十分具有吸引力.Spring托管的组件(POJO)可以与EJB共存,并允许使用AOP方法来处理企业应用程序中的横切方面--从监控和审计.缓存及应用程序级的安全性开始,直到处理特定于应用程序的业务需求. 本文将向您介绍Spring的AOP框架在

集成Spring和Struts的实例

本文想通过一个简单的实例阐述如何集成Spring和Struts. 1.Struts和Spring Struts 代表了MVC第二类架构的实现,在Struts中最重要的组件是 ActionServlet,Action和 ActionForm 子类,ActionServlet 代表controller ,他基于配置文件接受请求和 把这些请求转发到相应的ActionForm和Action子 类. ActionForm把用户输入的数据传送到Action,Action调用商务层组件完成 必要的操作,最后提

spring aop面向切面编程:如何来做一个强大的日志记录功能

这个东西怎么做:spring aop 面向切面编程 如何来做一个强大的日志记录功能模板; 昨天经理把这个任务交给我,让我为公司现在的项目加上一个详细的日志记录功能模板,对所有的操作,至少是增删改运作进行一个记录,其要记录操作者,以及执行的方法,IP,以及操作的方法的参数. 我以前做过类似的功能,不过是在filter里做的,通过filter来检查action请求,记录请求中的参数及action名字.但是今天公司这个是要求用spring aop来做,这样就可以在spring里对要进行的日志记录方法进

s2sh框架搭建(基于spring aop)

对于spring aop 是如何管理事务的,请看一下:http://bbs.csdn.net/topics/290021423 1.applicationContext.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3

Spring AOP框架

AOP正在成为软件开发的下一个圣杯.使用AOP,你可以将处理aspect的代码注入主程序,通常主程序的主要目的并不在于处理这些aspect.AOP可以防止代码混乱. 为了理解AOP如何做到这点,考虑一下记日志的工作.日志本身不太可能是你开发的主程序的主要任务.如果能将"不可见的".通用的日志代码注入主程序中,那该多好啊.AOP可以帮助你做到. Spring framework是很有前途的AOP技术.作为一种非侵略性的,轻型的AOP framework,你无需使用预编译器或其他的元标签,

spring AOP的方式监控方法的执行时间

前段时间有几个同行跟我吐槽说系统响应越来越慢,优化不知道从何入手!今天写写使用spring的aop来实现方法级的执行时间的记录监控,以此来评估方法的性能以及针对性的对已存在的方法进行优化. 对于监控,我们比较关注监控的可靠性和性能,准确,高效,这才能在不影响整体性能的情况下对我们的系统性能有个较准确的认识. 对于spring aop这个我就不多介绍了,网上一搜一大把,使用过spring的人都知道spring的ioc和aop.ioc我们常用,但在我们自己的系统中,aop的使用几乎为零,除了这个监控

基于Annotation拦截的Spring AOP权限验证方法

在 Web 开发过程中,一个非常理想的开发过程是,开发人员在开发中并不需要关心权限问题,不需要在 Java 方法中写 很多逻辑判断去判断用户是否具有合适的角色和权限,这样开发会花费非常多的人力成本,因为所有的开发人员都需要了解 关于权限的详细内容,也非常不容易进行后期维护.我们希望有专门的很少数量的开发人员了解权限内容,并且可以随时方 便的修改和配置.于是,我们使用 Annotation,在 Java 方法之前使用 Annotation 可以非常方便的添加,修改和删除对 于权限的管理功能. 本文