Spring AOP实战技巧:让你的应用更加模块化与可维护

Spring AOP :面向切面编程

  1. 概述:
    1. AOP 是一种编程示范,隶属于软工范畴,知道开发者如何组织程序结构
    2. AOP 是 OOP 的延续,是软甲开发中的一个热点,也是 Spring 框架中的一个总要的内容,是函数式编程的一种衍生范式
    3. 利用 AOP 可以对业务逻辑的各个部分进行分离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
      1. AOP 面向切面编程
      2. AOP 采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)
  2. 优势:
    1. 运行期间不修改源码的情况下对已有的方法进行增强
    2. 减少重复的代码
    3. 提供开发的效率
    4. 维护方便
    5. 利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各个部分之间的耦合度降低提高程序的可重用性,同时提高开发的效
  3. AOP 底层原理:
    1. JDK 的动态代理技术:
      1. 为接口创建代理类的字节码文件
      2. 使用 ClassLoader 将字节码文件加载到 JVM
      3. 创建代理类实例对象,执行对象的目标方法
      4. 拓展:cglib 代理技术
  4. AOP 相关术语:
    1. Joinpoint(连接点):类当中的哪些方法可以增强,这些方法就是连接点
    2. Pointcut(切入点):实际被增强的方法就是切入点
    3. Advice(增强 / 通知):增强功能的代码逻辑
    4. Target(目标对象):代理目标的对象
    5. Weaving(织入):把增强应用到目标对象来创建新的代理对象的过程
    6. Proxy(代理):一个类别 AOP 织入后,就产生一个结果代理类
    7. Aspect(切面):是一个动作,切入点和通知的结合
  5. AOP 配置文件方式的入门:
    1. 创建 maven 项目,导入依赖:
      <dependencies>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>commons-logging</groupId>
          <artifactId>commons-logging</artifactId>
          <version>1.2</version>
        </dependency>
        <dependency>
          <groupId>log4j</groupId>
          <artifactId>log4j</artifactId>
          <version>1.2.12</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-test</artifactId>
          <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.12</version>
        </dependency>
          
        <!-- AOP联盟 -->
        <dependency>
             <groupId>aopalliance</groupId>
             <artifactId>aopalliance</artifactId>
             <version>1.0</version>
        </dependency>
        <!-- Spring Aspects -->
        <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring-aspects</artifactId>
             <version>5.0.2.RELEASE</version>
        </dependency>
        <!-- aspectj -->
        <dependency>
             <groupId>org.aspectj</groupId>
             <artifactId>aspectjweaver</artifactId>
             <version>1.8.3</version>
        </dependency>
      </dependencies>
    2. 创建 Spring 的配置文件,具体引入 AOP 的 schema 约束:
      <?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">
      </beans>
    3. 编写具体的接口和实现类:
      //接口
      public interface UserService {
          public void save();
      }
      
      
      //实现类
      public class UserServiceImpl implements UserService {
          @Override
          public void save() {
              System.out.println("业务层:保存用户···");
          }
      }
    4. 将目标类配置到 Spring 中:
      <bean id="userService" class="org.example.service.impl.UserServiceImpl"/>
    5. 定义切面类: 
      public class MyXmlAspect {
          public void log(){
              //进行业务逻辑的增强···
              System.out.println("增强方法已被执行");
          }
      }
    6. 在配置文件中定义切面类:
      <bean id="myXmlAspect" class="org.example.util.MyXmlAspect"/>
    7. 在配置文件中完成 AOP 的配置:
      <!--配置 AOP 增强-->
      <aop:config>
          <!--配置切面 = 切入点 + 通知组成 -->
          <aop:aspect ref="myXmlAspect">
              <!--前置通知: UserServiceImpl 的 save 方法执行之前,会增强-->
              <aop:before method="log" pointcut="execution(public void org.example.service.impl.UserServiceImpl.save())"/>
          </aop:aspect>
      </aop:config>
    8. 编写测试类:
      @RunWith(SpringJUnit4ClassRunner.class)
      @ContextConfiguration("classpath:application.xml")
      public class AppTest {
      
          @Autowired
          private UserService userService;
      
          @Test
          public void run(){
              userService.save();
          }
      
      }
  6. 切入点表达式:
    1. excution([修饰符]  返回值类型  包名.类名.方法名(参数))
      1. 修饰符:可以省略不写
      2. 返回值类型:可以省略不写,可以用 * 代替。根据方法编写返回值类型
    2. 包名:com.qcby.demo.BookDaoImpl
      1. 首先 com  是不能省略不写的,但是可以使用 * 代替
      2. 中间的包名也可以使用 * 代替
      3. 如果想省略中间的包名,可以使用 .. 代替
    3. 类名:可以使用 * 代替,类似的写法:*DaoImpl
    4. 方法:可以使用 * 代替
    5. 参数:如果是一个参数可以用 * 代替,多个参数时用 .. (可以代表任意参数)
      <!--配置 AOP 增强-->
          <aop:config>
              <!--配置切面 = 切入点 + 通知组成 -->
              <aop:aspect ref="myXmlAspect">
                  <!--前置通知: UserServiceImpl 的 save 方法执行之前,会增强-->
      <!--            <aop:before method="log" pointcut="execution(public void org.example.service.impl.UserServiceImpl.save())"/>-->
                  
                      <!--切入点的表达式:
                              execution():固定的写法
                              public:  修饰符可省略不写
                              方法的返回值:   int  String 通用的写法,可以编写 * ,不能省略不写
                              包名 + 类名: 不能省略不写,可编写 *
                              方法名称: save() 可以写 *
                              参数列表: (..) 表示任意类型和个数的参数
                              
                              比较通用的表达式: execution(* com.qcby.*.*ServiceImpl.*(..)-->
                  <aop:before method="log" pointcut="execution(* org.example.*.*.*ServiceImpl.save*(..))"/>
                  
              </aop:aspect>
          </aop:config>
  7. AOP 通知类型:
    1. 前置通知:增强代码在切入点之前执行
    2. 后置通知:增强代码在切入点之后执行
    3. 环绕通知:增强代码在切入点之前和之后都会执行
    4. 异常通知:增强代码在切入点发生异常之后执行
    5. 最终通知:增强代码在切入点之后执行(类似于 finally)
      <!--获取类-->
      <bean id="user" class="com.spring.service.User"/>
      <bean id="demo" class="com.spring.service.Demo"/>
      <!--配置切面-->
      <aop:config>
          <!--切面 = 切入点 + 通知-->
          <aop:aspect ref="demo">
              <!--aop:after:最终通知-->
                  <!--pointcut:切入点-->
                  <!--execution():需要执行的切入点  写全名称-->
              <aop:after method="verify" pointcut="execution(public  void com.spring.service.User.LoginJudge())"/>
              <!--前置通知-->
              <aop:before method="verify" pointcut="execution(* com.spring.service.User.LoginJudge())"/>
              <!--后置通知-->
              <aop:after-returning method="verify" pointcut="execution(* com.spring.service.User.LoginJudge())"/>
              <!--异常通知-->
              <aop:after-throwing method="verify" pointcut="execution(* com.spring.service.User.LoginJudge())"/>
              <!--环绕通知-->
              <aop:around method="verify" pointcut="execution(* com.spring.service.User.LoginJudge())"/>
          </aop:aspect>
      
      </aop:config>
    6. 示例:环绕通知
      1. 导入依赖:
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.0.2.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
                <version>1.2</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-test</artifactId>
                <version>5.0.2.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.12</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <scope>test</scope>
            </dependency>
            <!--AOP联盟-->
            <dependency>
                <groupId>aopalliance</groupId>
                <artifactId>aopalliance</artifactId>
                <version>1.0</version>
            </dependency>
            <!--Spring Aspects-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aspects</artifactId>
                <version>5.0.2.RELEASE</version>
            </dependency>
            <!--aspectj-->
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.8.3</version>
            </dependency>
        </dependencies>
      2. xml 配置切面:
        <!--获取类-->
        <bean id="user" class="com.spring.service.User"/>
        <bean id="demo" class="com.spring.service.Demo"/>
        <!--配置切面-->
        <aop:config>
            <!--切面 = 切入点 + 通知-->
            <aop:aspect ref="demo">
                <!--aop:after:最终通知-->
                    <!--pointcut:切入点-->
                    <!--execution():切入点表达式-->
                <aop:after method="verify" pointcut="execution(public  void com.spring.service.User.LoginJudge())"/>
            </aop:aspect>
        </aop:config>
      3. 增强类:
        public class User {
            public void LoginJudge(){
                System.out.println("登录判断");
            }
        }
      4. 切入类:
        public class Demo {
            public void verify(){
                System.out.println("通知");
            }
        }
  8. AOP 注解方式
    1. 案例:
      1. 编写注解扫描:
        <!--扫描包-->
         <context:component-scan base-package="com.spring"/>
        
         <!--扫描注解-->
         <aop:aspectj-autoproxy/>
      2. 直接通知:
        @Component
        @Aspect     //切入类注解
        public class Demo {
        
            //环绕通知
            @Around(value = "execution(* com.spring.service.User.LoginJudge())")
            public void verify(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
                System.out.println("判断前通知");
                proceedingJoinPoint.proceed();
                System.out.println("判断后通知");
            }
        
            //最终通知
            @After(value = "execution(* com.spring.service.User.LoginJudge())")
        
            //前置通知
            @Before(value = "execution(* com.spring.service.User.LoginJudge())")
            
            //后置通知
            @AfterReturning(value = "execution(* com.spring.service.User.LoginJudge())")
            
            //异常通知
            @AfterThrowing(value = "execution(* com.spring.service.User.LoginJudge())")
            public void verify(){
                System.out.println("通知");
            }
        }
    2. 通知类型的注解:
      1. @Before:前置通知
      2. @AfterReturning:后置通知
      3. @Around:环绕通知(目标对象方法默认不执行,需要手动执行)
      4. @After:最终通知
      5. @AfterThrowing:异常抛出通知
    3. 纯注解的方式:
      @Configuration  //配置类
      @ComponentScan("org.example")  //扫描包
      @EnableAspectJAutoProxy     //开启自动代理 ==  <aop:aspectj-autoproxy/>
      public class SpringConfig {
          
      }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值