作者简介 :一只大皮卡丘,计算机专业学生,正在努力学习、努力敲代码中! 让我们一起继续努力学习!
该文章参考学习教材为:
《Java EE企业级应用开发教程 (Spring + Spring MVC +MyBatis)》 黑马程序员 / 编著
文章以课本知识点 + 代码为主线,结合自己看书学习过程中的理解和感悟 ,最终成就了该文章文章用于本人学习使用 , 同时希望能帮助大家。
欢迎大家点赞👍 收藏⭐ 关注💖哦!!!(侵权教材方可联系我,进行删除,如果雷同,纯属巧合)
1.JDK动态代理、CGLIB代理、AspectJ 三者的区别 ?
①JDK动态代理、CGLIB代理 是 实现AOP (面向切面编程) 功能的技术,而AspectJ是一个 AOP框架。
②JDK动态代理、CGLIB代理 是 动态代理,AspectJ是 静态代理。
③ 实现方式:
JDK动态代理是 基于接口 的动态代理,只能对实现了接口的类生成代理。CGLIB动态代理是针对 类 实现代理,原理是对指定的目标类生成一个子类,并覆其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
Aspect是基于编译时期的代理,它是在编译时期就将代理逻辑织入到目标类中,在运行期间不再需要任何代理逻辑,所以它 比JDK动态代理和CGLIB动态代理的性能要好。
④如果目标对象实现了接口,默认情况下Spring会采用 JDK动态代理 实现AOP。
如果目标对象没有实现接口,会采用CGLIB代理,Spring会自动在JDK动态代理和CGLIB之间转换。⑤性能:
AspectJ静态代理 是基于编译时期的代理, 性能优于 JDK动态代理 和 CGLIB动态代理。
2.为什么AspectJ的性能更好?
为什么 AspectJ的性能 比 JDK动态代理 和 CGLIB 更好?
- 这是由于AspectJ在编译阶段将代码织入class文件,能够 避免在运行时 “动态生成代理类” 所带来的额外开销。
同时,由于AspectJ对字节码的处理更加成熟和稳定,因此,它可以 大大减少由于字节码处理问题带来的性能损耗。因此,AspectJ的性能相对较好。
3.AspectJ开发
- AspectJ 是一个基于 Java语言 的 AOP框架,它提供了强大的AOP功能。Spring2.0以后,Spring AOP引入了对 AspectJ 的支持,并允许直接使用 AspectJ 进行编程,而Spring自身的AOP API也尽量与 AspectJ 保持一致。
- 新版本的Spring框架,也建议使用AspectJ来开发AOP。
- 使用AspectJ实现AOP有两种方式 :
一种是 基于XML的声明式AspectJ,另一种是基于 注解的声明式AspectJ。
3.1 基于“XML”的声明式AspectJ
AspectJ 的定义
基于XML的声明式AspectJ 是指通过 XML文件 来定义切面、切入点及通知。所有的切面、切入点和通知都必须定义在<aop:config>元素内。
<aop:config>元素及其子元素 如下图所示 :在上图中,Spring 配置文件中的 <beans>元素 下可以包含多个 <aop:config> 元素,一个<aop:config>元素中又可以包含属性和子元素,其子元素包括 <aop:pointcut>、<aop:advisor> 和 <aop:aspect>。在配置时,这3个子元素必须按照此顺序来定义。在 <aop:aspec> 元素下,同样包含了属性和多个子元素,通过使用 <aop:aspect> 元素及其子元素就可以在XIML文件中配置切面、切入点 和 通知。
aop:config (开始AspectJ开发)
上述图片中配置标签详解:
- <aop:config> :
开始进行AspectJ开发 (Spring 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"> <!-- 可在该配置文件中添加、配置和关闭Bean --> <!-- 也已在该配置文件中配置AOP切面等 --> <!-- 将切面类添加到IOC容器中 (配置切面Bean) --> <bean id="myAspectJ" class="com.myh.aspectJ.xml.MyAspect"></bean> <!-- 用<aop:config>标签开始进行AspectJ开发 (Spring AOP 开发) --> <aop:config> ..... </aop:config> </beans>
aop:aspect (配置切面)
<aop:aspect> (配置切面):
在Spring的配置文件中,配置切面使用的是 <aop:aspect>元素,该元素会将一个已定义好的Spring Bean转换成切面Bean (定义好哪个Bean/类为切面类),建议在配置文件中先配置文件中 “切面类” 交给IOC管理,成为Bean,在 <aop:aspect> 中用 ref属性 引用 已配置好的Bean则会方便开发,其中一个常指定的属性为 id 属性。
属性名称 描述 id 用于定义切面的唯一标识名称。 ref 用于引用普通的Spring Bean。 例子如 :
<!-- 用<aop:config>标签开始进行AspectJ开发 (Spring AOP 开发) --> <aop:config> <!-- 配置切面 --> <aop:aspect id="aspect" ref="myAspectJ"> ..... </aop:aspect> </aop:config>
aop:pointcut (配置切入点)
- <aop:pointcut> (配置切入点):
在Spring的配置文件中,可通过 <aop:pointcut>元素来定义切入点 (目标类中的“方法名”)的。当 <aop:pointct元素作为 <aop:config>元素的子元素定义时,表示该切入点是 全局切入点,它可被多个切面共享;当 <aop:pointcut>元素作为 <aop:aspect>元素的子元素时,表示该切入点 只对当前切面有效 。
(即该切入点仅对该切面有效)在 <aop:pointcut>中通常会指定id 和 expression 两个属性 :
属性名称 描述 id 用于指定切入点的“唯一标识名称”。 expression 用于指定切入点关联的“切入点表达式”。 例子如 :
<!-- 配置切入点(切入点: 方法名) --> <!-- 该"切入点表达式"的配置方式为: 开发中常用的配置方式,而非“基本格式” --> <aop:pointcut expression="execution(* com.myh.jdk.*.*(..))" id="myPoinCut" />
<?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"> <!-- 可在该配置文件中添加、配置和关闭Bean --> <!-- 也已在该配置文件中配置AOP切面等 --> <!-- 将切面类添加到IOC容器中 (配置切面Bean) --> <bean id="myAspectJ" class="com.myh.aspectJ.xml.MyAspect"></bean> <!-- 用<aop:config>标签开始进行AspectJ开发 (Spring AOP 开发) --> <aop:config> <!-- 配置切面 --> <aop:aspect id="aspect" ref="myAspectJ"> <!-- 配置切入点(切入点: 方法名) --> <!-- 该"切入点表达式"的配置方式为: 开发中常用的配置方式 --> <aop:pointcut expression="execution(* com.myh.jdk.*.*(..))" id="myPoinCut" /> <!-- 配置5种通知--> <!-- 前置通知--> <aop:before method="myBefore" pointcut-ref="myPoinCut"/> <!-- 后置通知--> <aop:after-returning method="myAfterReturing" pointcut-ref="myPoinCut" returning="returnVal"/> <!-- 环绕通知--> <aop:around method="myAround" pointcut-ref="myPoinCut"/> <!-- 异常通知--> <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPoinCut"/> <!-- 最终通知--> <aop:after method="myAfer" pointcut-ref="myPoinCut"/> </aop:aspect> </aop:config> </beans>
在上述代码中 execution( com.myh.jdk..(…))表示定义的 切入点表达式,该表达式的含义 : 匹配com.myh.jdk包下的任意类的任意方法的执行*。
其中 execution() 是表达式的主体,
第1个* 表示的是返回类型;使用*代表所有类型。 comitheima.jdk 表示的是需要拦截的包名。
后面第2个*表示的是类名,使用*代表所有的类。 第3个*表示的是方法名,使用*表示所有方法。
后面 (…) 表示方法的参数,其中的“…"表示任意参数。 (第1个 *与包名之间有一个空格。)
ps :
该"切入点表达式"的配置方式为 : 开发中常用的配置方式。
拓展 :上面代码中的“切入点表达式” 为 开发中常用的配置方式,Spring AOP中切入点表达式的基本格式 如下
execution ( modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern ( param-pattern) throws-pattern )具体讲解 :
●modifiers-pattern :
表示定义的目标方法的访问修饰符,如public private等。
●ret-type-pattern :
表示定义的目标方法的返回值类型,如void、 String等。
●declaring-type-pattern :
表示定义的目标方法的类路径,如com.myh.jdk.UserDaolmpl.
●name-pattern :
表示具体需要被代理的目标方法,如add()方法。
●param-pattern :
表示需要被代理的目标方法包含的参数。●throws-pattern : 表示需要被代理的目标方法抛出的异常类型。
其中带 有问号(? )的部分,如modfere ptte dlang-vpo paten和howo-aten表示可配置项;而 其他部分 属于 必须配置项。
aop:before aop:after-returning aop:around aop:after-throwing aop:after (配置通知)
配置 五种常用的通知 :
<aop:before> : 配置“前置通知”。
<aop:after-returning> : 配置“后置通知”。
<aop:around> : 配置“环绕通知”。
<aop:after-throwing> : 配置“异常通知”。
<aop:after> : 配置“最终通知”。
ps :(注意点) :在AOP的配置信息中,使用 <aop:after-returning> 配置的后置通知和使用 <aop:after> 配置的最终通知虽然都是在目标方法执行之后执行,但它们也是有所区别的。
后置通知 只有在目标方法 成功执行 后才会被织入,而 最终通知 不论目标方法如何结束 ( 包括 成功执行 和 异常中止两种情况),它都会被织入。
属性名称 描述 pointcut 该属性用于指定一个切入点表达式, Spring 将在匹配该表达式的连接点时织入该通知。 pointcut-ref 该属性指定一个已经存在的切入点名称,如配置代码中的myPointCut。通常pointcut 和 pointcut-ref两个属性只需要使用其中之一。 method 该属性指定一个方法名, 指定将==切面Bean中的该方法转换为增强处理==。 returning 该属性只对<after-returning>元素有效,它用于指定一个形参名, 后置通知方法可通知该形参访问目标方法的返回值。 throwing 该属性只对**<after-throwing>元素有效,它用于指定一个形参名**, 异常通知方法可以通过该形
参访问目标方法所抛出的异常。“通知”例子如 :
<!-- 配置5种通知--> <!-- 前置通知--> <aop:before method="myBefore" pointcut-ref="myPoinCut"/> <!-- 后置通知--> <aop:after-returning method="myAfterReturing" pointcut-ref="myPoinCut" returning="returnVal"/> <!-- 环绕通知--> <aop:around method="myAround" pointcut-ref="myPoinCut"/> <!-- 异常通知--> <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPoinCut"/> <!-- 最终通知--> <aop:after method="myAfer" pointcut-ref="myPoinCut"/>
<?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"> <!-- 可在该配置文件中添加、配置和关闭Bean --> <!-- 也已在该配置文件中配置AOP切面等 --> <!-- 将切面类添加到IOC容器中 (配置切面Bean) --> <bean id="myAspectJ" class="com.myh.aspectJ.xml.MyAspect"></bean> <!-- 用<aop:config>标签开始进行AspectJ开发 (Spring AOP 开发) --> <aop:config> <!-- 配置切面 --> <aop:aspect id="aspect" ref="myAspectJ"> <!-- 配置切入点(切入点: 方法名) --> <!-- 该"切入点表达式"的配置方式为: 开发中常用的配置方式 --> <aop:pointcut expression="execution(* com.myh.jdk.*.*(..))" id="myPoinCut" /> <!-- 配置5种通知--> <!-- 前置通知--> <aop:before method="myBefore" pointcut-ref="myPoinCut"/> <!-- 后置通知--> <aop:after-returning method="myAfterReturing" pointcut-ref="myPoinCut" returning="returnVal"/> <!-- 环绕通知--> <aop:around method="myAround" pointcut-ref="myPoinCut"/> <!-- 异常通知--> <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPoinCut"/> <!-- 最终通知--> <aop:after method="myAfer" pointcut-ref="myPoinCut"/> </aop:aspect> </aop:config> </beans>
3.2 基于“XML”的声明式AspectJ 的 “完整例子演示”
第一步、在DIEA中创建一个Java项目,添加web功能模块。
第二步、在项目WEB-INF目录中创建lib文件夹,在原有的Spring框架最基本核心的jar包的基础上 加上 spring-aspects.jar 和 aspectjweaver.jar ,让jar包生效。 (导入AspectJ 框架开发所需的JAR)
jar包 / maven( 依赖 ) 下载
获取spring框架基本核心jar包
AspectJ 框架开发需要的jar包第三步 创建UserDao接口 、 UserDaoImpl实现类 (目标类) :
UserDao.javapackage com.myh.aspectJ.xml; public interface UserDao{ public void addUser(); public void deleteUser(); }
UserDaoImpl.java
package com.myh.aspectJ.xml; public class UserDaoImpl implements UserDao { // ”目标类“,该类创建的对象为”目标对象“ @Override public void addUser() { //这方法将要被“增强处理” System.out.println("--------------------------"); System.out.println("添加用户 (此执行的是: 目标方法)"); System.out.println("--------------------------"); } @Override public void deleteUser() { //这方法将要被“增强处理” System.out.println("删除用户"); } }
第三步 创建 MyAspect (切面类):
MyAspect.java
package com.myh.aspectJ.xml; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; public class MyAspect { //切面类 : 以下为常用的5种通知,将用于“增强处理” /** * 前置通知 */ public void myBefore(JoinPoint joinPoint) { System.out.println("前置通知,模拟执行了...操作,"); //获得"目标类" System.out.println("目标类为: "+joinPoint.getTarget()); //获得“目标方法” ("目标方法"将被植入“增强处理”) System.out.println("目标方法为:" +joinPoint.getSignature().getName()); } /** * 后置通知 */ public void myAfterReturing(JoinPoint joinPoint) { System.out.println("后置通知,模拟执行了...操作,"); //获得目标方法 System.out.println("目标方法为: "+joinPoint.getSignature().getName()); } /** * 环绕通知 * ProceedingJoinPoint 是 JoinPoint的”子接口“ : 可以执行目标方法。 * 1.必须是Object类型的返回值 * 2.必须接收一个参数,类型为 ProceedingJoinPoint * 3.必须throws Throwable */ public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { //ProceedingJoinPoint : 可以执行目标方法 //环绕通知"开始" System.out.println("环绕通知开始,执行目标方法之前,模拟执行了...操作"); //执行目标方法 Object obj = proceedingJoinPoint.proceed(); //环绕通知"结束" System.out.println("环绕通知结束,执行目标方法之后,模拟执行了...操作"); return obj; } //异常通知 public void myAfterThrowing(JoinPoint joinPoint,Throwable e) { System.out.println("异常通知: " + "出错了" +e.getMessage()); } //最终通知 public void myAfer() { System.out.println("最终通知,模拟方法结束后的释放资源..."); } }
在上面的 MyAspect类中,分别定义了5种不同类型的通知,在通知中使用了 JoinPoint 接口及其子
接口 PoceedingJoinPoint 作为参数来获得目标对象的类名、目标方法名 和 目标方法参数等。ps :
① 环绕通知必须接收一个类型为 ProceedingJoinPoint的 参数
② 返回值也必须是Object类型,且必须抛出异常。
③ 异常通知中可以传入Throwable类型的参数来输出异常信息。第四步 创建 applicationContext.xml (xml配置文件):
applicationContext.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"> <!-- 创建配置applicationContext, 通过<bean>等标签可以添加、管理Bean 通过<aop:config> 等标签进行配置AspectJ框架进行AOP开发所需的配置信息 --> <!-- 1.目标类Bean --> <bean id="userDao" class="com.myh.aspectJ.xml.UserDaoImpl"></bean> <!-- 2.切面类Bean --> <bean id="myAspect" class="com.myh.aspectJ.xml.MyAspect"></bean> <!-- 3.开始AspectJ框架的AOP编程 --> <aop:config> <!-- 3.1配置切面 --> <aop:aspect ref="myAspect"> <!-- 3.2配置“切入点” : 对"哪些方法“进行”增强处理“/通知 --> <aop:pointcut expression="execution(* com.myh.aspectJ.xml.*.*(..))" id="myPointCut"/> <!-- 3.3将"各种通知"和"切入点"进行两者结合 (即配置通知/对"切入点"进行增强处理) --> <aop:before method="myBefore" pointcut-ref="myPointCut"/> <!-- 前置通知 --> <!-- 后置通知,在方法执行之后执行,就可以获得返回值。 returning属性 : 用于设置后置通知的第二个参数的名称,类型是Object --> <aop:after-returning method="myAfterReturing" pointcut-ref="myPointCut" returning="returnVal"/> <aop:around method="myAround" pointcut-ref="myPointCut"/> <!-- 环绕通知 --> <!-- 异常通知 : 抛出异常,用于处理程序发生异常。如果没有异常,则不会执行该增强 throwing属性 : 用于设置"通知的第二个参数"的名称,类型是Throwable --> <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/> <!-- 最终通知 : 无论程序发生任何事情,都将执行 --> <aop:after method="myAfer" pointcut-ref="myPointCut"/> </aop:aspect> </aop:config> </beans>
第四步 创建 TestXmlAspectj (测试类):
TestXmlAspectj.java :package com.myh.aspectJ.xml; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestXmlAspectj { //测试类 : 测试AspectJ框架下的XML开发的代码内容 public static void main(String[] args) { String xmlPath = "com/myh/aspectJ/xml/applicationContext.xml"; //通过"类路径"的方式来获取.xml配置文件,依次创建ApplicationContext ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); //从IOC容器中获得内容 UserDao userDao = (UserDao)applicationContext.getBean("userDao"); //执行方法(此时会通知AspectJ框架中的xml配置信息,实现AOP功能 : 面向切面编程) userDao.addUser(); } }
(方法执行成功) 运行效果图 :
查看异常通知的执行效果 :
由上图可知,使用基于XML的声明式AspectJ框架编程已经实现了AOP开发。
3.3 基于“注解”的声明式AspectJ
与基于代理类的AOP实现相比,基于XML的声明式 ApectJ 开发要便捷得多,但是它也存在着一些缺点,那就是 要在 .xml配置文件 中 配置大量的代码信息。
为了解决这个问题,AspectJ框架为AOP的实现提供了一套注解,用以取代Spring配置文件中为实现AOP功能所配置的臃肿代码。
AspectJ 的注解及其描述 :
注解名称 描述 @Aspect 用于 定义一个切面。 @Pointcut 用于定义切入点表达式。在使用时还需定义一个包含名字和任意参数的 方法签名 来表示 切入点名称。
实际上,这个 方法签名就是一个返回值为 void,且方法体为空的普通的方法。
(这样,这个普通的 方法名就是“带有切入点表达式”的切入点名)@Before 用于定义 前置通知,相当于BeforeAdvice。在使用时,通常需要指定一个 value属性值,该属性值用于指定一个切入点表达式
(此处的 value属性 相当于.xml配置文件中的 pointcut-ref 属性)
(可以是已有的切入点,也可以直接定义切入点表达式)@AfterReturning 用于定义 后置通知,相当于AfteRturingAdvice.在使用时可以指定poitcut / valule 和 returning属性,其中pointcut / value这两个属性的作用是一样,都用于指定切点表达式。
returning属性值用于表示Advice方法中可定义与此同名的形参,该形参可用于访问目标方法的返回值。@Around 用于定义 环绕通知,相当于Methodlnterceptor在使用时需要指定一个value属性,该属任用于指定该通知被植入的切入点。 @AferThrowing 用于定义 异常通知 来处理程序中未处理的异常,相当于ThrowAdvice。在使用时可指定 pointcut / value 和 throwing属性。其中pointcut / value用于指定切入点表达式,而throwing属性值用于指定一个形参名来表示Advice方法中可定义与此同名的形参,该形参可用于访问目标方法抛出的异常。 @After 用于定义 最终final通知,不管是否异常,该通知都会执行。使用时需要指定一个 value属性, 该属性用于指定该通知被植入的切入点。 @DeclareParents 用于定义 引介通知,相当于ItroductionInterceptor (不要求掌握)。 注意点 :
如果在同一个连接点有 多个通知需要执行,那么在 同一切面 中, 目标方法之前的前置通知 和环绕通知的执行顺序是未知的,目标方法之后的后置通知和环绕通知的执行顺序也是未知的。
3.4 基于“注解”的声明式AspectJ “完整例子演示”
第一步、在DIEA中创建一个Java项目,添加web功能模块。
第二步、在项目WEB-INF目录中创建lib文件夹,在原有的Spring框架最基本核心的jar包的基础上 加上 spring-aspects.jar 和 aspectjweaver.jar ,让jar包生效。 (导入AspectJ 框架开发所需的JAR)
jar包 / maven( 依赖 ) 下载
获取spring框架基本核心jar包
AspectJ 框架开发需要的jar包第三步 创建UserDao接口 、 UserDaoImpl实现类 (目标类) :
UserDao.javapackage com.myh.aspectJ.annotation; public interface UserDao{ public void addUser(); public void deleteUser(); }
UserDaoImpl.java
package com.myh.aspectJ.annotation; import org.springframework.stereotype.Repository; @Repository(value = "userDao") //将类加入到IOC容器中 public class UserDaoImpl implements UserDao { // ”目标类“,该类创建的对象为”目标对象“ @Override public void addUser() { //这方法将要被“增强处理” System.out.println("-----------------------------------------------------"); //int i = 10 / 0; System.out.println("添加用户 (此执行的是: 目标方法 )---AspectJ的基于注解开发实现AOP功能"); System.out.println("-----------------------------------------------------"); } @Override public void deleteUser() { //这方法将要被“增强处理” System.out.println("删除用户"); } }
第三步 创建 MyAspect (切面类):
MyAspect.java
package com.myh.aspectJ.annotation; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; @Aspect //定义切面 @Component //将该类加入到IOC容器中,作为一个Bean public class MyAspect { //切面类 :以下通过AspectJ框架的"注解"的方式来实现 AOP(面向切面编程)功能 //定义“切入点表达式” @Pointcut("execution(* com.myh.aspectJ.annotation.*.*(..))") private void myPointCut() { } //使用一个返回值为void,方法体为空的方法来命名切入点 /** * 前置通知 */ @Before(value = "myPointCut()") //对“切入点”进行“增强处理” public void myBefore(JoinPoint joinPoint) { System.out.println("前置通知,模拟执行了...操作,"); //获得"目标类" System.out.println("目标类为: "+joinPoint.getTarget()); //获得“目标方法” ("目标方法"将被植入“增强处理”) System.out.println("目标方法为:" +joinPoint.getSignature().getName()); } /** * 后置通知 */ @AfterReturning(value = "myPointCut()") public void myAfterReturing(JoinPoint joinPoint) { System.out.println("后置通知,模拟执行了...操作,"); //获得目标方法 System.out.println("目标方法为: "+joinPoint.getSignature().getName()); } /** * 环绕通知 * ProceedingJoinPoint 是 JoinPoint的”子接口“ : 可以执行目标方法。 * 1.必须是Object类型的返回值 * 2.必须接收一个参数,类型为 ProceedingJoinPoint * 3.必须throws Throwable */ @Around(value = "myPointCut()") public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { //ProceedingJoinPoint : 可以执行目标方法 //环绕通知"开始" System.out.println("环绕通知开始,执行目标方法之前,模拟执行了...操作"); //执行目标方法 Object obj = proceedingJoinPoint.proceed(); //环绕通知"结束" System.out.println("环绕通知结束,执行目标方法之后,模拟执行了...操作"); return obj; } /** 异常通知 */ @AfterThrowing(value = "myPointCut()",throwing = "e") public void myAfterThrowing(JoinPoint joinPoint,Throwable e) { System.out.println("异常通知: " + "出错了" +e.getMessage()); } /** 最终通知 */ @After(value = "myPointCut()") public void myAfer() { System.out.println("最终通知,模拟方法结束后的释放资源..."); } }
在上面的MyAspect.java中,首先使用 @Aspect 注解定义了切面类,由于该类在Spring中是作为组件使用的,所以还需要添加 @Component注解 (将其交给IOC容器管理,成为Bean) 才能生效。然后使用了 @Poincut注解来配置切入点表达式,并通过 定义方法来表示切入点名称。每个方法都添加了对应的注解,并将切入点名称"'myPointCut"作为参数传递给需要执行增强的通知方法。如果需要其他参数(如
异常通知的异常参数),可以根据代码提示传递相应的属性值。第四步 创建 applicationContext.xml ( xml配置文件 ):
applicationContext.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 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"> <!-- 该配置文件为AspectJ的“注解开发”:实现AOP功能 服务 --> <!-- 指定需要扫描的包 : 因为前面 @Component等注解,用该“标签”来让注解生效 --> <context:component-scan base-package="com.myh.aspectJ.annotation"/> <!-- 启动基于注解的声明式 AspectJ支持 : 让AspectJ的注解开发下使用的注解能生效 --> <!-- aspectj-autoproxy : aspectj自动代理 (启动AspectJ的"注解开发") --> <aop:aspectj-autoproxy/> </beans>
在上面的applicationContext.xml中,首先引入了context 约束信息,然后使用<context>元素设置了需的包,使注解生效 (可将带有指定注解的类加入到IOC容器中成为Bean)。 最后,使用 <aop:aspectj-autoproxy /> 来 启动Srping对基于注解的声明式AspectJ的支持。
第五步 创建 TestAnnotatioin(测试类):
TestAnnotatioin.java :package com.myh.aspectJ.annotation; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestAnnotatioin { //测试类 : 测试AspectJ的基于"注解"开发来实现AOP功能 public static void main(String[] args) { String xmlPath = "com/myh/aspectJ/annotation/applicationContext.xml"; //通过"类路径"的方式来获取.xml配置文件,依次创建ApplicationContext ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); //从IOC容器中获得内容 UserDao userDao = (UserDao)applicationContext.getBean("userDao"); //执行方法(此时会通知AspectJ框架中的xml配置信息,实现AOP功能 : 面向切面编程) userDao.addUser(); } }
(方法执行成功) 运行效果图 :
由上图可知,基于注解的方式 与基于xml的方式执行结果相同,相对来说,使用注解的方式更加简单、方便,开发中也是推荐使用基于注解的方式来进行AOP开发。
查看异常通知的执行效果 :