Spring:四、AOP

Spring:四、AOP

1 介绍

Spring有两个核心部分:IoC和AOP。IoC即控制反转,把创建对象过程交给Spring进行管理。AOP 的全称是“Aspect Oriented Programming”,即面向切面编程,和 OOP(面向对象编程)类似,也是一种编程思想。AOP 的作用就是保证开发者在不修改源代码的前提下,为系统中的业务组件添加某种通用功能。AOP 就是代理模式的典型应用,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面。

目前最流行的 AOP 框架有两个,分别为 Spring AOP 和 AspectJ。

Spring AOP 是基于 AOP 编程模式的一个框架,它能够有效的减少系统间的重复代码,达到松耦合的目的。Spring AOP 使用纯 Java 实现,不需要专门的编译过程和类加载器,在运行期间通过代理方式向目标类植入增强的代码。有两种实现方式:基于接口的 JDK 动态代理和基于继承的 CGLIB 动态代理。

AspectJ 是一个基于 Java 语言的 AOP 框架,从 Spring 2.0 开始,Spring AOP 引入了对 AspectJ 的支持。AspectJ 扩展了 Java 语言,提供了一个专门的编译器,在编译时提供横向代码的植入。

2 AOP底层原理

AOP底层使用动态代理

(1)两种情况动态代理

一、有接口情况,使用JDK动态代理:创建接口实现类代理对象,增强类的方法。使用JDK动态代理,需使用Proxy类里面的方法创建代理对象。

二、无接口情况,使用CGLIB动态代理:创建子类的代理对象,增强类的方法。

(一)JDK动态代理:

查看Proxy类,地址:https://www.matools.com/api/java8
在这里插入图片描述
调用Proxy的静态方法newProxyInstance,获取指定接口的代理类实例

newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h)
在这里插入图片描述
方法参数3个:

1 类加载器
2 增强方法所在的类,这个类实现的接口,支持多个接口
3 实现接口InvocationHandler,创建代理对象,写增强的方法

编写JDK动态代理代码:
(1)创建接口,定义方法

package com.xiaoxu;

/**
 * @author xiaoxu
 * @date 2021-11-20 16:51
 * learn_java:com.xiaoxu.UserDao
 */
public interface UserDao {
    public int add(int a,int b);
    public String update(String id);
}

(2)创建接口实现类,实现方法

package com.xiaoxu;

/**
 * @author xiaoxu
 * @date 2021-11-20 16:53
 * learn_java:com.xiaoxu.UserDaoImpl
 */
public class UserDaoImpl implements UserDao{

    @Override
    public int add(int a, int b) {
        return a+b;
    }

    @Override
    public String update(String id) {
        return id;
    }
}

(3)使用Proxy类创建接口代理对象

可以使用匿名内部类:

package com.xiaoxu;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author xiaoxu
 * @date 2021-11-20 16:57
 * learn_java:com.xiaoxu.JDKProxy
 */
public class JDKProxy {
    public static void main(String[] args) {
        //要实现的接口,是Class数组
        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;
            }
        });
    }
}

也可以写一个类来实现:

package com.xiaoxu;

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

/**
 * @author xiaoxu
 * @date 2021-11-20 16:57
 * learn_java:com.xiaoxu.JDKProxy
 */
public class JDKProxy {
    public static void main(String[] args) {
        //要实现的接口,是Class数组
        System.out.println(Arrays.toString(UserDaoImpl.class.getInterfaces()));
        //可以把Proxy.newProxyInstance的第二个参数:interfaces,替换成UserDaoImpl.class.getInterfaces()
        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 u=(UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(),interfaces,new UserDaoProxy(userDao));
        System.out.println(u.add(1, 5));
        System.out.println(u.update("123"));
    }
}

class UserDaoProxy implements InvocationHandler{
    //创建的是谁的代理对象,把谁传递过来
    //有参构造传递
    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 o=method.invoke(obj,args);
        //方法之后执行
        System.out.println("方法之后执行..."+obj);
        return o;
    }
}

