您当前的位置: 首页 >  spring

止步前行

暂无认证

  • 4浏览

    0关注

    247博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Spring中AOP详解和总结

止步前行 发布时间:2018-06-21 18:56:46 ,浏览量:4

一、前言

在上一篇中,介绍了使用代理来实现日志的记录,该方法在平时工作中不易于使用,因为要有一定的设计模式的基础。下面就来介绍下Spring的一个非常核心的概念AOP,即面向切面编程。

二、AOP术语说明

为了理解AOP,必须先了解AOP的相关术语:

1、通知(Advice):

在AOP中,切面的工作被称为通知。通知定义了切面“是什么”以及“何时”使用。除了描述切面要完成的工作,通知还解决了何时执行这个工作的问题。 Spring切面可以应用5种类型的通知:

(1)前置通知(Before):前置通知, 在方法执行之前执行;
(2)后置通知(After):后置通知, 在方法执行之后执行 ;
(3)返回通知(After-returning):返回通知, 在方法返回结果之后执行;
(4)异常通知(After-throwing):异常通知, 在方法抛出异常之后;
(5)环绕通知(Around):环绕通知, 围绕着方法执行;
2、连接点(Join point):

连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加行为。

3、切点(Pointcut):

如果说通知定义了切面“是什么”和“何时”的话,那么切点就定义了“何处”。比如我想把日志引入到某个具体的方法中,这个方法就是所谓的切点。

4、切面(Aspect):

切面是通知和切点的结合。通知和切点共同定义了切面的全部内容———它是什么,在何时和何处完成其功能。

三、使用基于注解的方式实现AOP 1、需要引入相关的jar,使用maven,此处略;
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、需要在Spring的配置文件中,加入如下配置:





组件扫描(component scanning):Spring 能够从classpath下自动扫描, 侦测和实例化具有特定注解的组件。

特定组件包括:

@Component: 基本注解, 标识了一个受Spring管理的组件
@Respository: 标识持久层组件
@Service: 标识服务层(业务层)组件
@Controller: 标识表现层组件

对于扫描到的组件, Spring有默认的命名策略: 使用非限定类名, 第一个字母小写。 也可以在注解中通过value属性值标识组件的名称

3、编写业务方法接口
public interface ArithmeticCalculator {
	
	int add(int i,int j);
	int sub(int i,int j);
	int mul(int i, int j);
	int div(int i, int j);
}
4、实现业务方法 (注意此处Component注解)
@Component
public class ArithmeticCalculatorImpl implements ArithmeticCalculator {

	@Override
	public int add(int i, int j) {
		int result = i + j;
		return result;
	}

	@Override
	public int sub(int i, int j) {
		int result = i - j;
		return result;
	}

	@Override
	public int mul(int i, int j) {
		int result = i * j;
		return result;
	}

	@Override
	public int div(int i, int j) {
		int result = i / j;
		return result;
	}
}
5、 编写切面类:(是一般的Java类,在其中添加要额外实现的功能,注意注解)
@Order(2)
@Aspect
@Component
public class LoggingAspect {

	/**
	 * 定义一个方法,用于声明切入点表达式,一般的,该方法中不再需要其他的代码 使用@Pointcut来声明切入点表达式
	 * 后面的其他通知直接使用方法名来引用当前的切入点表达式。
	 */
	@Pointcut("execution(public * com.scorpios.spring.aop.impl.ArithmeticCalculator.*(..))")
	public void declareJoinPointExpression() {
	}

	// 声明该方法是一个前置通知:在目标方法开始之前执行
	@Before("declareJoinPointExpression()")
	public void BeforeMethod(JoinPoint joinPoint) {
		String methodName = joinPoint.getSignature().getName();
		List args = Arrays.asList(joinPoint.getArgs());
		System.out.println("[BeforeMethod] the method " + methodName + " begins with " + args);
	}

	// 后置通知:在目标方法执行后(无论是否发生异常),执行的通知
	// 在后置通知中还不能访问目标方法执行的结果
	@After("declareJoinPointExpression()")
	public void afterMethod(JoinPoint joinPoint) {
		String methodName = joinPoint.getSignature().getName();
		System.out.println("[afterMethod] the method " + methodName + " ends.");
	}

