Spring AOP的作用及其有哪些实现? - 知乎

1 - 简介

AOP(Aspect Oriented Programming)- 面向切面编程
是通过预编译方式和运行期间动态代理实现程序统一维护的技术
  • 作用:在程序运行期间,在不修改源码的情况下对方法进行功能上的补充增强
  • 优势:减少重复的代码,提高开发效率,便于维护

2 - AOP 相关概念

  • Target(目标对象):目标对象
  • Proxy(代理):代理对象
  • Joinpoint(连接点):就是被拦截到的方法(可以被增强的方法)
  • Pointcut(切入点):用来指定被设置的连接点
  • Advice(通知/增强):拦截到 连接点之后 要做的动作
  • Aspect(切面):切入点 + 通知
  • Weaving(织入):切入点 与 通知 结合的过程

3 - AOP 开发注意事项

  1. 需要编写的内容
  • 编写核心业务代码(目标类的目标方法)
  • 编写切面类,切面类中有通知(增强功能方法)
  • 在配置文件中,配置织入关系,就是将通知与连接点相结合
  • AOP 技术实现
    做好相关的配置之后,让 Spring 快乐的帮我们干活
  • AOP 底层使用的代理方式
  • JDK 代理:如果目标类没有使用接口
  • cjlib代理:如果目标类使用了接口

4 - 配置pom.xml

注意添加org.aspectj
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.ityuyu</groupId>
  <artifactId>SpringDemo-07-AOP</artifactId>
  <version>1.0-SNAPSHOT</version>
  <name>SpringDemo-07-AOP</name>
  <packaging>war</packaging>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.target>1.8</maven.compiler.target>
    <maven.compiler.source>1.8</maven.compiler.source>
      <junit.version>5.7.1</junit.version>
      </properties>

  <dependencies>
                                                                        <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
      <scope>provided</scope>
    </dependency>

          <dependency>
              <groupId>javax.servlet</groupId>
              <artifactId>javax.servlet-api</artifactId>
              <version>4.0.1</version>
              <scope>provided</scope>
          </dependency>

          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-context</artifactId>
              <version>5.1.5.RELEASE</version>
          </dependency>

          <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
          <dependency>
              <groupId>org.aspectj</groupId>
              <artifactId>aspectjweaver</artifactId>
              <version>1.8.4</version>
          </dependency>

          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-test</artifactId>
              <version>5.1.5.RELEASE</version>
          </dependency>

          <dependency>
              <groupId>junit</groupId>
              <artifactId>junit</artifactId>
              <version>4.12</version>
          </dependency>

      </dependencies>
    
</project>

5 - 创建aop包,在包内创建TargetInterface接口(目标接口),及Target实现类(目标对象),MyAspect切面类(增强方法)

TargetInterface
package com.ityuyu.aop;

/**
 * @ClassName TargetInterface
 * @Description: TODO
 * @Author: YuYu_123844@outlook.com
 */
public interface TargetInterface {
    void Test();
}
Target
package com.ityuyu.aop;

/**
 * @ClassName Target
 * @Description: TODO
 * @Author: YuYu_123844@outlook.com
 */
public class Target implements TargetInterface{
    @Override
    public void Test() {
        System.out.println("Test is running....");
    }
}
MyAspect
package com.ityuyu.aop;

/**
 * @ClassName MyAspect
 * @Description: TODO
 * @Author: YuYu_123844@outlook.com
 */
public class MyAspect {
    public void before(){
        System.out.println("before is running ....");
    }

    public void after(){
        System.out.println("after is running ....");
    }
}

6 - 创建核心配置文件ApplicationContext

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
">

    <!--目标对象-->
    <bean id="target" class="com.ityuyu.aop.Target"/>

    <!--切面对象-->
    <bean id="myAspect" class="com.ityuyu.aop.MyAspect"/>

    <!--织入过程 - 那些切点(方法)需要进行那些增强(前置、后置...)-->
    <aop:config>
        <!--声明切面-->
        <aop:aspect ref="myAspect">
            <!--切面 = 切点 + 通知-->
            <aop:before method="before" pointcut="execution(public void com.ityuyu.aop.Target.Test())"/>
            <aop:after method="after" pointcut="execution(public void com.ityuyu.aop.Target.Test())"/>
        </aop:aspect>
    </aop:config>
</beans>

7 - 在test包中创建测试类AopRunTest

package com.ityuyu;

import com.ityuyu.aop.TargetInterface;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * @ClassName AopRunTest
 * @Description: TODO
 * @Author: YuYu_123844@outlook.com
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:ApplicationContext.xml")
public class AopRunTest {

    @Autowired
    private TargetInterface target;

    @Test
    public void Test01(){
        target.Test();
    }
}

8 - 切点表达式

