Spring+Spring MVC+MyBatis应用开发学习笔记(3)--Spring AOP

3.1Spring AOP 简介

3.1.1 什么是 AOP
AOP 的全称是 Aspect-Oriented Programming ,即面向切面编程(也称面向方面编程),是面向对象编程 (OOP) 的一种补充,目前已成为一种比较成熟的编程方式。
在传统的业务处理代码中,通常都会进行事务处理、日志记录等操作 虽然使用 OOP 可以通过组合或者继承的方式来达到代码的重用,但如果要实现某个功能( 如日志记录),同样的代码仍然会分散到各个方法中 这样,如果想要关闭某个功能,或者对其进行修改,就必须要修改所有的相关方法 这不但增加了开发人员的工作量,而且提高了代码的出错率。为了解决这一问题, AOP 思想随之产生 AOP 采取横向抽取机制,将分散在各个方法中的重复代码提取出来,然后在程序编译或运行时,再将这些提取出来的代码应用到需要执行的地方这种采用横向抽取机制的方式,采用传统的 OOP 思想显然是无法办到的,因为 OOP 只能实现父子关系的纵向的重用 虽然 AOP 是一种新的编程思想,但却不是 OOP 的替代品,它只是 OOP的延伸和补充。
AOP 的使用,使开发人员在编写业务逻辑时可以专心于核心业务,而不用过多地关注于其他业务逻辑的实现,这不但提高了开发效率,而且增强了代码的可维护性。目前最流行的 AOP 框架有两个,分别为 Spring AOPAspectJ。 Spring AOP 使用纯 Java实现,不需要专门的编译过程和类加载器,在运行期间通过代理方式向目标类织入增强的代码。AspectJ 是一个基于 Java 语言的 AOP 框架,从 Spring 2.0 开始, Spring AOP 引入了对 AspectJ的支持, AspectJ 扩展了 Java 语言,提供了一个专门的编译器,在编译时提供横向代码的织入。
3.1.2 AOP 术语
Aspect (切面):在实际应用中,切面通常是指封装的用于横向插入系统功能(如事务、曰志等)的类;
Joinpoint (连接点):在程序执行过程中的某个阶段点,它实际上是对象的一个操作,例如方法的调用或异常的抛出 Spring AOP 中,连接点就是指方法的调用;
Pointcut (切入点):是指切面与程序流程的交叉点,即那些需要处理的连接点,通常在程序中,切入点指的是类或者方法名,如某个通知要应用到所有以 add方法中,那么所有满足这一规则的方法都是切入点;
Advice( 通知/增强处理): AOP 框架在特定的切入点执行的增强处理,即在定义好的切入点处所要执行的程序代码 可以将其理解为切面类中的方法,它是切面的具体实现;
• **Target Object (目标对象)?*是指所有被通知的对象,也称为被增强对象 如果 AOP架采用的是动态的 AOP 实现,那么该对象就是一个被代理对象;
Proxy (代理):将通知应用到目标对象之后,被动态创建的对象;
Weaving (织入):将切面代码插入到目标对象上,从而生成代理对象的过程;

3.2动态代理

AOP 中的代理就是由 AOP 框架动态生成的 个对象,该对象可以作为目标对象使用 Spring 中的 AOP 代理,可以是 JDK 动态代理,也可以是 CGL代理。
3.2.1 JDK 动态代理

JDK 动态代理是通过 java.lang. reflect. Proxy 类来实现的,我们可以调用 Pro 类的newProxyl nstanceO 方法来创建代理对象 对于使用业务接口的类, Spring 默认会使用 JDK态代理来实现 AOP。
通过一个案例来演示 Spring JDK 动态代理的实现过程,具体步骤如下
( 1 )创建一个名为 chapter03 Web 项目,导入 Spring 框架所需 JAR 包到项目的 lib 目录中,并发布到类路径下。
(2 )在 src 目录下,创建一个 com.itheima.jdk 包,在该包下创建接口 UserDao ,并在该接口中编写添加和删除的方法;
UserDao.java

package com.itheima.jdk; 
public interface UserDao { 
public void addUser(); 
public void deleteUser();
}

(3 )在 com itheima jdk 包中,创建 UserDao 接口的实现类 UserDaolmpl ,分别实现接口中的方法,并在每个方法中添加一条输出语句
UserDaolmpl. java

