浅谈Spring的AOP

123 阅读2分钟

什么是 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**********");
    }
}