execution([修饰符] 返回值类型 包名.类名.方法名(参数类型))

  • 权限修饰符可以省略
  • 返回值、包名、类名、方法名都可以使用 * 表示任意
  • 包名与类名之间使用一个点儿,表示当前包下的类;两个点儿,表示当前包及其子包下的类
  • 参数列表可以使用两个点儿,表示任意个数,任意类型的参数(用于方法的重载)
  • EX:
    execution(public void com.itlaoxie.aop.Target.method())
    <!--指定一个绝对方法的路径-->
    execution(void com.itlaoxie.aop.Target.*(..))
    <!--void com.itlaoxie.aop.Target.任意方法(任意参数)-->
    execution( com.itlaoxie.aop..*(..))
    <!--任意返回值 com.itlaoxie.aop.任意类.任意方法(任意参数)-->
    execution( com.itlaoxie.aop...*(..))
    <!--任意返回值 com.itlaoxie.aop..aop包及其子包的任意类.任意方法(任意参数)-->
    execution( ...(..))
    <!--各种任意-->

通知类型

通知配置语法:<aop:通知类型 method:"切面类中的方法名" pointcut="切点表达式"/>

名称

标签

说明

前置通知

<aop:before>

指定目标方法执行之前

后置通知

<aop:returning>

指定目标方法执行之后

环绕通知

<aop:around>

指定目标方法执行之前、之后都执行; ProceedingJoinPoint 作为形参

异常抛出通知

<aop:after-throwing>

指定目标方法出现异常时

最终通知

<aop:after>

各种情况都会执行

9 - 测试多个目标方法

TargetInterface接口
 @Override
    public void Test03(ProceedingJoinPoint point) throws Throwable {
        System.out.println("around before is running....");

        point.proceed();

        System.out.println("around after is running....");
    }
Target目标对象
package com.ityuyu.aop;

import org.aspectj.lang.ProceedingJoinPoint;

/**
 * @ClassName Target
 * @Description: TODO
 * @Author: YuYu_123844@outlook.com
 */
public class Target implements TargetInterface{
    @Override
    public void Test01() {
        System.out.println("Test01 is running....");
    }

    @Override
    public void Test02() {
        System.out.println("Test02 is running....");
    }
}
MyAspect切面类
package com.ityuyu.aop;

import org.aspectj.lang.ProceedingJoinPoint;

/**
 * @ClassName MyAspect
 * @Description: TODO
 * @Author: YuYu_123844@outlook.com
 */
public class MyAspect {
    public void before(){
        System.out.println("before is running ....");
    }

    public void after(){
        System.out.println("after is running ....");
    }

    public void around(ProceedingJoinPoint point) throws Throwable {
        System.out.println("around before is running....");

        point.proceed(); // 调用的是目标对象当中的需要增强的目标方法

        System.out.println("around after is running....");
    }
}
AopRunTest测试类
package com.ityuyu;

import com.ityuyu.aop.TargetInterface;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * @ClassName AopRunTest
 * @Description: TODO
 * @Author: YuYu_123844@outlook.com
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:ApplicationContext.xml")
public class AopRunTest {

    @Autowired
    private TargetInterface target;

    @Test
    public void Test01(){
        target.Test01();
        target.Test02();
    }

}
核心配置文件ApplicationContext
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
">

    <!--目标对象-->
    <bean id="target" class="com.ityuyu.aop.Target"/>

    <!--切面对象-->
    <bean id="myAspect" class="com.ityuyu.aop.MyAspect"/>

    <!--织入过程 - 那些切点(方法)需要进行那些增强(前置、后置...)-->
    <aop:config>
        <!--声明切面-->
        <aop:aspect ref="myAspect">
            <!--切面 = 切点 + 通知-->
<!--            <aop:before method="before" pointcut="execution(public void com.ityuyu.aop.*.*(..))"/>-->
<!--            <aop:after method="after" pointcut="execution(public void com.ityuyu.aop.*.*(..))"/>-->
            <aop:around method="around" pointcut="execution(public void com.ityuyu.aop.*.*(..))"/>
        </aop:aspect>
    </aop:config>
</beans>

10 - 核心配置的另一种写法(简化版)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
">

    <!--目标对象-->
    <bean id="target" class="com.ityuyu.aop.Target"/>

    <!--切面对象-->
    <bean id="myAspect" class="com.ityuyu.aop.MyAspect"/>

    <!--织入过程 - 那些切点(方法)需要进行那些增强(前置、后置...)-->
    <aop:config>
        <aop:pointcut id="myPointCut" expression="execution(public void com.ityuyu.aop.*.*(..))"/>
        <!--声明切面-->
        <aop:aspect ref="myAspect">
            <!--切面 = 切点 + 通知-->
<!--            <aop:before method="before" pointcut="execution(public void com.ityuyu.aop.*.*(..))"/>-->
<!--            <aop:after method="after" pointcut="execution(public void com.ityuyu.aop.*.*(..))"/>-->
<!--            <aop:around method="around" pointcut="execution(public void com.ityuyu.aop.*.*(..))"/>-->
<!--            /////////////-->
            <aop:before method="before" pointcut-ref="myPointCut"/>
            <aop:after method="after" pointcut-ref="myPointCut"/>
            <aop:around method="around" pointcut-ref="myPointCut"/>
        </aop:aspect>
    </aop:config>
</beans>

。。。。。下一节 通过XML来实现AOP


原网址: 访问
创建于: 2022-10-12 17:10:50
目录: default
标签: 无

请先后发表评论
  • 最新评论
  • 总共0条评论