执行JDKProxy的main方法,可知代理类中传入了接口实现类对象,那么接口实现类对象实现的接口方法,全部都增强了:

[interface com.xiaoxu.UserDao]
方法之前执行...add:传递的参数:[1, 5]
方法之后执行...com.xiaoxu.UserDaoImpl@3764951d
6
方法之前执行...update:传递的参数:[123]
方法之后执行...com.xiaoxu.UserDaoImpl@3764951d
123

(二)CGLIB动态代理:

JDK 动态代理的目标类必须要实现一个或多个接口,具有一定的局限性。如果不希望实现接口,可以使用 CGLIB代理。

CGLIB(Code Generation Library)是一个高性能开源的代码生成包,它被许多 AOP 框架所使用,其底层是通过使用一个小而快的字节码处理框架 ASM(Java 字节码操控框架)转换字节码并生成新的类。使用 CGLIB 需要导入 CGLIB 和 ASM 包,即 asm-x.x.jar 和 CGLIB-x.x.x.jar 。如果您已经导入了 Spring 的核心包 spring-core-x.x.x.RELEASE.jar,就不用再导入 asm-x.x.jar 和 cglib-x.x.x.jar 了。

Spring 核心包中包含 CGLIB 和 asm,也就是说 Spring 核心包已经集成了 CGLIB 所需要的包,所以在开发中不需要另外导入asm-x.x.jar 和 cglib-x.x.x.jar 包了。

对于类实现接口,可以看做是特殊的类继承,可看做接口是父类,接口实现类是子类
UserDao:

package com.xiaoxu;

/**
 * @author xiaoxu
 * @date 2021-11-20 16:51
 * learn_java:com.xiaoxu.UserDao
 */
public interface UserDao {
    public int add(int a,int b);
    public String update(String id);
}

UserDaoImpl:

package com.xiaoxu;

/**
 * @author xiaoxu
 * @date 2021-11-20 16:53
 * learn_java:com.xiaoxu.UserDaoImpl
 */
public class UserDaoImpl implements UserDao {

    @Override
    public int add(int a, int b) {
        return a+b;
    }

    @Override
    public String update(String id) {
        return id;
    }
}

CGLIBProxy:

package com.xiaoxu;


import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

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

/**
 * @author xiaoxu
 * @date 2021-11-20 18:57
 * learn_java:com.xiaoxu.CGLIBProxy
 */
public class CGLIBProxy implements MethodInterceptor {
    private Object obj;
    public CGLIBProxy(Object obj){
        this.obj=obj;
    }
    
    //CGLIB中参数是Object[] objects,和JDK动态代理有一定的区别
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //方法之前执行
        System.out.println("CGLIB:方法之前执行..."+method.getName()+":传递的参数:"+ Arrays.toString(objects));
        //被增强的方法执行
        Object o1=method.invoke(obj,objects);
        //方法之后执行
        System.out.println("CGLIB:方法之后执行..."+obj);
        return o1;
    }

    public static void main(String[] args) {
        Enhancer e=new Enhancer();
        //设置父类,因为CGLIB是针对指定的类生成一个子类,所以需要指定父类
        e.setSuperclass(UserDaoImpl.class);
        e.setCallback(new CGLIBProxy(new UserDaoImpl()));//设置回调,传入代理对象
        UserDao o= (UserDao) e.create();
        System.out.println(o.add(1,6));
        System.out.println(o.update("34"));
    }
}
CGLIB:方法之前执行...add:传递的参数:[1, 6]
CGLIB:方法之后执行...com.xiaoxu.UserDaoImpl@22927a81
7
CGLIB:方法之前执行...update:传递的参数:[34]
CGLIB:方法之后执行...com.xiaoxu.UserDaoImpl@22927a81
34

3 AOP术语

(1)连接点

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

(2)切入点

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

(3)通知(增强)

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