package com.itheima.jdk; 
// 目标类
public class UserDaolmpl implements UserDao { 
public void addUser() { 
System.out.println(" 添加用户") ; 
public void deleteUser() { 
System.out.println(" 删除用户 n) ;
}
}

会将实现类 UserDaolmpl 作为目标类,对其中的方法进行增强处理
(4 )在 src 目录下,创建一个 com. itheima.aspect 包,并在该包下创建切面类 MyAspect在该类中定义一个模拟权限检查的方法和一个模拟记录日志的方法,这两个方法就表示切面中的通知,
MyAspect.java

package com.itheima.aspect; 
//切面类:可以存在多个通知 Advice (即增强的方法)
public class MyAspect { 
public void check Permissions() { 
System.out.println(" 模拟检查权限. . . ") ; 
public void log() { 
System.out.println(" 模拟记录日志. . . ") ;
}
}

(5 )在 com.itheima.jdk 包下,创建代理类 JdkProxy ,该类需要实现 InvocationHandler口,并编写代理方法 在代理方法中,需要通过 Proxy 类实现动态代理,
JdkProxy.java

package com.itheima.jdk; 
import java.lang.reflect.lnvocationHandler; 
import java.lang.reflect.Method;
import java laηg . reflect . proxy ; 
 import com.ithe ma.aspect MyAspect;
 /** 
 * JDK 代理类
 *! 
 public class JdkProxy implements InvocationHandler{ 
 //声明目标类接口
 private UserDao userDao; 
 / /创建代理方法
 public Object createProxy(UserDao userDao) 
 this . userDao = userDao ; 
 // 1.类加载器
 ClassLoader classLoader = JdkProxy.class . getClassLoader() ; 
 // 被代理对象实现的所有接口
 Class[) clazz = userDao.getClass() .getlnterfaces(); 
 // 3. 使用代理类,进行增强 返回的是代理后的对象
 return Proxy.newProxylnstance(classLoader, clazz , this); 
 
 /* 
 *所有动态代理类的方法调用,都会交由 invoke ()方法去处理
 * proxy 被代理后的对象
 * method 将要被执行的方法信息(反射)
 * args 执行方法时需要的参数
 * / 
 @Override
 public Object invoke (Object proxy, Method method, Object[) args) 
 throws Throwable 
 **//声明切面
 MyAspect myAspect = new MyAspect(); 
 //前增强
 myAspect.check_Permissions(); 
 //在目标类上调用方法,并传入参数
 Object obj = method.invoke(userDao , args); 
 / /后增强
 myAspect.log(); 
 return obj ;** 
}
}

(6 )在 com.itheima.jdk 包中,创建测试类 JdkTest 在该类中的 mainO 方法中创建代理对象和目标对象,然后从代理对象中获得对目标对象 userDao 增强后的对象,最后调用该对象中的添加和删除方法,
JdkTest.java

package com.itheima.jdk; 
 pub1ic c1ass JdkTest{ 
 pub1ic static void main(String[) args) { 
**// 创建代理对象
 JdkProxy jdkproxy = new JdkProxy() ; 
 //创建目标对象
 UserDao userDao= new UserDaoI l();
//从代理对象中获取增强后的日标对象
 UserDao userDao1 = (UserDao) jdkProxy.createProxy(userDao); 
// 执行方法
 userDao1.addUser(); 
 userDao1.deleteUser();** 
 }
}

执行程序后,控制台的输出结果
在这里插入图片描述
3.2.2 CGLl 代理
JDK 动态代理的使用非常简单,但它还有一定的局限性一一使用动态代理的对象必须实现一个或多个接口 。如果要对没有实现接口的类进行代理,那么可以使用 CGLIB代理.CGLIB( Code Generation Library )是一个高性能开源的代码生成包,它采用非常底层的字节码技术,对指定的目标类生成一个子类,并对子类进行增强 Spring 的核心包中已经集成CGL旧所需要的包,所以开发中不需要另外导入 JAR包.
接下来,通过一个案例来演示 CGLIB代理的实现过程,具体步骤如下
( 1 )在 src 目录下,创建一个 com itheima.cglib 包,在包中创建一个目标类 UserDao, UserDao不需要实现任何接口,只需定义一个添加用户的方法和一个删除用户的方法
UserDao.java

package com . itheima . cglib; 
 //目标类
 pub1ic class UserDao { 
 public void addUser(){
System.out.println(" 添加用户") ; 
 }
 public void deleteUser() { 
 System.out.println(" 删除用户") ; 
}
}

(2 )在 com.itheima cglib 包中,创建代理类 CglibProxy ,该代理类需要实现 Method Interceptor 接口,并实现接口中的 interceptO 方法
CglibProxy.java

package com.itheima.cglib; 
 import java.lang.reflect.Method; 
 import org.springframework.cglib.proxy.Enhancer; 
 import org.springframework.cglib.proxy.Methodlnterceptor; 
 import org.springframework.cglib.proxy.MethodProxy; 
 import com.itheima.aspect.MyAspect; 
//代理类
 public class Cglibproxy implements Methodlnterceptor{ 
 //代理方法
 public Object createProxy(Object target) 
 //创建 个动态类对象
 Enhancer enhancer = new Enhancer(); 
 // 确定需要增强的类,设置其父类
 enhancer.setSuperclass(target.getClass()); 
 // 添加回调函数
 enhancer.setCallback(this); 
 //返回创建的代理类
 return enhancer.create(); 
 
