Spring基础知识(7)-Aspectj
2016-05-16 11:25
417 查看
一、使用AspectJ 实现AOP
AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。
Spring2.0之后 为了简化 AOP编程,支持AspectJ 技术
@AspectJ 是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面
新版本Spring框架,建议使用AspectJ方式来开发AOP ,而不需要使用传统 Spring AOP 编程
1、基于注解的@AspectJ 编程
1) 下载 导入 aspectJ 开发包 (AspectJ 依赖 AOP 的 jar包 )
AspectJ开发包 com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
导入spring 整合 aspectj 的jar包 spring-aspects-3.2.0.RELEASE.jar
2) 编写Spring配置文件 (需要aop名称空间,使用Aspectj开发,我们需要AOP的schema)
使用 <aop:aspectj-autoproxy /> 配置自动代理
* 底层 <bean class="... AnnotationAwareAspectJAutoProxyCreator" />
3) @AspectJ 常用注解
@Aspect 定义切面通知类型
@Before 前置通知,相当于BeforeAdvice
@AfterReturning 后置通知,相当于AfterReturningAdvice
@Around 环绕通知,相当于MethodInterceptor
@AfterThrowing抛出通知,相当于ThrowAdvice
@After 最终final通知,不管是否异常,该通知都会执行
@DeclareParents 引介通知,相当于IntroductionInterceptor (不要求掌握)
4) 切点表达式定义
切点使用指定哪些连接点 会被增强 , 通过execution函数,可以定义切点的方法切入
语法: execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)
例如:
execution(public * *(..)) 匹配所有public修饰符 任何方法
execution(* lsq.spring.service.HelloService.*(..)) 匹配HelloService类中所有方法,第一个*表示返回类型
execution(int lsq.spring.service.UserService.regist(..)) 匹配UserService中int返回类型 regist方法
execution(* lsq.spring.dao..*(..)) 匹配lsq.spring.dao包下 (包含子包) 所有类 所有方法
execution(* lsq.spring.dao.*(..)) 不包含子包
execution(* lsq.spring.dao.GenericDAO+.*(..)) 匹配GenericDAO 所有子类 或者 实现类 所有方法
开发步骤:
第一步 : 导入jar、 自动代理
第二步 : 编写被代理对象 UserDAO
第四步: 配置applicationContext.xml 注册被代理对象 和 切面类
测试:
@AspectJ 支持通知类型详解 :
第一种 前置通知 @Before : 前置通知不能拦截目标方法执行 , 每个前置通知方法接收JoinPoint
* JoinPoint 引包 org.aspectj.lang.JoinPoint
* JoinPoint 获得拦截增强方法信息
第二种 后置通知 @AfterReturning : 后置通知,在目标方法执行,返回后 调用增强代码
* 通过 returning 获得目标方法返回值
第三种 环绕通知 @Around : 在目标方法前后增强 ,阻止目标方法执行
* 参数为ProceedingJoinPoint 可以调用拦截目标方法执行
如果 不写 proceedingJoinPoint.proceed(); 目标方法就无法执行
第四种 抛出通知 @AfterThrowing : 在方法出现异常后,该方法获得执行
* 在方法没有错误时,不会得到执行
第五种 最终通知 @After : 不管目标方法是否存在异常,都将执行 类似finally 代码块
@Pointcut 切点定义
直接在通知上定义切点表达式,会造成一些切点表达式重复
在每个通知内定义切点,会造成工作量大,不易维护,对于重复的切点,可以使用@Poingcut进行定义
☆☆格式: private void 无参数方法,方法名为切点名
☆☆☆:advisor 和 aspect 区别 ?
advisor是spring中aop定义的切面,通常由一个切点和一个通知组成。
aspect是规范中定义的切面,允许由多个切点和多个通知组成。
2、 基于XML配置 AspectJ 编程
1) 定义被代理对象 ProductDAO
AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。
Spring2.0之后 为了简化 AOP编程,支持AspectJ 技术
@AspectJ 是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面
新版本Spring框架,建议使用AspectJ方式来开发AOP ,而不需要使用传统 Spring AOP 编程
1、基于注解的@AspectJ 编程
1) 下载 导入 aspectJ 开发包 (AspectJ 依赖 AOP 的 jar包 )
AspectJ开发包 com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
导入spring 整合 aspectj 的jar包 spring-aspects-3.2.0.RELEASE.jar
2) 编写Spring配置文件 (需要aop名称空间,使用Aspectj开发,我们需要AOP的schema)
使用 <aop:aspectj-autoproxy /> 配置自动代理
* 底层 <bean class="... AnnotationAwareAspectJAutoProxyCreator" />
3) @AspectJ 常用注解
@Aspect 定义切面通知类型
@Before 前置通知,相当于BeforeAdvice
@AfterReturning 后置通知,相当于AfterReturningAdvice
@Around 环绕通知,相当于MethodInterceptor
@AfterThrowing抛出通知,相当于ThrowAdvice
@After 最终final通知,不管是否异常,该通知都会执行
@DeclareParents 引介通知,相当于IntroductionInterceptor (不要求掌握)
4) 切点表达式定义
切点使用指定哪些连接点 会被增强 , 通过execution函数,可以定义切点的方法切入
语法: execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)
例如:
execution(public * *(..)) 匹配所有public修饰符 任何方法
execution(* lsq.spring.service.HelloService.*(..)) 匹配HelloService类中所有方法,第一个*表示返回类型
execution(int lsq.spring.service.UserService.regist(..)) 匹配UserService中int返回类型 regist方法
execution(* lsq.spring.dao..*(..)) 匹配lsq.spring.dao包下 (包含子包) 所有类 所有方法
execution(* lsq.spring.dao.*(..)) 不包含子包
execution(* lsq.spring.dao.GenericDAO+.*(..)) 匹配GenericDAO 所有子类 或者 实现类 所有方法
开发步骤:
第一步 : 导入jar、 自动代理
第二步 : 编写被代理对象 UserDAO
package lsq.spring.aspectj.a_annotation; /** * 被代理对象 * * @author lishanquan * */ public class UserDao { public void save(){ System.out.println("用户保存……"); } public void delete(){ System.out.println("用户删除……"); } public void update(){ System.out.println("用户修改……"); } public void search(){ System.out.println("用户查询……"); } }第三步 : 编写切面类
package lsq.spring.aspectj.a_annotation; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; /** * 自定义切面类 * * @author lishanquan * */ @Aspect //声明当前类是一个切面类 public class MyAspect { //定义一个前置增强 @Before("execution(* lsq.spring.aspectj.a_annotation.UserDao.*(..))")//对UserDao中所有方法增强 public void before1(){ System.out.println("前置增强1……"); } @Before("execution(* lsq.spring.aspectj.a_annotation.UserDao.save(..))")//对UserDao中save方法增强 public void before2(){ System.out.println("前置增强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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 配置自动代理 --> <aop:aspectj-autoproxy/> <!-- 被代理目标 --> <bean id="userDao" class="lsq.spring.aspectj.a_annotation.UserDao"></bean> <!-- 切面配置 --> <bean id="myAspect" class="lsq.spring.aspectj.a_annotation.MyAspect"></bean> </beans>
测试:
package lsq.spring.aspectj.a_annotation; 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 SpringTest { //注入代理对象 @Autowired @Qualifier("userDao") private UserDao userDao; @Test public void demo(){ userDao.save(); userDao.delete(); userDao.update(); userDao.search(); } }显示结果:
@AspectJ 支持通知类型详解 :
第一种 前置通知 @Before : 前置通知不能拦截目标方法执行 , 每个前置通知方法接收JoinPoint
* JoinPoint 引包 org.aspectj.lang.JoinPoint
* JoinPoint 获得拦截增强方法信息
第二种 后置通知 @AfterReturning : 后置通知,在目标方法执行,返回后 调用增强代码
* 通过 returning 获得目标方法返回值
<span style="white-space:pre"> </span>@AfterReturning(value = "execution(* lsq.spring.aspectj.a_annotation.UserDao.update(..))", returning = "returnValue") // returnValue 是代理方法 ,参数名, 用来获得目标业务方法执行后返回值 public void afterReturning(Object returnValue) { System.out.println("后置增强.... 获得方法返回值:" + returnValue); }
第三种 环绕通知 @Around : 在目标方法前后增强 ,阻止目标方法执行
* 参数为ProceedingJoinPoint 可以调用拦截目标方法执行
<span style="white-space:pre"> </span>// 环绕增强 @Around("execution(* lsq.spring.aspectj.a_annotation.UserDAO.search(..))") // 可以阻止目标方法执行,通过参数 public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("环绕前增强..."); Object result = proceedingJoinPoint.proceed();// 执行目标方法 System.out.println("环绕后增强..."); return result; }
如果 不写 proceedingJoinPoint.proceed(); 目标方法就无法执行
第四种 抛出通知 @AfterThrowing : 在方法出现异常后,该方法获得执行
* 在方法没有错误时,不会得到执行
<span style="white-space:pre"> </span>// 抛出增强 @AfterThrowing(value = "execution(* lsq.spring.aspectj.a_annotation.UserDAO.search(..))", throwing = "e") // throwing 用来指定异常对象 参数名称 public void afterThrowing(Throwable e) { System.out.println("不好了,出问题了, " + e.getMessage()); }
第五种 最终通知 @After : 不管目标方法是否存在异常,都将执行 类似finally 代码块
<span style="white-space:pre"> </span>// 最终增强 ,无论目标方法是否有异常,都必须执行 @After("execution(* lsq.spring.aspectj.a_annotation.UserDAO.search(..))") public void after() { System.out.println("这是必须执行的代码 ....."); }
@Pointcut 切点定义
直接在通知上定义切点表达式,会造成一些切点表达式重复
在每个通知内定义切点,会造成工作量大,不易维护,对于重复的切点,可以使用@Poingcut进行定义
☆☆格式: private void 无参数方法,方法名为切点名
<span style="white-space:pre"> </span>@Pointcut("execution(* lsq.spring.aspectj.a_annotation.UserDAO.search(..))") private void mypointcut() {}应用切点的通知
@After("MyAspect.mypointcut()") public void after() { System.out.println("这是必须执行的代码 ....."); }
☆☆☆:advisor 和 aspect 区别 ?
advisor是spring中aop定义的切面,通常由一个切点和一个通知组成。
aspect是规范中定义的切面,允许由多个切点和多个通知组成。
2、 基于XML配置 AspectJ 编程
1) 定义被代理对象 ProductDAO
package lsq.spring.aspectj.b_xml; /** * 被代理对象 * * @author lishanquan * */ public class ProductDao { public void addProduct(){ System.out.println("添加商品……"); } public int deleteProduct(){ System.out.println("删除商品……"); return 1/0; } }2) 定义切面 MyAspect
package lsq.spring.aspectj.b_xml; import org.aspectj.lang.ProceedingJoinPoint; /** * 自定义切面类 * 切面类就是切点和通知的结合 * @author lishanquan * */ public class MyAspect { //前置增强 public void before(){ System.out.println("前置增强……"); } //后置增强 public void afterReturing(Object returnVal){ System.out.println("后置增强……,返回值:"+returnVal); } //环绕增强 public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{ System.out.println("环绕前增强……"); Object result = proceedingJoinPoint.proceed(); System.out.println("环绕后增强……"); return result; } //异常增强 public void afterThrowing(Throwable ex){ System.out.println("发生异常,原因:"+ex.getMessage()); } //最终通知 public void after(){ System.out.println("最终通知……"); } }3) 编写Advice增强方法,在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 id="productDao" class="lsq.spring.aspectj.b_xml.ProductDao"></bean> <!-- 切面 --> <bean id="myAspect" class="lsq.spring.aspectj.b_xml.MyAspect"></bean> <!-- xml配置使用Aspectj --> <!-- 进行aop配置 --> <aop:config> <!-- 定义切面 --> <aop:aspect ref="myAspect"> <!-- 配置切点 --> <aop:pointcut expression="execution(* lsq.spring.aspectj.b_xml.ProductDao.*(..))" id="myPointcut"/> <!-- 通知 --> <!-- <aop:before method="before" pointcut-ref="myPointcut"/> --> <!-- <aop:after-returning method="afterReturing" returning="returnVal" pointcut-ref="myPointcut"/> --> <!-- <aop:around method="around" pointcut-ref="myPointcut"/> --> <!-- <aop:after-throwing method="afterThrowing" throwing="ex" pointcut-ref="myPointcut"/> --> <aop:after method="after" pointcut-ref="myPointcut"/> </aop:aspect> </aop:config> </beans>测试:
package lsq.spring.aspectj.b_xml; 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:applicationContext2.xml") public class SpringTest { @Autowired @Qualifier("productDao") private ProductDao productDao; @Test public void demo(){ productDao.addProduct(); productDao.deleteProduct(); } }
相关文章推荐
- 在ASP.NET 2.0中操作数据之五十二:使用FileUpload上传文件
- asp.net web 开发登录相关操作的控件LoginName、LoginStatus和LoginView控件使用详解
- asp.net 微信支付 错误解决方案
- Spring AOP @AspectJ 入门基础
- 转载 asp.net的Request.ServerVariables参数说明
- 在ASP.NET 2.0中操作数据之五十一:从GridView的页脚插入新记录
- 在ASP.NET 2.0中操作数据之五十:为GridView控件添加Checkbox
- .Net中导出数据到Excel(asp.net和winform程序中)
- raspberry树莓派安装CUPS实现打印服务器共享HP P1007打印机
- C# ASP.NET FILEUPLOAD详解
- 在ASP.NET 2.0中操作数据之四十九:为GridView控件添加RadioButton
- 在ASP.NET 2.0中操作数据之四十八:对SqlDataSource控件使用开放式并发
- IdeaSpace安装
- asp.net4.5尚未在web服务器上注册 解决方案
- 利用记事本创建一个ASP.NET Core RC2 MVC应用
- 在ASP.NET 2.0中操作数据之四十八:对SqlDataSource控件使用开放式并发
- 在ASP.NET 2.0中操作数据之四十九:为GridView控件添加RadioButton
- 在ASP.NET 2.0中操作数据之五十:为GridView控件添加Checkbox
- 在ASP.NET 2.0中操作数据之五十一:从GridView的页脚插入新记录
- 在ASP.NET 2.0中操作数据之五十二:使用FileUpload上传文件