zxpnet网站 zxpnet网站
首页
前端
后端服务器
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

zxpnet

一个爱学习的java开发攻城狮
首页
前端
后端服务器
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 大后端课程视频归档
  • 南航面试题
  • 并发编程

  • 性能调优

  • java8语法

  • lombok

  • 日志

  • 工具类

  • spring

    • Spring源码整体脉络介绍及源码编译
    • Ioc容器加载过程-Bean的生命周期源码深度剖析
    • 内置后置PostProcess处理器深度讲解
    • Untitled
    • IOC-Endgame
    • Spring-AOP源码探究
    • 基于注解构建项目
    • springmvc原理深度解析
    • SpringMVC常见面试题总结
    • Spring常见面试题总结
    • spring5基础
    • spring注解驱动开发笔记
    • webflux基础
    • spring工厂
    • Spring AOP 编程
      • aop的注解
      • SpringAOP的执行流程:
      • AOP执行顺序
      • 与SpringBoot整合
        • 引入starter
        • starter分析
        • 开启aop注解
        • 写代码
      • 应用场景
      • 注意事项
    • Spring 持久层
    • Spring 注解编程
    • spring整合springmvc
  • mybatis

  • springboot

  • redis

  • zookeeper

  • springcloud

  • dubbo

  • netty

  • springsecurity

  • mq消息中间件

  • shiro

  • beetle

  • 模板引擎

  • jpa

  • 数据结构与算法

  • 数据库知识与设计

  • gradle

  • maven

  • bus

  • 定时任务

  • docker

  • centos

  • 加解密

  • biz业务

  • pigx项目

  • 开源项目

  • 品达通用权限项目-黑马

  • 货币交易项目coin-尚学堂

  • php

  • backend
  • spring
shollin
2021-06-24
目录

Spring AOP 编程

  • aop的注解
  • SpringAOP的执行流程:
  • AOP执行顺序
  • 与SpringBoot整合
    • 引入starter
    • starter分析
    • 开启aop注解
    • 写代码
  • 应用场景
  • 注意事项

# aop的注解

@Before @After @AfterReturning @AfterThrowing @Around @Pointcut

AOP就是一种更高级的动态代理的使用; Aspect Oritention Programming(面向切面编程) 切入点:要加入业务逻辑的点(在哪些类的哪些方法上面) 通知:通知包含两个方面,1,代表方法的执行时间,2,在这个时间上面要做什么事情; 切面:一个切入点+一个通知=一个切面(在什么地方,在什么时候,做什么事情); 织入:把一个切面应用到真实对象上面的过程,就叫做织入;

# SpringAOP的执行流程:

1,解析xml; 2,实例化所有的bean; 3,解析aop:config;
1,解析aop:aspect,得到aspect引用的对象;txManager
2,解析aop:aspect里面的每一个切面;
1,得到该aspect对应的pointcut-ref;
2,得到pointcut-ref对应的pointcut的表达式;
3,使用表达式中用于匹配类型的表达式;
4,使用该表达式去和spring里面配置的所有的bean的类型进行匹配;
1,如果匹配不上,不管;
2,如果匹配上了,该对象作为spring动态代理的目标对象;
1,如果该对象实现了接口,使用JDK的动态代理包装;
2,如果该对象没有实现接口,使用cglib包装;
3,得到配置的拦截时机+逻辑提供类(txManager)的对应方法(从method解析)+pointcut表达式中方法的匹配模式创建一个拦截器
4,在把该拦截器使用对应的动态代理机制代理成代理对象;
5,替换spring容器中的对应bean的实例;

# AOP执行顺序

指定order,order越小越是最先执行

img

spring aop就是一个同心圆,要执行的方法为圆心,最外层的order最小。从最外层按照AOP1、AOP2的顺序依次执行doAround方法,doBefore方法。然后执行method方法,最后按照AOP2、AOP1的顺序依次执行doAfter、doAfterReturn方法。也就是说对多个AOP来说,先before的,一定后after。

如果我们要在同一个方法事务提交后执行自己的AOP,那么把事务的AOP order设置为2,自己的AOP order设置为1,然后在doAfterReturn里边处理自己的业务逻辑。

image-20210720075504073

@Around相当于一个蛋,里面先@Before,再执行切入点代码,@AfterReturning返回结果 @After 相当于try-catch-finally里面的finally,最后执行

参考文章:

Spring中AOP的执行流程_ming13849012515的博客-CSDN博客_aop执行流程 (opens new window)

# 与SpringBoot整合

# 引入starter

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
1
2
3
4

