什么是 AOP
AOP(Aspect Oriented Programming),即面向切面编程,利用一种称为"横切"的技术,剖开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
AOP 开发术语
- 连接点(Joinpoint):连接点是程序类中客观存在的方法,可被Spring拦截并切入内容。
- 切入点(Pointcut):被Spring切入连接点。
- 通知、增强(Advice):可以为切入点添加额外功能,分为:前置通知、后置通知、异常通知、环绕通知等。
- 目标对象(Target):代理的目标对象
- 引介(Introduction):一种特殊的增强,可在运行期为类动态添加Field和Method。
- 织入(Weaving):把通知应用到具体的类,进而创建新的代理类的过程。
- 代理(Proxy):被AOP织入通知后,产生的结果类。
- 切面(Aspect):由切点和通知组成,将横切逻辑织入切面所指定的连接点中。
AOP 的实现
环境搭建
引入AOP相关依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
spring-context.xml引入AOP命名空间
<?xml version="1.0" encoding="UTF-8"?>
<!--
schema: 规范
xxx.xsd
xml schema definition
-->
<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">
</beans>
开发流程
定义原始类
public interface UserService {
public List<User> queryUsers();
public Integer updateUser(User user);
public Integer saveUser(User user);
public Integer deleteUser(Integer id);
}
public class UserServiceImpl implements UserService {
@Override
public List<User> queryUsers() {
// System.out.println("事务控制");
// System.out.println("日志打印");
System.out.println("queryUser");
return new ArrayList<>();
}
@Override
public Integer updateUser(User user) {
// System.out.println("事务控制");
// System.out.println("日志打印");
System.out.println("update User");
return 1;
}
@Override
public Integer saveUser(User user) {
// System.out.println("事务控制");
// System.out.println("日志打印");
System.out.println("Save User");
return 1;
}
@Override
public Integer deleteUser(Integer id) {
// System.out.println("事务控制");
// System.out.println("日志打印");
System.out.println("delete User");
return 1;
}
}
定义通知类(添加额外功能)
/**
* 前置通知类
*/
public class MyBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
// 额外功能
System.out.println("事务控制2");
System.out.println("日志打印2");
}
}
/**
* 后置通知,在核心之后执行,如果核心有异常,则不执行
*/
public class MyAfterAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("after!! 后置通知辅助内容!!!");
}
}
/**
* 环绕通知,在核心功能前后都执行辅助功能
*/
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("环绕通知: 开始辅助内容tx begin~~");
Object ret = invocation.proceed();// 触发,执行核心功能
System.out.println("环绕通知: 结尾复制内容tx end!!");
return ret;
}
}
/**
* 异常通知,在核心功能中抛异常时执行
*/
public class MyThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(Exception ex){
System.out.println("my throws 异常通知辅助功能");
}
}
定义bean标签
<!-- 目标:原始业务-->
<bean id="us" class="com.qf.service.UserServiceImpl" />
<!-- 前置通知类 -->
<bean id="before" class="com.qf.aop.MyBeforeAdvice"></bean>
<!-- 后置通知类 -->
<bean id="after" class="com.qf.aop.MyAfterAdvice"></bean>
<!-- 异常通知类 -->
<bean id="throws" class="com.qf.aop.MyThrowsAdvice"></bean>
<!-- 环绕通知类 -->
<bean id="mi" class="com.qf.aop.MyMethodInterceptor"></bean>
定义切入点,形成切面
<aop:config>
<!-- 切入点【修饰符 返回值 包.类 方法名 参数表 】 -->
<aop:pointcut id="pc_shine" expression="execution(* queryUsers())"/>
<aop:pointcut id="pc_shine2" expression="execution(* deleteUser(..))"/>
<aop:pointcut id="pc_shine3" expression="execution(* updateUser(..))"/>
<aop:pointcut id="pc_shine4" expression="execution(* saveUser(..))"/>
<!-- 组装 -->
<aop:advisor advice-ref="before" pointcut-ref="pc_shine"/>
<aop:advisor advice-ref="after" pointcut-ref="pc_shine2"/>
<aop:advisor advice-ref="throws" pointcut-ref="pc_shine3"/>
<aop:advisor advice-ref="mi" pointcut-ref="pc_shine4"/>
</aop:config>
测试代码
public class ProcessorTest {
@Test
public void testProcessor(){
ApplicationContext context = new ClassPathXmlApplicationContext("/spring-context.xml");
UserService userService = (UserService) context.getBean("us");
//演示前置通知
userService.queryUsers();
System.out.println("**********queryUsers() end**********");
//演示环绕通知
userService.saveUser(new User());
System.out.println("**********saveUser(new User()) end**********");
//演示异常通知
userService.updateUser(new User());
System.out.println("**********updateUser(new User()) end**********");
//演示后置通知
userService.deleteUser(1);
System.out.println("**********deleteUser(1) end**********");
}
}