AspectJ 是基于java语言的AOP框架,它提供了强大的AOP功能。Spring2.0 以后,Spring AOP引入了对AspectJ的支持,并允许直接使用AspectJ进行编程,而AspectJ自身的AOP API也尽量与AspectJ 保持一致。新版本的Spring框架,也建议使用AspectJ来开发AOP。使用AspectJ 实现AOP?️两种方式:一种是基于XML的声明式AspectJ,另一种是基于注解的声明式AspectJ。
一、基于XML的声明式AspectJ
基于XML的声明式AspectJ 是指通过XML文件来定义切面、切入点及通知,所有的切面、切入点和通知都必须定义在<aop:config>元素内。<aop:config>元素及其子元素如图3-7所示
需要导入
spring-aspects-4.3.6.RELEASE.jar 包
aspectjweaver-1.8.10.jar 下载地址 http://www.mvnjar.com/org.aspectj/aspectjweaver/1.8.10/detail.html
1.创建切片类
package com.kangxg.aspect.xml;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
/*
* 切面类,在此类中编写通知
*/
public class MyAspect {
//前置通知
public void myBefore(JoinPoint joinPoint)
{
System.out.println("前置通知: 模拟执行 检查权限。。。");
System.out.println("目标类是:"+joinPoint.getTarget());
System.out.println(",被植入增强处理的目标方法为:"+joinPoint.getSignature().getName());
}
//后置通知
public void myAfterReturining(JoinPoint joinPoint)
{
System.out.println("后置通知: 模拟记录日志。。。");
System.out.println(",被植入增强处理的目标方法为:"+joinPoint.getSignature().getName());
}
/*
* 环绕通知
* ProceedingJoinPoint 是 JoinPoint 子接口,表示可以执行目标方法
* 1 必须是Object类型的返回型
* 2 必须接收一个参数,类型为ProceedingJoinPoint
* 必须 throws Throwable
*/
public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable
{
//开始
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 myAfter(JoinPoint joinPoint)
{
System.out.println("最终通知: 模拟方法结束后的释放资源。。。");
}
}
需要注意:
环绕同事必须接受一个 ProceedingJoinPoint 的参数,返回值也必须是Object类型,且必须抛出异常。异常通知可传入Throwable类型的参数来输出异常信息
2.创建 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.springframework.org/schema/aop/spring-aop-4.3.xsd">
<!--1 目标类 -->
<bean id="userDao" class="com.kangxg.jdk.UserDaoImpl" />
<!-- 2 切面 -->
<bean id="myAspect" class="com.kangxg.aspect.xml.MyAspect" />
<!-- 3 aop 编程 -->
<aop:config >
<!-- aop 切面 -->
<aop:aspect ref = "myAspect">
<!-- 3.1 配置切入点 ,通知最后增强哪些方法 -->
<aop:pointcut expression="execution(* com.kangxg.jdk.*.*(..))" id="myPointcut"/>
<!-- 3.2 关联通知Advice 和切入点 pointCut -->
<!-- 3.2.1 前置切入点 -->
<aop:before method="myBefore" pointcut-ref = "myPointcut"/>
<!-- 3.2.2 后置通知,在方法返回之后执行,就可以获得返回值 returning属性 :用于设置后置通知的第二个参数的名称,类型是Object-->
<aop:after-returning method="myAfterReturining" pointcut-ref = "myPointcut" returning ="returnVal"/>
<!-- 3.2.3 环绕通知 -->
<aop:around method="myAround" pointcut-ref = "myPointcut"/>
<!-- 3.2.4 抛出通知:用户处理程序发生异常 -->
<!-- * 如果程序没有异常,将不会执行增强 -->
<!-- * throwing 属性:用于设置通知第二个参数的名称,类型 Throwable -->
<aop:after-throwing method="myAfterThrowing" pointcut-ref = "myPointcut" throwing ="e" />
<!-- 3.2.5 最终通知,无论程序发生任何事情,都将执行 -->
<aop:after method="myAfter" pointcut-ref = "myPointcut"/>
</aop:aspect>
</aop:config>
</beans>
需要注意:
4 7 8 红色标注行代码中,分别引入了AOP的Schema约束,然后在配置文件中分别定义了目标类、切面和AOP配置信息
AOP 切入点表达式说明
execution(* com.kangxg.jdk.*.*(..))
第一个 * 表示的是返回类型,使用* 代表所有类型;com.kangxg.jdk 表示的是需要拦截的包名,后面第二个* 表示的是类名,使用* 代表所有的类;第三个* 表示的是方法名,使用* 表示所有方法;后面(..)表示方法的参数,其中..表示任何参数。需要注意的是第一个个* 与包名之间有一个空格
3. 创建 测试类
package com.kangxg.aspect.xml;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.kangxg.jdk.UserDao;
public class TextXmlAspectj {
public static void main(String[] args) {
// TODO Auto-generated method stub
String xmlPath = "com/kangxg/aspect/xml/applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
//从Spring 容器中获取内容
UserDao userDao =(UserDao) applicationContext.getBean("userDao");
//执行方法
userDao.addUser();
}
}
4 debug 运行程序 控制台输出结果
要查看异常通知的执行结果,可以在UserDaoImpl 类的addUser()方法中增加错误代码 如int i = 100/0,重新运行测试类,可以看到异常通知的执行,此时控制台的输出结果:
二 基于注释的生命式Aspect J
1.创建 package com.kangxg.aspect.annotaion 包下的切面类 MyAspect
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/*
* 切面类,在此类中编写通知
*/
@Aspect
@Component
public class MyAspect {
// 定义切入点表达式
@Pointcut("execution(* com.kangxg.jdk.*.*(..))")
// 使用 一个返回值为void ,方法体为空的方法来命名切入点
private void myPointCut(){}
//前置通知
@Before("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 myAfterReturining(JoinPoint joinPoint)
{
System.out.println("后置通知: 模拟记录日志。。。");
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("环绕开始结束:执行目标方法之后,模拟关闭事务。。。");
return obj;
}
// 异常通知
@AfterThrowing(value = "myPointCut()",throwing = "e")
public void myAfterThrowing(JoinPoint joinPoint,Throwable e)
{
System.out.println("异常通知:出错了"+e.getMessage());
}
//最终通知
@After("myPointCut()")
public void myAfter(JoinPoint joinPoint)
{
System.out.println("最终通知: 模拟方法结束后的释放资源。。。");
}
}
首先使用@Aspect 注释定义了切片类,由于该类在Spring中作为组建使用的,所以还需要添加@Componnet 注释才能生效
2 创建配置文件
<?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-context-4.3.xsd">
<!--1 指定需要扫描的包,使注释生效 -->
<context:component-scan base-package = "com.kangxg" />
<!-- 2 启动基于注释的声明是Aspect J支持 -->
<aop:aspectj-autoproxy />
</beans>
3.创建测试类
package com.kangxg.aspect.annotaion;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.kangxg.jdk.UserDao;
public class TestAnnotation {
public static void main(String[] args) {
String xmlPath = "com/kangxg/aspect/annotaion/applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
UserDao userDao =(UserDao) applicationContext.getBean("userDao");
userDao.addUser();
}
}
4 debug运行 程序,控制台输出
s
手动设置异常 ,控制台输出