一、什么是AOP

来自百度百科:AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

通俗描述:不通过修改源代码方式,在主干功能里面添加新功能;

1.1、AOP相关术语

连接点:

  • 类里面哪些方法可以被代理,被切入,被称为连接点

切入点:

  • 实际被代理的方法,称为切入点

通知:

  • 代理类处理的逻辑

1.2、AOP通知类型

  • 前置通知
  • 后置通知
  • 环绕通知
  • 异常通知
  • 最终通知

1.3、切入点表达式

切入点表达式作用:明确对哪个类里面的哪个方法进行通知;
语法结构:
execution:

  1. execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]) )

举例 1:对 com.wells.dao.BookDao 类里面的 add 进行增强

execution(* com.wells.dao.BookDao.add(..))

举例 2:对 com.wells.dao.BookDao 类里面的所有的方法进行增强

execution(* com.wells.dao.BookDao.* (..))

举例 3:对 com.wells.dao 包里面所有类,类里面所有方法进行增强

execution(* com.atguigu.dao.*.* (..))

注解方式:

annotation(com.wells.demo.LogAnnotation)

二、AOP底层原理

动态代理

2.1、JDK动态代理:针对有接口的情况

创建接口实现类的代理对象,增强类的方法功能
【Spring5】第四篇:AOP - 图1

2.1.1、使用JDK动态代理

使用 Proxy 类实现代理对象的创建

实例:
创建UserService接口

package com.wells.demo.aop.proxy;

/**
 * Description 
 * Created by wells on 2020-07-21 09:52:15
 */

public interface UserService {
    void delUser();
}

创建UserServiceImpl实现类

package com.wells.demo.aop.proxy;

import org.junit.Test;

import java.lang.reflect.Proxy;

/**
 * Description 
 * Created by wells on 2020-07-21 09:52:33
 */

public class UserServiceImpl implements UserService {
    public void delUser() {
        System.out.println("del user");
    }

    @Test
    public void testJDKProxy() {
        Class[] interfaces = {UserService.class};

        UserServiceImpl userServiceImpl = new UserServiceImpl();

        /**
         * newProxyInstance有三个参数:
         * 1、类加载器
         * 2、代理方法所在类的实现接口,可以支持多个接口
         * 3、代理类: 需要将代理的对象传递过去
         */
        UserService userService = (UserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(), interfaces,
                new UserServiceProxy(userServiceImpl));

        userService.delUser();
    }
}

创建对象的代理类

package com.wells.demo.aop.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * Description 
 * Created by wells on 2020-07-21 09:53:05
 */

public class UserServiceProxy implements InvocationHandler {
    private Object obj;

    public UserServiceProxy(Object obj) {
        this.obj = obj;
    }

    /**
     * @desc
     * @method invoke
     * @param proxy 代理的实现类
     * @param method 代理的方法
     * @param args 代理方法的参数
     * @return java.lang.Object
     * @date 2020-07-21 10:20:16
     * @author wells
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法执行之前: " + method.getName() + ", 参数: " + Arrays.toString(args));

        // 执行方法
        Object res = method.invoke(obj, args);

        System.out.println("方法执行之后: " + method.getName() + ", 结果: " + res);
        return res;
    }
}

2.2、CGLIB动态代理:针对无接口的情况

创建子类的代理对象,增强类的方法
【Spring5】第四篇:AOP - 图2
示例:略

三、AOP操作

Spring 框架一般都是基于 AspectJ 实现 AOP 操作,AspectJ 不是 Spring 组成部分,是独立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使用,进行 AOP 操作

3.1、基于xml配置文件实现

spring-aop.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: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">

    <!-- 创建对象 -->
    <bean id="bookService" class="com.wells.demo.aop.xml.impl.BooServiceImpl"/>
    <bean id="bookProxy" class="com.wells.demo.aop.xml.BookProxy"/>

    <!-- 开启aop代理 -->
    <aop:aspectj-autoproxy/>

    <!-- aop相关配置 -->
    <aop:config>
        <!-- 配置切入点 -->
        <aop:pointcut id="p" expression="execution(* com.wells.demo.aop.xml.impl.BooServiceImpl.*(..))"/>

        <!-- 配置切面 -->
        <aop:aspect ref="bookProxy">
            <aop:before method="before" pointcut-ref="p"/>
            <aop:after method="after" pointcut-ref="p"/>
        </aop:aspect>
    </aop:config>

</beans>

BookProxy.java 切面类

package com.wells.demo.aop.xml;

/**
 * Description 
 * Created by wells on 2020-07-22 09:37:34
 */

public class BookProxy {
    public void before(){
        System.out.println("before");
    }

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

BookServiceImpl.java

package com.wells.demo.aop.xml.impl;

import com.wells.demo.aop.xml.BookService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Description 
 * Created by wells on 2020-07-22 09:36:53
 */

public class BooServiceImpl implements BookService {
    public void delBook() {
        System.out.println("del book in service");
    }

    @Test
    public void testAopByXMl(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aop.xml");
        BookService bookService = applicationContext.getBean("bookService", BookService.class);
        bookService.delBook();
    }
}

3.2、基于注解方式实现

SpringConfig.java 为了替代spring-aop.xml

package com.wells.demo.aop.annotation;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

/**
 * Description 
 * Created by wells on 2020-07-22 09:53:49
 */

@Configuration
@ComponentScan(value = "com.wells.demo.aop.annotation")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class SpringConfig {
}

AnimalAspect.java

package com.wells.demo.aop.annotation;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * Description 
 * Created by wells on 2020-07-22 09:53:31
 */

@Component
@Aspect
public class AnimalAspect {
    @Pointcut(value = "execution(* com.wells.demo.aop.annotation.impl.AniamlServiceImpl.*(..))")
    public void point() {
    }

    @Before(value = "execution(* com.wells.demo.aop.annotation.impl.AniamlServiceImpl.*(..))")
    public void before() {
        System.out.println("before method");
    }

    @After(value = "execution(* com.wells.demo.aop.annotation.impl.AniamlServiceImpl.*(..))")
    public void after() {
        System.out.println("after method");
    }

    @Around(value = "point()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("around before");
        Object result = proceedingJoinPoint.proceed();
        System.out.println("around after");
        return result;
    }
}

AnimalServiceImpl.java

package com.wells.demo.aop.annotation.impl;

import com.wells.demo.aop.annotation.AnimalService;
import com.wells.demo.aop.annotation.SpringConfig;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Service;

/**
 * Description 
 * Created by wells on 2020-07-22 09:52:50
 */

@Service(value = "animalServiceImpl")
public class AniamlServiceImpl implements AnimalService {
    public void eat() {
        System.out.println("animal eat in service");
    }

    @Test
    public void testAopByAnnotation(){
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
        AnimalService animalService = applicationContext.getBean("animalServiceImpl", AnimalService.class);
        animalService.eat();
    }
}

切面的顺序
当有多个切面的时候,可以在切面类上添加注解 @Order(数字类型值),数字类型值越小,优先级越高;

@Component
@Aspect
@Order(0)   // 为切面处理类增加顺序
public class AnimalAspect {
}

完整代码

SpringAOP