基于Spring4注解的AOP

AOP 的 helloWorld

1. 加入 jar 包

* com.springsource.net.sf.cglib-2.2.0.jar
* com.springsource.org.aopalliance-1.0.0.jar
* com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
* spring-aspects-4.0.0.RELEASE.jar

2. 在 Spring 的配置文件中加入 aop 的命名空间

3. 基于注解的方式来使用 AOP

	<!--在配置文件中配置自动扫描的包-->
	<context:component-scan base-package="Spring扫描的包如(com.xxx.xxx)"></context:component-scan>
   <!-- 要在 Spring IOC 容器中启用 AspectJ 注解支持, 只要在 Bean 配置文件中定义一个空的 XML 元素  -->
   <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
   <!-- 当 Spring IOC 容器侦测到 Bean 配置文件中的 <aop:aspectj-autoproxy> 元素时,会自动为与 AspectJ 切面匹配的 Bean 创建代理 -->

4. 编写切面类:

  • 4.1 一个一般的 Java 类
  • 4.2 在其中添加要额外实现的功能.
public class LoggingAspect {
	public void beforeMethod(JoinPoint joinPoint){
			String methodName = joinPoint.getSignature().getName();
			Object [] args = joinPoint.getArgs();
			
			System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
	}
	public void afterMethod(JoinPoint joinPoint){
		String methodName = joinPoint.getSignature().getName();
		System.out.println("The method " + methodName + " ends");
	}
}

5. 配置切面

5.1 切面必须是 IOC 中的 bean: 实际添加了 @Component 注解
5.2 声明是一个切面: 添加 @Aspect
@Aspect
@Component
public class LoggingAspect {
	5.3 声明通知: 即额外加入功能对应的方法. 
	 AspectJ 支持 5 种类型的通知注解: 
		 * @Before: 前置通知, 在方法执行之前执行
		 * @After: 后置通知, 在方法执行之后执行 
		 * @AfterRunning: 返回通知, 在方法返回结果之后执行
		 * @AfterThrowing: 异常通知, 在方法抛出异常之后
		 * @Around: 环绕通知, 围绕着方法执行
	5.3.1 前置通知: @Before("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(int, int))")
	 * @Before 表示在目标方法执行之前,执行 @Before 标记的方法(括号中的)的方法体. 
	 * @Before 括号里面的是切入点表达式。
	 
	@Before("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(int, int))")
	public void beforeMethod(JoinPoint joinPoint){
			5.4. 在通知中访问连接细节: 可以在通知方法中添加 JoinPoint 类型的参数, 从中可以访问到方法的签名和方法的参数. 
			String methodName = joinPoint.getSignature().getName();
			Object [] args = joinPoint.getArgs();
			
			System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
	}

	5.5. @After 表示后置通知: 在被调用方法执行之后执行的代码. 
	5.6 无论连接点是正常返回还是抛出异常, 后置通知都会执行.如果只想在连接点返回的时候记录日志, 应使用返回通知代替后置通知
	
	@After("execution(* com.atguigu.spring.aop.*.*(..))")
	public void afterMethod(JoinPoint joinPoint){
		String methodName = joinPoint.getSignature().getName();
		System.out.println("The method " + methodName + " ends");
	} 
	
	5.7 返回通知
	/**
	 * 在方法法正常结束受执行的代码
	 * 返回通知时可以访问到方法的返回值的!
	 * 原始的切点表达式需要出现在 pointcut 属性中
	 * returning属性的值,即为用来传入返回值的参数名称.
	 */
	@AfterReturning(pointcut = "execution(* *.*(..))",returning="result")
	public void afterReturning(JoinPoint joinPoint, Object result){
		String methodName = joinPoint.getSignature().getName();
		System.out.println("The method " + methodName + " ends with " + result);
	}
	5.8 异常通知
	/**
	 * 在目标方法出现异常时会执行的代码.
	 * 将 throwing 属性添加到 @AfterThrowing 注解中,可以访问到异常对象; 
	 * 且可以指定在出现特定异常时在执行通知代码(可以将参数声明为其他异常的参数类型)
	 */
	@AfterThrowing(pointcut = "execution(* *.*(..))",
			throwing="e")
	public void afterThrowing(JoinPoint joinPoint, Exception e){
		String methodName = joinPoint.getSignature().getName();
		System.out.println("The method " + methodName + " occurs excetion:" + e);
	}
	5.9环绕通知
	/**
	 * 环绕通知需要携带 ProceedingJoinPoint 类型的参数. 
	 * 环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法.
	 * 且环绕通知必须有返回值, 返回值即为目标方法的返回值
	 */
	@Around("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(..))")
	public Object aroundMethod(ProceedingJoinPoint pjd){
		
		Object result = null;
		String methodName = pjd.getSignature().getName();
		
		try {
			//前置通知
			System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));
			//执行目标方法
			result = pjd.proceed();
			//返回通知
			System.out.println("The method " + methodName + " ends with " + result);
		} catch (Throwable e) {
			//异常通知
			System.out.println("The method " + methodName + " occurs exception:" + e);
			throw new RuntimeException(e);
		}
		//后置通知
		System.out.println("The method " + methodName + " ends");
		
		return result;
	}
}

