Spring | Spring AOP (以“AspectJ框架“开发实现AOP功能)


在这里插入图片描述

作者简介 :一只大皮卡丘,计算机专业学生,正在努力学习、努力敲代码中! 让我们一起继续努力学习!

该文章参考学习教材为:
《Java EE企业级应用开发教程 (Spring + Spring MVC +MyBatis)》 黑马程序员 / 编著
文章以课本知识点 + 代码为主线,结合自己看书学习过程中的理解和感悟 ,最终成就了该文章

文章用于本人学习使用 , 同时希望能帮助大家。
欢迎大家点赞👍 收藏⭐ 关注💖哦!!!

(侵权教材方可联系我,进行删除,如果雷同,纯属巧合)


1.JDK动态代理、CGLIB代理、AspectJ 三者的区别 ?

  • JDK动态代理CGLIB代理实现AOP (面向切面编程) 功能的技术,而AspectJ是一个 AOP框架

  • JDK动态代理CGLIB代理动态代理AspectJ静态代理

  • 实现方式
    JDK动态代理基于接口动态代理,只能对实现了接口的类生成代理

    CGLIB动态代理是针对 实现代理,原理是对指定的目标类生成一个子类,并覆其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理

    Aspect基于编译时期代理它是在编译时期就将代理逻辑织入目标类,在运行期间不再需要任何代理逻辑,所以它 比JDK动态代理和CGLIB动态代理的性能要好

  • ④如果目标对象实现了接口默认情况Spring会采用 JDK动态代理 实现AOP。
    如果目标对象没有实现接口,会采用CGLIB代理,Spring会自动在JDK动态代理和CGLIB之间转换

  • 性能:
    AspectJ静态代理 是基编译时期的代理性能优于 JDK动态代理 和 CGLIB动态代理。

2.为什么AspectJ的性能更好?

为什么 AspectJ性能JDK动态代理CGLIB 更好

  • 这是由于AspectJ编译阶段代码织入class文件,能够 避免运行时 “动态生成代理类” 所带来的额外开销
    同时,由于AspectJ对字节码的处理更加成熟和稳定,因此,它可以 大大减少由于字节码处理问题带来的性能损耗。因此,AspectJ的性能相对较好

3.AspectJ开发

  • AspectJ 是一个基于 Java语言AOP框架,它提供了强大的AOP功能Spring2.0以后,Spring AOP引入了AspectJ 的支持,并允许直接使用 AspectJ 进行编程,而Spring自身的AOP API也尽量与 AspectJ 保持一致。
  • 新版本的Spring框架建议使用AspectJ开发AOP
  • 使用AspectJ实现AOP两种方式 :
    一种基于XML声明式AspectJ另一种是基于 注解声明式AspectJ

3.1 基于“XML”的声明式AspectJ

AspectJ 的定义
  • 基于XML的声明式AspectJ 是指通过 XML文件定义切面切入点通知。所有的切面、切入点和通知都必须定义在<aop:config>元素内
    <aop:config>元素其子元素 如下图所示 :

    在这里插入图片描述

    在上图中,Spring 配置文件中的 <beans>元素 下可以包含多个 <aop:config> 元素,一个<aop:config>元素中又可以包含属性子元素,其子元素包括 <aop:pointcut><aop:advisor><aop:aspect>。在配置时,这3个子元素必须按照此顺序来定义。在 <aop:aspec> 元素下,同样包含了属性多个子元素通过使用 <aop:aspect> 元素及其子元素就可以在XIML文件中配置切面切入点通知

aop:config (开始AspectJ开发)

上述图片中配置标签详解

  • <aop:config> :
    开始进行AspectJ开发 (Spring AOP 开发) ,在其中配置相关信息。
    例子如 :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

<!--  可在该配置文件中添加、配置和关闭Bean  -->
<!--   也已在该配置文件中配置AOP切面等 -->

 <!--  将切面类添加到IOC容器中 (配置切面Bean)  -->
 <bean id="myAspectJ" class="com.myh.aspectJ.xml.MyAspect"></bean>

 <!--  用<aop:config>标签开始进行AspectJ开发 (Spring AOP 开发)  -->
 <aop:config>
    .....
 </aop:config>
