AspectJ实现AOP
上一篇文章Spring框架(4)---AOP讲解铺垫,讲了一些基础AOP理解性的东西,那么这篇文章真正开始讲解AOP
通过AspectJ实现AOP要比普通的实现Aop要方便的多,所以第五篇文章有关SpringAOP我暂且不写,后面整理好了再补充上;
那我们首先还是讲一些有的没的的东西:
什么是Spring的AspectJ的AOP
AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。
Spring2.0以后新增了对AspectJ切点表达式支持,@AspectJ 是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面
新版本Spring框架,建议使用AspectJ方式来开发AOP
那我们先来一个小案例,还是以前面狗的案例举例:
1.导入相关架包
在原来的基础上导入下面四个架包:
- spring-aop-4.2.0.RELEASE.jar
- com.springsource.org.aopalliance-1.0.0.jar
- spring-aspects-4.2.0.RELEASE.jar
- com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
2.编写配置文件applicationContext.xml 导入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:p="http://www.springframework.org/schema/p" 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/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"> <!-- bean definitions here --> <!-- 配置注解bean所在的包 --> <context:annotation-config/> <context:component-scan base-package="com.study.spring.d_advisor"></context:component-scan> <!-- 打开自动代理 --> <aop:aspectj-autoproxy/>
</beans>
3.编写代理对象
1 public class Dog { 2 3 public void run() { 4 System.out.println("狗会跑"); 5 } 6 7 public void jump() { 8 9 System.out.println("狗会跳"); 10 11 } 12 13 public void eat() { 14 15 System.out.println("狗能吃"); 16 } 17 }
4)编写切面类
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; //@Aspect声明当前类是一个切面 @Aspect public class MyAspectJ { //@Before代表在执行方法之前执行增强代码 @Before("execution(* com.study.dog.Dog.*(..))") public void before1() { System.out.println("饲养员叫你你猜能动"); } }
5编写配置文件
<?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:p="http://www.springframework.org/schema/p" 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/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"> <!-- bean definitions here --> <!-- 配置注解bean所在的包 --> <context:annotation-config/> <context:component-scan base-package="com.study.spring.d_advisor"></context:component-scan> <!-- 打开自动代理 --> <aop:aspectj-autoproxy/> <!-- 被代理对象 --> <bean id="Dog" class="com.study.dog.Dog"></bean> <!-- 切面 --> <bean id="MyAspectJ" class="com.study.dog.MyAspectJ"></bean> </beans>
6.编写测试类
1 import org.junit.Test; 2 import org.junit.runner.RunWith; 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.beans.factory.annotation.Qualifier; 5 import org.springframework.test.context.ContextConfiguration; 6 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 7 8 //完成配置文件的加载 9 @RunWith(SpringJUnit4ClassRunner.class) 10 @ContextConfiguration(locations="classpath:applicationContext.xml") 11 public class AspectJTest { 12 // 得到dog对象 13 @Autowired 14 @Qualifier("Dog") 15 private Dog dog; 16 17 @Test 18 public void Demo1() { 19 dog.eat(); 20 dog.run(); 21 dog.jump(); 22 } 23 /* 24 * 输出结果: 25 * 饲养员叫你你猜能动 26 * 狗能吃 27 * 饲养员叫你你猜能动 28 * 狗会跑 29 * 饲养员叫你你猜能动 30 * 狗会跳 31 */ 32 }
总结:上面是一个最简单的AspectJ的AOP,我们这样就可以通过输入一个”饲养员叫你你猜能动",就可以在多个方法中存在,在以后开发非常需要
那么下来来说一些非常重要的干货!
AspectJ表达式:
语法:execution(表达式) 通过execution函数,可以定义切点的方法切入
execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)
例如
execution(public * *(..)) 匹配所有类public方法
execution(* cn.study.dao..*(..)) 匹配指定包下所有类方法,不包含子包
execution(* cn.study.dao..*(..)) ..*表示包、子孙包下所有类
execution(* cn.study.service.UserService.*(..)) 匹配指定类所有方法
execution(* cn.study.dao.GenericDAO+.*(..)) 匹配实现特定接口所有类方法
execution(* save*(..)) 匹配所有save开头的方法
AspectJ增强
@Before 前置通知,相当于BeforeAdvice
@AfterReturning 后置通知,相当于AfterReturningAdvice
@Around 环绕通知,相当于MethodInterceptor
@AfterThrowing抛出通知,相当于ThrowAdvice
@After 最终final通知,不管是否异常,该通知都会执行
@DeclareParents 引介通知,相当于IntroductionInterceptor (不要求掌握)
那现在我们通过上面的列子,来全面贯彻上面的干货!
对上面MyAspectJ的进行全面增强:
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; //@Aspect声明当前类是一个切面 @Aspect public class MyAspectJ { //@Before代表在执行方法之前执行增强代码 @Before("execution(* com.study.dog.Dog.run(..))") public void before(JoinPoint joinPoint){ System.out.println("前置增强...."+joinPoint); } //后置通知 @AfterReturning (value="execution(* com.study.dog.Dog.jump(..))",returning="returnVal") public void afterReturin(Object returnVal){ System.out.println("后置增强....方法的返回值:"+returnVal); } //环绕通知 @Around(value="MyAspectJ.myPointcut()") public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{ System.out.println("环绕前增强...."); Object obj = proceedingJoinPoint.proceed(); System.out.println("环绕后增强...."); return obj; } //抛出通知 @AfterThrowing(value="MyAspectJ.myPointcut()",throwing="e") public void afterThrowing(Throwable e){ System.out.println("不好了 出异常了!!!"+e.getMessage()); } //最终final通知,不管是否异常,该通知都会执行 @After("MyAspectJ.myPointcut()") public void after(){ System.out.println("最终通知..."); } //切点的定义 @Pointcut("execution(* com.study.dog.Dog.eat(..))") private void myPointcut(){} }
编写测试类AspectJTest 和上面一点不变
import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; //完成配置文件的加载 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations="classpath:applicationContext.xml") public class AspectJTest { // 得到dog对象 @Autowired @Qualifier("Dog") private Dog dog; @Test public void Demo1() { dog.eat(); dog.run(); dog.jump(); } }
运行结果:
通过这个例子,相信大家对于AspectJ的AOP有了一定的了解了。
最后补充一个面试题:
Advisor和Aspect的区别?
Advisor:Spring传统意义上的切面:支持一个切点和一个通知的组合.
Aspect:可以支持多个切点和多个通知的组合.
这篇文章就讲到这里,有不足之处,欢迎大家指出,谢谢!