6.关于AspectJ 切入点表达式

execution (* *.*(..))
第一个 * 代表匹配任意修饰符及任意返回值,  
第二个 * 代表任意类的对象, 
第三个 * 代表任意方法,
参数列表中的 ..  匹配任意数量的参数 	
execution (* com.atguigu.spring.ArithmeticCalculator.*(..))
匹配 ArithmeticCalculator 中声明的所有方法
第一个 * 代表任意修饰符及任意返回值. 
第二个 * 代表任意方法. .. 匹配任意数量的参数. 
若目标类与接口与该切面在同一个包中, 可以省略包名.
execution (public * ArithmeticCalculator.*(..))
匹配 ArithmeticCalculator 接口的所有公有方法.
execution (public double ArithmeticCalculator.*(..))
匹配 ArithmeticCalculator 中返回 double 类型数值的公有方法
execution (public double ArithmeticCalculator.*(double, ..))
匹配第一个参数为 double 类型的方法, .. 匹配任意数量任意类型的参数
execution (public double ArithmeticCalculator.*(double, double)):
匹配参数类型为 double, double 类型的方法.

6.1 在 AspectJ 中, 切入点表达式可以通过操作符 &&, ||, ! 结合起来.

execution (* *.add(..) || * *.sub(..)):
匹配 1.任意修饰符及任意返回值 2.任意类的对象 3.匹配任意数量的参数 的 add与sub方法.

7. 指定切面的优先级
切面的优先级可以通过实现 Ordered 接口或利用 @Order 注解指定.
实现 Ordered 接口, getOrder() 方法的返回值越小, 优先级越高.
若使用 @Order 注解, 序号出现在注解中

/**
 * 可以使用 @Order 注解指定切面的优先级, 值越小优先级越高
 */
@Order(2)
@Aspect
@Component
public class LoggingAspect {
@Order(1)
@Aspect
@Component
public class VlidationAspect {

8.通知的调用顺序

1.前置通知 2.后置通知 3.返回(异常)通知

9.重用切入点定义

①在 AspectJ 切面中, 可以通过 @Pointcut 注解将一个切入点声明成简单的方法. 切入点的方法体通常是空的
②切入点方法的访问控制符同时也控制着这个切入点的可见性. 如果切入点要在多个切面中共用, 最好将它们集中在一个公共的类中. 在这种情况下, 它们必须被声明为 public.
③在引入这个切入点时, 必须将类名也包括在内. 如果类没有与这个切面放在同一个包中, 还必须包含包名.
④其他通知可以通过方法名称引入该切入点.

/**
	 * 定义一个方法, 用于声明切入点表达式. 一般地, 该方法中再不需要添入其他的代码. 
	 * 使用 @Pointcut 来声明切入点表达式. 
	 * 后面的其他通知直接使用方法名来引用当前的切入点表达式. 
	 */
	@Pointcut("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(..))")
	public void declareJointPointExpression(){ }
	
	@Before("declareJointPointExpression()")
	public void beforeMethod(JoinPoint joinPoint){}
	
	@After("declareJointPointExpression()")
	public void afterMethod(JoinPoint joinPoint){}

	@AfterReturning(value="declareJointPointExpression()",returning="result")
	public void afterReturning(JoinPoint joinPoint, Object result){}

	@AfterThrowing(value="declareJointPointExpression()",throwing="e")
	public void afterThrowing(JoinPoint joinPoint, Exception e){}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值