AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程。
AOP相关术语:
目标对象(Target):
你要去代理的对象,可以理解为之前很单纯的那个对象。
代理对象(Proxy):
你把你那个单纯的对象给我,我给你一个更强的对象。
连接点(Joinpoint):
所谓的连接点就是指哪些可以被拦截到的方法。
切入点(Pointcut):
对连接点再去具体一点,真正要拦截的方法。
通知/增强(Advice):
你把方法拦截了,你要干嘛?要去做一些增强(通知),前置通知、后置通知、异常通知、最终通知、环绕通知。
切面(Aspect):
切入点和通知的结合。
总结以上术语的图:
AOP开发明确事项:
-
编写核心业务代码(目标类的目标方法)切入点。
-
把公用代码抽取出来 ,制作成通知(增强功能方法)通知。
-
在配置文件中,声明切入点与通知间的关系,即切面。
导入AOP相关坐标:
XML
<!-- aspectj的织入(切点表达式需要用到该jar包) -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
创建目标接口和目标实现类:
java
public interface AccountService {
/* 目标方法: 切入点 要进行拦截的方法 */
public void transfer();
}
java
public class AccountServiceImpl implements AccountService {
/*要切入的方法*/
@Override
public void transfer() {
System.out.println("this is transfer method");
}
}
创建通知类:
这个类里面写的就是具体怎么去增强对象。
java
public class MyAdvice {
/**
* 前置通知
*/
public void before() {
System.out.println("before advice");
}
/**
* 后置通知
*/
public void after() {
System.out.println("after advice");
}
}
将目标类和通知类对象创建权交给spring:
XML
<!--目标类交给IOC容器-->
<bean id="accountService" class="com.findyou.service.Impl.AccountServiceImpl"></bean>
<!--通知类交给IOC容器-->
<bean id="myAdvice" class="com.findyou.advice.Myadvice"></bean>
xml里面的aop配置:
xml里面的aop配置,用的是**<aop:config>。**
里面写你写好的通知类,也就是你要用哪个类去增强我的对象,用ref去引入,前提是你已经把这个类放到ioc容器里面了。用的标签是**<aop:aspect ret = "">。**
在这个基础上再进行续写,这个通知类里面有很多的方法啊,你要说出来,哪一个是前置通知,哪一个是后置通知等等。前置通知也即是执行原方法之前要做的事情。前置通知的标签是**<aop:before>**, 里面的参数有 method = "类里面的哪个方法", pointcut= "你要限制谁,增强谁",可以理解为:当我触发pointcut里面的方法的时候之前就回去执行 method里面的方法。
无论是 前置通知和后置通知,里面都要有个属性:pointcut , point里面写的就是下面要讲的 切点表达式。如果这个要经常去写的话,可以把pointcut给提取出来。用的时候把之前的pointcut属性改为 pointcut-ref = "提取出来的pointcutId";
XML
<aop:pointcut id="myPointcut" expression="execution(* com.findyou.service.Impl.AccountServiceImpl.*(..))" />
切点表达式:
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
- 访问修饰符可以省略
- 返回值类型、包名、类名、方法名可以使用星号 * 代替,代表任意
- 包名与类名之间一个点 . 代表当前包下的类,两个点 .. 表示当前包及其子包下的类
- 参数列表可以使用两个点 .. 表示任意个数,任意类型的参数列表。
当然最常用的也还是: * com.findyou.service.Impl.AccountServiceImpl.* 这样的格式。
总的xml配置文件是:
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/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">
<!--目标类交给IOC容器-->
<bean id="accountService" class="com.findyou.service.Impl.AccountServiceImpl"></bean>
<!--通知类交给IOC容器-->
<bean id="myAdvice" class="com.findyou.advice.Myadvice"></bean>
<!-- AOP配置 -->
<aop:config>
<!--execution 翻译为 执行-->
<!-- 定义切点与通知 -->
<aop:aspect ref="myAdvice">
<!-- 切点表达式,指定在哪些方法执行前后添加通知 -->
<aop:pointcut id="myPointcut" expression="execution(* com.findyou.service.Impl.AccountServiceImpl.*(..))" />
<!-- 将通知应用于切点 配置的是前置通知 -->
<aop:before method="before" pointcut = "execution(public void com.findyou.service.Impl.AccountServiceImpl.transfer())"/>
<aop:after-returning method="after" pointcut-ref="myPointcut"/>
</aop:aspect>
</aop:config>
</beans>
测试代码:
java
package com.findyou;
import com.findyou.service.AccountService;
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;
/**
* @Title: MyTest
* @Author FindYou
* @Package com.findyou
* @Date 2024/12/5 上午11:17
* @description: 测试类
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class MyTest {
@Autowired
private AccountService accountService;
@Test
public void testTransfer() throws Exception {
accountService.transfer(); // 先去执行的是前置通知
}
}