Spring-AOP (AOP底层实现、AOP术语、AOP实现)

时间:2021-10-11
本文章向大家介绍Spring-AOP (AOP底层实现、AOP术语、AOP实现),主要包括Spring-AOP (AOP底层实现、AOP术语、AOP实现)使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

AOP

  面向方面编程 (AOP) 通过提供另一种思考程序结构的方式来补充面向对象编程 (OOP)。OOP 中模块化的关键单位是类,

  而 AOP 中模块化的单位是方面。方面能够实现跨越多种类型和对象的关注点(例如事务管理)的模块化。

AOP 代理:

  (1)由 AOP 框架创建的对象,用于实现方面契约(建议方法执行等)。在 Spring Framework 中,AOP 代理是 JDK 动态代理或 CGLIB 代理。

  (2)AOP底层使用动态代理

      动态代理的两种情况:

      ①有接口的情况,使用JDK动态代理:创建接口实现类代理对象,增强类的方法

      

      ②没有接口的情况,使用CGLIB 代理:创建子类的代理对象,增强类的方法

       

  (3)AOP实现JDK动态代理

      不使用AOP进行动态代理:

      ① 使用 JDK 动态代理,使用 Proxy 类里面的方法创建代理对象

       

      调用Proxy中的newProxyInstance 方法 

      

方法有三个参数:
第一参数,类加载器
第二参数,增强方法所在的类,这个类实现的接口,支持多个接口
第三参数,实现这个接口 InvocationHandler,创建代理对象,写增强的部分 

      ③实现动态代理的代码:

        1、创建接口,定义方法

public interface UserDao {
   public int add(int a,int b);
   public String update(String id);
}

       

       2、创建接口实现类,实现方法

public class UserDaoImpl implements UserDao {
   @Override
   public int add(int a, int b) {
     return a+b;
     }
   @Override
   public String update(String id) {
     return id;
   }   
}

        

      3、使用Proxy类创建接口代理对象

public class JDKProxy {
   public static void main(String[] args) {
     //创建接口实现类代理对象
     Class[] interfaces = {UserDao.class};
    // Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
    // @Override
    // public Object invoke(Object proxy, Method method, Object[] args) 
    //  throws Throwable {
        // return null;
    // }
    // });
     UserDaoImpl userDao = new UserDaoImpl();
     UserDao dao = 
        (UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
     int result = dao.add(1, 2);
     System.out.println("result:"+result);
   } 
}

      4、创建代理对象

//创建代理对象代码
class UserDaoProxy implements InvocationHandler {
   //1 把创建的是谁的代理对象,把谁传递过来
   //有参数构造传递
   private Object obj;
   public UserDaoProxy(Object obj) {
   this.obj = obj;
   }
   //增强的逻辑
   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
     //方法之前
     System.out.println("方法之前执行...."+method.getName()+" :传递的参
        数..."+ Arrays.toString(args));
     //被增强的方法执行
     Object res = method.invoke(obj, args);
     //方法之后
     System.out.println("方法之后执行...."+obj);
     return res;
   } 
}

AOP术语

    1、连接点:类中哪些方法可以被增强,这些方法称为连接点

    2、切入点:实际被增强的方法,这些方法称为切入点

    3、通知(增强):实际增强的逻辑部分为通知(扩充的代码)

    4、通知的分类:

        (1)前置通知:在需要被增强的代码之前执行

        (2)后置通知:在需要被增强的代码之后执行

        (3)环绕通知:既有需要在被增强代码之前执行的代码,也有在被增强代码之后执行的代码

        (4)异常通知; 被增强的方法出现异常会执行

        (5)最终通知:一定会被执行的代码

      5、切面:是动作 (把通知应用到切点的过程)

AOP操作
  1、Spring 框架一般都是基于 AspectJ 实现 AOP 操作
    (1)AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使用,进行 AOP 操作
  
  2、基于AspectJ实现AOP同样有两种方法实现
    (1)基于xml配置方式
    (2)基于注解方式
  