</beans>
aop:aspect (配置切面)
  • <aop:aspect> (配置切面):
    在Spring的配置文件中,配置切面使用的是 <aop:aspect>元素,该元素会将一个已定义好的Spring Bean转换成切面Bean (定义好哪个Bean/类切面类),建议在配置文件中先配置文件中 “切面类” 交给IOC管理,成为Bean,在 <aop:aspect> 中用 ref属性 引用 已配置好的Bean则会方便开发,其中一个常指定的属性为 id 属性。

    属性名称 描述
    id 用于定义切面唯一标识名称
    ref 用于引用普通的Spring Bean

    例子如

     <!--  用<aop:config>标签开始进行AspectJ开发 (Spring AOP 开发)  -->
        <aop:config>
            <!-- 配置切面 -->
            <aop:aspect id="aspect" ref="myAspectJ">
               .....
            </aop:aspect>
        </aop:config>
    
aop:pointcut (配置切入点)
  • <aop:pointcut> (配置切入点):

在Spring的配置文件中,可通过 <aop:pointcut>元素来定义切入点 (目标类中的“方法名”)的。当 <aop:pointct元素作为 <aop:config>元素的子元素定义时,表示该切入点全局切入点,它可被多个切面共享;当 <aop:pointcut>元素作为 <aop:aspect>元素的子元素时,表示该切入点 只对当前切面有效
(即该切入点对该切面有效)

在这里插入图片描述

<aop:pointcut>中通常会指定idexpression 两个属性

属性名称 描述
id 用于指定切入点的“唯一标识名称”。
expression 用于指定切入点关联的“切入点表达式”。

例子如

<!-- 配置切入点(切入点: 方法名) -->
<!-- 该"切入点表达式"的配置方式为: 开发中常用的配置方式,而非“基本格式” -->
<aop:pointcut expression="execution(* com.myh.jdk.*.*(..))" id="myPoinCut" />
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

<!--  可在该配置文件中添加、配置和关闭Bean  -->
<!--   也已在该配置文件中配置AOP切面等 -->

   <!--  将切面类添加到IOC容器中 (配置切面Bean)  -->
   <bean id="myAspectJ" class="com.myh.aspectJ.xml.MyAspect"></bean>

   <!--  用<aop:config>标签开始进行AspectJ开发 (Spring AOP 开发)  -->
   <aop:config>
       <!-- 配置切面 -->
       <aop:aspect id="aspect" ref="myAspectJ">
           <!-- 配置切入点(切入点: 方法名) -->
           <!-- 该"切入点表达式"的配置方式为: 开发中常用的配置方式 -->
           <aop:pointcut expression="execution(* com.myh.jdk.*.*(..))" id="myPoinCut" />

           <!-- 配置5种通知-->
           <!-- 前置通知-->
           <aop:before method="myBefore" pointcut-ref="myPoinCut"/>
           <!-- 后置通知-->
           <aop:after-returning method="myAfterReturing" pointcut-ref="myPoinCut" returning="returnVal"/>
           <!-- 环绕通知-->
           <aop:around method="myAround" pointcut-ref="myPoinCut"/>
           <!-- 异常通知-->
           <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPoinCut"/>
           <!-- 最终通知-->
           <aop:after method="myAfer" pointcut-ref="myPoinCut"/>
       </aop:aspect>
   </aop:config>
</beans>

在上述代码中 execution( com.myh.jdk..(…))表示定义的 切入点表达式,该表达式的含义 : 匹配com.myh.jdk包下的任意类任意方法执行*。
其中 execution() 是表达式的主体
第1个* 表示的是返回类型;使用*代表所有类型。 comitheima.jdk 表示的是需要拦截包名
后面第2个*表示的是类名,使用*代表所有的类。 第3个*表示的是方法名,使用*表示所有方法
后面 (…) 表示方法的参数,其中的“…"表示任意参数。 (第1个 *与包名之间有一个空格。)
ps :
该"切入点表达式"的配置方式为 : 开发中常用的配置方式
拓展 :

上面代码中的“切入点表达式开发中常用的配置方式,Spring AOP切入点表达式基本格式 如下
execution ( modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern ( param-pattern) throws-pattern )

具体讲解 :

