SpringBoot学习日记四(整合实现AOP-自定义注解式拦截&方法规则拦截)

AOP的介绍我就不说了,大家自行百度。

还是springBootDemo这个项目,使用aop,先在pow.xml中引入依赖。

<!--AOP依赖,此依赖已包含AspectJ相关依赖包。-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <version>2.0.6.RELEASE</version>
</dependency>

添加依赖之后我们就可以去实现AOP的一个简单例子。

我们首先在com.lds.springbootdemo下新建aop包。既然是注解实现就先在这个aop包下新建一个自定义注解:

AnnotationAOP.java

package com.lds.springbootdemo.aop;
import java.lang.annotation.*;

/**
 * @program: springBootDemo
 * @description: 使用AOP注解拦截
 * @author:
 * @createData:
 * @updateAuthor:
 * @updateData:
 * @updateContent: 使用AOP注解拦截
 * @Version: 1.0
 * @email: lidongshenglife@163.com
 * @blog: www.b0c0.com
 */
    /**
     * 定义了该注解可以出现在哪里。常用的:
     * TYPE:类, 接口 (包括注释类型), 或 枚举 声明
     *     FIELD:字段声明(包括枚举常量)
     *     METHOD:方法声明
     */
    @Target(ElementType.METHOD)
    /**
     * Reteniton的作用是定义被它所注解的注解保留多久
     * SOURCE
     * 被编译器忽略。
     * CLASS
     * 注解将会被保留在Class文件中,但在运行时并不会被VM保留。
     * 这是默认行为,所有没有用Retention注解的注解,都会采用这种策略。
     * RUNTIME
     * 保留至运行时。所以我们可以通过反射去获取注解信息。
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface AnnotationAOP {
        String name();
    }

然后在com.lds.springbootdemo.aop包下面新建一个aspect包。里面来存放我们的切面。

我们来新建一个TestAspect.java

TestAspect.java

package com.lds.springbootdemo.aop.aspect;

import com.lds.springbootdemo.aop.AnnotationAOP;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

/**
 * @program: springBootDemo
 * @description: AOP拦截切面类
 * @author: lidongsheng
 * @createData:
 * @updateAuthor: lidongsheng
 * @updateData:
 * @updateContent: AOP拦截切面类
 * @Version: 1.0
 * @email: lidongshenglife@163.com
 * @blog: www.b0c0.com
 */
@Aspect
@Component
public class TestAspect {

    /**
     * 注解是拦截定义切入点,切入点为com.example.aop下的所有函数
     */
    @Pointcut("@annotation(com.lds.springbootdemo.aop.AnnotationAOP)")
    public void annotationPoinCut(){
    }

    /**
     * 前置通知,方法调用前被调用 执行顺序:1
     * @param joinPoint/null
     */
    @Before("annotationPoinCut()")
    public void before(JoinPoint joinPoint){
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        AnnotationAOP action = method.getAnnotation(AnnotationAOP.class);
        Parameter[] ps =method.getParameters();
        System.out.println("该方法执行之前:注解式拦截.第一个参数:"+ps[0].toString()+"自定义注解的name值"+action.name());
    }
    /**
     * 后置通知(目标方法只要执行完了就会执行后置通知方法,不管是否出现异常) 属于方法规则式拦截 执行顺序:2
     * @param joinPoint
     */
    @After("execution(* com.lds.springbootdemo.controller.login_register.LoginController.loginValidate(..))")
    public void after(JoinPoint joinPoint){
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        AnnotationAOP action = method.getAnnotation(AnnotationAOP.class);
        System.out.println("该方法执行之后:方法规则式拦截,"+method.getName());
    }
    /**
     * 后置返回通知  在切入点return内容之后切入内容(可以用来对处理返回值做一些加工处理) 执行顺序:3
     * 这里需要注意的是:
     *      如果参数中的第一个参数为JoinPoint,则第二个参数为返回值的信息
     *       returning:限定了只有目标方法返回值与通知方法相应参数类型时才能执行后置返回通知,否则不执行,
     *       对于returning对应的通知方法参数为Object类型将匹配任何目标返回值
     * @param joinPoint
     * @param keys
     */
    @AfterReturning(value = "annotationPoinCut()",returning = "keys")
    public void doAfterReturningAdvice1(JoinPoint joinPoint,Object keys){
        System.out.println("该方法执行返回结果:注解式拦截 "+keys);
    }

}

如何使用呢?就好比我们想在登录的service中去进行aop自定义的注解去拦截来实现具体的方法。 然后我们就在登录的LoginServiceImpl的loginValidate方法上面加上:

@AnnotationAOP(name = “loginValidate”)

name就是我们自定义注解接口中写的name属性。而name的值自己随便定义,就是来达到自定义功能的效果。所以自定义注解的里面的属性自己随便定义来达到我们预期的功能即可。

LoginServiceImpl.java

package com.lds.springbootdemo.service.login_register;

import com.lds.springbootdemo.aop.AnnotationAOP;
import com.lds.springbootdemo.dao.DaoSupport;
import com.lds.springbootdemo.domain.sbd_user;
import com.lds.springbootdemo.mapper.sbd_userMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @program: springbootdemo
 * @description:
 * @author:
 * @createData: 
 * @updateAuthor:
 * @updateData: 
 * @updateContent:
 * @Version: 1.0
 * @email: lidongshenglife@163.com
 * @blog: www.b0c0.com
 */