(3.2)通知有多种类型:前置通知、后置通知、环绕通知、异常通知、最终通知

(4)切面

动作,把通知应用到切入点过程

4 AOP操作

(1)Spring框架一般是基于AspectJ实现AOP操作

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

(2)基于AspectJ实现AOP操作

(2.1)基于XML配置文件实现

(2.2)基于注解方式实现(使用)

(3)引入AOP相关依赖

若已经引入了spring-webmvc依赖(包含spring-aop):

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.12</version>
</dependency>

然后引入aspectJ相关依赖:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.2.13.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.5</version>
</dependency>

导入aspectjweaver依赖,就可以使用@Aspect注解修饰代理类,@Before等修饰增强的方法(均在org.aspectj.lang.annotation下)

(4)切入点表达式

(4.1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强

(4.2)语法结构:

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

其中权限修饰符是可以省略的,例如:execution (* com.xiaoxu.service . .* . *(. .))

整个表达式可以分为五个部分:

1、execution(): 表达式主体。

2、第一个*号:表示返回类型,*号表示所有的类型。

3、包名:表示需要拦截的包名,即com.xiaoxu.service,后面的两个句点表示当前包和当前包的所有子包,这样可以选择性覆盖com.xiaoxu.service包和子包下所有类的方法。

4、第二个*号:表示类名,*号表示所有的类。

5、*(. .):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示方法的任意参数。

还可以具体到某个类中的某个方法进行AOP增强,例如:

execution(*com.xiaoxu.dao.UserDao.add(. .)),意思是权限修饰符直接省略,然后返回类型不可省略,*代表全部返回类型,对于com.xiaoxu.dao包下的UserDao类中的add方法进行增强,add方法的参数由两个点表示。

(5)AspectJ注解

在这里插入图片描述
(5.1)创建类,在类里面定义方法

package com.xiaoxu.aopano;

public class User {
    public void add(){
        System.out.println("add......");
    }
}

(5.2)创建增强类(编写增强逻辑)

在增强类中,创建方法,让不同方法代表不同通知类型

package com.xiaoxu.aopano;

public class UserProxy {
    //前置通知
    public void before(){
        System.out.println("before...");
    }
}

(5.3)进行通知的配置

在spring的配置文件中,开启注解扫描;使用注解创建User和UserProxy对象;在增强类上面添加注解@Aspect;在Spring配置文件中开启生成代理对象

步骤如下:

1.context命名空间
在这里插入图片描述
2.aop命名空间
在这里插入图片描述
3. 开启注解扫描-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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">
        <!--  开启注解扫描  -->
        <context:component-scan base-package="com.xiaoxu.aopano"/>

</beans>

4.使用@Component注解创建User和UserProxy对象
在这里插入图片描述
在这里插入图片描述
5. 代理类(增强类)加上@Aspect注解

package com.xiaoxu.aopano;

import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Component
@Aspect  //生成代理对象
public class UserProxy {
    //前置通知
    public void before(){
        System.out.println("before...");
    }
}

6.开启AspectJ生成代理对象-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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">
        <!--  开启注解扫描  -->
        <context:component-scan base-package="com.xiaoxu.aopano"/>
        
        <!--开启AspectJ生成代理对象-->
        <aop:aspectj-autoproxy/>

</beans>

7.配置不同类型的通知

在增强类里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置

package com.xiaoxu.aopano;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component
@Aspect  //生成代理对象
public class UserProxy {
    //前置通知
    //before注解表示作为前置通知
    @Before(value = "execution(* com.xiaoxu.aopano.User.add(..))")
    public void before(){
        System.out.println("before...");
    }
}

8.编写测试类

import com.xiaoxu.aopano.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestAopUser {
    @Test
    public void test_01(){
        ApplicationContext context =new ClassPathXmlApplicationContext("MyAOP.xml");
        User u=context.getBean("user",User.class);
        u.add();
    }
}

结果:

before...
add......

9.增加新的类型通知

package com.xiaoxu.aopano;

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

@Component
@Aspect  //生成代理对象
public class UserProxy {
    //前置通知
    //before注解表示作为前置通知
    @Before(value = "execution(* com.xiaoxu.aopano.User.add(..))")
    public void before(){
        System.out.println("before...");
    }

    @AfterReturning(value = "execution(* com.xiaoxu.aopano.User.add(..))")
    public void afterReturning(){
        System.out.println("afterReturning...");
    }

    @After(value = "execution(* com.xiaoxu.aopano.User.add(..))")
    public void after(){
        System.out.println("after...");
    }

    @AfterThrowing(value = "execution(* com.xiaoxu.aopano.User.add(..))")
    public void afterThrowing(){
        System.out.println("afterThrowing...");
    }

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

重新执行测试类:

环绕之前...
before...
add......
afterReturning...
after...
环绕之后...

afterThrowing是异常通知,有异常才会通知,修改User.add:

public void add(){
    int i=26/0;
    System.out.println("add......");
}

在这里插入图片描述
可见抛出异常后,后面的通知也不会执行了。

10.公共切入点抽取

修改UserProxy :

package com.xiaoxu.aopano;

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

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

    //相同切入点抽取
    @Pointcut(value = "execution(* com.xiaoxu.aopano.User.add(..))")
    public void commonPoint(){

    }

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

    @AfterReturning(value = "commonPoint())")
    public void afterReturning(){
        System.out.println("afterReturning...");
    }

    @After(value = "commonPoint())")
    public void after(){
        System.out.println("after...");
    }

    @AfterThrowing(value = "commonPoint())")
    public void afterThrowing(){
        System.out.println("afterThrowing...");
    }

    //环绕通知
    @Around(value = "commonPoint())")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        System.out.println("环绕之前...");
        proceedingJoinPoint.proceed();
        System.out.println("环绕之后...");
    }
}

