Spring基础知识(7)-Aspectj

一、使用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

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();
	}
}







      

      

  






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值