spring aop(一)

spring aop 是 spring 面向切面编程的实现,不影响原业务的基础上,加入通用的功能,如所有业务功能都需要加入登录功能,所有业务代码加入输入输出日志,加入参数校验,加入统一的异常处理等,都可以用aop 实现。OOP 的编程的补偿

在这里插入图片描述


1.概念:

1.目标对象(Target Object)::你写的业务类,要增强的类,通常也是指被代理被通知的对象。
2.连接点(JointPoint):程序执行过程中明确的点,一般是方法调用。被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的是被拦截的方法,实际上连接点还可以是字段或者构造器(自己的理解:目标方法的连接点 ,目标类的要被做操作的方法)
3.切面(Aspect): 通常是个类,里面可以定义切入点通知
4.切入点(Pointcut): 再切面中定义的切点,就是通知上的连接点,再程序中主要体现为书写切入点表达式。

5.织入(weave): 切面应用到目标对象并导致代理对象创建过程
6.引入(introduction):再不修改代码的前提下,引入可以再运行期为类动态地添加一些方法或字段
7.AOP 代理(AOP Proxy):AOP 框架创建的对象,代理就是目标对象的加强。Spring 中的AOP 代理可以使JDK动态代理,也可以是CGLAB 代理,前者基于接口,后者基于子类。

8.通知(Advice): AOP 再特定的切入点上执行的增强处理,有before(前置),after(后置),afterReturning(最终),afterThrowing(异常),around(环绕)
(1)Before:在目标方法被调用之前做增强处理,@Before只需要指定切入点表达式即可

(2)AfterReturning:在目标方法正常完成后做增强,@AfterReturning除了指定切入点表达式后,还可以指定一个返回值形参名returning,代表目标方法的返回值

(3)AfterThrowing:主要用来处理程序中未处理的异常,@AfterThrowing除了指定切入点表达式后,还可以指定一个throwing的返回值形参名,可以通过该形参名

来访问目标方法中所抛出的异常对象

(4)After:在目标方法完成之后做增强,无论目标方法时候成功完成。@After可以指定一个切入点表达式

(5)Around:环绕通知,在目标方法完成前后做增强处理,环绕通知是最重要的通知类型,像事务,日志等都是环绕通知,注意编程中核心是一个ProceedingJoinPoint

9.横切关注点::对哪些方法进行拦截,拦截后怎么处理,这些关注点被称为横切关注点


2.使用场景:

Authentication 权限
Caching 缓存
Context passing 内容传递
Error handling 错误处理
Lazy loading 懒加载
Debugging  调试
logging, tracing, profiling and monitoring 记录跟踪 优化 校准
Performance optimization 性能优化
Persistence  持久化
Resource pooling 资源池
Synchronization 同步
Transactions 事务



3.使用AOP的几种方式:

1.经典的基于代理的AOP
2.@AspectJ注解驱动的切面
3.纯POJO切面(纯粹通过aop:fonfig标签配置)
4.注入式AspectJ切面

4.具体使用方式(Springboot aop 实现)

引入aop 依赖

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

不需要使用@EnableAspectJAutoProxy来启用切面,因为spring.aop.auto属性是默认开启的,也就是说引入aop 依赖默认添加了@EnableAspectJAutoProxy

当我们使用CGLIB 来实现aop 实现,需要配置spring.aop.proxy-target-class=true,不然默认使用的是标准java的实现。

实现代码

@Aspect
@Component
public class AopAspectDemo {

    private Logger logger = Logger.getLogger(getClass().getName());

    ThreadLocal<Long> startTime = new ThreadLocal<Long>();

    @Pointcut("execution(public * com.study.project.api.*.*(..))")
    public void webLog(){}

    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        startTime.set(System.currentTimeMillis());
        // 接收到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        // 记录下请求内容
        logger.info("URL : " + request.getRequestURL().toString());
        logger.info("HTTP_METHOD : " + request.getMethod());
        logger.info("IP : " + request.getRemoteAddr());
        logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));

    }

    @AfterReturning(returning = "ret", pointcut = "webLog()")
    public void doAfterReturning(Object ret) throws Throwable {
        // 处理完请求,返回内容
        logger.info("RESPONSE : " + ret);
        logger.info("SPEND TIME : " + (System.currentTimeMillis() - startTime.get()));
    }

}

aop切面中的同步问题:切面中通过doBefore 和 AfterReturning 两个独立的函数,计算运算请求处理时间,前置通知和返回后置通知处理一个变量,基本类型会有同步问题,所以要引入 ThrealLocal 对象。

由于通过aop实现,程序员得到了很好的解耦,但是也会带来一些问题,比如:我们可能会对web层做了多个切面,校验用户,校验信息等等,这时候就碰到处理顺序问题。
所以我们用@Order(i) 注解来标识切面的优先级。
在切入点前的操作,按order的值由小到大执行
在切入点后的操作,按order的值由大到小执行

参考博客:aop 概念和方案
springboot aop 实现


要克服生活的焦虑和沮丧,得先学会做自己的主人,学会感恩,学会打磨生活,有问题留言,没问题留下你的赞
博客声明:
1.博客内容全是对工作学习的总结。
2.知识点都经过测试和推敲,如有疑问请留言,一定及时解决。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章