AOP部分知识点

1、概念总述

AOP的主要编程对象是切面(aspect),而切面横切关注点。

概念说明
切面(Aspect)一个横切关注点的模块化,这个关注点可能会横切多个对象。它是横切关注点的另一种表达方式。
连接点(Joinpoint)在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。在Spring AOP中,一个连接点表示一个方法的执行,即所有方法都可以嵌入横切逻辑。
切入点(Pointcut)匹配连接点的断言。它通常是一个表达式,有专门的语法,用于指明在哪里(或者说在哪些方法调用上)嵌入横切逻辑
通知(Advice)在切面的某个特定的连接点上执行的动作,也就是我们前面提到的横切逻辑,如日志处理逻辑,事务处理逻辑。切入点致命在哪里嵌入横切逻辑,通知指明切入什么,即干什么。
目标对象(Target Object)被一个或者多个切面所通知的对象,也被称作被通知对象
代理对象(Proxy Object)AOP框架创建的对象,它和目标对象遵循同样的接口,使用它的方式和使用目标对象的方式是一样的,但是它是目标对象的增强版,“通知”中的代码执行将会被代理对象的方法调用触发。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。

2、XML配置实现AOP

  • 通过XML配置AOP是Spring框架专有的,但由于AspectJ得到越来越多的AOP框架支持,所以通过注解实现AOP将会有更多重用的机会。

CalculatorAspect类:

package com.jd.calculator;

import org.aspectj.lang.JoinPoint;

public class CalculatorAspect {

	//前置增强
	public void before(JoinPoint joinPoint) {
		Object target=joinPoint.getTarget();
		String methodName = joinPoint.getSignature().getName();
		Object[] params = joinPoint.getArgs();
		
		System.out.println(target.getClass().getName()+":The "+methodName+" method begins.");
		System.out.println(target.getClass().getName()+":Parameters of the "+methodName+" method: ["+params[0]+","+params[1]+"]");
	}
	
	//返回增强
	public void afterReturning(JoinPoint joinPoint,Object result) {
		Object target=joinPoint.getTarget();
		String methodName = joinPoint.getSignature().getName();
		System.out.println(target.getClass().getName()+":Result of the "+methodName+" method:"+result);
	}
	
	//异常增强
	public void afterThrowing(JoinPoint joinPoint,Exception e) {
		Object target=joinPoint.getTarget();
		String methodName = joinPoint.getSignature().getName();
		System.out.println(target.getClass().getName()+":Exception of the method "+methodName+": "+e);
	}
	
	//后置增强
	public void after(JoinPoint joinPoint) {
		Object target=joinPoint.getTarget();
		String methodName = joinPoint.getSignature().getName();
		System.out.println(target.getClass().getName()+":The "+methodName+" method ends.");
	}
}

application.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:context="http://www.springframework.org/schema/context"
	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/context 
		http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop 
		http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

	<bean id="calculatorAspect" class="com.jd.calculator.CalculatorAspect"></bean>
	
	<!-- 配置AOP -->
	<aop:config proxy-target-class="true">
		<!-- 配置切点表达式 -->
		<aop:pointcut expression="execution(public int com.jd.calculator.CalculatorService.*(..))" id="pointcut"/>
		<!-- 配置切面及增强类型:可以有多个切面,每个切面又可以配置多个增强类型-->
		<aop:aspect ref="calculatorAspect">
			<aop:before method="before" pointcut-ref="pointcut"/>
			<aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/>
			<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e"/>
			<aop:after method="after" pointcut-ref="pointcut"/>
		</aop:aspect>
	</aop:config>
</beans>