@Service
public class LoginServiceImpl implements LoginService {

    //@Autowired
    //private sbd_userMapper userMapper;
    @Autowired
    private DaoSupport dao;
    /**
     * 登录验证Service
     * @return
     */
    @Override
    @AnnotationAOP(name = "loginValidate")
    public boolean loginValidate(String account,String password) {
        boolean retu=false;
        //sbd_user em=userMapper.selectByAccount(account);
        sbd_user user= null;
        try {
            user = (sbd_user) dao.selectForOne("sbd_userMapper.selectByAccount",account);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if(user!=null&&password.equals(user.getPassword())){
            retu=true;
        }
        return retu;
    }
}

然后我们就可以启动项目,去在浏览器中登录,然后通过查看控制台的输出结果来确定是否拦截成功了。

运行结果如下图所示:

上面只是用到了两个 @Before、@After、@AfterReturning。

其实最常用的还有@Around环绕通知也是最重要的通知类型,在目标方法完成前后做增强处理,像事务,日志等都是环绕通知。

TestAspect.java

package com.lds.springbootdemo.aop.aspect;

import com.lds.springbootdemo.aop.AnnotationAOP;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

/**
 * @program: springBootDemo
 * @description: AOP拦截切面类
 * @author: lidongsheng
 * @createData:
 * @updateAuthor: lidongsheng
 * @updateData:
 * @updateContent: AOP拦截切面类
 * @Version: 1.0
 * @email: lidongshenglife@163.com
 * @blog: www.b0c0.com
 */
@Aspect
@Component
public class TestAspect {


//    /**
//     * 注解是拦截定义切入点,切入点为com.example.aop下的所有函数
//     */
//    @Pointcut("@annotation(com.lds.springbootdemo.aop.AnnotationAOP)")
//    public void annotationPoinCut(){
//    }
//
//    /**
//     * 前置通知,方法调用前被调用 执行顺序:1
//     * @param joinPoint/null
//     */
//    @Before("annotationPoinCut()")
//    public void before(JoinPoint joinPoint){
//        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//        Method method = signature.getMethod();
//        AnnotationAOP action = method.getAnnotation(AnnotationAOP.class);
//        Parameter[] ps =method.getParameters();
//        System.out.println("注解式拦截 该方法执行之前:第一个参数:"+ps[0].toString()+"    自定义注解的name值:"+action.name());
//    }
//
//    /**
//     * 后置通知(目标方法只要执行完了就会执行后置通知方法,不管是否出现异常) 执行顺序:2
//     * @param joinPoint
//     */
//    @After("execution(* com.lds.springbootdemo.service.login_register.LoginServiceImpl.loginValidate(..))")
//    public void after(JoinPoint joinPoint){
//        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//        Method method = signature.getMethod();
//        AnnotationAOP action = method.getAnnotation(AnnotationAOP.class);
//        System.out.println("方法规则式拦截 该方法执行之后:"+method.getName());
//    }
//    /**
//     * 后置返回通知  在切入点return内容之后切入内容(可以用来对处理返回值做一些加工处理) 执行顺序:3
//     * 这里需要注意的是:
//     *      如果参数中的第一个参数为JoinPoint,则第二个参数为返回值的信息
//     *       returning:限定了只有目标方法返回值与通知方法相应参数类型时才能执行后置返回通知,否则不执行,
//     *       对于returning对应的通知方法参数为Object类型将匹配任何目标返回值
//     * @param joinPoint
//     * @param keys
//     */
//    @AfterReturning(value = "annotationPoinCut()",returning = "keys")
//    public void doAfterReturningAdvice1(JoinPoint joinPoint,Object keys){
//        System.out.println("注解式拦截 该方法执行返回结果: "+keys);
//    }

    /**
     * 环绕通知 在目标方法完成前后做增强处理,也是最重要的通知类型,像事务,日志等都是环绕通知。
     * @param joinPoint
     * @return
     */
    //@Around("annotationPoinCut()") ProceedingJoinPoint是子类,只能用在@Around中
    @Around("execution(* com.lds.springbootdemo.service.login_register.LoginServiceImpl.loginValidate(..))")
    public Object invoke(ProceedingJoinPoint joinPoint) {
        //.这里获取到所有的参数值的数组
        Object[] parameterValues = joinPoint.getArgs();
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        //.通过这获取到方法的所有参数名称的字符串数组
        String[] parameterNames = methodSignature.getParameterNames();
        //得到执行方法的方法名
        String methodName=methodSignature.getMethod().getName();
        System.out.println("环绕拦截前");
        Object obj = null;
        try {
            obj = joinPoint.proceed();//调用执行目标方法并得到执行方法的返回值
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("环绕拦截执行方法出错");
        }
        System.out.println("环绕拦截后");
        return obj;
    }


}

如果是自定义注解拦截就直接在目标方法上面加自定义注解,如果方法规则拦截直接在写切入点表达式来写要拦截的规则即可。

运行结果如下:

我已经把这个的实例放在git中了,欢迎大家拉取下载以及指出错误,可以的话给标个小星星~~~地址:

https://github.com/DeBug-Bug/springBootDemo.git

发表评论

电子邮件地址不会被公开。