  3、切入点表达式
    (1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强
    (2)语法结构: execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]) )
          举例 1:对 com.atguigu.dao.BookDao 类里面的 add 进行增强
            execution(* com.atguigu.dao.BookDao.add(..))      (".."代表方法的参数)
          举例 2:对 com.atguigu.dao.BookDao 类里面的所有的方法进行增强
            execution(* com.atguigu.dao.BookDao.* (..))

           举例 3:对 com.atguigu.dao 包里面所有类,类里面所有方法进行增强

            execution(* com.atguigu.dao.*.* (..))

   4、AspectJ注解实现

    (1)创建类,在类中定义需要增强的方法

public class User {
   public void add() {
   System.out.println("add.......");
   }   
}

    

    (2)创建增强类(编写增强逻辑):在增强类里面,创建方法,让不同方法代表不同通知类型

//增强的类
public class UserProxy {
     public void before() {//前置通知
     System.out.println("before......");
     } 
}

     

     (3)进行通知配置

      ①开启注解

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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">
    <!--开启注解扫描-->
    <context:component-scan base-package="com.jianghao.spring5.aopanno"></context:component-scan>

    <!--开启AspectJ生成代理对象
            在类中查找存在Aspect注解的对象,则把该对象设置成为代理对象
    -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

   ②使用注解创建 User 和 UserProxy 对象

//被增强的类
@Component
public class User {
    public void add(){
        System.out.println("add ......");
    }
}
  
      
//增强的类
@Component
public class UserProxy { }

      ③在被增强的类中添加@Aspect注解

@Component
@Aspect //生成代理对象
public class UserProxy {}

      ④在 spring 配置文件中开启生成代理对象

 <!--开启AspectJ生成代理对象
            在类中查找存在Aspect注解的对象,则把该对象设置成为代理对象
    -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

  

5、配置不同类型的通知

(1)在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置
//增强类
@Component
@Aspect
@Order(3)//@Order:定义了组件的加载顺序。值越小优先级越高
public class UserProxy {


    //相同切入点抽取
    @Pointcut(value = "execution(* com.jianghao.spring5.aopanno.User.add(..))")
    public void pointdemp(){
    }

    //前置通知
    @Before(value = "pointdemp()")
    public void before(){
        System.out.println("before.....");
    }

    //最终通知
    @After(value = "pointdemp()")
    public void after(){
        System.out.println("after.......");
    }

    //后置通知 :在返回结果后执行
    @AfterReturning(value = "pointdemp()")
    public void afterReturning(){
        System.out.println("afterReturning.......");
    }

    //异常通知
    @AfterThrowing(value = "pointdemp()")
    public void afterThrowing(){
        System.out.println("AfterThrowing.......");
    }

    //环绕通知 :当被增强方法中抛出错误,则环绕之后的代码不执行
    @Around(value = "execution(* com.jianghao.spring5.aopanno.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕之前.......");

        proceedingJoinPoint.proceed();

        System.out.println("环绕之后.......");
    }
}

6、切入点抽取:当一个类被多个方法增强时,将切入点表达式抽取出来,减少代码的冗余

 //相同切入点抽取,之后的注入可以使用该类名家"()"如:“ pointdemp()”
    @Pointcut(value = "execution(* com.jianghao.spring5.aopanno.User.add(..))")
    public void pointdemp(){
    }

  

7、有多个增强类多同一个方法进行增强,设置增强类优先级

  (1)在增强类上面添加注解 @Order(数字类型值),数字类型值越小优先级越高

//增强类
@Component
@Aspect
@Order(3)//@Order:定义了组件的加载顺序。值越小优先级越高
public class UserProxy {

8、完全注解开发

  (1)创建配置类

@Configuration
@ComponentScan(basePackages = {"com.jianghao"})//开启扫描
@EnableAspectJAutoProxy(proxyTargetClass = true)//开启Aspect生成代理对象
public class ConfigAop {
}

原文地址:https://www.cnblogs.com/zhu-bu-jin/p/15395362.html