Java教程

Spring-AOP

本文主要是介绍Spring-AOP,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

1. 面向切面

1.1 什么是面向切面

面向切面(AOP,Aspect Orient Programming):通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP 为 Spring IoC 提供一个非常强大的中间件解决方案,是函数式编程的一种衍生泛型。

利用AOP编程可以对业务部分进行分离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

AOP 在 Spring 框架中用于:

  • 提供声明式企业服务。最重要的此类服务是 声明式事务管理。
  • 让用户实现自定义切面,用 AOP 补充他们对 OOP 的使用。

OOP:面对对象编程

面向方面编程 (AOP) 通过提供另一种思考程序结构的方式来补充面向对象编程 (OOP)。

区别:OOP 中模块化的关键单位是类,而 AOP 中模块化的单位是方面。

1.2 AOP名词

1.2.1 名词术语

名词说明备注
横切关注点跨越应用程序中多个模块的方法或功能方法拦截处
切面(Aspect)是一个关注点的模块化,一个切面能包含多个增强方法如事务处理、日志处理
连接点(Joint point)程序执行过程中的一个点通知开始执行的地方
切入点(Point Cut)对连接点进行拦截的条件定义匹配连接点的谓词
引介(Introduction)代表类型声明额外的方法或字段添加新的方法
通知(Advice)连接点后要执行的代码完成功能的实现
目标(Target)被一个或多个切面所通知的对象被通知(代理)的对象
织入(Weave)将增强方法添加到目标的具体连接点上的过程AOP有三种织入方式

1.2.2 通知类型

通知类型说明实现
前置通知(Before Advice)在连接点之前执行的通知<aop:before>
后置通知(After Advice)在连接点结束的时候执行的通知<aop:after>
返回后通知(After Return Advice)在连接点正常完成后执行的通知<aop:after-returning>
环绕通知(Around Advice)包围一个连接点的通知<aop:around>
异常通知(After Throwing Advice)在方法异常退出时执行的通知<aop:after-throwing>

1.3 Spring-AOP

Spring AOP 是用纯 Java 实现的。不需要特殊的编译过程。Spring AOP 不需要控制类加载器层次结构,因此适用于 servlet 容器或应用程序服务器。

Spring 中的 AOP 是通过动态代理实现的。

Spring AOP 目前仅支持方法执行连接点。

1.4 AOP代理

Spring AOP 默认为 AOP 代理使用标准的 JDK 动态代理。Spring AOP 也可以使用 CGLIB 代理。

默认情况下,如果业务对象未实现接口,则使用 CGLIB。

2. AOP的Spring实现

需要导入aspectjweaver依赖:

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

2.1 基于 Spring API 实现

利用之前在代理模式中用到的增删查改的业务,同样完成添加日志的功能。

基本业务:

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}
public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("添加一个用户");
    }

    @Override
    public void delete() {
        System.out.println("删除一个用户");
    }

    @Override
    public void update() {
        System.out.println("更改一个用户");
    }

    @Override
    public void query() {
        System.out.println("查询用户");
    }
}

2.1.1 需要实现的接口

需要实现的接口有:

  • MethodBeforeAdvice:在调用方法之前调用的通知。这样的通知不能阻止方法调用继续进行,除非它们抛出Throwable。
  • AfterReturningAdvice:返回通知仅在正常方法返回时调用,而不是在抛出异常时调用。这样的建议可以看到返回值,但不能更改它。
public interface MethodBeforeAdvice extends BeforeAdvice {

   /**
    * method:被调用的方法
    * args:方法的参数
    * target:方法调用的目标(可能是null)
    */
   void before(Method method, Object[] args, @Nullable Object target) throws Throwable;

}
public interface AfterReturningAdvice extends AfterAdvice {

   /**
    * returnValue:方法返回的值(如果有的话)
    * method:被调用的方法
    * args:方法的参数
    * target:方法调用的目标(可能是null)
    */
   void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable;

}

2.1.2 实现接口

我们需要两个增强类,一个前置增强、一个后置增强。

public class Before_log implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行 " + target.getClass().getName() + " 的 " + method.getName() + " 方法");
    }
}
public class After_log implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("该方法的返回值为:" + returnValue);
    }
}

2.1.3 applicationContext

在xml文件中注册bean,并配置aop。(需要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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="UserService" class="com.service.UserServiceImpl"/>
    <bean id="before_log" class="com.service.Before_log"/>
    <bean id="after_log" class="com.service.After_log"/>

    <!--配置AOP-->
    <aop:config>
        <!--配置切入点--> 
        <!--execution(要执行的位置!修饰词 返回值 类名 方法名 参数)-->
        <aop:pointcut id="pointcut" expression="execution(* com.service.UserServiceImpl.*(..))"/>
        <!--配置环绕增强-->
        <aop:advisor advice-ref="before_log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="after_log" pointcut-ref="pointcut"/>
    </aop:config>
</beans>

2.1.4 测试

public class MyTest {
    @Test
    public void TestUserService(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        UserService userService = applicationContext.getBean("UserService", UserService.class);
        userService.add();
    }
}

/*
输出:
执行 com.service.UserServiceImpl 的 add 方法
添加一个用户
该方法的返回值为:null
*/

2.2 自定义 AOP 实现

2.2.1 定义代理类

代理中需要提供两个增强类,一个前置增强、一个后置增强。

public class DiyPoint {
    public void After(){
        System.out.println("===========开始前==============");
    }
    public void Before(){
        System.out.println("===========开始后==============");
    }
}

2.2.2 applicationContext

<bean id="UserService" class="com.service.UserServiceImpl"/>
<bean id="diyPoint" class="com.DiyPointcut.DiyPoint"/>

<aop:config>
    <!--ref:切面要引用的类-->
    <aop:aspect ref="diyPoint">
        <aop:pointcut id="pointcut" expression="execution( * com.service.UserServiceImpl.*(..))"/>
        <!--前置通知-->
        <aop:before method="before" pointcut-ref="pointcut"/>
        <!--后置通知-->
        <aop:after method="after" pointcut-ref="pointcut"/>
    </aop:aspect>
</aop:config>

2.2.3 测试

测试类不变!

/*
输出:
===========开始后==============
添加一个用户
===========开始前==============
*/

2.3 基于注解 AOP 实现

2.3.1 注解类

@Configuration   // Java配置类
@Aspect  // 标识一个切面
public class AnnotationPoint {

    @After("execution(* com.service.UserServiceImpl.*(..))")
    public void After(){
        System.out.println("===========开始前==============");
    }
    @Before("execution(* com.service.UserServiceImpl.*(..))")
    public void Before(){
        System.out.println("===========开始后==============");
    }
}

2.3.2 applicationContext

<!--开启 context 注解支持-->
<context:annotation-config/>
<!--配置扫描-->
<context:component-scan base-package="com.Anno"/>
<!--开启 aop 注解支持-->
<aop:aspectj-autoproxy/>

2.3.3 测试

测试类不变!

/*
输出:
===========开始后==============
添加一个用户
===========开始前==============
*/

3. 写在最后

通过上面三种实现AOP的例子,我们可以看到AOP是基于动态代理实现的。在实际应用中,我们应当选择前两种,而酌情(小项目)选择第三种。

 


❤️ END ❤️
这篇关于Spring-AOP的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!