 /** 
 * proxy CGlib 根据指定父类生成的代理对象
 * method 拦截的方法
 * args 拦截方法的参数数组
 * methodProxy 方法的代理对象,用于执行父类的方法
 */ 
@Override
 public Object intercept(Object proxy, Method method, Object[) args, 
 MethodProxy methodProxy) throws Throwable { 
 // 创建切面类对象
 **MyAspect myAspect = new MyAspect(); 
//前增强
 myAspect.checkPermissions(); 
 //目标方法执行
 Object obj = methodProxy.invokeSuper(proxy , args); 
// 后增强
 myAspect.log(); 
 return obj;** 
 }
}

(3 )在 com.itheima.cglib 包中,创建测试类 CglibTest 在该类的 main() 方法中首先创建代对象和目标对象,然后从代理对象中获得增强后的目标对象,最后调用对象的添加和删除方法
CglibTest.java

package com.itheima . cglib; 
 //测试类
 public class CglibTest { 
 public static void main(String[] args) { 
 //创建代理对象
 Cglibproxy cglibproxy = new CglibProxy(); 
 //创毡口标对象
 UserDao userDao = new UserDao(); 
 //获取增强后的目标对象
 UserDao userDao1 = (UserDao) cglibProxy.createProxy (userDao) ; 
 //执行方法
 userDao1 . addUser() ; 
 userDao1.deleteUser() ; 
 }
 }

执行程序后,控制台的输出结果
在这里插入图片描述
目标类 UserDao 中的方法被成功调用并增强了 这种没有实现接口的代理方式,就是 CGL旧代理.

3.3基于代理类的AOP 实现

实际上, Spring中的AOP 代理默认就是使用JDK 动态代理的方式来实现的。在Spring 中,使用ProxyFactoryBean 是创建AOP 代理的最基本方式。
3.3.1 Spring 的通知类型
先了解一下Spring 的通知类型。Spring 中的通知按照在目标类方法的连接点位置,可以分为以下5 种类型。
• org. aopalliance. intercept. Method Interceptor (环绕通知)
目标方法执行前后实施增强,可以应用于曰志、事务管理等功能。
• org.springframework.aop.MethodBeforeAdvice (前置通知)
目标方法执行前实施增强,可以应用于权限管理等功能。
• org.springframework.aop.AfterReturningAdvice (后置通知)
目标方法执行后实施增强,可以应用于关闭流、上传文件、删除临时文件等功能。
• org .springframework.aop.ThrowsAdvice (异常通知)
方法抛出异常后实施增强,可以应用于处理异常记录曰志等功能。
• org .springframework.aop.1 ntroduction I nterceptor (引介通知)
目标类中添加一些新的方法和属性,可以应用于修改老版本程序(增强类)。
3.3.2 ProxyFactoryBean
ProxyFactoryBean 是FactoryBean 接口的实现类, FactoryBean 负责实例化一个Bean ,而ProxyFactoryBean 负责为其他Bean 创建代理实例。在Spring 中,使用ProxyFactoryBean 是创建AOP 代理的基本方式。
ProxyFactoryBean 类中的常用可配置属性
在这里插入图片描述
接下来通过一个典型的环绕通知案例,来演示Spring 使用ProxyFactoryBean 创建AOP 代理的过程,具体步骤如下。
( 1 )在核心JAR 包的基础上,再向chapter03 项目的lib 目录中导入AOP 的JAR 包spring-aop-4.3 . 6.RELEASE.jar 和aopalliance-1.0.jar。
关于这两个JAR 包的介绍如下。
spring-aop-4.3 . 6. RELEASE . jar: 是Spring 为AOP 提供的实现包, Spring 的包中已经提供。
aopalliance- 1 . 0 . jar: 是AOP 联盟提供的规范包,该JAR包可以通过地址" http://mvnrepository . com/artifact/aopall iance/
aopallian ce/1.0" 下载。
( 2 )在src 目录下,创建一个com . itheima. factorybean 包,在该包中创建切面类MyAspect 。由于实现环绕通知需要实现
org .aopalliance. intercept. Method I nterceptor 接口,所以MyAspect 类需要实现该接口,并实现接口中的invokeO方法,来执行目标方法.
MyAspect.java

package com .itheima.factorybean;
 import org . aopalliance.intercept .Methodlnterceptor;
 import org.aopalliance . intercept . Methodlnvocation ;
 //切面类
 public class MyAspect implements Methodlnterceptor
 @Override
 public Object invoke(Methodlnvocation mi) throws Throwable
 check Permissions();
 / /执行日标}j法
 Object obj = mi.proceed();
 log();
 return obj ;

 public void checkPermissions() {
 System . out.println( " 模拟检查权限. . . ") ;

 public void log() {
 System . out .println("模拟记录日志. . . ") ;
}
 }

