2020-09-04 Spring

tech2026-10-04  1

Spring之AOP入门

一、代理模式1. 代理模式设计原理1.1 原理1.2 两种代理模式 2. 静态代理2.1 简介2.2 实现静态代理三要素2.3 代码演示2.4 弊端 二、反射1. 反射的一般使用步骤1. 1 获取Class类型的对象1. 2 获取构造方法并使用1.3.1 获取成员变量并使用1.3.2 获取成员方法并使用 2. 反射的应用之JDK动态代理2.1 InvocationHandler接口2.2 Proxy类2.3 一般使用步骤2.4 完整代码展示 二、Spring的内核之AOP1. 什么是AOP(AOP简单描述)2. AOP的作用3. AOP的优势4. AOP的底层原理实现_动态代理技术4.1 基于接口的JDK动态代理4.2 基于父类的cglib动态代理4.2.1 一般使用步骤4.2.2 完整代码展示 5 Spring_AOP配置5.1 SpringAOP相关概念5.2 SpringAOP相关术语 5.3 AOP开发明确的事项5.3.1 需要编写的内容5.3.2 AOP 技术实现的内容5.3.3 AOP 底层使用哪种代理方式 5.4 基于XML的AOP开发5.4.1 一般使用步骤5.4.2 XML配置AOP详解5.4.3 基于注解的AOP开发

一、代理模式

1. 代理模式设计原理

1.1 原理

使用一个代理将代理对象包装起来,然后用该代理对象取代原始对象的创建,任何对原始对象的调用都需要通过代理,代理对象决定 是否 以及 何时 将方法调用转到原始对象上。

1.2 两种代理模式

静态代理动态代理

2. 静态代理

2.1 简介

让 代理类 替 被代理类 完成一些“非业务”代码,核心业务代码还是交给 被代理者 自己完成。

2.2 实现静态代理三要素

主题接口被代理类代理类

2.3 代码演示