image-20210720075940755

# starter分析

AopAutoConfiguration: 配置了AspectJAutoProxyingConfiguration,默认使用了ClassProxyingConfiguration(引入starter就用这个做动态代理,不然就是cglib)、CglibAutoProxyConfiguration动态代理,设置spring.aop.proxy-target-class=false,可以使用jdk动态代理(这种方式原类必须要定义接口)

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {}
1
2
3

# 开启aop注解

@EnableAspectJAutoProxy(exposeProxy = true) // 开始aop注解,暴露动态代理
1

# 写代码

@Aspect
@Component
@Slf4j
public class CalcAop {

    @Pointcut("execution(public * com.zxp.springboot.service..CalServiceImpl.*(..) )")
    public void pointcut(){

    }

    @Before(value = "pointcut()")
    public void before(){
        log.info(" before ...");
    }

    @After(value = "pointcut()")
    public void after(){
        log.info(" After ...");
    }

    @Around(value = "pointcut()")
    public Object around(ProceedingJoinPoint pjp){

        log.info(" Around前 ...");
        Object result = null;
        try {
            result = pjp.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        log.info(" Around后 ...");
        return result;
    }
    @AfterReturning(value = "pointcut()")
    public void afterReturning(){
        log.info(" AfterReturning ...");
    }

    @AfterThrowing(value = "pointcut()")
    public void afterThrowing(){
        log.info(" AfterThrowing ...");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@Override
    public int div(int x, int y) {
        int result = x / y;
        Object o = AopContext.currentProxy(); // 获取动态代理对象
        log.info("动态代理对象:{} 执行结果:{}",o.getClass(), result);
        return result;
    }
1
2
3
4
5
6
7

# 应用场景

1、日志记录

2、权限控制

3、统一参数校验

4、异常拦截,一般用@ControllerAdvice

/**
 * 日志记录
 *
 * @author fengshuonan
 * @date 2016年12月6日 下午8:48:30
 */
@Aspect
@Component
public class LogAop {

    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Pointcut(value = "@annotation(cn.stylefeng.guns.base.log.BussinessLog)")
    public void cutService() {
    }

    @Around("cutService()")
    public Object recordSysLog(ProceedingJoinPoint point) throws Throwable {

        //先执行业务
        Object result = point.proceed();

        try {
            handle(point);
        } catch (Exception e) {
            log.error("日志记录出错!", e);
        }

        return result;
    }

    private void handle(ProceedingJoinPoint point) throws Exception {

        //获取拦截的方法名
        Signature sig = point.getSignature();
        MethodSignature msig = null;
        if (!(sig instanceof MethodSignature)) {
            throw new IllegalArgumentException("该注解只能用于方法");
        }
        msig = (MethodSignature) sig;
        Object target = point.getTarget();
        Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
        String methodName = currentMethod.getName();

        //如果当前用户未登录,不做日志
        LoginUser user = LoginContextHolder.getContext().getUser();
        if (null == user) {
            return;
        }

        //获取拦截方法的参数
        String className = point.getTarget().getClass().getName();
        Object[] params = point.getArgs();

        //获取操作名称
        BussinessLog annotation = currentMethod.getAnnotation(BussinessLog.class);
        String bussinessName = annotation.value();
        String key = annotation.key();
        Class dictClass = annotation.dict();

        StringBuilder sb = new StringBuilder();
        for (Object param : params) {
            sb.append(param);
            sb.append(" & ");
        }

        //如果涉及到修改,比对变化
        String msg;
        if (bussinessName.contains("修改") || bussinessName.contains("编辑")) {
            Object obj1 = LogObjectHolder.me().get();
            Map<String, String> obj2 = HttpContext.getRequestParameters();
            msg = Contrast.contrastObj(dictClass, key, obj1, obj2);
        } else {
            Map<String, String> parameters = HttpContext.getRequestParameters();
            AbstractDictMap dictMap = (AbstractDictMap) dictClass.newInstance();
            msg = Contrast.parseMutiKey(dictMap, key, parameters);
        }

        LogManager.me().executeLog(LogTaskFactory.bussinessLog(user.getId(), bussinessName, className, methodName, msg));
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/**
 * 权限检查的aop
 *
 * @author fengshuonan
 * @date 2017-07-13 21:05
 */
@Aspect
@Order(200)
public class PermissionAop {

    @Autowired
    private AuthService authService;

    @Pointcut(value = "@annotation(cn.stylefeng.guns.base.auth.annotion.Permission)")
    private void cutPermission() {

    }

    @Around("cutPermission()")
    public Object doPermission(ProceedingJoinPoint point) throws Throwable {
        MethodSignature ms = (MethodSignature) point.getSignature();
        Method method = ms.getMethod();
        Permission permission = method.getAnnotation(Permission.class);
        String[] permissions = permission.value();
        if (permissions.length == 0) {

            //检查全体角色
            boolean result = authService.checkAll();
            if (result) {
                return point.proceed();
            } else {
                throw new PermissionException();
            }

        } else {

            //检查指定角色
            boolean result = authService.check(permissions);
            if (result) {
                return point.proceed();
            } else {
                throw new PermissionException();
            }
        }
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@Aspect
@Order(510)
public class ParamValidateAop {
    public ParamValidateAop() {
    }

    @Pointcut("@annotation(cn.stylefeng.roses.kernel.validator.stereotype.ParamValidator)")
    private void cutService() {
    }

    @Around("cutService()")
    public Object doInvoke(ProceedingJoinPoint point) throws Throwable {
        Object[] methodParams = point.getArgs();
        if (methodParams != null && methodParams.length > 0) {
            CheckUtil.validateParameters(methodParams);
            return point.proceed();
        } else {
            return point.proceed();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * 参数校验器,静态方法调用
 * <p>
 * 手动验证带有校验注解的参数是否合法
 *
 * @author fengshuonan
 * @date 2020/10/31 14:13
 */
public class ValidatorUtil {

    /**
     * 验证器实例
     */
    private static final Validator VALIDATOR_INSTANCE = Validation.buildDefaultValidatorFactory().getValidator();

    /**
     * 校验参数是否合法,返回校验的结果
     *
     * @param object 被校验的包装类参数
     * @param groups 校验组
     * @return 参数校验的结果,为ConstraintViolation的集合
     * @author fengshuonan
     * @date 2020/10/31 14:13
     */
    public static Set<ConstraintViolation<Object>> validate(Object object, Class<?>... groups) {
        return VALIDATOR_INSTANCE.validate(object, groups);
    }

    /**
     * 校验参数是否合法,直接返回成功和失败
     *
     * @param object 被校验的包装类参数
     * @param groups 校验组
     * @return true-参数合法,false-参数不合法
     * @author fengshuonan
     * @date 2020/10/31 14:13
     */
    public static boolean simpleValidate(Object object, Class<?>... groups) {
        Set<ConstraintViolation<Object>> constraintViolations = VALIDATOR_INSTANCE.validate(object, groups);
        return constraintViolations.isEmpty();
    }

    /**
     * 校验参数是否合法,不返回结果,有问题直接抛出异常
     *
     * @param object 被校验的包装类参数
     * @param groups 校验组
     * @author fengshuonan
     * @date 2020/10/31 14:13
     */
    public static void validateThrowMessage(Object object, Class<?>... groups) {
        String errorMessage = validateGetMessage(object, groups);
        if (errorMessage != null) {
            throw new ParamValidateException(ValidatorExceptionEnum.VALIDATED_RESULT_ERROR, errorMessage);
        }
    }

    /**
     * 校验参数是否合法
     * <p>
     * 不合法会返回不合法的提示,合法的话会返回null
     *
     * @param object 被校验的包装类参数
     * @param groups 校验组
     * @author fengshuonan
     * @date 2020/10/31 14:13
     */
    public static String validateGetMessage(Object object, Class<?>... groups) {
        Set<ConstraintViolation<Object>> constraintViolations = VALIDATOR_INSTANCE.validate(object, groups);
        if (!constraintViolations.isEmpty()) {
            StringBuilder errorMessage = new StringBuilder();
            for (Iterator<ConstraintViolation<Object>> it = constraintViolations.iterator(); it.hasNext(); ) {
                ConstraintViolation<Object> violation = it.next();
                errorMessage.append(violation.getMessage());
                if (it.hasNext()) {
                    errorMessage.append(", ");
                }
            }
            return StrUtil.format(ValidatorExceptionEnum.VALIDATED_RESULT_ERROR.getUserTip(), errorMessage);
        }
        return null;
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

# 注意事项

  • 切面里面如果报异常,则不会往下执行

  • aop有顺序之分,可以用@Order(-100)定义顺序,越小越靠前执行

spring工厂
Spring 持久层

← spring工厂 Spring 持久层→

最近更新
01
国际象棋
09-15
02
成语
09-15
03
自然拼读
09-15
更多文章>
Theme by Vdoing | Copyright © 2019-2023 zxpnet | 粤ICP备14079330号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式