这里为了演示效果,在目标方法前后分别执行了检查权限和记录日志的方法,这两个方法也就是增强的方法,也就是通知。
(3 )在com.itheima.factorybean 包中,创建配置文件applicationContext. 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"
 xsi : schemaLocation="http://www . springframework.org/schema/beans
 http : //www . springframework . org/schema/beans/spring-beans- 4 . 3.xsd">
 < !-- 1 目标类一〉
 <bean id="userDao" class= "com . itheima . jdk . UserDaolmpl " />
 < ! 一2 切面类一〉
 <bean id="myAspect" class="com.itheima .factorybean . MyAspect" />
 <!一3 使用Spring 代理工厂定义一个名称为userDaoProxy 的代理对象一〉
 <bean id="userDaoProxy"
 class="orq.sprinqframework.aop.framework.ProxyFactoryBean">
 <!一3 .1 指定**代理实现的接口**一〉
 <property name="proxylnterfaces "
 value=" com.itheima.jdk.UserDao" />
 <! -- 3 . 2 指定**目标对象**一〉
 <property name="target" ref="userDao" />
 <!一3 . 3 **指定切面**,植入环绕通知一〉
 <property name= " InterceptorNames " value= "myAspect" />
 < ! --3 .4 指定**代理方式**, true : 使用cglib , false (默认) : 使用jdk 动态代理一〉
 <property name="proxyTargetClass " value="true" />
 </bean>
 </beans>

(4 )在com.itheima.factorybean 包中,创建测试类ProxyFactoryBeanTest ,在类中通过Spring 容器获取代理对象的实例,并执行目标方法
ProxyFactoryBeanTest.java

 package com . 工theima.factorybean ;
 import org . springframework.context.ApplicationContext;
 import  org .springframework.context .support . ClassPathXmlApplicationContext ;
 import com.itheima.jdk.UserDao;
 //测试类
 public class ProxyFactoryBeanTest
 public static void main(String args[])
 String xmlPath = "com/itheima/factorybean/applicationContext.xml";
 ApplicationContext applicationContext =
 new ClassPathXmlApplicationContext(xmlPath);
 / /从spring 容器获得内容
 UserDao userDao = (UserDao) applicationContext. qetBean ("userDaoProxy") ;
 / /执行方法
 userDao.addUser();
 userDao.deleteUser();
}
}

执行程序后,控制台的输出结果
在这里插入图片描述
**

3.4AspectJ开发