例如:给所有方法都增加一个功能,统计该方法的运行时间。 public class TestStaticProxy { public static void main (String[] args) { //new UserDaoImpl() 被代理者对象 //new UserDaoProxy() 是代理者对象 UserDao ud = new UserDaoProxy(new UserDaoImpl()); ud.add(); } } //需求,给UserDaoImpl的方法,增加求运行时间的功能。 //1.代理接口 interface UserDao{ void add(); } //2.被代理类 class UserDaoImpl implements UserDao{ @Override public void add() { System.out.println("add method running...."); } } //3.代理类 // (1)和被代理类必须实现同样的主题接口 // (2)因为要把核心业务代码交给被代理者自己完成,因此,需要在代理类中,保存(持有)被代理者对象的引用 class UserDaoProxy implements UserDao{ private UserDao target; public UserDaoProxy(UserDao target) { super(); this.target = target; } @Override public void add() { long start = System.currentTimeMillis(); System.out.println("Start..."); //执行真正的业务代码 target.add(); long end = System.currentTimeMillis(); System.out.println("end..."); System.out.println("totaltime:" + (end-start)); } }

2.4 弊端

在静态代理中,代理类和目标对象的类(被代理类)都是在编译期间就已经确定,不利于程序的扩展,同时,每一个代理类只能为一个接口服务,会导致程序开发中出现过多的代理,为了解决这个方法,我们会用到后续讲述的动态代理。

二、反射

1. 反射的一般使用步骤

1. 1 获取Class类型的对象

三种方式获取Class类型的对象 使用类的class属性 Class<Student> c1 = Student.class; 调用对象的getClass()方法 Student s = new Student(); Class<? extends Student> c3 = s.getClass(); 使用Class类中的静态方法forName Class<?> c4 = Class.forName("根路径名"); //根路径名,如:com.abc.类名

1. 2 获取构造方法并使用

4种常用获取构造器的方法 Constructor<?>[] getConstructors​() 返回所有公共构造方法对象的数组 onstructor<?>[] 构造器名字 = class类型对象.getConstructors(); Constructor<?>[] getDeclaredConstructors​() 返回所有构造方法对象的数组 Constructor<?>[] 构造器名字 = class类型对象.getDeclaredConstructors(); Constructor getConstructor​(Class<?>… parameterTypes) 返回单个公共构造方法的对象 Constructor<?> 构造器名字 = class类型对象.getConstructor(); //或者传入参数 //这里的参数是指:你要获取的构造方法的参数的个数和数据类型对应的字节码文件对象(.class) Constructor<?> 构造器名字 = class类型对象.getConstructor(String.class, int.class, String.class); Constructor getDeclaredConstructor​(Class<?>… parameterTypes) 返回单个指定的构造方法的对象 Constructor<?> 构造器名字 = class类型对象.getDeclaredConstructor(String.class); 使用构造器创建目标对象 常用的创建方法:T newInstance​(Object… initargs) Constructor<?> 构造器名字 = class类型对象.getConstructor(String.class, int.class, String.class); Object 对象名称 = 构造器名字.newInstance("帅哥", 26, "美女"); //若已经知道对象类型可以直接强转 //如已知的对象类型是Student Student 对象名称 = (Student)构造器名字.newInstance("帅哥", 26, "美女"); 暴力反射 使用私有(private修饰)的构造方法创建对象 方法:public void setAccessible​(boolean flag) 方法解析: 将此反射对象的accessible标志设置为指示的布尔值值为true,表示反射对象应该在使用Java语言访问控制时抑制检查。值为false,表示反射对象应该在使用Java语言访问控制时执行检查,并在类描述中指出变体。 //假设参数类型为String 的构造方法是私有构造方法 Constructor<?> 构造器名称 = class类型对象.getConstructor(String.class); 构造器名称.setAccessible(true);

1.3.1 获取成员变量并使用

4种获取成员变量的常用方法 Field[] getFields​() 返回所有公共成员变量对象的数组 Field[] 数组名称 = class类型对象.getFields(); Field[] getDeclaredFields​() 返回所有成员变量对象的数组 Field[] 数组名称 = class类型对象.getDeclaredFields(); Field getField​(String name) 返回单个公共成员变量对象 Field 返回的公共成员变量对象名称 = class类型对象.getField("公共成员变量名称"); Field getDeclaredField​(String name) 返回单个成员变量对象 Field 返回的成员变量对象名称 = c.getDeclaredField("成员变量名称"); 常用给成员变量赋值的方法 void set​(Object obj, Object value) 参数的意思是:给Object对象赋值为value 需要注意的是:需要先获得目标对象才能赋值 //例子:给obj目标对象的某个成员变量返回的对象f赋值为中国 f.set(obj,"中国") 暴力反射 public void setAccessible​(boolean flag) //例子:给obj目标对象的某个私有成员变量返回的对象age age.setAccessible(true); age.set(obj,18);

1.3.2 获取成员方法并使用

4种获取成员方法的常用方法 Method[] getMethods​() 返回所有公共成员方法对象的数组,包括继承的 Method[] 返回的数组名称 = class类型对象.getMethods(); Method[] getDeclaredMethods​() 返回所有成员方法对象的数组,不包括继承的 Method[] 返回的数组名称 = class类型对象.getDeclaredMethods(); Method getMethod​(String name, Class<?>… parameterTypes) 返回单个公共成员方法对象 Method 返回的公共成员方法对象名称 = class类型对象.getMethod("指定的公共成员方法名称"); Method getDeclaredMethod​(String name, Class<?>… parameterTypes) 返回单个成员方法对象 Method 返回的成员方法对象名称 = class类型对象.getDeclaredMethod("指定的成员方法名称"); Method类中用于调用成员方法的常用方法 Object invoke​(Object obj, Object… args) 这里参数的意思是:调用obj对象的成员方法,args是指该成员方法的参数,返回值是Object类型 //例子:使用某成员方法返回的成员对象me2调用obj目标对象的某成员方法,并且该成员方法的参数是aaa。 me2.invoke(obj,"aaa"); 暴力反射 public void setAccessible​(boolean flag) //例子:使用某私有(private修饰的)成员方法返回的成员对象me2调用obj目标对象的某私有成员方法,并且该成员方法的参数是aaa。 me2.setAccessible(true); me2.invoke(obj,"aaa"); //注意:如果是空参,aaa可以不写

2. 反射的应用之JDK动态代理

2.1 InvocationHandler接口

用到的方法:Object invoke(Object proxy, Method method, Object[] args)参数的含义解析: 第一个参数:proxy 代理类对象第二个参数:method 代理类要执行的真正的方法第三个参数:给 method 方法的实参列表,如果有的话 方法的注意事项: 在调用代理类方法时该方法自动执行该方法内部需要编写代理类要替被代理者完成的工作该方法的返回值就是method方法的返回值,就是invoke的返回值,或者说invoke方法的返回值将来就是作为method方法的返回值

2.2 Proxy类

用到的方法:static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 参数的含义解析: 第一个参数:传入被代理者的类加载器第二个参数:传入被代理者实现的接口们第三个参数:传入代理者要替被代理者完成得工作的处理器对象

2.3 一般使用步骤

创建InvocationHandler实例编写实例中的Object invoke方法编写实例中的构造方法创建被代理者的对象获取被代理者的类加载器对象获取被代理者实现的接口们创建代理者要替被代理者完成的工作的处理器对象动态创建代理类和它的对象执行方法

2.4 完整代码展示

以尚硅谷的教学代码为例:

/* * 动态代理 * * 代理模式: * 1、主题接口 * 2、被代理类 * 3、代理类(变化) * * 代理类分两个部分 * 1、 * 不直接写代理类,而是写一个“代理工作处理器”的类。 * 代理工作处理器:这个代理类要替被代理者完成xx事。 * 这个代理工作处理器必须实现一个接口:InvocationHandler * 2、动态的生成代理类及其它的对象 * 这个时候就需要借助java.lang.reflect.Proxy * (Proxy 提供用于创建动态代理类和实例的静态方法,它还是这些方法创建的所有动态代理类的超类) * static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) * 第一个参数:传入被代理者的类加载器 * 第二个参数:传入被代理者实现的接口们 * 第三个参数:传入代理者要替被代理者完成得工作的处理器对象 * * 不同的类加载器加载的Class,JVM认为是不同的类型。 */ public class TestDynamicProxy { public static void main (String[] args) { //(1)创建被代理者的对象 UserDao u = new UserDaoImpl(); //(2)获取被代理者的类加载器对象 ClassLoader loader = u.getClass().getClassLoader(); //(3)获取被代理者实现的接口们 Class<?>[] interfaces = u.getClass().getInterfaces(); //(4)创建代理者要替被代理者完成的工作的处理器对象 Handler h = new Handler(u);//传入被代理者 //动态创建代理类和它的对象 //得到的就是代理类的对象,代理类在内存中自动生成 UserDao ud = (UserDao) Proxy.newProxyInstance(loader,interfaces,h); ud.add(); } } //1.代理接口 interface UserDao{ void add(); } //2.被代理类 class UserDaoImpl implements UserDao{ @Override public void add() { System.out.println("add method running...."); } } //3.代理工作处理器 class Handler implements InvocationHandler{ private Object target; public Handler(Object target) { super(); this.target = target; } /* * 这个方法: * (1)它不是由程序员手动调用的,这个方法的代码会被编译器自动生成到代理类的对应方法中,当你调用代理类的方法时,自动执行这个方法 * (2)参数列表 * 第一参数:proxy 代理类对象 * 第二个参数:method 代理类要执行的真正的方法 * 第三个参数:给 method 方法的实参列表,如果有的话 * (3)编写代理类要替被代理者完成的工作 * 返回值:method方法的返回值,就是invoke的返回值,或者说invoke方法的返回值将来就是作为method方法的返回值 * 例如:给所有方法都增加一个功能,统计该方法的运行时间 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long start = System.currentTimeMillis(); System.out.println("开始执行"+ method.getName()+"方法"); System.out.println("代理类型"+ proxy.getClass().getName()); //执行真正的业务代码 Object retuenValue = method.invoke(target,args); long end = System.currentTimeMillis(); System.out.println("结束执行"+ method.getName() +"方法"); System.out.println("耗时:" + (end-start)); return retuenValue; } }

二、Spring的内核之AOP

1. 什么是AOP(AOP简单描述)

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

2. AOP的作用

在程序运行期间,在不修改源码的情况下对方法进行功能增强

3. AOP的优势

减少重复代码提高开发效率便于维护

4. AOP的底层原理实现_动态代理技术

4.1 基于接口的JDK动态代理

参考前面的反射之动态代理进行回顾黑马代码演示: TargetInterface接口 public interface TargetInerface { void save(); }

Target类

public class Target implements TargetInerface { public void save() { System.out.println("save running..."); } }

方法增强类

public class Advice { public void before(){ System.out.println("前置增强..."); } public void afterReturning(){ System.out.println("后置增强..."); } }

动态代理 这里要注意:

动态生成的代理对象proxy要用他的父类如:TargetInterface或Object接收,否则会报错 public class ProxyText { public static void main(String[] args) { //获得目标对象 final Target target = new Target(); //获得增强对象 final Advice advice = new Advice(); //返回值 就是动态生成的代理对象 TargetInerface proxy = (TargetInerface) Proxy.newProxyInstance( target.getClass().getClassLoader(),//目标对象类加载器 target.getClass().getInterfaces(),//目标对象相同的接口字节码对象数组 new InvocationHandler() { //调用代理对象的任何方法 实质执行的都是invoke方法 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { advice.before();//前置增强 Object invoke = method.invoke(target, args);//执行目标方法 advice.afterReturning();//后置增强 return invoke; } } ); proxy.save(); } }

4.2 基于父类的cglib动态代理

4.2.1 一般使用步骤

导入Jar包(若是Maven工程中,导入坐标)创建增强器 Enhancer enhancer = new Enhancer(); 给增强器设置父类(目标) enhancer.setSuperclass(Target.class); 设置回调 enhancer.setCallback(new MethodInterceptor() { public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { advice.before();//执行前置 method.invoke(target,args);//执行目标 advice.afterReturning();//执行后置 return null; } }); 创建代理对象 //这里可以用Target接,因为Proxy是基于Target生成的子类 Target proxy = (Target) enhancer.create(); 执行方法

4.2.2 完整代码展示

以黑马课程代码案例为例 Target类

public class Target { public void save() { System.out.println("save running..."); } }

增强类

public class Advice { public void before(){ System.out.println("前置增强..."); } public void afterReturning(){ System.out.println("后置增强..."); } }

测试类

public class ProxyText { public static void main(String[] args) { //获得目标对象 final Target target = new Target(); //获得增强对象 final Advice advice = new Advice(); //返回值 就是动态生成的代理对象 基于cglib //1、创建增强器 Enhancer enhancer = new Enhancer(); //2、给增强器设置父类 enhancer.setSuperclass(Target.class); //3、设置回调 enhancer.setCallback(new MethodInterceptor() { public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { advice.before();//执行前置 method.invoke(target,args);//执行目标 advice.afterReturning();//执行后置 return null; } }); //4、创建代理对象 //这里可以用Target接,因为Proxy是基于Target生成的子类 Target proxy = (Target) enhancer.create(); proxy.save(); } }

5 Spring_AOP配置

5.1 SpringAOP相关概念

Spring 的 AOP 实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。

5.2 SpringAOP相关术语

Target(目标对象):代理的目标对象。Proxy (代理):一个类被 AOP 织入增强后,就产生一个结果代理类。Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。 简单来说:就是可以被增强的方法Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint 进行拦截的定义。 简单来说:就是可以被增强的方法中需要被增强的方法。Advice(通知/ 增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。 简单来说:就是增强方法。Aspect(切面):是切入点和通知(引介)的结合。Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。 简单来说就是用增强方法对需要被增强的方法增强的过程。

5.3 AOP开发明确的事项

5.3.1 需要编写的内容

编写核心业务代码(目标类的目标方法)编写切面类,切面类中有通知(增强功能方法)在配置文件中,配置织入关系,即将哪些通知与哪些连接点进行结合

5.3.2 AOP 技术实现的内容

Spring 框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。

5.3.3 AOP 底层使用哪种代理方式

在 spring 中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式

5.4 基于XML的AOP开发

5.4.1 一般使用步骤

导入 AOP 相关坐标创建目标接口和目标类(内部有切点)创建切面类(内部有增强方法)将目标类和切面类的对象创建权交给 spring <!--目标对象--> <bean id ="target" class = "com.itheima.aop.Target"></bean> <!--切面对象--> <bean id="myAspect" class = "com.itheima.aop.MyAspect"></bean> 在 applicationContext.xml 中配置织入关系 <!--配置织入:告诉Spring框架 哪些方法(切点)需要进行哪些增强(前置、后置...)--> <aop:config> <!--声明切面--> <aop:aspect ref="myAspect"> <!--切面:切点+通知--> <aop:before method="before" pointcut="execution(public void com.itheima.aop.Target.save())"/> </aop:aspect> </aop:config> 测试代码

5.4.2 XML配置AOP详解

切点表达式的写法 execution([修饰符] 返回值类型 包名.类名.方法名(参数))

注意:

访问修饰符可以省略 execution(public void com.itheima.aop.Target.method()) 返回值类型、包名、类名、方法名可以使用星号* 代表任意 execution(void com.itheima.aop.Target.*(..)) 包名与类名之间一个点 . 代表当前包下的类,两个点 … 表示当前包及其子包下的类 execution(* com.itheima.aop.*.*(..)) execution(* com.itheima.aop..*.*(..)) 参数列表可以使用两个点 … 表示任意个数,任意类型的参数列表 execution(* *..*.*(..)) 通知的类型 通知的配置语法: <aop:通知类型 method=“切面类中方法名” pointcut=“切点表达式"></aop:通知类型>. 通知的类型: 前置通知,aop:before 用于配置前置通知。指定增强的方法在切入点方法之前执行后置通知,aop:after-returning 用于配置后置通知。指定增强的方法在切入点方法之后执行环绕通知,aop:around 用于配置环绕通知。指定增强的方法在切入点方法之前和之后都执行 注意:环绕通知的增强方法略有不同 //Proceeding JoinPoint:正在执行的连接点 == 切点 public Object around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("环绕前增强..."); Object proceed = pjp.proceed();//切点方法 System.out.println("环绕后增强..."); return proceed; } <aop:config> <!--声明切面--> <aop:aspect ref="myAspect"> <!--切面:切点+通知--> <aop:around method="around" pointcut="execution(public void com.itheima.aop.Target.save())"/> </aop:aspect> </aop:config> 异常抛出通知,aop:throwing 用于配置异常抛出通知。指定增强的方法在出现异常时执行最终通知,aop:after 用于配置最终通知。无论增强方式执行是否有异常都会执行 切点表达式的抽取 当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用 pointcut-ref 属性代替 pointcut 属性来引用抽取后的切点表达式。 <aop:config> <!--声明切面--> <aop:aspect ref="myAspect"> <!--切面:切点+通知--> <aop:pointcut id="myPointcut" expression="execution(public void com.itheima.aop.Target.save())"/> <aop:before method="before" pointcut-ref = "myPointcut"/> <aop:around method="around" pointcut-ref = "myPointcut"/> </aop:aspect> </aop:config>

5.4.3 基于注解的AOP开发

一般使用步骤 创建目标接口和目标类(内部有切点)创建切面类(内部有增强方法)将目标类和切面类的对象创建权交给 spring 使用注解:@Component(“id名称”) 目标类 @Component("target") public class Target implements TargetInterface { @Override public void save() { System.out.println("save running..."); } }

切面类

@Component("myAspect") public class MyAspect { public void before(){ System.out.println("前置增强......"); } //Proceeding JoinPoint:正在执行的连接点 == 切点 public Object around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("环绕前增强..."); Object proceed = pjp.proceed();//切点方法 System.out.println("环绕后增强..."); return proceed; } } 在切面类中使用注解配置织入关系 首先需要设置切面类:@Aspect 然后设置通知类型:@Before(“execution([修饰符] 返回值类型 包名.类名.方法名(参数类型,…))”) @Component("myAspect") @Aspect public class MyAspect { @Before(value = "execution(* com.itheima.anno.*.*(..))") public void before(){ System.out.println("前置增强......"); } //Proceeding JoinPoint:正在执行的连接点 == 切点 public Object around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("环绕前增强..."); Object proceed = pjp.proceed();//切点方法 System.out.println("环绕后增强..."); return proceed; } } 在配置文件中开启组件扫描和 AOP 的自动代理 首先要设置aop和context的工作空间:xmlns:context=“http://www.springframework.org/schema/context” http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd 然后开启组件扫描和AOP的自动代理: <context:component-scan base-package = “com.itheima.anno”/>和<aop:aspectj-autoproxy > </aop:aspectj-autoproxy> <?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" xmlns:context="http://www.springframework.org/schema/context" 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--开启组件扫描--> <context:component-scan base-package="com.itheima.anno"/> <!--aop的自动代理--> <aop:aspectj-autoproxy/> </beans> 测试 代码演示 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext-anno.xml") public class AnnoTest { @Autowired private TargetInterface target; @Test public void test1(){ target.save(); } } 注解通知的类型 通知的配置语法:@通知注解(“切点表达式") 前置通知:@Before 用于配置前置通知。指定增强的方法在切入点方法之前执行后置通知: @AfterReturning 用于配置后置通知。指定增强的方法在切入点方法之后执行环绕通知:@Around 用于配置环绕通知。指定增强的方法在切入点方法之前和之后都执行异常抛出通知:@AfterThrowing 用于配置异常抛出通知。指定增强的方法在出现异常时执行最终通知:@After 用于配置最终通知。无论增强方式执行是否有异常都会执行 切点表达式的抽取 同 xml 配置 aop 一样,我们可以将切点表达式抽取。抽取方式是在切面内定义方法,在该方法上使用@Pointcut注解定义切点表达式,然后在在增强注解中进行引用。 @Component("myAspect") @Aspect public class MyAspect { @Before(value = "MyAspect.myPoint()") public void before(){ System.out.println("前置增强......"); } @Around(value = "MyAspect.myPoint()") //Proceeding JoinPoint:正在执行的连接点 == 切点 public Object around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("环绕前增强..."); //获取当前执行方法所需要的的参数 Object[] args = pjp.getArgs(); Object proceed = pjp.proceed(args);//切点方法 System.out.println("环绕后增强..."); return proceed; } @Pointcut("execution(* com.itheima.anno.*.*(..))") public void myPoint(){} }

本文用来复习用~ 内容引用1 内容引用2


部分内容参考尚硅谷教学视频 ↩︎

部分内容参考黑马程序员教学视频 ↩︎

最新回复(0)