分析:
(1)通过在xml文件中进行配置也可以实现AOP的配置,实现与AspectJ支持的AOP同样的效果。(AspectJ支持的AOP详情请看https://blog.csdn.net/qq_42865575/article/details/96145531)
(2)<aop:config proxy-target-class="true">
该配置作用:如果创建目标对象的目标类中的方法与AspectJ切面中切入点表达式匹配,则自动为该目标对象生成动态代理对象,该代理对象默认使用JDK动态代理,当配置为<aop:aspectj-autoproxy poxy-target-class="true"/>时,则使用CGLib动态代理。


3、Spring AOP支持的通配符

1、* :匹配任何数量字符,用于参数列表表示参数可以是任意数据类型,但是必须有参数,

例子:
java.*.Date——>匹配java包的下一级子包中的任何Date类型;如匹配java.util.Date、java.sql.Date,但不匹配java.util.sql.Date;
java.lang.*e——>匹配任何java.lang包下的以e结尾的类型,如匹配java.util.Hashtable、java.util.Date等等;

2、. .:方法中表示任意数量参数,在包中表示当前包及其子包,

例子:
java..*——>匹配java包及其任何子包下的任何类型,如匹配java.lang.String、java.lang.annotation.Annotation等等;

3、+:匹配指定类型的子类型(不是子类);仅能作为后缀放在类型模式后边,

例子:
java.lang.Number+——>匹配java.lang包下任何Number的子类型,如匹配java.lang.Integer、java.math.BigInteger等等;
java.util.List+——>匹配java.util.List接口实现类,如匹配java.util.ArrayList,但不匹配java.util.HashMap

4、切入点表达式

  • 用于指定执行哪些类中的哪些方法时触发增强方法, Spring AOP支持execution、within、this、target、args、@within、@target、@args和@annotation等AspectJ切入点表达式,其中execution最常使用。

execution
语法:execution([修饰符] 返回值类型 [包名.类名/接口名.]方法名([参数])[异常]),
说明:
a、该表达式用于指定匹配的方法;
b、修饰符包括访问权限和static、final以及synchronized;
c、中括号框起的部分可以省略。

举例:
execution(* *..*.*(..)):匹配所有的方法;
execution(public * *(..)):匹配任意公共方法;
execution(* *(..) throws IllegalArgumentException, ArrayIndexOutOfBoundsException):匹配抛出IllegalArgumentException和ArrayIndexOutOfBoundsException异常任何方法;
execution(* set*(..)):匹配任何一个名字以 set 开始的方法;
execution(* com.jd.calculator.ICalculatorService.* (*)):匹配com.jd.calculator.ICalculatorService接口定义的只有一个参数的任意方法;
execution(* com.jd.calculator.ICalculatorService.* (*)):匹配com.jd.calculator.ICalculatorService接口定义的只有一个参数的任意方法;
execution(* com.jd.calculator.ICalculatorService.* (..)):匹配com.jd.calculator.ICalculatorService接口定义的任意方法;
execution(* com.jd.calculator.ICalculatorService+.* ()):匹配com.jd.calculator.ICalculatorService接口及子类型的的任何无参方法;
execution(* com.jd.calculator.Calculator*.m*(com.jd.vo.Data)):匹配以com.jd.calculator.Calculator开头的类或接口中以m开头且只有一个com.jd.vo.Data类型参数的方法

注意:
该匹配根据方法签名的参数类型进行匹配,而不是根据执行时传入的实参类型决定的,即方法中参数数据类型一定要和切入点表达式中参数数据类型完全一致,如切入点表达式为execution(* com.jd.calculator.Calculator*.m*(com.jd.vo.Data)) ,定义的方法为public int mul(Object object){} ,执行该方法时传入com.jd.vo.Data类型的参数,切入点表达式依然不匹配;args则可以;

@annotation
语法:@annotation(注解全名),说明:该表达式用于匹配任意指定注解修饰的方法;

例子:
@annotation(com.jd.annotation.Secure):匹配com.jd.annotation.Secure修饰的任意方法;
@annotation(org.springframework.transaction.annotation.Transactional):匹配org.springframework.transaction.annotation.Transactional修饰的任意方法;

5、​​​​​​​@Pointcut注解

  • 通过观察发现CalculatorAspect类中@Before,@After,@AfterReturning、@AfterThrowing和@Around注解中切入点表达式相同,为了简化代码,可以单独自定义一个@Pointcut注解修饰的空方法,通过该方法可以简化@Before,@After,@AfterReturning、@AfterThrowing和@Around注解中的切入点表达式。
package com.jd.calculator;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class CalculatorAspect {
	
	@Pointcut("execution(public int com.jd.calculator.CalculatorService.*(..))")
	public void pointCut() {
		
	}

	//前置增强
	@Before("pointCut()")
	public void before(JoinPoint joinPoint) {
		Object target=joinPoint.getTarget();
		String methodName = joinPoint.getSignature().getName();
		Object[] params = joinPoint.getArgs();
		
		System.out.println(target.getClass().getName()+":The "+methodName+" method begins.");
		System.out.println(target.getClass().getName()+":Parameters of the "+methodName+" method: ["+params[0]+","+params[1]+"]");
	}
	
	//后置增强
	@After("pointCut()")
	public void after(JoinPoint joinPoint) {
		Object target=joinPoint.getTarget();
		String methodName = joinPoint.getSignature().getName();
		System.out.println(target.getClass().getName()+":The "+methodName+" method ends.");
	}
	
	//返回增强
	@AfterReturning(pointcut="pointCut()", returning="result")
	public void afterReturning(JoinPoint joinPoint,Object result) {
		Object target=joinPoint.getTarget();
		String methodName = joinPoint.getSignature().getName();
		System.out.println(target.getClass().getName()+":Result of the "+methodName+" method:"+result);
	}
	
	//异常增强
	@AfterThrowing(pointcut="pointCut()", throwing="e")
	public void afterThrowing(JoinPoint joinPoint,Exception e) {
		Object target=joinPoint.getTarget();
		String methodName = joinPoint.getSignature().getName();
		System.out.println(target.getClass().getName()+":Exception of the method "+methodName+": "+e);
	}

	//环绕增强
	/*@Around("pointCut()")
	public Object around(ProceedingJoinPoint joinPoint) {
		Object result = null;
		Object target=joinPoint.getTarget();//目标对象
		String methodName = joinPoint.getSignature().getName();
		Object[] params = joinPoint.getArgs();

		try {
			try {
				//前置增强
				System.out.println(target.getClass().getName()+":The "+methodName+" method begins.");
				System.out.println(target.getClass().getName()+":Parameters of the "+methodName+" method: ["+params[0]+","+params[1]+"]");
				//执行目标对象内的方法
				result = joinPoint.proceed();
			}finally {
				//后置增强
				System.out.println(target.getClass().getName()+":The "+methodName+" method ends.");
			}
			//返回增强
			System.out.println(target.getClass().getName()+":Result of the "+methodName+" method:"+result);
		} catch (Throwable e) {
			//异常增强
			System.out.println(target.getClass().getName()+":Exception of the method "+methodName+": "+e);
		}
		
		return result;
	}*/
}

6、切面优先级

  • 如果一个方法匹配多个切面中相同类型增强方法,那么必须明确指定各个切面优先级,否则切面优先级不确定,切面优先级的确定既可以通过在切面类添加@Order注解或实现Ordered接口实现,也可以在XML配置aop:aspect标签的order属性来实现。
  • (1)@Order注解指定切面优先级

MethodAspect切面类

package com.jd.calculator;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Aspect
@Order(1)//值越小,优先级越高,越先执行
@Component
public class MethodAspect {

	@Before("execution(public int com.jd.calculator.CalculatorService.*(..))")
	public void before(JoinPoint joinPoint) {
		Object target=joinPoint.getTarget();
		String methodName = joinPoint.getSignature().getName();
		
		System.out.println(target.getClass().getName()+":The "+methodName+" method begins.");
	}
}

ArgumentsAspect切面类

package com.jd.calculator;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Aspect
@Order(2)//值越小,优先级越高,越先执行
@Component
public class ArgumentsAspect {

	@Before("execution(public int com.jd.calculator.CalculatorService.*(..))")
	public void before(JoinPoint joinPoint) {
		Object target=joinPoint.getTarget();
		String methodName = joinPoint.getSignature().getName();
		Object[] params = joinPoint.getArgs();
		System.out.println(target.getClass().getName()+":Parameters of the "+methodName+" method: ["+params[0]+","+params[1]+"]");
	}
}

说明:
(1)默认优先级是按照切面类类名的第一个首字母为准,按照字母顺序排列优先级。
(2)com.jd.calculator.CalculatorService类中每个public访问权限的方法都匹配MethodAspect切面类与ArgumentsAspect切面类内@Before注解修饰的方法,但由于MethodAspect切面优先级比ArgumentsAspect切面优先级高,所以执行com.jd.calculator.CalculatorService类mul方法时一定是MethodAspect切面类中前置增强先执行,而后ArgumentsAspect切面类中前置增强后执行。

  • (2)XML内指定切面优先级

MethodAspect类

package com.jd.calculator;

import org.aspectj.lang.JoinPoint;

public class MethodAspect {

	public void before(JoinPoint joinPoint) {
		Object target=joinPoint.getTarget();
		String methodName = joinPoint.getSignature().getName();
		
		System.out.println(target.getClass().getName()+":The "+methodName+" method begins.");
	}
}

ArgumentsAspect类

package com.jd.calculator;

import org.aspectj.lang.JoinPoint;

public class ArgumentsAspect {

	public void before(JoinPoint joinPoint) {
		Object target=joinPoint.getTarget();
		String methodName = joinPoint.getSignature().getName();
		Object[] params = joinPoint.getArgs();
		System.out.println(target.getClass().getName()+":Parameters of the "+methodName+" method: ["+params[0]+","+params[1]+"]");
	}
}

application.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:context="http://www.springframework.org/schema/context"
	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/context 
		http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop 
		http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

	<bean class="com.jd.calculator.CalculatorService"></bean>
	
	<bean id="methodAspect" class="com.jd.calculator.MethodAspect"></bean>
	<bean id="argumentsAspect" class="com.jd.calculator.ArgumentsAspect"></bean>
	
	<!-- 配置AOP -->
	<aop:config proxy-target-class="true">
		<!-- 配置切点表达式 -->
		<aop:pointcut expression="execution(public int com.jd.calculator.CalculatorService.*(..))" id="pointcut"/>
		<!-- 配置切面及增强类型 -->
		<aop:aspect ref="argumentsAspect" order="2">
			<aop:before method="before" pointcut-ref="pointcut"/>
		</aop:aspect>
		
		<aop:aspect ref="methodAspect" order="1">
			<aop:before method="before" pointcut-ref="pointcut"/>
		</aop:aspect>
	</aop:config>
</beans>

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值