AspectJ 是一个基于Java 语言的AOP 框架,它提供了强大的AOP 功能。Spring 2.0 以后,Spring AOP 引入了对AspectJ 的支持,并允许直接使用AspectJ 进行编程,而Spring 自身的AOPAPI 也尽量与AspectJ保持一致。新版本的Spring 框架,也建议使用AspectJ 来开发AOP 。
使用AspectJ 实现AOP 有两种方式:一种是基于XML 的声明式AspectJ ,另一种是基于注解的声明式AspectJ 。**
3.4.1 基于XML 的声明或AspectJ
基于XML 的声明式AspectJ 是指通过XML 文件来定义切面、切入点及通知,所有的切面、切入点和通知都必须定义在aop:config元素内。
元素及其子元素
在这里插入图片描述
图中灰色部分标注的元素即为常用的配置元素,这些常用元素的配置代码如下所示。

< ! 一定义切面Bean 一〉
<bean id= "myAspect " class="com . itheima . aspectj.xml.MyAspect" />
<aop : config>
	<! --1.配置切面一〉
	<aop:aspect id="aspect" ref="myAspect">
		< ! 一2 . 配置切入点一〉
		<aop :pointcut expression= "execution (*com.itheima. jdk. *. * (..))" id="myPointCut" />
		< !一3 . 配置通知一〉
		< !一前置通知一〉
		<aop :before method="myBefore" pointcut-ref="myPointCut" />
		< !一后置通知一〉
		<aop :after-returning method="myAfterReturning"
		pointcut- ref="myPointCut" returning= " returnVal " />
		<! 一环绕通知一>
		<aop : around method="myAround" pointcut-ref= " myPointCut " />
		< !一异常通知一〉
		<aop : after-throwing method="myAfterThrowing"
		pointcut-ref= " myPointCut " throwing=" e " />
		<!一最终通知…〉
		<aop :after method="myAfter" pointcut-ref="myPointCut " 1>
	</aop:aspect>
</ aop : config>

来通过一个案例来演示如何在Spring中使用基于XML 的声明式AspectJ ,具体实现步骤如下。
( 1 )导入AspectJ 框架相关的JAR 包,具体如下。
spring-aspects-4.3.6.RELEASE.jar: Spring 为AspectJ 提供的实现, Spring 的包中已经提供。
aspectjweaver -1.8.10.jar: 是AspectJ 框架所提供的规范,读者可以通过网址、ttp : //mvn repository. com/artifact/org. aspectj/aspectjweaver/1.8 .10" 下载。
(2 )在chapter03 项目的src 目录下,创建一个com.itheima.aspectj . xml 包,在该包中创建切面类MyAspect ,并在类中分别定义不同类型的通知,
MyAspect.java

