Spring 学习笔记 —— AOP

AOP 简介

AOP(Aspect-Oriented Programming, 面向切面编程): 是一种新的方法论, 是对传统 OOP(Object-Oriented Programming, 面向对象编程) 的补充.
AOP 的主要编程对象是切面(aspect), 而切面模块化横切关注点.
在应用 AOP 编程时, 仍然需要定义公共功能, 但可以明确的定义这个功能在哪里, 以什么方式应用, 并且不必修改受影响的类. 这样一来横切关注点就被模块化到特殊的对象(切面)里.
AOP 的好处:
每个事物逻辑位于一个位置, 代码不分散, 便于维护和升级
业务模块更简洁, 只包含核心业务代码.

AOP 术语 

切面(Aspect):  横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象
通知(Advice):  切面必须要完成的工作
目标(Target): 被通知的对象
代理(Proxy): 向目标对象应用通知之后创建的对象
连接点(Joinpoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位。例如 ArithmethicCalculator#add() 方法执行前的连接点,执行点为 ArithmethicCalculator#add(); 方位为该方法执行前的位置
切点(pointcut):每个类都拥有多个连接点:例如 ArithmethicCalculator 的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP 通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。

AspectJ

AspectJ:Java 社区里最完整最流行的 AOP 框架.
在 Spring2.0 以上版本中, 可以使用基于 AspectJ 注解或基于 XML 配置的 AOP

使用AOP(@AspectJ注解)

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId> <!-- 检查是否有spring-aop,有了就不用引了  -->
			<version>4.3.10.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aspects</artifactId>
			<version>4.3.10.RELEASE</version>
		</dependency>
		<!-- spring-aspects已经包含了 aspectjweaver
                <dependency>
		    <groupId>org.aspectj</groupId>
		    <artifactId>aspectjweaver</artifactId>
		    <version>1.8.14</version>
		    <scope>runtime</scope>
		</dependency>
                -->
<?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.0.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
		
	<context:component-scan base-package="cn.fg.aop" />
	
	<!-- 开启@Aspect注解,自动代理目标类 -->
	<aop:aspectj-autoproxy />
	
</beans>
package cn.fg.aop;

import java.util.Arrays;

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

@Order(1)   //切面优先级,存在多个切面时,值越小优先级越高,默认优先级是Integer.MAX_VALUE

//切面注解,开启<aop:aspectj-autoproxy>时有效,若只在父容器中开启切面,则切面只会在<context:component-scan>包下的bean被调用时进行切入,
//若在子容器中开启切面,由于子容器可以调用父容器的bean,所以在可在所有bean切入
@Aspect    

@Component  //必须向ioc容器中注入bean
public class LoggingAspect { //日志切面类
	
	/**
	 * execution表达式语法
	 * execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)throws-pattern?)
	 * modifier-pattern:修饰符匹配,如public
	 * ret-type-pattern:返回值匹配,如int或全路径类名,可以为*表示任何返回值
	 * declaring-type-pattern:类路径匹配(需要和name-pattern配合使用)
	 * 	1. cn.fg.aop.ArithmeticCalculator.<name-pattern>  精准的类路径匹配
	 *  2. cn.fg.aop.*.<name-pattern> cn.fg.aop包名下的任意类匹配
	 *  3. cn.fg..ArithmeticCalculator.<name-pattern> cn.fg及子包下的ArithmeticCalculator匹配
	 *  4. cn.fg..*.<name-pattern> cn.fg及子包下的任意类匹配
	 * name-pattern:方法名匹配,如add;可以为 *表示所有方法或者set*代表以set开头的所有方法
	 * param-pattern:参数类型匹配,如int,int 多个参数逗号分隔;可以为*表示任意参数类型,如*,int; ..表示零个或多个任意参数
	 * throws-pattern:异常类型匹配
	 * 表达式中的?号表示是可选的,所以最短的表达式为execution(* *(..))
	 */

	//前置通知,在目标方法之前执行
	@Before("execution(* cn.fg..*.*(..))")
	public void beforeMethod(JoinPoint joinPoint){
		//在这里写切入的逻辑
		String methodName = joinPoint.getSignature().getName();  //目标的方法名
		Object [] args = joinPoint.getArgs();  //目标方法的参数
		Object targetObject = joinPoint.getTarget();  //目标对象
		Object proxyObject = joinPoint.getThis(); //AOP框架为目标对象生成的代理对象
		System.out.println("methodName=" + methodName);
		System.out.println("args=" + Arrays.asList(args));
		System.out.println("targetObject=" + targetObject);
		System.out.println("proxyObject=" + proxyObject);
	}
	
	//定义公共execution表达式
	@Pointcut("execution(* cn.fg..*.*(..))")
	public void declareJointPointExpression(){}
	
	//后置通知,在目标方法执行后执行(即使目标方法抛出异常也会执行)
	//在后置通知中不能拿到目标方法返回的结果
	@After("declareJointPointExpression()") //使用公共execution
	//@After("cn.fg.aop.VlidationAspect.declareJointPointExpression()") 引用其他切面的表达式
	public void afterMethod(JoinPoint joinPoint){
		System.out.println("@After.........................");
	}
	
	/**
	 * 在方法法正常结束受执行的代码
	 * 返回通知是可以访问到方法的返回值的!
	 * returning:设置接收返回值的参数名,名字随便取要与接收的参数名对应=Object result
	 */
	@AfterReturning(value="execution(* cn.fg..*.*(..))",returning="result")
	public void afterReturning(JoinPoint joinPoint, Object result){
		System.out.println("返回值="+result);
		System.out.println("@AfterReturning.......................");
	}
	
	/**
	 * 在目标方法出现异常时会执行的代码.
	 * 可以访问到异常对象; 且可以指定在出现特定异常时在执行通知代码
	 * throwing:设置接收异常的参数名称,名字随便取要与接收的参数名对应=Exception e
	 */
	@AfterThrowing(value = "execution(* cn.fg..*.*(..))", throwing = "e")
	public void afterThrowing(JoinPoint joinPoint, Exception e) {
		String methodName = joinPoint.getSignature().getName();
		System.out.println("The method " + methodName + " occurs excetion:" + e);
	}
	
	/*
	 * aop通知结构
	 *  try {
	 *		//前置通知
	 *		int result = 0;
	 *		//返回通知
	 *	} catch (Exception e) {
	 *		//异常通知
	 *	}
	 *	//后置通知
	 */
	
	
	/**
	 * 环绕通知需要携带 ProceedingJoinPoint 类型的参数. 
	 * 环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法.
	 * 且环绕通知必须有返回值, 返回值即为目标方法的返回值
	 */
	@Around("execution(* cn.fg..*.*(..))")
	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;  //可以修改返回值
	}


    //切入点表达式可以通过操作符 && || !结合起来
    //@Pointcut("execution(* Spring4_AOP.aopAnnotation.*.add(..)) || execution(* Spring4_AOP.aopAnnotation.*.sub(..))")
	
}

使用AOP(xml)

仅了解,可以这样配,省略。。。。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值