modifiers-pattern :
表示定义的目标方法访问修饰符,如public private等。
ret-type-pattern :
表示定义的目标方法返回值类型,如void、 String等。
declaring-type-pattern :
表示定义的目标方法类路径,如com.myh.jdk.UserDaolmpl.
name-pattern :
表示具体需要被代理目标方法,如add()方法。
param-pattern :
表示需要被代理的目标方法包含的参数

throws-pattern : 表示需要被代理的目标方法抛出异常类型

其中带 有问号(? )部分,如modfere ptte dlang-vpo paten和howo-aten表示可配置项;而 其他部分 属于 必须配置项

aop:before aop:after-returning aop:around aop:after-throwing aop:after (配置通知)
  • 配置 五种常用的通知
    <aop:before> : 配置“前置通知”。
    <aop:after-returning> : 配置“后置通知”。
    <aop:around> : 配置“环绕通知”。
    <aop:after-throwing> : 配置“异常通知”。
    <aop:after> : 配置“最终通知”。
    ps :(注意点) :

    在AOP的配置信息中,使用 <aop:after-returning> 配置的后置通知和使用 <aop:after> 配置的最终通知虽然都是在目标方法执行之后执行,但它们也是有所区别的
    后置通知 只有在目标方法 成功执行 后才会被织入,而 最终通知 不论目标方法如何结束 ( 包括 成功执行异常中止两种情况),它都会被织入

  • 属性名称 描述
    pointcut 该属性用于指定一个切入点表达式, Spring 将在匹配该表达式连接点时织入该通知
    pointcut-ref 该属性指定一个已经存在切入点名称配置代码中的myPointCut。通常pointcut 和 pointcut-ref两个属性只需要使用其中之一。
    method 属性指定一个方法名, 指定将==切面Bean中该方法转换为增强处理==。
    returning 该属性只对<after-returning>元素有效,它用于指定一个形参名后置通知方法可通知该形参访问目标方法返回值
    throwing 该属性只对**<after-throwing>元素有效,它用于指定一个形参名**, 异常通知方法可以通过该形
    访问目标方法所抛出的异常

    “通知”例子如

       <!-- 配置5种通知-->
                <!-- 前置通知-->
                <aop:before method="myBefore" pointcut-ref="myPoinCut"/>
                <!-- 后置通知-->
                <aop:after-returning method="myAfterReturing" pointcut-ref="myPoinCut" returning="returnVal"/>
                <!-- 环绕通知-->
                <aop:around method="myAround" pointcut-ref="myPoinCut"/>
                <!-- 异常通知-->
                <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPoinCut"/>
                <!-- 最终通知-->
                <aop:after method="myAfer" pointcut-ref="myPoinCut"/>
    
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <!--  可在该配置文件中添加、配置和关闭Bean  -->
    <!--   也已在该配置文件中配置AOP切面等 -->
    
        <!--  将切面类添加到IOC容器中 (配置切面Bean)  -->
        <bean id="myAspectJ" class="com.myh.aspectJ.xml.MyAspect"></bean>
    
        <!--  用<aop:config>标签开始进行AspectJ开发 (Spring AOP 开发)  -->
        <aop:config>
            <!-- 配置切面 -->
            <aop:aspect id="aspect" ref="myAspectJ">
                <!-- 配置切入点(切入点: 方法名) -->
                <!-- 该"切入点表达式"的配置方式为: 开发中常用的配置方式 -->
                <aop:pointcut expression="execution(* com.myh.jdk.*.*(..))" id="myPoinCut" />
    
                <!-- 配置5种通知-->
                <!-- 前置通知-->
                <aop:before method="myBefore" pointcut-ref="myPoinCut"/>
                <!-- 后置通知-->
                <aop:after-returning method="myAfterReturing" pointcut-ref="myPoinCut" returning="returnVal"/>
                <!-- 环绕通知-->
                <aop:around method="myAround" pointcut-ref="myPoinCut"/>
                <!-- 异常通知-->
                <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPoinCut"/>
                <!-- 最终通知-->
                <aop:after method="myAfer" pointcut-ref="myPoinCut"/>
                
            </aop:aspect>
        </aop:config>
    </beans>
    

