Spring(四)-AOP

  1. 什么是AOP?

    (1)面向切面编程(方面),利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高开发效率

    (2)不修改源代码方式,在主干功能里添加新功能

  2. AOP底层使用动态代理

    (1)有两种的动态代理

    1. 有接口的情况,使用JDK动态代理
    2. 没有接口的情况,使用CGLIB动态代理

AOP(JDK动态代理)

使用JDK动态代理,使用Proxy类里面的方法创建代理对象

  1. 调用newProxyInstance方法

    方法里面有三个参数:

    第一参数,类加载器

    第二参数,增强方法所在的类,这个类实现的接口,支持多个接口

    第三参数,实现这个接口InnovationHandler,创建代理对象,写增强的方法

  2. 编写JDK动态代理代码

    (1)创建接口,定义方法

    1
    2
    3
    4
    public interface UserDao {
    public int add(int a,int b);
    public String update(String id);
    }

    (2)创建接口实现类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class UserDaoImpl implements UserDao {
    @Override
    public int add(int a, int b) {
    System.out.println("add方法执行了.....");
    return a+b;
    }

    @Override
    public String update(String id) {
    System.out.println("update方法执行了.....");
    return id;
    }
    }

    (3)使用Proxy类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    public class JDKProxy {

    public static void main(String[] args) {
    //创建接口实现类代理对象
    Class[] interfaces = {UserDao.class};
    // Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
    // @Override
    // public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // return null;
    // }
    // });
    UserDaoImpl userDao = new UserDaoImpl();
    UserDao dao = (UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
    int result = dao.add(1, 2);
    System.out.println("result:"+result);
    }
    }

    //创建代理对象代码
    class UserDaoProxy implements InvocationHandler {

    //1 把创建的是谁的代理对象,把谁传递过来
    //有参数构造传递
    private Object obj;
    public UserDaoProxy(Object obj) {
    this.obj = obj;
    }

    //增强的逻辑
    @Override
    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("方法之后执行...."+obj);
    return res;
    }
    }

AOP(术语)

  1. 连接点

    类里面哪些方法可以被增强,这些方法称为连接点

  2. 切入点

    实际被真正增强的方法,称为切入点

  3. 通知(增强)

    (1)实际增强的逻辑部分称为通知(增强)

    (2)通知有多种类型

    • 前置通知
    • 后置通知
    • 环绕通知
    • 异常通知
    • 最终通知(类似与 try catch里的finally)
  4. 切面

    是动作上的操作

    (1)把通知应用到切入点的过程

AOP操作准备

  1. Spring框架中,一般都是基于AspectJ实现AOP操作

    • 什么是AspectJ

      不是Spring组成部分,独立AOP框架,一般把AspectJ和Spring框架一起使用,完成AOP操作

  2. 基于AspectJ实现AOP操作

    • 基于XML配置文件
    • 基于注解方式实现(常用)
  3. 在项目工程里面引入AOP相关依赖

  4. 切入点表达式

    • 切入点表达式作用: 知道对哪个类里面的哪个方法进行增强
    • 语法结构:

    Execution([权限修饰符][返回类型][类全路径]][方法名称][全类名])

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

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

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

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

    举例3: 对com.xyt.dao类里面的所有方法进行增强

    execution(* com.xyt.dao.*.*(..))