	// 在方法正常结束时执行的代码
	// 返回通知时可以访问到方法的返回值的!
	@AfterReturning(value = "declareJoinPointExpression()", returning = "result")
	public void afterReturing(JoinPoint joinPoint, Object result) {
		String methodName = joinPoint.getSignature().getName();
		System.out.println("[afterReturing] the method " + methodName + " ends with :" + result);
	}

	// 在目标方法出现异常时会执行的代码
	// 可以访问到异常对象;且可以指定在出现特定异常时在执行通知代码
	@AfterThrowing(value = "declareJoinPointExpression()", throwing = "ex")
	public void afterThrowing(JoinPoint joinPoint, Exception ex) {
		String methodName = joinPoint.getSignature().getName();
		System.out.println("[afterThrowing] the method " + methodName + " occurs with :" + ex);
	}

	// 环绕通知需要携带ProceedingJoinPoint类型的参数
	// 环绕通知类似于动态代理的全过程:ProceedingJoinPoint类型的参数可以决定是否执行目标方法
	// 且环绕通知必须有返回值,返回值即为目标方法的返回值
	@Around("declareJoinPointExpression()")
	public Object aroundMethod(ProceedingJoinPoint pjp) {

		Object result = null;
		String methodName = pjp.getSignature().getName();

		try {
			// 前置通知
			System.out.println(
					"[aroundMethod before] the method " + methodName + " begins with " + Arrays.asList(pjp.getArgs()));
			result = pjp.proceed();
			// 返回通知
			System.out.println("[aroundMethod returning] the method ends with " + result);
		} catch (Throwable e) {
			// 异常通知
			System.out.println("[aroundMethod exception] the method " + methodName + "occurs exception:" + e);
		}

		// 后置通知
		System.out.println("[aroundMethod after] the method " + methodName + " ends");
		return result;
	}


}
6、测试代码
public class Main {

	public static void main(String[] args) {
		
		ApplicationContext apx = new ClassPathXmlApplicationContext("applicationContext.xml");
		
		ArithmeticCalculator arithmeticCalculator = apx.getBean(ArithmeticCalculator.class);
		
		int result = arithmeticCalculator.add(2, 8);
		System.out.println("-->" + result);
	}
}
7、运行结果

这里写图片描述

8、具有优先级的切面类(注意切入点表达式,引用LoggingAspect中定义的表达式)
@Order(1)		//可以使用@Order注解指定切面的优先级,值越小,优先级越高
@Aspect
@Component
public class ValidateAspect {

	// 声明该方法是一个前置通知:在目标方法开始之前执行
	@Before("LoggingAspect.declareJoinPointExpression()")
	public void ValidateBefore(JoinPoint joinPoint) {
		String methodName = joinPoint.getSignature().getName();
		List args = Arrays.asList(joinPoint.getArgs());
		System.out.println("**** validate  " + args);
	}

}
9、再次执行测试代码,结果如下

这里写图片描述

四、总结

1、引入jar包;

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

3、在配置文件中配置自动扫描的包:


4、加入使 AspjectJ 注解起作用的配置:

为匹配的类自动生成动态代理对象.

5、编写切面类:

6、配置切面

a.切面必须是IOC中的bean: 实际添加了@Component注解 
b.声明是一个切面: 通过添加@Aspect注解声明一个bean是一个切面!
c.声明通知: 即额外加入功能对应的方法. 
	前置通知: @Before("execution(public int com.scorpios.spring.aop.ArithmeticCalculator.*(int, int))")

@Before 表示在目标方法执行之前执行 @Before 标记的方法的方法体.
@Before 里面的是切入点表达式:

7、在通知中访问连接细节: 可以在通知方法中添加 JoinPoint 类型的参数, 从中可以访问到方法的签名和方法的参数.

五、补充:

关于切入点表达式的解释: 这里写图片描述 如图所示,我们使用execution()指示器选择UserServiceImpl的sayHello方法。方法表达式以“*”号开始,表明了我们不关心方法返回值的类型。然后,我们指定了全限定类名和方法名。对于方法参数列表,我们使用两个点号(…)表明切点要选择任意的sayHello()方法,无论该方法的入参是什么。

关注
打赏
1657848381
查看更多评论
立即登录/注册

微信扫码登录

0.0921s