3.2 基于“XML”的声明式AspectJ 的 “完整例子演示”

  • 第一步、在DIEA中创建一个Java项目,添加web功能模块。

  • 第二步、在项目WEB-INF目录中创建lib文件夹,在原有的Spring框架最基本核心的jar包的基础上 加上 spring-aspects.jaraspectjweaver.jar让jar包生效(导入AspectJ 框架开发所需的JAR)

  • jar包 / maven( 依赖 ) 下载
    获取spring框架基本核心jar包
    AspectJ 框架开发需要的jar包

  • 第三步 创建UserDao接口UserDaoImpl实现类 (目标类)
    UserDao.java

    package com.myh.aspectJ.xml;
    
    public interface UserDao{
          
    
         public void addUser();
    
         public void deleteUser();
    }
    

    UserDaoImpl.java

    package com.myh.aspectJ.xml;
    
    public class UserDaoImpl implements UserDao {
           // ”目标类“,该类创建的对象为”目标对象“
    
        @Override
        public void addUser() {
            //这方法将要被“增强处理”
            System.out.println("--------------------------");
            System.out.println("添加用户 (此执行的是: 目标方法)");
            System.out.println("--------------------------");
        }
    
        @Override
        public void deleteUser() {
           //这方法将要被“增强处理”
            System.out.println("删除用户");
        }
    }
    
  • 第三步 创建 MyAspect (切面类)

    MyAspect.java

    package com.myh.aspectJ.xml;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    
    public class MyAspect {
           //切面类 : 以下为常用的5种通知,将用于“增强处理”
    
        /**
         * 前置通知
         */
        public void myBefore(JoinPoint joinPoint) {
          
            System.out.println("前置通知,模拟执行了...操作,");
            //获得"目标类"
            System.out.println("目标类为: "+joinPoint.getTarget());
            //获得“目标方法”  ("目标方法"将被植入“增强处理”)
            System.out.println("目标方法为:" +joinPoint.getSignature().getName());
        }
    
        /**
         * 后置通知
         */
        public void myAfterReturing(JoinPoint joinPoint) {
          
            System.out.println("后置通知,模拟执行了...操作,");
            //获得目标方法
            System.out.println("目标方法为: "+joinPoint.getSignature().getName());
        }
    
        /**
         *  环绕通知
         *  ProceedingJoinPoint 是 JoinPoint的”子接口“ : 可以执行目标方法。
         *  1.必须是Object类型的返回值
         *  2.必须接收一个参数,类型为 ProceedingJoinPoint
         *  3.必须throws Throwable
         */
        public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
           //ProceedingJoinPoint : 可以执行目标方法
            //环绕通知"开始"
            System.out.println("环绕通知开始,执行目标方法之前,模拟执行了...操作");
            //执行目标方法
            Object obj = proceedingJoinPoint.proceed();
    
            //环绕通知"结束"
            System.out.println("环绕通知结束,执行目标方法之后,模拟执行了...操作");
            return obj;
        }
    
        //异常通知
        public void myAfterThrowing(JoinPoint joinPoint,Throwable e) {
          
            System.out.println("异常通知: " + "出错了" +e.getMessage());
        }
    
        //最终通知
        public void myAfer() {
          
            System.out.println("最终通知,模拟方法结束后的释放资源...");
        }
    }
    

    在上面MyAspect类中,分别定义了5种不同类型通知,在通知中使用了 JoinPoint 接口其子
    接口 PoceedingJoinPoint 作为参数来获得目标对象类名目标方法名目标方法参数等。

    ps :
    环绕通知必须接收一个类型ProceedingJoinPoint参数
    返回值也必须是Object类型,且必须抛出异常
    异常通知中可以传入Throwable类型的参数来输出异常信息。

  • 第四步 创建 applicationContext.xml (xml配置文件)
    applicationContext.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!--
         创建配置applicationContext,
         通过<bean>等标签可以添加、管理Bean
         通过<aop:config> 等标签进行配置AspectJ框架进行AOP开发所需的配置信息
         -->
    
        <!--  1.目标类Bean  -->
        <bean id="userDao" class="com.myh.aspectJ.xml.UserDaoImpl"></bean>
        <!--  2.切面类Bean  -->
        <bean id="myAspect" class="com.myh.aspectJ.xml.MyAspect"></bean>
    
        <!--  3.开始AspectJ框架的AOP编程  -->
         <aop:config>
            <!-- 3.1配置切面  -->
             <aop:aspect ref="myAspect">
                 <!-- 3.2配置“切入点” : 对"哪些方法“进行”增强处理“/通知  -->
                 <aop:pointcut  expression="execution(* com.myh.aspectJ.xml.*.*(..))" id="myPointCut"/>
                 <!-- 3.3将"各种通知"和"切入点"进行两者结合 (即配置通知/对"切入点"进行增强处理)  -->
                 <aop:before method="myBefore" pointcut-ref="myPointCut"/>  <!-- 前置通知  -->
                 <!--
                   后置通知,在方法执行之后执行,就可以获得返回值。
                   returning属性 : 用于设置后置通知的第二个参数的名称,类型是Object
                   -->
                 <aop:after-returning method="myAfterReturing" pointcut-ref="myPointCut" returning="returnVal"/>
                 <aop:around method="myAround" pointcut-ref="myPointCut"/>  <!-- 环绕通知  -->
                 <!--
                   异常通知 : 抛出异常,用于处理程序发生异常。如果没有异常,则不会执行该增强
                   throwing属性 : 用于设置"通知的第二个参数"的名称,类型是Throwable
                   -->
                 <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/>
                 <!-- 最终通知 : 无论程序发生任何事情,都将执行  -->
                 <aop:after method="myAfer" pointcut-ref="myPointCut"/>
             </aop:aspect>
         </aop:config>
    </beans>
    
  • 第四步 创建 TestXmlAspectj (测试类)
    TestXmlAspectj.java

    package com.myh.aspectJ.xml;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestXmlAspectj {
           //测试类 : 测试AspectJ框架下的XML开发的代码内容
        public static void main(String[] args) {
          
            String xmlPath = "com/myh/aspectJ/xml/applicationContext.xml";
            //通过"类路径"的方式来获取.xml配置文件,依次创建ApplicationContext
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
            //从IOC容器中获得内容
            UserDao userDao  = (UserDao)applicationContext.getBean("userDao");
            //执行方法(此时会通知AspectJ框架中的xml配置信息,实现AOP功能 : 面向切面编程)
            userDao.addUser();
        }
    }
    

    (方法执行成功) 运行效果图
    在这里插入图片描述

    查看异常通知的执行效果 :

    在这里插入图片描述

    由上图可知使用基于XML的声明式AspectJ框架编程已经实现AOP开发