AOP操作(AspectJ注解)

  1. 创建类 在类里面定义方法

    1
    2
    3
    4
    5
    public class User {
    public void add() {
    System.out.println("add.......");
    }
    }
  2. 创建增强类

    • 在增强类里创建方法,让不同方法代表不同的通知类型
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    //增强的类
    @Component
    @Aspect //生成代理对象
    @Order(3)
    public class UserProxy {

    //相同切入点抽取
    @Pointcut(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    public void pointdemo() {
    }

    //前置通知
    //@Before注解表示作为前置通知
    @Before(value = "pointdemo()")
    public void before() {
    System.out.println("before.........");
    }

    //后置通知(返回通知)
    @AfterReturning(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    public void afterReturning() {
    System.out.println("afterReturning.........");
    }

    //最终通知
    @After(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    public void after() {
    System.out.println("after.........");
    }

    //异常通知
    @AfterThrowing(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    public void afterThrowing() {
    System.out.println("afterThrowing.........");
    }

    //环绕通知
    @Around(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    System.out.println("环绕之前.........");

    //被增强的方法执行
    proceedingJoinPoint.proceed();

    System.out.println("环绕之后.........");
    }
    }
  3. 进行通知的配置

    (1)在Spring配置文件中开启注解扫描

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <?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/context http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 开启注解扫描 -->
    <context:component-scan base-package="com.atguigu.spring5.aopanno"></context:component-scan>

    <!-- 开启Aspect生成代理对象-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    </beans>

    (2)使用注解创建User和UserProxy对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @Component
    public class User {
    public void add() {
    int i = 10/0;
    System.out.println("add.......");
    }
    }

    @Component
    @Aspect //生成代理对象
    public class UserProxy {
    //...
    }

    (3)在增强类上面添加注解@Aspect

    (4)在Spring配置文件中开启生成代理对象

    1
    2
    <!-- 开启Aspect生成代理对象-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

    (5)配置不同类型通知

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    //@Before注解表示作为前置通知
    @Before(value = "pointdemo()")
    public void before() {
    System.out.println("before.........");
    }

    //后置通知(返回通知)
    @AfterReturning(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    public void afterReturning() {
    System.out.println("afterReturning.........");
    }

    //最终通知
    @After(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    public void after() {
    System.out.println("after.........");
    }

    //异常通知
    @AfterThrowing(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    public void afterThrowing() {
    System.out.println("afterThrowing.........");
    }

    //环绕通知
    @Around(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    System.out.println("环绕之前.........");

    //被增强的方法执行
    proceedingJoinPoint.proceed();

    System.out.println("环绕之后.........");
    }

    (6)编写测试类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class TestAop {

    @Test
    public void testAopAnno() {
    ApplicationContext context =
    new ClassPathXmlApplicationContext("bean1.xml");
    User user = context.getBean("user", User.class);
    user.add();
    }
    }

    (7)相同切入点抽取

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //相同切入点抽取
    @Pointcut(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    public void pointdemo() {

    }
    //@Before注解表示作为前置通知
    @Before(value = "pointdemo()")
    public void before() {
    System.out.println("before.........");
    }

    (8)有多个增强类对同一个类进行增强

    在增强类的上面添加注解 @order() 数字越小 优先级越高

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Component
    @Aspect
    @Order(1)
    public class PersonProxy {
    //后置通知(返回通知)
    @Before(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    public void afterReturning() {
    System.out.println("Person Before.........");
    }
    }

AOP操作(配置文件) 了解

  1. 创建两个类,增强类,被增强类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class Book {
    public void buy() {
    System.out.println("buy.............");
    }
    }
    public class BookProxy {
    public void before() {
    System.out.println("before.........");
    }
    }
  2. 在Spring配置文件中创建两个类的对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <?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/context http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--创建对象-->
    <bean id="book" class="com.atguigu.spring5.aopxml.Book"></bean>
    <bean id="bookProxy" class="com.atguigu.spring5.aopxml.BookProxy"></bean>

    <!--配置aop增强-->
    <aop:config>
    <!--切入点-->
    <aop:pointcut id="p" expression="execution(* com.atguigu.spring5.aopxml.Book.buy(..))"/>
    <!--配置切面-->
    <aop:aspect ref="bookProxy">
    <!--增强作用在具体的方法上-->
    <aop:before method="before" pointcut-ref="p"/>
    </aop:aspect>
    </aop:config>
    </beans>
  3. 测试类

    1
    2
    3
    4
    5
    6
    7
    @Test
    public void testAopXml() {
    ApplicationContext context =
    new ClassPathXmlApplicationContext("bean2.xml");
    Book book = context.getBean("book", Book.class);
    book.buy();
    }

全注解开发

不需要xml配置文件

1
2
3
4
5
@Configuration
@ComponentScan(basePackages = {"com.atguigu"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAop {
}