基于AspectJ的AOP开发
一、AspectJ概述
1.1 AspectJ的简介
1、AspectJ是一个基于Java语言的AOP框架
2、Spring2.0以后新增了对AspectJ切点表达式支持
3、@AspectJ是AspectJ1.5新增的功能,通过JDK5注解技术,允许直接在Bean类中定义切面
4、新版本Spring框架,建议使用AspectJ方式来开发AOP
5、使用AspectJ需要导入Spring AOP和AspectJ相关jar包
二、AspectJ的注解开发AOP
2.1 环境准备
pom.xml
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
<!--引入Spring的基本开发包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
</dependencies>
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">
<!--开启AspectJ的注解开发,自动代理=====================-->
<aop:aspectj-autoproxy/>
</beans>
2.2 通知的类型介绍
传统AOP:
基于AspectJ的AOP:
通知类型 | 介绍 |
---|---|
@Before | 前置通知,相当于BeforeAdvice |
@AfterReturning | 后置通知,相当于AfterReturningAdvice |
@Around | 环绕通知,相当于MethodInterceptor |
@AfterThrowing | 异常抛出通知,相当于ThrowAdvice |
@After | 最终final通知,不管是否异常,该通知都会执行 |
@DeclareParents | 引介通知,相当于IntroductionInterceptor(不要求掌握) |
2.3 切入点表达式的定义
在通知中通过value属性定义切点
通过execution函数,可以定义切点的方法切入
语法:
-execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)
访问修饰符可以省略
例如:
-
匹配所有类public方法 execution(public * * (…))
(第一个* 代表任意方法返回值,第二个* 代表任意名称,…代表任意参数) -
匹配指定包下所有类方法 execution(* com.linmin.dao.* (…))不包含子包
(第一个* 代表返回值类型,是任意的;第二个* 代表方法名称,任意的;…任意参数) -
execution(* com.linmin.dao…*(…)) …*表示包含包、子孙包下所有类(第一…代表包含子包)
-
匹配指定类所有方法 execution(* com.linmin.service.UserService.*(…))
-
匹配实现特定接口所有类方法 execution(* com.linmin.dao.GenericDAO+.*(…))
+号代表子类 -
匹配所有save开头的方法 execution(* save*(…))
2.4 前置通知
在applicationContext.xml中添加目标类和切面类,进行注解开发。
<!--目标类===================-->
<bean id="productDao" class="com.linmin.aspectJ.demo1.ProductDao"/>
<!--定义切面-->
<bean class="com.linmin.aspectJ.demo1.MyAspectAnno"/>
public class ProductDao {
public void save(){
System.out.println("保存商品...");
}
public String update(){
System.out.println("修改商品...");
return "hello";
}
public void delete(){
System.out.println("删除商品...");
}
public void findOne(){
System.out.println("查询一个商品...");
}
public void findAll(){
System.out.println("查询所有商品...");
}
}
/**
* 切面类
*/
@Aspect
public class MyAspectAnno {
@Before(value="myPointcut1()")
public void before(JoinPoint joinPoint){
System.out.println("前置通知=================="+joinPoint);
}
@Pointcut(value="execution(* com.linmin.aspectJ.demo1.ProductDao.save(..))")
private void myPointcut1(){}
}
测试类:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringDemo1 {
@Resource(name="productDao")
private ProductDao productDao;
@Test
public void demo1(){
productDao.save();
productDao.update();
productDao.delete();
productDao.findAll();
productDao.findOne();
}
}
2.5 后置通知
@Aspect
public class MyAspectAnno {
@AfterReturning(value="myPointcut2()",returning = "result")
public void afterReturing(Object result){
System.out.println("后置通知=================="+result);
}
@Pointcut(value="execution(* com.linmin.aspectJ.demo1.ProductDao.update(..))")
private void myPointcut2(){}
}
若增强的方法有返回值,则通过returning获取返回值。
2.6 环绕通知
around方法的返回值就是目标代理方法执行的返回值
参数为ProceedingJoinPoint可以调用拦截目标方法执行
@Aspect
public class MyAspectAnno {
@Around(value="myPointcut3()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前通知================");
Object obj = joinPoint.proceed(); // 执行目标方法
System.out.println("环绕后通知================");
return obj;
}
@Pointcut(value="execution(* com.linmin.aspectJ.demo1.ProductDao.delete(..))")
private void myPointcut3(){}
}
重点:如果不调用ProceedingJoinPoint的proceed方法,那么目标方法就被拦截了
2.7 异常抛出通知
通过设置throwing属性,可以设置发生异常对象参数。
@Aspect
public class MyAspectAnno {
@AfterThrowing(value="myPointcut4()",throwing = "e")
public void afterThrowing(Throwable e){
System.out.println("异常抛出通知=============="+e.getMessage());
}
@Pointcut(value="execution(* com.linmin.aspectJ.demo1.ProductDao.findOne(..))")
private void myPointcut4(){}
}
2.8 最终通知
无论是否出现异常,最终通知总是会被执行的。
@Aspect
public class MyAspectAnno {
@After(value="myPointcut5()")
public void after(){
System.out.println("最终通知==================");
}
@Pointcut(value="execution(* com.linmin.aspectJ.demo1.ProductDao.findAll(..))")
private void myPointcut5(){}
}
2.9 切点命名
通过@Pointcut为切点命名
- 在每个通知内定义切点,会造成工作量大,不易维护,对于重复的切点,可以使用@Pointcut进行定义;
- 切点方法:private void 无参数方法,方法名为切点命;
- 当通知多个切点时,可以使用 || 进行连接。
在上面的各个通知中已经使用。
三、AspectJ的XML方式的AOP开发
3.1 各类通知类型的配置
public interface CustomerDao {
public void save();
public String update();
public void delete();
public void findOne();
public void findAll();
}
public class CustomerDaoImpl implements CustomerDao {
public void save() {
System.out.println("保存客户...");
}
public String update() {
System.out.println("修改客户...");
return "spring";
}
public void delete() {
System.out.println("删除客户...");
}
public void findOne() {
System.out.println("查询一个客户...");
// int a = 1/ 0;
}
public void findAll() {
System.out.println("查询多个客户...");
// int b = 1/0;
}
}
public class MyAspectXml {
// 前置通知
public void before(JoinPoint joinPoint){
System.out.println("XML方式的前置通知=============="+joinPoint);
}
// 后置通知
public void afterReturing(Object result){
System.out.println("XML方式的后置通知=============="+result);
}
// 环绕通知
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("XML方式的环绕前通知==============");
Object obj = joinPoint.proceed();
System.out.println("XML方式的环绕后通知==============");
return obj;
}
// 异常抛出通知
public void afterThrowing(Throwable e){
System.out.println("XML方式的异常抛出通知============="+e.getMessage());
}
// 最终通知
public void after(){
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" 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">
<!--XML的配置方式完成AOP的开发===============-->
<!--配置目标类=================-->
<bean id="customerDao" class="com.linmin.aspectJ.demo2.CustomerDaoImpl"/>
<!--配置切面类-->
<bean id="myAspectXml" class="com.linmin.aspectJ.demo2.MyAspectXml"/>
<!--aop的相关配置=================-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pointcut1" expression="execution(* com.linmin.aspectJ.demo2.CustomerDao.save(..))"/>
<aop:pointcut id="pointcut2" expression="execution(* com.linmin.aspectJ.demo2.CustomerDao.update(..))"/>
<aop:pointcut id="pointcut3" expression="execution(* com.linmin.aspectJ.demo2.CustomerDao.delete(..))"/>
<aop:pointcut id="pointcut4" expression="execution(* com.linmin.aspectJ.demo2.CustomerDao.findOne(..))"/>
<aop:pointcut id="pointcut5" expression="execution(* com.linmin.aspectJ.demo2.CustomerDao.findAll(..))"/>
<!--配置AOP的切面-->
<aop:aspect ref="myAspectXml">
<!--配置前置通知-->
<aop:before method="before" pointcut-ref="pointcut1"/>
<!--配置后置通知-->
<aop:after-returning method="afterReturing" pointcut-ref="pointcut2" returning="result"/>
<!--配置环绕通知-->
<aop:around method="around" pointcut-ref="pointcut3"/>
<!--配置异常抛出通知-->
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="e"/>
<!--配置最终通知-->
<aop:after method="after" pointcut-ref="pointcut5"/>
</aop:aspect>
</aop:config>
</beans>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value="classpath:applicationContext2.xml")
public class SpringDemo2 {
@Resource(name="customerDao")
private CustomerDao customerDao;
@Test
public void demo1(){
customerDao.save();
customerDao.update();
customerDao.delete();
customerDao.findOne();
customerDao.findAll();
}
}