AspectJ 开发
基于XML 的声明式 AspectJ
基于XML 的声明式 AspectJ 是通过 xml 文件来定义切面、切入点、通知的。所有的切面、切入点、通知必须定义在<aop:config>元素内。
- Spring配置文件中元素中可以包含多个<aop:config>元素,
- 注意:在标签<aop:config> 里面配置时,要按照<aop:pointcut>、<aop:advisor>、<aop:aspect>顺序来配置
- 在<aop:aspect>标签中也可以配置相关的子元素,在<aop:aspect>中配置的切入点只在本切面类中起作用。
配置切面 <aop:aspect>
切面的配置比较简单:主要是指他的两个属性的配置:
属性 | 说明 |
---|---|
id | 切面的唯一标识符 |
ref | 引用普通的Spring Bean |
配置切入点 <aop:pointcut>
当该元素作为<aop:config>标签的子元素时,表示是全局切入点,所有的切面类都可以使用;作为<aop:aspect>的子元素时,仅对当前的切面类有效。
属性 | 说明 |
---|---|
id | 标签的唯一标识符 |
expression | 关联切入点的表达式 |
切入点的表达式一般为:execution(* 包名.类名.方法名.(…))
配置通知
只介绍5种常用的通知
属性 | 描述 |
---|---|
pointcut | 指定一个切入点的表达式,在该切入点 将通知织入到程序中 |
pointcut-ref | 指定一个已经存在的切入点, |
method | 指定一个方法名,将切面类中该方法转换为增强处理 |
throwing | 该属性在存在<after-throwing>标签中,用于指定一个形参,通知方法可以通过该形参访问目标方法所抛出的异常 |
returning | 只存在<aop:after-returning>标签中,用于指定一个形参,通知方法可以通过该形参访问目标方法的返回值 |
创建接口:
package com.itheima.aspectj.xml;
public interface UserDao {
public void addUser();
public void deleteUser();
}
创建接口的实现类:
package com.itheima.aspectj.xml;
public class UserDaoImpl implements UserDao {
@Override
public void addUser() {
//这一句语句可以测试织入的异常通知
//int i = 19/0;
System.out.println("添加用户。。。。");
}
@Override
public void deleteUser() {
// TODO Auto-generated method stub
System.out.println("删除用户。。。。");
}
}
创建切面类:
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.println("前增强通知: 模拟检查执行权限。。。。。");
System.out.println("目标类是:"+joinPoint.getTarget());
System.out.println("被植入增强处理的方法为:"+joinPoint.getSignature().getName());
//getSignature() 获取增强的 方法 的签名
System.out.println("被植入增强处理的方法签名:"+joinPoint.getSignature());
System.out.println("======================前增强结束======================================");
}
//后增强
public void myAfterReturning(JoinPoint joinPoint) {
System.out.println("后增强通知: 模拟记录日志。。。。。");
System.out.println("被植入的后增强的方法为:"+joinPoint.getSignature().getName());
System.out.println("======================后增强结束======================================");
}
//环绕增强
// ProceedingJoinPoint 是JoinPoint的一个子接口
// 环绕通知有一个 Object 类型的返回值
// 参数是 ProceedingJoinPoint类型的参数
// 必须有一个 throws throwable 来抛出异常
public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
System.out.println("环绕通知:方法开始前模拟检查,开启事务。。。。。。。。。");
//执行当前的方法
Object obj = proceedingJoinPoint.proceed();
System.out.println("环绕通知:方法结束前模拟检查,关闭事务。。。。。。。。。");
System.out.println("======================环绕增强结束======================================");
return obj;
}
//异常通知
public void myAfterThrowing(JoinPoint joinPoint , Throwable e) {
System.out.println("异常通知:"+e.getMessage());
System.out.println("======================异常通知结束======================================");
}
//最终通知
public void myAfter() {
System.out.println("最终通知:模拟方法结束后并 释放资源");
System.out.println("======================最终通知结束======================================");
}
}
创建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.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.xsd">
<!-- 目标类 -->
<bean id="userDao" class="com.itheima.aspectj.xml.UserDaoImpl"></bean>
<!-- 切面类 -->
<bean id="myAspect" class="com.itheima.aspectj.xml.MyAspect"></bean>
<!-- AOP编程 -->
<aop:config>
<!-- 配置切面类 -->
<aop:aspect ref ="myAspect">
<!-- 配置切入点 -->
<aop:pointcut id="myPointCut" expression="execution(* com.itheima.aspectj.xml.*.*(..))" />
<!-- 前增强 -->
<aop:before method="myBefore" pointcut-ref="myPointCut"/>
<!-- 后增强 returning="returnVal" 不可省略,否则会报错-->
<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"/>
</aop:aspect>
</aop:config>
</beans>
创建测试类:
package com.itheima.aspectj.xml;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
String xmlPath = "com/itheima/aspectj/xml/applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
UserDao userDao = (UserDao)applicationContext.getBean("userDao");
userDao.addUser();
((AbstractApplicationContext) applicationContext).close();
}
}
运行结果:
由运行结果可以看出:当前增强、后增强、环绕增强、最终增强在一其运行时,其运行顺序是:
基于注解的声明式 AspectJ
AspectJ注解的描述
注解名称 | 描述 |
---|---|
@Aspect | 定义一个切面 |
@Pointcut | 定义切入点的表达式。在使用时需要定义一个包含名字和任意参数的普通方法来表示切入点名称。这个方法就是一个返回值为void,且方法体为空的普通方法。 |
@Before | 定义前置通知。需要指定value的值,该属性值用于指定一个切入点的表达式 |
@AfterReturning | 定义后置通知。需要定义pointcut/value值和returning属性值。pointcut/value属性值用于表示切入点表达式,returning属性值表示advice 方法中可定义于此同名的形参,该形参可以访问目标方法的返回值 |
@Around | 用于定义环绕通知。需要指定value的属性值,该值表示该通知植入的切入点 |
@AfterThrowing | 定义异常通知来处理程序中未处理的异常。需要定义pointcut/value值和returning属性值。pointcut/value属性值用于表示切入点表达式,throwing属性值表示advice 方法中可定义于此同名的形参,该形参可以访问目标方法抛出的异常 |
@After | 定义最终通知,不管是否异常,该通知都会执行。需要指定value属性值,该属性值用于指定通知被植入的切入点 |
@DeclareParents | 定义引介通知 |
接口和接口的实现类和上面的一样
创建切面类:
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.aspectj.annotation.*.*(..))")
//定一个空方法体来表示切入点
public void myPointCut() {}
@Before("myPointCut()")
//前增强
public void myBefore(JoinPoint joinPoint) {
System.out.println("前增强通知: 模拟检查执行权限。。。。。");
System.out.println("目标类是:"+joinPoint.getTarget());
System.out.println("被植入增强处理的方法为:"+joinPoint.getSignature().getName());
System.out.println("被植入增强处理的方法签名:"+joinPoint.getSignature());
System.out.println("======================前增强结束======================================");
}
@AfterReturning("myPointCut()")
//后增强
public void myAfterReturning(JoinPoint joinPoint) {
System.out.println("后增强通知: 模拟记录日志。。。。。");
System.out.println("被植入的后增强的方法为:"+joinPoint.getSignature().getName());
System.out.println("======================后增强结束======================================");
}
//环绕增强
// ProceedingJoinPoint 是JoinPoint的一个子接口
// 环绕通知有一个 Obect 类型的返回值
// 参数是 ProceedingJoinPoint类型的参数
// 必须有一个 throws throwable 来抛出异常
@Around("myPointCut()")
public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
System.out.println("环绕通知:方法开始前模拟检查,开启事务。。。。。。。。。");
//执行当前的方法
Object obj = proceedingJoinPoint.proceed();
System.out.println("环绕通知:方法结束前模拟检查,关闭事务。。。。。。。。。");
System.out.println("======================环绕增强结束======================================");
return obj;
}
//异常通知
@AfterThrowing(value = "myPointCut()", throwing="e")
public void myAfterThrowing(JoinPoint joinPoint , Throwable e) {
System.out.println("异常通知:"+e.getMessage());
System.out.println("======================异常通知结束======================================");
}
//最终通知
@After("myPointCut()")
public void myAfter() {
System.out.println("最终通知:模拟方法结束后并 释放资源");
System.out.println("======================最终通知结束======================================");
}
}
在使用@Aspect注解定义了切面类,由于该类在 Spring 中是作为组件使用的,所以还需要添加@Repository注解才能生效
在UserDaoImpl接口实现类中添加注解@Repository(“userDao”)
创建配置文件:
<?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-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<context:component-scan base-package="com.itheima.aspectj"/>
</beans>
- <aop:aspectj-autoproxy></aop:aspectj-autoproxy> 开启Spring对基于注解的声明式 AspectJ 的支持
- <context:component-scan base-package = “com.itheima.aspectj”/> 开启注解扫描
创建测试类:
package com.itheima.aspectj.annotation;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TextAnnotation {
@Test
public void myTest() {
String xmlPath = "com/itheima/aspectj/annotation/applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
UserDao userDao = (UserDao)applicationContext.getBean("userDao");
userDao.addUser();
((AbstractApplicationContext) applicationContext).close();
}
}
在校学习生,不定时更新。还请各位看官谅解。
(参考书籍:JAVA EE 企业级应用开发教程(Spring+SpringMVC+MyBatis))