重新执行,效果一致:

环绕之前...
before...
add......
afterReturning...
after...
环绕之后...

11.有多个增强类用于增强相同的方法,可设置增强类优先级

在增强类上增加@Order(数字类型值),数字越小,优先级越高
在这里插入图片描述
UserProxy :

package com.xiaoxu.aopano;

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

@Component
@Aspect  //生成代理对象
@Order(22)
public class UserProxy {

    //相同切入点抽取
    @Pointcut(value = "execution(* com.xiaoxu.aopano.User.add(..))")
    public void commonPoint(){

    }

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

    @AfterReturning(value = "commonPoint())")
    public void afterReturning(){
        System.out.println("afterReturning...");
    }

    @After(value = "commonPoint())")
    public void after(){
        System.out.println("after...");
    }

    @AfterThrowing(value = "commonPoint())")
    public void afterThrowing(){
        System.out.println("afterThrowing...");
    }

    //环绕通知
    @Around(value = "commonPoint())")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        System.out.println("环绕之前...");
        proceedingJoinPoint.proceed();
        System.out.println("环绕之后...");
    }
}

PersonProxy :

package com.xiaoxu.aopano;

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

@Aspect
@Component
@Order(11)
public class PersonProxy {
    //相同切入点抽取
    @Pointcut(value = "execution(* com.xiaoxu.aopano.User.add(..))")
    public void commonPoint(){

    }

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

    @AfterReturning(value = "commonPoint())")
    public void afterReturning(){
        System.out.println("Person afterReturning...");
    }

    @After(value = "commonPoint())")
    public void after(){
        System.out.println("Person after...");
    }

    @AfterThrowing(value = "commonPoint())")
    public void afterThrowing(){
        System.out.println("Person afterThrowing...");
    }

    //环绕通知
    @Around(value = "commonPoint())")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        System.out.println("Person 环绕之前...");
        proceedingJoinPoint.proceed();
        System.out.println("Person 环绕之后...");
    }
}
Person 环绕之前...
Person before...
环绕之前...
before...
add......
afterReturning...
after...
环绕之后...
Person afterReturning...
Person after...
Person 环绕之后...