package com.itheima.aspectj.xml;
 import org .aspectj.lang.JoinPoint;
 import org .aspectj .lang.ProceedingJoinPoint;
 /**
*切面类,在此类中编写通知
**/
 public class MyAspect {
 / /前置通知



public void myBefore(JoinPoint joinPoint) {
System.out.print(" 前置通知:模拟执行权限检查. . . , " ) ;
System.out.print(" 目标类是: "+joinPoint.getTarget() );
System.out.println(" ,被植入增强处理的目标方法为: "
 +joinPoint.getSignature() .getName()) ;
 }
 / /后置通知
 public void myAfterReturning(JoinPoint joinPoint) {
 System.out.print(" 后置通知:模拟记录日志. . . ," );
 System.out.println(" 被植入增强处理的目标方法为: "
 + joinPoint.getSignature() .getName());

 /**
 '"环绕通知
 • Proceedi呵JoinPoint 是JoinPoint 子接口,表示可以执行目标}j法
 • 1. 必须是Object 类型的返回值
 • 2. 必须接收一个参数,类型为ProceedingJoinPoint
 • 3 . 必须throws Throwable
 **/
 public Object myAround(ProceedingJoinPoint proceedingJoinPoint)
 throws Throwable {
 //开始
 System.out.println( " 环绕开始:执行目标方法之前,模拟开启事务. . . ") ;
//执行当前门标}J沾
 Object obj = proceedingJoinPoint . proceed() ;
 //结束
 System . out . println( " 环绕结束: 执行门标方法之后, 模拟关闭事务. . . ") ;
 return obj;
 }
 //异常通知
 public void myAfterThrowing(JoinPoint joinPoint, Throwable e ) {
 System . out . println( " 异常通知: tt + " 出错了" + e . getMessage ());
 }
 //最终通知
 public void myAfter() {
 System . out . println( " 最终通知: 模拟方法结束后的释放资源. . . ") ;
}
 }

(3 ) 在com. itheima.aspectj .xm l 包中,创建配置文件a p p l icat ionContext. 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-4 . 3 . xsd
 http://www . springframework.org/schema/aop
 http://www.springfræ睡work.org/schema/aop/spring-aop-4.3 . xsd" >
 <! -- 1 目标类一〉
 <bean id="userDao" class= "com . itheima . jdk . UserDaolmpl " />
 <!一2 切面一〉
 <bean id= "myAspect " class="com.itheima . aspectj . xml . MyAspect" />
 < ! 一3 aop 编程一〉
 <aop : config>
 < ! 一配置切面一〉
<aop:aspect ref= "myAspect ">
 < ! 一3.1 配置切入点,通知最后增强哪些方法一〉
 <aop :pointcut expression="execution(* com . itheima . jdk . *.*( . . ) )"
 id= "myPointCut" />
 <!-- 3 . 2 关联通知Advice 和切入点pointCut 一〉
 <!-- 3 . 2 . 1 前置通知一〉
 <aop:before method="myBefore" pOintcut-ref="myPointCut" 1>
 <!一3 . 2 . 2 后置通知, 在方法返回之后执行, 就可以我得返回值
 returning 属性: 用于设置后置通知的第二个参数的名称, 类型是Object …>
 <aop:after-returning method="myAfterReturning"
pointcut-ref="myPointCut" returning="returnVal" />
<! -- 3.2.3 环绕通知一>
<aop:around method="myAround" pointcut-ref="myPointCut" />
< !- - 3 . 2 . 4 抛出通知:用于处理程序发生异常一〉
<! 一* 注意:如果程序没有异常,将不会执行增强一〉
<!一* throwi呵属性:用于设置通知第- 个参数的名称,类型Throwable 一〉
<aop:after-throwing method="myAfterThrowing"
pointcut-ref="町PointCut" throwing="e" />
<!一3.2.5 最终通知: 无论程序发生任何安怕,部将执行一〉
 <aop:after method="myAfter" pointcut-ref="myPointCut" />
 </aop : aspect>
 </aop:config>
 </beans>

( 4 ) 在com. itheima.aspectj .xml 包下,创建测试类TestXmlAspectj ,在类中为了更加清晰地演示几种通知的执行情况,这里只对addUserO方法进行增强测试
TestXmlAspectj.java

package com . itheima . aspectj . xml;
 import org . springframework.context . ApplicationContext;
 import  org . springframework . context . support.ClassPathXmlApplicationContext;
 import com . itheima . jdk.UserDao;
 //测试类
 public class TestXmlAspectj {
 public static void main(String args[))
 String xmlPath = "com/itheima/aspectj/xml/applicationContext . xml";
 ApplicationContext applicationContext = new ClassPath泊nlApplicationContext(xmlPath);
 // 1 从spring 容器获得内容
 UserDao userDao = (UserDao) applicationContext.getBean("userDao");
 // 2 执行方法
 userDao.addUser();
}
}

执行程序后,控制台的输出结果在这里插入图片描述
要查看异常通知的执行效果,可以在UserDaolmpl 类的addUserO方法中添加错误代码,如"int i = 10/0;" ,重新运好测试类,将可以看到异常通知的执行,此时控制台的输出结果
在这里插入图片描述
3.4.2 基于注解的声明式AspectJ(重点)
与基于代理类的AOP 实现相比,基于XML 的声明式ApectJ 要便捷得多,但是它也存在着一些缺点,那就是要在Spring 文件中配置大量的代码信息。为了解决这个问题, AspectJ 框架为AOP的实现提供了一套注解,用以取代Spring 配置文件中为实现AOP 功能所配置的臃肿代码。
关于AspectJ 注解的介绍
在这里插入图片描述
在这里插入图片描述
具体步骤如下。
( 1 ) 在chapter03 项目的src 目录下,创建com. itheima.aspectj .annotation 包,将切面类MyAspect 复制到该包下,并对该文件进行编辑,
MyAspect.java

package com . itheima . aspectj . annotation;
 import org . aspectj . lang . JoinPoint;
 import org . aspectj . lang . ProceedingJoinPoint;
 import org.aspectj . lang . annotation . After ;
 import org . aspectj . lang . annotation.AfterReturning;
 import org.aspectj . lang.annotation . AfterThrowing ;
 import org . aspectj . lang.annotation.Around;
 import org . aspectj.lang . annotation . Aspect ;
 import org . aspectj . lang.annotation.Before;
 import org.aspectj . lang.annotation . Pointcut;
 import org . springframework . stereotype.Component ;
 /**
 *切面类,在此类中编写通知
 */
 **@Aspect
 @Component**
 public class MyAspect
 //定义切入点表达式
 **@Pointcut("execution ("com. itheima . jdk . * . * ( . . ) ) ")**
 //使用一个返回值为void 、方法体为空的方法来命名切入点
 private void myPointCut() {}
 //前置通知
 **@Before("myPointCut () ")**
 public void myBefore(JoinPoint joinPoint) {
 System . out . print( " 前置通知:模拟执行权限检查. . ., " );
 System . out.print(" 目标类是: "+joinPoint . getTarget() ) ;
 System . out . println( " , 被植入增强处理的日标方法为: "
 +joinPoint.getSig 口ature() .getName()) ;
 }
 //后置通知
 **@AfterReturning(value="myPointCut() ")**
 public void myAfterReturning(JoinPoint joinPoint){
System . out.print(" 后置通知:模拟记录日志. . . , 11 );
 System . out.println( " 被植入增强处理的目标方法为: "
 + joinPoint.getSignature() . getName());
 }
 //环绕通知
 **@Around( "myPointCut() ")**
 public Object myAround(ProceedingJoinPoint proceedingJoinPoint)
 throws Throwable {
 //开始
 System.out . println( "环绕开始: 执行目标方法之前, 模拟开启事务. . . " ) ;
 / /执行当前日标方法
 Object obj = proceedingJoinPoint.proceed();
 //结束
 System . out.println (" 环绕结束: 执行目标方法之后, 模拟关闭事务. . . ") i
 return obji
}
 //异常通知
 **@AfterThrowing(value="myPointCut()" ,throwing="e")**
 public void myAfterThrowing (JoinPoint joinPoint , Throwable e)
 System.out . println( " 异常通知: " + " 出错了" + e . getMessage())i
 }
 //最终通知
 **@After("myPointCut () ")**
 public void myAfter() {
 System . out . println (" 最终通知:模拟方法结束后的释放资源. . . " ) i
}
 }

