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的装饰器原理是闭包。