3.3 基于“注解”的声明式AspectJ

  • 与基于代理类AOP实现相比,基于XML声明式 ApectJ 开发便捷得多,但是它也存在着一些缺点,那就是 要在 .xml配置文件配置大量的代码信息

  • 为了解决这个问题AspectJ框架为AOP的实现提供了一套注解,用以取代Spring配置文件中为实现AOP功能所配置的臃肿代码

  • AspectJ注解及其描述

    注解名称 描述
    @Aspect 用于 定义一个切面
    @Pointcut 用于定义切入点表达式。在使用时还需定义一个包含名字任意参数方法签名 来表示 切入点名称
    实际上,这个 方法签名就是一个返回值为 void,且方法体为空普通的方法
    (这样,这个普通的 方法名就是“带有切入点表达式”的切入点名)
    @Before 用于定义 前置通知,相当于BeforeAdvice。在使用时,通常需要指定一个 value属性值,该属性值用于指定一个切入点表达式
    (此处的 value属性 相当于.xml配置文件中的 pointcut-ref 属性)
    (可以是已有的切入点,也可以直接定义切入点表达式)
    @AfterReturning 用于定义 后置通知,相当于AfteRturingAdvice.在使用时可以指定poitcut / valulereturning属性,其中pointcut / value这两个属性的作用是一样,都用于指定切点表达式
    returning属性值用于表示Advice方法中可定义与此同名形参,该形参可用于访问目标方法返回值
    @Around 用于定义 环绕通知,相当于Methodlnterceptor在使用时需要指定一个value属性,该属任用于指定该通知被植入切入点
    @AferThrowing 用于定义 异常通知处理程序未处理的异常,相当于ThrowAdvice。在使用时可指定 pointcut / value throwing属性。其中pointcut / value用于指定切入点表达式,而throwing属性值用于指定一个形参名表示Advice方法中定义与此同名形参,该形参可用于访问目标方法抛出异常
    @After 用于定义 最终final通知不管是否异常,该通知都会执行。使用时需要指定一个 value属性, 该属性用于指定该通知被植入切入点
    @DeclareParents 用于定义 引介通知,相当于ItroductionInterceptor (不要求掌握)。

    注意点 :
    如果在同一个连接点多个通知需要执行,那么在 同一切面 中, 目标方法之前前置通知 和环绕通知执行顺序是未知的,目标方法之后后置通知环绕通知执行顺序也是未知的。