(2 ) 在目标类com.itheima . jdk.UserDaolmpl 中, 添加注解**@ Repository(“userDao”)** 。
(3 ) 在com.itheima.aspectj.annotation 包下,创建配置文件applicationContext.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"
 xmlns:context="http://www.springframework.org/schema/context"
 xsi : schemaLocation="http : //www . springframework . org/schema/beans
 http : //www . springframework . org/schema/beans/spring-beans-4 . 3 . xsd
 http : //www.springframework.org/schema/aop
 http : //www . springframework.org/schema/aop/spring- aop- 4 . 3 . xsd
 http://www . springframework.org/schema/context
http://www.springframework.org/schema/context/spring-contaxt-4.3.xsd">
 **<!一指定需要扫描的包,使注解生效一〉
 <context:component-scan base-package="com.itheima" />
 < !一启动基于注解的声明式AspectJ 支持一〉
 <aop:aspectj-autoproxy />**
 </beans>

(4 )在com . itheima.aspectj .annotation 包中,创建测试类TestAnnotation
TestAnnotation.java

package com.itheima . aspectj.annotation ;
Import org . springframework.context . ApplicationContext;
 import
 org.springframework.context . support.ClassPathXmlApplicationContext ;
 import com.itheima . jdk . UserDao ;
 //测试类
 public class TestAnnotationAspectj{
public static void main(String args[] )
String xmlPath ="com/itheima/aspectj/annotation/applicationContaxt.xml";
 ApplicationContext applicationContext = new ClassPathXm1ApplicationContext(xmlPath);
 **// 1 从spring 容器获得内容
 UserDao userDao = (UserDao) app1icationContext . getBean( "userDao");
 // 2 执行方法
 userDao . addUser() ;**
}
 }

执行程序后,控制台的输出结果
在这里插入图片描述
以上一小节的方式来演示异常通知的执行,控制台的输出结果
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值