配套视频:【编程不良人】快速入门Spring,SpringBoot、SpringCloud学不好完全是因为Spring没有掌握!_哔哩哔哩_bilibili
5.1 基本概念
AOP(Aspect Oriented Programming)编程:即 面向切面编程
其中:Aspect(切面)、Oriented(面向)、Programming(编程)
底层原理:java代理设计模式(动态代理)
好处:在保证原始业务逻辑不变的情况下,通过代理对象完成业务中的附加操作(如事务等),将业务中核心操作放在目标对象中执行,实现了附加操作和核心操作解耦。
基本概念:
-
通知(Advice):除了目标方法之外的操作都可以称之为通知,可理解为附加操作、额外功能,比如:事务通知、日志通知、性能通知......
-
切入点(Pointcut):将开发好的通知应用于项目中的哪些组件中的哪些方法,给方法加入通知,一般通知多用于业务层,例如:UserService
-
切面(Aspect)=通知(Advice)+切入点(Pointcut)
AOP面向切面编程实现可分为如下几步:
-
开发通知类(附加功能)---新建通知
-
配置切面点---指定通知位置
-
利用通知和切入点组装切面,对通知和切入点进行绑定
5.2 编程步骤
配套视频:【编程不良人】快速入门Spring,SpringBoot、SpringCloud学不好完全是因为Spring没有掌握!_哔哩哔哩_bilibili
# 1.引入AOP编程相关依赖 spring-aop spring-expression spring-aspects # 2.开发通知类 MethodBeforeAdvice 前置通知 MethodInterceptor 环绕通知 AfterReturningAdvice 后置通知 / 返回后通知 ThrowsAdvice 异常通知 MyAdvice implements 通知接口{.....} //自定义通知类:用来完成额外功能 //自定义记录业务方法名称前置通知,前置通知即目标方法执行之前先执行的额外操作 public class MyAdvice implements MethodBeforeAdvice { @Override //参数1:当前调用的方法对象 //参数2:当前调用方法对象的参数 //参数3:目标对象 public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("目标方法名: "+method.getName()); System.out.println("目标方法的参数: "+args[0]); System.out.println("目标对象: "+target.getClass()); } } # 3.配置切面 a.引入aop命名空间 <?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"> b.注册通知类 <!--注册通知类--> <bean id="myAdvice" class="before.MyAdvice"/> c.配置/组装切面 aspect = advice + pointcut <!--组装切面--> <aop:config> <!--配置切入点pointcut id:切入点在工厂中的唯一标识 expression:用来指定切入项目中哪些组件中哪些方法 execution(返回值 包名.类名.方法名(参数)) *表示任意、所有 --> <aop:pointcut id="pc" expression="execution(* aop.UserServiceImpl.*(..))"/> <!--配置切面 advice-ref:工厂中通知id pointcut-ref:工厂切入点唯一标识--> <aop:advisor advice-ref="myAdvice" pointcut-ref="pc"/> </aop:config> # 4.启动工厂测试 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("aop/spring.xml"); UserService userSerivce = (UserService) context.getBean("userService"); System.out.println(userSerivce.getClass()); //代理对象 userSerivce.save("小黑");
5.3 前置通知使用
EmpService.java
package aop; /** * @ClassName EmpService * @Description 接口 * @Author Jiangnan Cui * @Date 2022/4/4 18:36 * @Version 1.0 */ public interface EmpService { void save(String name); String find(String name); }
EmpServiceImpl.java
package aop; /** * @ClassName EmpServiceImpl * @Description EmpService实现类 原始业务对象 / 目标对象 * @Author Jiangnan Cui * @Date 2022/4/4 18:37 * @Version 1.0 */ public class EmpServiceImpl implements EmpService{ @Override public void save(String name) { System.out.println("处理业务逻辑saveDao!" + name); } @Override public String find(String name) { System.out.println("处理业务逻辑findDAO!" + name); return name; } }
MyBeforeAdvice.java自定义前置通知
package aop; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; /** * @ClassName MyBeforeAdvice * @Description 自定义记录业务方法名称的前置通知 * @Author Jiangnan Cui * @Date 2022/4/4 18:46 * @Version 1.0 */ public class MyBeforeAdvice implements MethodBeforeAdvice { @Override //参数1:method表示当前执行方法的对象 //参数2:objects表示当前执行方法的参数 //参数3:o表示目标对象 public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println("当前执行方法的名称:" + method.getName()); } }
spring.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"> <!--管理Service组件对象--> <bean class="aop.EmpServiceImpl" id="empService"></bean> <!--管理BeforeAdvice组件 注册通知--> <bean class="aop.MyBeforeAdvice" id="myBeforeAdvice"></bean> <!--组装切面--> <aop:config> <!--配置切入点 pointcut id:切入点在工厂中的唯一标识 expression:用来指定切入项目中哪些组件中哪些方法 execution(返回值 包.类名.方法名(..)) --> <aop:pointcut id="pc" expression="execution(* aop.*ServiceImpl.*(..))"/> <!--配置切面 advisor advice-ref:工厂中通知id pointcut-ref:工厂切入点唯一标识 --> <aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="pc"/> </aop:config> </beans>
TestSpring.java
package aop; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @ClassName TestSpring * @Description 测试 * @Author Jiangnan Cui * @Date 2022/4/4 20:49 * @Version 1.0 */ public class TestSpring { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("aop/spring.xml"); EmpService empService = (EmpService)context.getBean("empService"); System.out.println(empService.getClass()); //此时的EmpService不再是单纯的类,而是动态代理类 empService.save("小崔"); } }
输出结果:
class com.sun.proxy.$Proxy2 当前执行方法的名称:save 处理业务逻辑saveDao!小崔
5.4 环绕通知的使用
配套视频:https://www.bilibili.com/video/BV12k4y167jK?p=11
DeptDAO.java
package dao; /** * @ClassName DeptDAO * @Description TODO * @Author Jiangnan Cui * @Date 2022/4/5 20:58 * @Version 1.0 */ public interface DeptDAO { void save(String name); void update(String name); void delete(Integer id); String find(String name); }
DeptDAOImpl.java
package dao; /** * @ClassName DeptDAOImpl * @Description TODO * @Author Jiangnan Cui * @Date 2022/4/5 20:59 * @Version 1.0 */ public class DeptDAOImpl implements DeptDAO{ @Override public void save(String name) { System.out.println("save DAO :" + name); } @Override public void update(String name) { System.out.println("update DAO :" + name); } @Override public void delete(Integer id) { System.out.println("delete DAO :" + id); } @Override public String find(String name) { System.out.println("find DAO :" + name); return name; } }
DeptService.java
package service; /** * @ClassName DeptService * @Description TODO * @Author Jiangnan Cui * @Date 2022/4/5 21:02 * @Version 1.0 */ public interface DeptService { void save(String name); void update(String name); void delete(Integer id); String find(String name); }
DeptServiceImpl.java
package service; import dao.DeptDAO; /** * @ClassName DeptServiceImpl * @Description 业务层组件 * @Author Jiangnan Cui * @Date 2022/4/5 21:03 * @Version 1.0 */ public class DeptServiceImpl implements DeptService{ //业务层需要DAO组件 private DeptDAO deptDAO; public void setDeptDAO(DeptDAO deptDAO) { this.deptDAO = deptDAO; } @Override public void save(String name) { System.out.println("处理save业务逻辑!"); deptDAO.save(name); } @Override public void update(String name) { System.out.println("处理update业务逻辑!"); deptDAO.update(name); } @Override public void delete(Integer id) { System.out.println("处理delete业务逻辑!"); deptDAO.delete(id); } @Override public String find(String name) { System.out.println("处理find业务逻辑!"); deptDAO.find(name); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //throw new RuntimeException("执行出错啦!"); return null; } }
MethodInvokeTimeAdvice.java自定义环绕通知用来记录目标方法的执行时长
package advices; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import java.util.Date; /** * @ClassName MethodInvokeTimeAdvice * @Description 自定义环绕通知用来记录目标方法的执行时长 * @Author Jiangnan Cui * @Date 2022/4/5 21:17 * @Version 1.0 */ public class MethodInvokeTimeAdvice implements MethodInterceptor { @Override //参数:methodInvocation将invoke之前的参数封装到一个参数里, // 可通过该参数获得当前执行的方法、当前执行的方法参数、目标对象 public Object invoke(MethodInvocation methodInvocation) throws Throwable { System.out.println("进入环绕通知......"); System.out.println("当前执行方法:" + methodInvocation.getMethod().getName()); System.out.println("当前执行方法的参数:" + methodInvocation.getArguments()[0]); System.out.println("当前执行的目标对象:" + methodInvocation.getThis()); try { long start = new Date().getTime(); //放行目标方法:返回值为目标方法返回值,这个返回值一定要在通知中返回 Object proceed = methodInvocation.proceed();//继续处理 long end = new Date().getTime(); System.out.println(methodInvocation.getMethod().getName() + "方法执行了" + (end-start) + "ms!"); return proceed; } catch (Exception e) { e.printStackTrace(); System.out.println("出现异常时业务处理"); } return null; } }
spring.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"> <!--管理DAO组件--> <bean class="dao.DeptDAOImpl" id="deptDAO"/> <!--管理Service组件--> <bean class="service.DeptServiceImpl" id="deptService"> <property name="deptDAO" ref="deptDAO"/> </bean> <!--注册通知类--> <bean class="advices.MethodInvokeTimeAdvice" id="methodInvokeTimeAdvice"/> <!--配置切面--> <aop:config> <!--配置切入点--> <aop:pointcut id="pc" expression="execution(* service.*ServiceImpl.*(..))"/> <!--组装切面--> <aop:advisor advice-ref="methodInvokeTimeAdvice" pointcut-ref="pc"/> </aop:config> </beans>
环绕通知测试
package test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import service.DeptService; /** * @ClassName TestDeptService * @Description 测试环绕通知 * @Author Jiangnan Cui * @Date 2022/4/5 21:32 * @Version 1.0 */ public class TestDeptService { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); DeptService deptService = (DeptService) context.getBean("deptService"); System.out.println(deptService.getClass()); deptService.find("小崔"); } }
输出结果:
(1)正常执行时
class com.sun.proxy.$Proxy2 进入环绕通知...... 当前执行方法:find 当前执行方法的参数:小崔 当前执行的目标对象:service.DeptServiceImpl@5ad851c9 处理find业务逻辑! find DAO :小崔 find方法执行了1000ms!
(2)执行出错时
class com.sun.proxy.$Proxy2 进入环绕通知...... 当前执行方法:find 当前执行方法的参数:小崔 当前执行的目标对象:service.DeptServiceImpl@5ad851c9 处理find业务逻辑! find DAO :小崔 java.lang.RuntimeException: 执行出错啦! at service.DeptServiceImpl.find(DeptServiceImpl.java:47) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) at advices.MethodInvokeTimeAdvice.invoke(MethodInvokeTimeAdvice.java:27) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) at com.sun.proxy.$Proxy2.find(Unknown Source) at test.TestDeptService.main(TestDeptService.java:19) 出现异常时业务处理
5.5 后置通知与异常通知使用
配套视频:【编程不良人】快速入门Spring,SpringBoot、SpringCloud学不好完全是因为Spring没有掌握!_哔哩哔哩_bilibili
UserService.java
package service; /** * @ClassName UserService * @Description TODO * @Author Jiangnan Cui * @Date 2022/4/6 17:34 * @Version 1.0 */ public interface UserService { String save(String name); }
UserServiceImpl.java
package service; /** * @ClassName UserServiceImpl * @Description TODO * @Author Jiangnan Cui * @Date 2022/4/6 17:34 * @Version 1.0 */ public class UserServiceImpl implements UserService{ @Override public String save(String name) { System.out.println("UserService save" + name); return name; } }
自定义后置通知和异常通知MyAfterAndThrowsAdvice.java
package advices; import org.springframework.aop.AfterReturningAdvice; import org.springframework.aop.ThrowsAdvice; import java.lang.reflect.Method; /** * @ClassName MyAfterAndThrowsAdvice * @Description 自定义后置通知和异常通知 * @Author Jiangnan Cui * @Date 2022/4/6 17:36 * @Version 1.0 */ public class MyAfterAndThrowsAdvice implements AfterReturningAdvice, ThrowsAdvice { @Override //后置通知 //主体逻辑:此中的过程将在核心业务执行之后执行 //参数1:目标方法返回值 如果方法返回值时void,返回值为null //参数2:当前执行方法对象 //参数3:执行方法参数 //参数4:目标对象 public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("进入后置通知..."); System.out.println("返回值:" + returnValue); System.out.println("当前执行方法对象:" + method.getName()); System.out.println("当前执行方法的参数:" + args[0]); System.out.println("目标对象:" + target); } //异常通知 //注意:异常通知继承了后置通知,虽然实现该接口时不用实现方法,但最好实现afterThrowing方法,以免异常报错 //主体逻辑:此中的过程将在核心业务出现异常之后执行 //参数1:当前执行方法对象 //参数2:执行方法参数 //参数3:目标对象 //参数4:目标抛出的异常 public void afterThrowing(Method method, Object[] args, Object target, Exception ex){ System.out.println("进入异常通知......."); System.out.println("方法名: " + method.getName()); System.out.println("方法的参数: " + args[0]); System.out.println("目标对象: " + target); System.out.println("异常信息: "+ ex.getMessage()); } //注意:两者不能同时工作 }
spring.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"> <!--管理Service组件--> <bean class="service.UserServiceImpl" id="userService"/> <!--注册通知类--> <bean class="advices.MyAfterAndThrowsAdvice" id="myAfterAndThrowsAdvice"/> <!--组装切面--> <aop:config> <aop:pointcut id="pc" expression="execution(* service.*ServiceImpl.*(..))"/> <aop:advisor advice-ref="myAfterAndThrowsAdvice" pointcut-ref="pc"/> </aop:config> </beans>
测试自定义后置通知和异常通知MyAfterAndThrowsAdviceTest.java
package test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import service.UserService; /** * @ClassName MyAfterAndThrowsAdviceTest * @Description 测试自定义后置通知和异常通知 * @Author Jiangnan Cui * @Date 2022/4/6 17:47 * @Version 1.0 */ public class MyAfterAndThrowsAdviceTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("after/spring.xml"); UserService userService = (UserService) context.getBean("userService"); System.out.println(userService.getClass()); userService.save("小崔"); } }
测试结果:
(1)正常执行时,后置通知起作用
class com.sun.proxy.$Proxy2 UserService save 小崔 进入后置通知... 返回值:小崔 当前执行方法对象:save 当前执行方法的参数:小崔 目标对象:service.UserServiceImpl@17c1bced
(2)抛出异常时,异常通知起作用
class com.sun.proxy.$Proxy2 UserService save 小崔 进入异常通知....... 方法名: save 方法的参数: 小崔 目标对象: service.UserServiceImpl@2d9d4f9d 异常信息: save方法执行出错啦 Exception in thread "main" java.lang.RuntimeException: save方法执行出错啦 at service.UserServiceImpl.save(UserServiceImpl.java:15) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) at org.springframework.aop.framework.adapter.ThrowsAdviceInterceptor.invoke(ThrowsAdviceInterceptor.java:125) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:52) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) at com.sun.proxy.$Proxy2.save(Unknown Source) at test.MyAfterReturningAdviceTest.main(MyAfterReturningAdviceTest.java:19) Process finished with exit code 1
5.6 切入点表达表
配套视频:【编程不良人】快速入门Spring,SpringBoot、SpringCloud学不好完全是因为Spring没有掌握!_哔哩哔哩_bilibili
spring (pointcut)切入点表达式:主要是用来决定项目中那些组件中那些方法需要加入通知
1.execution方法级别
execution 切入点表达式 --->方法级别的切入点表达式 控制粒度: 方法级别 效率低 格式:expression="切入点表达式" 完整语法: 1.execution(访问权限修饰符 返回值 包名.类名.方法名(参数类型)) 2.execution(返回值 包名.类名.方法名(参数类型)),默认public访问,因此可以省略访问修饰符 * 任意多个字符 ..作用于参数类型:表示任意多个 作用于类:表示当前包及子包 1).execution(* com.baizhi.service.*.*(..)) [比较多一种] 包: com.baizhi.service 类: 任意类 方法: 任意方法 参数: 参数任意 返回值: 任意返回值类型 2).execution(String com.baizhi.service.*ServiceImpl.*(..)) 包: com.baizhi.service 类: 以ServiceImpl结尾的类 方法: 方法名任意 参数: 任意参数 返回值: 返回值必须String类型相关方法 3).execution(String com.baizhi.service.*Service*.*(String)) 包: com.baizhi.service 类: 类名中包含Service关键字的类 方法: 任意 参数: 参数只有一个类型必须是String 返回值: 返回值必须是String 4).execution(* com.baizhi.service..*.*(..)) [比较多一种] 包: com.baizhi.service及这个包中子包的子包 类: 任意类 方法: 任意方法 参数: 任意参数 返回值: 任意类型 5).execution(* com.baizhi.service.*ServiceImpl.*(..)) [比较多一种] 包: com.baizhi.service 包 类: 以ServiceImpl结尾的类 方法: 任意方法 参数: 任意参数 返回值: 任意类型 6).execution(* *.*(..)) //全部方法 避免使用这种方式 包: 项目中所有包 类: 项目中所有类 方法: 所有方法 参数: 所有参数 返回值: 任意类型 *注意*: 方法级别的切入点表达式尽可能精准,避免不必要的切入,否则程序运行可能出现异常
2.within类级别
within 切入点表达式 --->类级别的切入点表达式 控制粒度: 类级别 效率高 推荐 格式:expression="within()" 完整语法: 1. within(包.类名) 1).within(com.baizhi.service.*ServiceImpl) 包:com.baizhi.service 类:以ServiceImpl结尾的类 2)within(com.baizhi.service.*) 包: com.baizhi.service 类: 所有类中所有方法不关心返回值和参数 3)within(com.baizhi.service.UserServiceImpl) 包: com.baizhi.service 类: UserServiceImpl类中所有方法不关心返回值和参数
注意:within的效率高于execution表达式,推荐使用within表达式,切入到方法级别时在使用execution表达式
5.7 IOC和AOP总结
配套视频:【编程不良人】快速入门Spring,SpringBoot、SpringCloud学不好完全是因为Spring没有掌握!_哔哩哔哩_bilibili
spring 框架核心 1).IOC and DI Inversion of Controll 控制反转 Dependency Injection 依赖注入 定义: 就是将手动通过new关键字创建对象的权力交给spring,交给工厂由工厂创建对象, spring不仅要创建对象还要在创建对象的同时维护组件与组件的依赖关系 2).AOP Aspects(切面) Oriented(面向) Programming(编程) 底层原理: java中动态代理对象的封装 定义: 通过在程序运行的过程中动态的为项目中某些组件生成动态代理对象, 通过在生成的动态代理对象中执行相应的附加操作|额外功能,减少 项目中通用代码的冗余问题 通知(advice): 除了核心业务方法以外的附加操作都称之为通知 额外功能 事务通知....性能 日志 ... 切入点(pointcut): 用来指定项目中那些组件中那些方法需要加入 通知(附加操作,额外功能) 常用于业务层组件 切面(aspect) = 通知 + 切入点 注意:切面、aop:pointcut、aop:advisor都可以有多个,可以配置不同的切入点表达式 myadvice1 性能通知 myadvice2 日志通知 1).工厂管理通知 myadvice1 myadvice2 2).配置切面 <aop:config> <aop:pointcut id="pc" expression="execution()|within()"/> <aop:pointcut id="pc1" expression="execution()|within()"/> ....... <aop:advisor advice-ref="myadvice1" pointcut-ref="pc"/> <aop:advisor advice-ref="myadvice2" pointcut-ref="pc"/> ........ <aop:config>