【Spring】—AOP 概念、原理及应用

前言

    上回说到SpringIOC的实现原理,这次来说下AOP的实现原理。

正文

AOP 思想

     AOP(Aspect Orient Programming),面向切面编程,作为面向对象的一种补充,用于处理系统中分布于各个模块的横切关注点,比如事务管理、日志、异常处理等。
    为了将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,Spring 提供了面向切面的编程方式,形成一个独立的切面,AOP可以将这部分独立的代码动态的切入到指定方法、指定位置中。
《【Spring】—AOP 概念、原理及应用》
     横向重复,纵向抽取

概念

  • Aspect (切面)
        一个关注点的模块化,这个关注点可能会横切多个对象,切面由切入点和通知组成。
  • Adivice(通知/增强)
         想要在目标方法出增加的功能或操作,包括前置、后置、环绕等通知类型,很多时候就是所说的拦截器。
  • Pointcut(切入点)
         目标对象中已经增强的方法,就是说在连接点的基础上选中想要的方法,使用通知。
  • JointPoint (连接点)
         在目标对象中所有可以增强的方法,Spring中允许你通知的地方,Spring只支持方法连接点,和方法有关的前前后后都是连接点。
  • Target(目标)
  •     被一个或者多个切面增强的对象,也就是被通知的对象,应为Spring AOP是通过动态代理实现的,所以这个对象也是被代理的对象。
  • Proxy(代理)
         将通知织入到目标对象之后,形成了代理对象。
  • Weaving(织入)
        将切面连接到切点,并形成一个代理对象的过程。
    《【Spring】—AOP 概念、原理及应用》
    通知类型
  •      前置通知(Before advice): 在某连接点之前执行的通知,但这个通知不能阻止连接点之前的执行流程(除非抛出异常)。
  •      后置通知(After returning advice):在某连接点正常完成后执行的通知。
  •      异常通知(After throw advice): 在方法抛出异常退出时执行的通知。
  •      最终通知(After/finally advice): 当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
  •      环绕通知(Around advice): 包围一个连接点的通知,如方法调用。这是一种最强大的通知类型,环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或者直接返回它自己的返回这或者抛出异常来结束执行。

原理

    以前我们要使用动态代理对象,需要自己调用JDK反射的Proxy 提供的newProxyInstance 方法来生成代理对象,具体可以看博客:JDK动态代理现在Spring可以帮我们生成动态代理对象
Spring 生成AOP的原理 :JDK 动态代理 和 CGlib 代理 。JDK动态代理中被代理对象必须要实现接口,才能产生代理对象,如果没有接口将不能使用动态代理;为了让所有的对象都能使用动态代理,Spring 引入了第三方代理技术:CGlib 代理,可以对任何类生成代理,代理的原理是对目标对象进行继承代理。
关于JDK 动态代理和CGlib代理的实现,可以看博客:JDK动态代理和CGlib代理

Spring中实现AOP的Demo(XML中配置)

    1、准备目标对象

package cn.itcast.service;

public interface UserService {
      void save();
      void delete();
      void find();
      void update();      
}
/*********************************************************/
package cn.itcast.service;

import org.springframework.stereotype.Service;

public class UserServiceImpl implements UserService {

    @Override
    public void save() {
        System.out.println("保存用户");

    }

    @Override
    public void delete() {
        System.out.println("删除用户");
    }

    @Override
    public void find() {
        System.out.println("查找用户");
    }

    @Override
    public void update() {
        System.out.println("更新用户");
    }

}

    2、准备通知

package cn.itcast.d_springaop;

import org.aspectj.lang.ProceedingJoinPoint;
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;

//通知类

public class MyAdvice {

    //前置通知 目标方法运行之前调用
    //后置通知 (如果出现异常不会调用) 目标方法运行之后调用
    //环绕通知 目标方法之前和之后都调用
    //异常拦截通知 如果出现异常,就会调用
    //后置通知(无论是否出现异常,都会调用) 在目标方法运行之后调用
    //---------------------------------------


    //前置通知
    public void before(){
        System.out.println("这是前置通知");
    }

    //后置通知
    public void afterReturning(){
        System.out.println("这是后置通知(如果出现异常不会调用 )");
        }
    //环绕通知
     public Object around(ProceedingJoinPoint pjp) throws Throwable{
            System.out.println("这是环绕通知(之前的部分 )");
            Object proceed = pjp.proceed();  //调用目标方法
            System.out.println("这是环绕通知(之后的部分)");
            return proceed;
        }

        //异常通知
        public void afterException(){
            System.out.println("出事啦!出现异常了!!");
        }

        //后置通知
        public void after(){
            System.out.println("这是后置通知(如果出现异常也会调用 )");
        }

}

    3、配置织入,将通知织入目标对象中(applicationContext.xml)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd ">
<!-- 1、 准备工作:导入aop(约束)命名空间 2、 配置目标对象 3、 配置通知对象 4、 配置将通知织入目标对象 -->
    <!-- 目标对象 -->
   <bean name = "userService" class = "cn.itcast.service.UserServiceImpl"></bean>
    <!-- 通知对象 -->
   <bean name = "myAdvice" class = "cn.itcast.d_springaop.MyAdvice"></bean>
    <!-- 配置将通知织入目标对象 -->
    <aop:config>

       <!-- 配置切入点 (用表达式体现) public void cn.itcast.service.UserServiceImpl.save() 默认值是public ,所以public 可以省略掉 返回值不做要求,* 号代替 -->
       <aop:pointcut expression="execution(* cn.itcast.service.UserServiceImpl.*(..))" id="pc"/>
       <aop:aspect ref = "myAdvice">
         <!-- 指定before方法作为前置通知,切入点为pc -->
          <aop:before method="before" pointcut-ref = "pc"/> 
          <!--后置 -->
          <aop:after-returning method="afterReturning" pointcut-ref = "pc"/> 
           <!--环绕通知 -->
          <aop:around method="around" pointcut-ref = "pc"/>
           <!--异常拦截通知 -->
          <aop:after-throwing method="afterException" pointcut-ref = "pc"/>
           <!--后置 -->
          <aop:after method="after" pointcut-ref = "pc"/>
       </aop:aspect>
    </aop:config>
</beans>

    4、测试(Junit测试)

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:cn/itcast/d_springaop/applicationContext.xml")
public class demo {

    @Resource(name="userService")
    private UserService us;
    @Test
    public void fun1(){
        us.save();

    }
}

输出结果:
《【Spring】—AOP 概念、原理及应用》

应用(部分)

  • 权限控制
  • 缓存
  • 错误处理
  • 日志、优化、监控
  • 事务

总结

    将AOP的东西梳理了下,在问自己问题的过程中也明白了些东西,感谢阅读!

    原文作者:AOP
    原文地址: https://blog.csdn.net/zt15732625878/article/details/81328460
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