3.4 基于“注解”的声明式AspectJ “完整例子演示”

  • 第一步、在DIEA中创建一个Java项目,添加web功能模块。

  • 第二步、在项目WEB-INF目录中创建lib文件夹,在原有的Spring框架最基本核心的jar包的基础上 加上 spring-aspects.jaraspectjweaver.jar让jar包生效(导入AspectJ 框架开发所需的JAR)

  • jar包 / maven( 依赖 ) 下载
    获取spring框架基本核心jar包
    AspectJ 框架开发需要的jar包

  • 第三步 创建UserDao接口UserDaoImpl实现类 (目标类)
    UserDao.java

    package com.myh.aspectJ.annotation;
    
    public interface UserDao{
          
    
         public void addUser();
    
         public void deleteUser();
    }
    

    UserDaoImpl.java

    package com.myh.aspectJ.annotation;
    
    import org.springframework.stereotype.Repository;
    
    @Repository(value = "userDao") //将类加入到IOC容器中
    public class UserDaoImpl implements UserDao {
           // ”目标类“,该类创建的对象为”目标对象“
    
        @Override
        public void addUser() {
            //这方法将要被“增强处理”
    System.out.println("-----------------------------------------------------");
            //int i = 10 / 0;
            System.out.println("添加用户 (此执行的是: 目标方法 )---AspectJ的基于注解开发实现AOP功能");
    System.out.println("-----------------------------------------------------");
        }
    
        @Override
        public void deleteUser() {
           //这方法将要被“增强处理”
            System.out.println("删除用户");
        }
    }
    
  • 第三步 创建 MyAspect (切面类)

    MyAspect.java

    package com.myh.aspectJ.annotation;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    @Aspect //定义切面
    @Component //将该类加入到IOC容器中,作为一个Bean
    public class MyAspect {
           //切面类 :以下通过AspectJ框架的"注解"的方式来实现 AOP(面向切面编程)功能
    
        //定义“切入点表达式”
        @Pointcut("execution(* com.myh.aspectJ.annotation.*.*(..))")
        private void myPointCut() {
          } //使用一个返回值为void,方法体为空的方法来命名切入点
    
        /**
         * 前置通知
         */
        @Before(value = "myPointCut()") //对“切入点”进行“增强处理”
        public void myBefore(JoinPoint joinPoint) {
          
            System.out.println("前置通知,模拟执行了...操作,");
            //获得"目标类"
            System.out.println("目标类为: "+joinPoint.getTarget());
            //获得“目标方法”  ("目标方法"将被植入“增强处理”)
            System.out.println("目标方法为:" +joinPoint.getSignature().getName());
        }
    
        /**
         * 后置通知
         */
        @AfterReturning(value = "myPointCut()")
        public void myAfterReturing(JoinPoint joinPoint) {
          
            System.out.println("后置通知,模拟执行了...操作,");
            //获得目标方法
            System.out.println("目标方法为: "+joinPoint.getSignature().getName());
        }
    
        /**
         *  环绕通知
         *  ProceedingJoinPoint 是 JoinPoint的”子接口“ : 可以执行目标方法。
         *  1.必须是Object类型的返回值
         *  2.必须接收一个参数,类型为 ProceedingJoinPoint
         *  3.必须throws Throwable
         */
        @Around(value = "myPointCut()")
        public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
           //ProceedingJoinPoint : 可以执行目标方法
            //环绕通知"开始"
            System.out.println("环绕通知开始,执行目标方法之前,模拟执行了...操作");
            //执行目标方法
            Object obj = proceedingJoinPoint.proceed();
    
            //环绕通知"结束"
            System.out.println("环绕通知结束,执行目标方法之后,模拟执行了...操作");
            return obj;
        }
    
        /**
         异常通知
         */
        @AfterThrowing(value = "myPointCut()",throwing = "e")
        public void myAfterThrowing(JoinPoint joinPoint,Throwable e) {
          
            System.out.println("异常通知: " + "出错了" +e.getMessage());
        }
    
        /**
          最终通知
         */
        @After(value = "myPointCut()")
        public void myAfer() {
          
            System.out.println("最终通知,模拟方法结束后的释放资源...");
        }
    }
    

    在上面的MyAspect.java中,首先使用 @Aspect 注解定义了切面类,由于该Spring中是作为组件使用的,所以还需要添加 @Component注解 (将其交给IOC容器管理,成为Bean) 才能生效。然后使用了 @Poincut注解来配置切入点表达式,并通过 定义方法表示切入点名称。每个方法都添加了对应的注解,并将切入点名称"'myPointCut"作为参数传递需要执行增强通知方法。如果需要其他参数(如
    异常通知的异常参数),可以根据代码提示传递相应的属性值。

  • 第四步 创建 applicationContext.xml ( xml配置文件 )
    applicationContext.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           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">
    
        <!--  该配置文件为AspectJ的“注解开发”:实现AOP功能 服务 -->
    
        <!--  指定需要扫描的包 : 因为前面 @Component等注解,用该“标签”来让注解生效  -->
        <context:component-scan base-package="com.myh.aspectJ.annotation"/>
    
        <!--  启动基于注解的声明式 AspectJ支持 : 让AspectJ的注解开发下使用的注解能生效  -->
        <!--  aspectj-autoproxy : aspectj自动代理 (启动AspectJ的"注解开发")  -->
        <aop:aspectj-autoproxy/>
    </beans>
    

    在上面的applicationContext.xml中,首先引入了context 约束信息,然后使用<context>元素设置了需的包,使注解生效可将带有指定注解加入到IOC容器成为Bean)。 最后,使用 <aop:aspectj-autoproxy />启动Srping基于注解声明式AspectJ支持

  • 第五步 创建 TestAnnotatioin(测试类)
    TestAnnotatioin.java

    package com.myh.aspectJ.annotation;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestAnnotatioin {
           //测试类 : 测试AspectJ的基于"注解"开发来实现AOP功能
        public static void main(String[] args) {
          
            String xmlPath = "com/myh/aspectJ/annotation/applicationContext.xml";
            //通过"类路径"的方式来获取.xml配置文件,依次创建ApplicationContext
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
            //从IOC容器中获得内容
            UserDao userDao  = (UserDao)applicationContext.getBean("userDao");
            //执行方法(此时会通知AspectJ框架中的xml配置信息,实现AOP功能 : 面向切面编程)
            userDao.addUser();
        }
    }
    

    (方法执行成功) 运行效果图
    在这里插入图片描述

    由上图可知,基于注解的方式 与基于xml的方式执行结果相同,相对来说,使用注解的方式更加简单方便,开发中也是推荐使用基于注解方式来进行AOP开发

    查看异常通知的执行效果 :

    在这里插入图片描述

