注解方式与XML方式实现AOP

62 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第11天,点击查看活动详情

哈喽,大家好!我是Why,一名在读学生,目前刚刚开始进入自己的编程学习生涯。虽然学习起步较晚,但我坚信做了才有0或1的可能。学了一段时间以后也是选择在掘金上分享自己的日常笔记,也希望能够在众多道友的大家庭中打成一片。 本文主要讲解注解方式与XML方式实现AOP,如果大家读后觉得有用的话,还请大家多多支持博主:欢迎 ❤️点赞👍、收藏⭐、留言💬 ✨✨✨个人主页:JinHuan

注解方式实现AOP

开发阶段:关注核心业务和AOP代码 运行阶段:spring框架会在运行的时候将核心业务和AOP代码通过动态代理的方式编织在一起代理方式的选择:是否实现了接口:有接口就选择JDK动态代理;没有就选择CGLIB动态代理。

1、创建项目引入依赖

 <dependencies>
     <!--spring 核心依赖-->
     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-context</artifactId>
         <version>5.2.13.RELEASE</version>
     </dependency>
     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-aspects</artifactId>
         <version>5.2.13.RELEASE</version>
     </dependency>
 ​
     <!--测试依赖-->
     <dependency>
         <groupId>junit</groupId>
         <artifactId>junit</artifactId>
         <version>4.12</version>
         <scope>test</scope>
     </dependency>
 <dependencies>
 <build>
     <plugins>
         <plugin>
          <!--编译依赖-->
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-compiler-plugin</artifactId>
             <version>3.8.0</version>
             <configuration>
                 <source>1.8</source>
                 <target>1.8</target>
             </configuration>
         </plugin>
     </plugins>
 </build>

2、创建spring配置文件引入约束

 <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"
        xmlns:context="http://www.springframework.org/schema/context"
        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
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context.xsd">
     <!--在beans标签中 引入AOP和context约束-->
 </beans>

3、创建核心业务类

 package com.jinhuan.service;
 ​
 import org.springframework.stereotype.Service;
 ​
 /**
  * @Author jinhuan
  * @Date 2022/7/4 17:35
  * Description:没有实现接口的Service
  */
 @Service
 public class BookService {
 ​
     public void add(Integer id,String name){
         System.out.println("BookService---add()---");
     }
 ​
     public String say(){
         System.out.println("BookService-----say()---");
         return "你好";
     }
 ​
     /**
      * 测试异常通知
      * */
     public void excep(){
         System.out.println("BookService-----ex()---");
         //int num = 1/0;
     }
 }

4、定义切面类

 package com.jinhuan.aop;
 ​
 import org.aspectj.lang.JoinPoint;
 import org.aspectj.lang.ProceedingJoinPoint;
 import org.aspectj.lang.annotation.*;
 import org.springframework.stereotype.Component;
 ​
 /**
  * @Author jinhuan
  * @Date 2022/5/16 11:12
  * Description:
  */
 ​
 @Component //切面对象的创建权限依旧交给spring
 //@Aspect //aspectj框架中的注解,表明当前类是一个切面
 public class MyAspect {
     /**
      * pointcut
      * 当较多的通知使用相同的execution切入点表达式的时候
      * 编写维护起来均十分麻烦
      * AspectJ提供了@PointCut注解在一个方法上
      * 使得以后所有的execution 的value属性值均可以使用这个方法名代替
      * 一般该方法使用private做为权限修饰
      * */
     @Pointcut(value = "execution(* com.jinhuan.service.*.*(..))")
     private void pointcut01(){}
     @Pointcut(value = "execution(* com.jinhuan.service.*.add(..))")
     private void pointcut02(){}
 ​
 ​
     /**
      * @Before 注解声明前置通知
      */
     @Before(value = "pointcut01()")
     public void before() {
         System.out.println("前置通知----before()------");
     }
 ​
     /**
      * @AfterReturning 注解声明后置通知
      *      value表名切入点的表达式
      *      returning表名 返回的结果
      * */
     @AfterReturning(value = "pointcut02()")
     public void after() {
         System.out.println("后置通知------after()-------");
     }
 ​
     /**
      * @param point:  环绕节点
      * @return java.lang.Object
      * decription:
      *  Around 注解声明环绕通知
      *  ProceedingJoinPoint中的proceed方法表示目标方法被执行
      *  有时候为了方便就把前置通知写进环绕通知里
      */
     @Around(value = "pointcut02()")
     public Object around(ProceedingJoinPoint point) throws Throwable {
         System.out.println("环绕方法执行之前--------");
         Object proceed = point.proceed();
         System.out.println("环绕方法执行值之后-------");
         return proceed;
     }
 ​
     /**
      * @AfterThrowing 注解声明异常通知方法
      * value表明切入点的表达式
      *
      * */
     @AfterThrowing(value = "pointcut01()",throwing = "ex")
     public void exception( JoinPoint jp,Throwable ex) {
         //一般会记录下异发生的时间、位置等
         System.out.println("异常通知------exception()-------");
         System.out.println(jp.getSignature()+"方法出现异常,异常信息为:"+ex.getMessage());
     }
 ​
 ​
     /**
      * @After 表示最终通知
      * */
     @After(value = "pointcut02()")
     public void myFinally() {
         System.out.println("结束通知------myFinally()-------");
     }
 }

