5.AspectJ的注解方式完成AOP的开发
**AspectJ的概述** 1. AspectJ的概述 * AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。 * Spring为了简化AOP开发引入了AspectJ作为自身AOP开发. * AspectJ是一个基于Java语言的AOP框架 * Spring2.0以后新增了对AspectJ切点表达式支持 * @AspectJ 是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面 * 新版本Spring框架,建议使用AspectJ方式来开发AOP
sping的aop名词解释
JoinPoint(连接点):目标对象,所有可以增强的方法
PointCut(切入点):目标对象,已经增强的方法
Advice(通知/增强):增强的代码
Target(目标对象):被代理对象
Weaving(织入):将通知应用到切入点的过程
Proxy(代理):将通知织入到目标对象之后,形成代理对象
Aspect(切面):切入点+通知
---------- **AspectJ的注解的方式完成AOP的开发** 1. AspectJ的注解方式完成AOP的开发 2. 具体的开发步骤 1. 步骤一:创建JavaWEB项目,引入具体的开发的jar包 * 先引入Spring框架开发的基本开发包,还需要引入AOP的开发包(AOP联盟和AOP的开发包)
--spring-aop-4.3.9.RELEASE.jar
--com.springsource.org.aopalliance-1.0.0.jar * 再引入AspectJ的开发包 * aspectj 本身jar包 -- com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar * spring支持aspectj的包 -- spring-aspects-3.2.0.RELEASE.jar 2. 步骤二:创建Spring的配置文件,引入具体的AOP的schema约束 <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"> 3. 步骤三:创建包结构,编写具体的实现类代码 * com.itheima.demo1 * OrderDaoImpl 4. 步骤四:将目标类配置到Spring中 <!-- 目标类 --> <bean id="orderDaoImpl" class="com.itheima.spring.OrderDaoImpl"/> 5. 步骤五:编写切面(切面 = 通知 + 切入点),所以需要先来了解通知的类型和切入点的表达式定义!! 1. AspectJ的通知类型如下 @Before -- 前置通知,相当于BeforeAdvice @AfterReturning -- 后置通知,相当于AfterReturningAdvice @Around -- 环绕通知,相当于MethodInterceptor @AfterThrowing -- 抛出通知,相当于ThrowAdvice @After -- 最终final通知,不管是否异常,该通知都会执行 @DeclareParents -- 引介通知,相当于IntroductionInterceptor (不要求掌握) 2. AspectJ的切入点表达式定义:就是用来限制哪些类的哪些方法需要进行增强!! * 如果想完成切入点的定义,需要大家来了解 execution(切入点表达式)的函数,括号中就是具体的表达式的写法 * execution(切入点表达式)的函数的语法 * 表达式的语法 * [访问修饰符] 方法返回值 方法名(参数) * execution(public * com.itheim.demo1.OrderDao.save(..)) * execution(public * *.*(..)) * execution(public * com.itheim.demo1.*.*(..)) * execution(public * com.itheim.demo1..*.*(..)) * execution(public * com.itheim.demo1.OrderDao+.*(..)) * 匹配所有类public方法 execution(public * *(..)) * 匹配指定包下所有类方法 execution(* cn.itcast.dao.*(..)) 不包含子包 * 匹配指定包及其子包下所有的类 execution(* cn.itcast.dao..*(..)) ..*表示包、子孙包下所有类 * 匹配指定类所有方法 execution(* cn.itcast.service.UserService.*(..)) * 匹配实现特定接口所有类方法 execution(* cn.itcast.dao.GenericDAO+.*(..)) * 匹配所有save开头的方法 execution(* save*(..)) 3. 编写切面代码 @Aspect public class MyAspectAnno { @Before(value="execution(public * com.itheima.demo1.OrderDaoImpl.save(..))") public void before(){ System.out.println("前置通知..."); } } 在核心的配置文件中,配置切面类 <!-- 配置切面 --> <bean id="myAspectAnno" class="com.itheima.demo1.MyAspectAnno"/> 6. 步骤六:在核心的配置文件中开启AspectJ的注解的自动代理 <!-- 开启自动代理 --> <aop:aspectj-autoproxy/> 7. 步骤七:编写测试代码 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class Demo1 { @Resource(name="orderDaoImpl") private OrderDaoImpl orderDaoImpl; @Test public void run(){ orderDaoImpl.save(); } } ---------- **AspectJ的具体通知类型的使用** 1. 前置通知: * @Before -- 代表在目标方法之前执行增强. * 前置通知中有一个参数:Joinpoint:获得切入点信息. 2. 后置通知: * @AfterReturing -- 代表在目标方法之后执行增强. * 可以获得到目标方法的返回值,需要在注解中添加returning="result"属性 * 代码: // 后置通知: @AfterReturning(value="execution(* com.itheima.spring.demo1.OrderDao.update(..))",returning="result") public void afterReturing(Object result){ System.out.println("后置通知================"+result); } 3. 环绕通知: * @Around -- 代表在目标方法之前和之后执行增强. * 用来阻止目标方法执行,需要使用ProceedingJoinPoint对象作为参数!! * 代码: // 环绕通知: @Around("execution(* com.itheima.spring.demo1.OrderDao.delete(..))") public Object around(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("环绕前通知==============="); // 执行目标方法: Object obj = joinPoint.proceed(); System.out.println("环绕后通知==============="); return obj; } 4. 异常抛出通知: * @AfterThrowing -- 代表在目标方法出现异常的时候执行增强. * 获得到异常的信息. * 代码: // 异常抛出通知: @AfterThrowing(value="execution(* com.itheima.spring.demo1.OrderDao.find(..))",throwing="e") public void afterThrowing(Throwable e){ System.out.println("异常抛出通知=============="+e.getMessage()); } 5. 最终通知 * @After -- 代表无论目标方法是否出现异常.这种通知总是会执行的.类似try{}catch(){}finally{} * 代码 // 最终通知 @After(value="execution(* com.itheima.spring.demo1.OrderDao.find(..))") public void after(){ System.out.println("最终通知================"); } ---------- **AspectJ的切入点的定义** 1. 概述 * 可以在切面的类中来定义切入点的方法,这样做的好处是在具体的通知方法中直接引入切入点,程序维护最简单!! 2. 相关的代码 @Pointcut(value="execution(* com.itheima.spring.demo1.OrderDao.find(..))") private void myPointcut(){} 3. 在通知的方法中引入切入点 * @Around(value="切面类名.切入点的方法()") * 例如:@Around(value="MyAspectjAnno.myPointcut()")
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"> <!-- bean definitions here --> <!-- 开启Aspect自动代理 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> <bean id="orderDao" class="demo1.OrderDao"></bean> <!-- 配置切面的类 --> <bean id="myAspect" class="demo1.MyAspect"></bean> </beans>
OrderDao:
package demo1; public class OrderDao { public void save(){ System.out.println("保存订单。。。"); } public String update(){ System.out.println("修改订单。。。"); return "修改订单成功"; } public void delete(){ System.out.println("删除订单"); } public void find(){ System.out.println("查询订单"); int a=10/0; } }
MyAspect:
package demo1; 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; /** * 自定义切面=通知+切入点 * @author mjl * */ @Aspect public class MyAspect { /** * 前置通知 */ @Before(value="execution(public * demo1.OrderDao.save(..))") public void before(){ System.out.println("===Aspect前置通知======="); } //编写一个新的方法,配置成后置通知 @AfterReturning(value="execution(public * demo1.OrderDao.update(..))",returning="result") public void afterreturning(Object result){ System.out.println("后置通知========="+","+result); } //环绕(方法执行之前,和方法执行之后 @Around(value="execution(public * demo1.OrderDao.delete(..))") public void myaround(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("环绕方法执行之前===="); //让目标方法执行 joinPoint.proceed(); System.out.println("环绕方法执行之后===="); } //如果find方法跑出了异常,myAfterThrowing就会执行了 @AfterThrowing(value="execution(public * demo1.OrderDao.find(..))") public void myAfterThrowing(){ System.out.println("异常抛出了"); } @After(value="execution(public * demo1.OrderDao.find(..))") public void myAfter(){ System.out.println("最终执行"); } }
SpringDemo1:
package demo1; import javax.annotation.Resource; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class SpringDemo1 { @Resource(name="orderDao") private OrderDao orderDao; @Test public void run1(){ orderDao.save(); orderDao.update(); orderDao.delete(); orderDao.find(); } }