相关推荐

  1. AspectJ框架实现AOP简介

    2024-01-29 10:56:02       13 阅读
  2. Spring AOPAspectJ AOP区别

    2024-01-29 10:56:02       19 阅读
  3. spring AOPAspectJ AOP的区别

    2024-01-29 10:56:02       16 阅读
  4. 使用 @AspectJ 注解配置 Spring AOP

    2024-01-29 10:56:02       18 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-01-29 10:56:02       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-01-29 10:56:02       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-01-29 10:56:02       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-01-29 10:56:02       18 阅读

热门阅读

  1. COMM394 Coding Literacy for Managers

    2024-01-29 10:56:02       30 阅读
  2. docker入门 问题一

    2024-01-29 10:56:02       38 阅读
  3. 状态管理与导航守卫

    2024-01-29 10:56:02       33 阅读
  4. Ubuntu系统桌面卡死,解决办法

    2024-01-29 10:56:02       35 阅读
  5. 计算机视觉(CV)技术的优势和挑战

    2024-01-29 10:56:02       31 阅读
  6. 【漏洞复现】金蝶云星空-AppDesigner-反序列化-rce

    2024-01-29 10:56:02       35 阅读
  7. Vue3+Echarts实现实时曲线及开始与暂停功能

    2024-01-29 10:56:02       31 阅读