5、业务类和切面类添加注解

在定义好切面 Aspect 后,需要通知 Spring 容器,让容器生成“目标类+ 切面”的代理对象。这个代理是由容器自动生成的。只需要在 Spring 配置文件中注册一个基于 aspectj 的自动代理生成器,其就会自动扫描到@Aspect 注解,并按通知类型与切入点,将其织入,并生成代理。

5、spring.xml配置文件中开启包扫描和 注册aspectj的自动代理

 <!--包扫描-->
 <context:component-scan base-package="com.kkb.service,com.kkb.aop"/>  
 <!--开启注解AOP的使用-->
  <aop:aspectj-autoproxy proxy-target-class="true"/>
 <!--aop:aspectj-autoproxy的底层是由 AnnotationAwareAspectJAutoProxyCreator 实现的,是基于 AspectJ 的注解适配自动代理生成器。其工作原理是,aop:aspectj-autoproxy通过扫描找到@Aspect 定义的切面类,再由切面类根据切入点找到目标类的目标方法,再由通知类型找到切入的时间点。-->

6、测试类

 package com.jinhuan.test;
 ​
 import com.jinhuan.service.BookService;
 import com.jinhuan.service.TeamService;
 import org.junit.Test;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.support.ClassPathXmlApplicationContext;
 ​
 /**
  * @Author jinhuan
  * @Date 2022/5/16 11:29
  * Description:
  */
 public class Test01 {
     @Test
     public void test(){
         ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
         BookService bookService = (BookService) ac.getBean("bookService");
         bookService.add(1,"666");
         System.out.println("=======分界线================");
         String say = bookService.say();
         bookService.excep();
 ​
 ​
         System.out.println("=======分界线================");
 ​
         /**
          * 测试多个被代理对象
          * */
         TeamService teamService = (TeamService) ac.getBean("teamService");
         teamService.add(1,"热火");
     }
 }

XML方式实现AOP

 package com.jinhuan.aop;
 ​
 import org.aspectj.lang.JoinPoint;
 import org.aspectj.lang.ProceedingJoinPoint;
 import org.aspectj.lang.Signature;
 import org.aspectj.lang.annotation.*;
 import org.springframework.stereotype.Component;
 ​
 /**
  * @Author jinhuan
  * @Date 2022/7/4 11:20
  * Description:使用XML方式实现AOP
  */
 @Component//切面对象的创建权限交给Spring
 @Aspect//Aspectj框架的注解,标识当前类是切面类
 public class MyAsp {
     public void before(JoinPoint jp) {
         System.out.println("MyAsp前置通知----before()------");
     }
 ​
     public void  around(ProceedingJoinPoint pjp) throws Throwable {
         System.out.println("MyAsp方法执行之前环绕------");
         Object proceed = pjp.proceed();//核心方法
         System.out.println("MyAsp方法执行之后环绕------");
     }
 ​
     public void after(Object result) {
         System.out.println("MyAsp后置通知------after()-------");
     }
 ​
     public void exception(JoinPoint jp,Throwable ex) {
         System.out.println("MyAsp异常通知------exception()-------");
         System.out.println(jp.getSignature()+"方法出现了"+ex.getMessage()+"异常");
     }
 ​
     public void myFinally() {
         System.out.println("MyAsp结束通知------myFinally()-------");
     }
 }
 <!--XML方式实现AOP-->
 <aop:config>
     <aop:pointcut id="pt1" expression="execution(* com.jinhuan.service.*.*(..))"/>
     <aop:pointcut id="pt2" expression="execution(* com.jinhuan.service.*.add(..))"/>
     <aop:aspect ref="myAsp">
         <aop:before method="before" pointcut-ref="pt1"/>
         <aop:after-returning method="after" pointcut-ref="pt1" returning="result"/>
         <aop:after-throwing method="exception" pointcut-ref="pt1" throwing="ex"/>
         <aop:after method="myFinally" pointcut-ref="pt1"/>
         <aop:around method="around" pointcut-ref="pt2"/>
     </aop:aspect>
 </aop:config>