12.全注解运行AOP
在这里插入图片描述
AopConfig:

package com.xiaoxu.aopconfig;

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

@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = {"com.xiaoxu.aopano"})
public class AopConfig {
}

增加注解@ComponentScan(basePackages = {“com.xiaoxu.aopano”}),是为了代替如下xml的注解扫描,可获取到com.xiaoxu.aopano包下,@Component修饰的User和UserProxy类对象:

<!--  开启注解扫描  -->
<context:component-scan base-package="com.xiaoxu.aopano"/>

启用 @AspectJ 注解有以下两种方法:

(1)使用@Configuration和@EnableAspectJAutoProxy注解

@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
}

(2)基于XML配置

在xml中如下配置:

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

编写测试类:

import com.xiaoxu.aopano.User;
import com.xiaoxu.aopconfig.AopConfig;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestAopUser {
    @Test
    public void test_02(){
        ApplicationContext context2=new AnnotationConfigApplicationContext(AopConfig.class);
        User u2=context2.getBean("user",User.class);
        u2.add();
    }
}

执行结果(和xml配置运行的AOP效果一致):

Person 环绕之前...
Person before...
环绕之前...
before...
add......
afterReturning...
after...
环绕之后...
Person afterReturning...
Person after...
Person 环绕之后...

(6)AspectJ配置文件(了解)

常用还是(5)中AspectJ的注解方式,配置文件仅做了解

(6.1)创建两个类,增强类和被增强类,创建方法
在这里插入图片描述

package com.xiaoxu.aopxml;

public class Book {
    public void buy(){
        System.out.println("buy...");
    }
}
package com.xiaoxu.aopxml;

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

(6.2)在spring配置文件中创建两个类对象
在这里插入图片描述
(6.3)在spring配置文件中配置切入点
在这里插入图片描述
AspectJ支持的5种通知类型如下:

<aop:aspect id="myAspect" ref="aBean">
    <!-- 前置通知 -->
    <aop:before pointcut-ref="myPointCut" method="..."/>
    <!-- 后置通知 -->
    <aop:after-returning pointcut-ref="myPointCut" method="..."/>
    <!-- 环绕通知 -->
    <aop:around pointcut-ref="myPointCut" method="..."/>
    <!-- 异常通知 -->
    <aop:after-throwing pointcut-ref="myPointCut" method="..."/>
    <!-- 最终通知 -->
    <aop:after pointcut-ref="myPointCut" method="..."/>
</aop:aspect>

MyAOP2.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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">
        <!--创建对象-->
        <bean id="book" class="com.xiaoxu.aopxml.Book"/>
        <bean id="bookProxy" class="com.xiaoxu.aopxml.BookProxy"/>
        <!--  配置aop增强  -->
        <aop:config>
            <!--   切入点:被增强的Book类的buy方法    -->
            <aop:pointcut id="p1" expression="execution(* com.xiaoxu.aopxml.Book.buy(..))"/>
            <!--    配置切面   切面就是增强类(代理类)BookProxy
              aop:aspect的ref是代理类的bean id;aop:before决定了通知类型,pointcut-ref是
              切入点(被增强类)aop:pointcut的id-->
            <aop:aspect ref="bookProxy">
                <aop:before method="before" pointcut-ref="p1"/>
            </aop:aspect>
        </aop:config>

</beans>

(6.4)编写测试类

import com.xiaoxu.aopxml.Book;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestAopBook {
    @Test
    public void test_01(){
        ApplicationContext context=new ClassPathXmlApplicationContext("MyAOP2.xml");
        Book b=context.getBean("book",Book.class);
        b.buy();
    }
}

执行结果如下:

book before...
buy...

(7)总结

java的AOP,效果和python的装饰器一致,常用于登录鉴权、添加日志等等。不过区别有许多:java的AOP原理是动态代理,而python的装饰器原理是闭包。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值