AOP

本文介绍了面向切面编程(AOP)的概念,旨在减少代码耦合性并扩展业务方法。AOP切面由切入点和通知组成,切入点包括Bean类路径、within表达式、execution表达式和@annotation注解。通知类型分为before、afterReturning、afterThrowing、after和around,其中around通知功能最强大。文章还通过案例和图解展示了AOP的整合及应用场景,如实现缓存处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.2.1 AOP说明

名称: 面向切面编程.
作用: 减少代码的耦合性,扩展业务方法.

1.2.2AOP公式
AOP切面 = 切入点(IF判断) + 通知(方法)

1.2.2.1切入点表达式

1.Bean 类的路径 类中的方法执行都会执行通知.
指定具体某一个bean
2.within(包名.类名)
within(com.jt.service.) service包中全部的类中的方法执行都会执行通知方法.
上述的2种通知粒度较粗.
3.execution(返回值类型 包名.类名.方法名(参数列表))
例子1:
execution(int com.jt.service…
.(…))
返回值类型int类型,com.jt.service下的所有包下的所有类
的所有方法,并且参数列表是任意的.
例子2:
execution(
com..service..*(int))
返回值为任意类型 com包下的一级包路径的service包的一级包的所有类型的所有方法的参数有一个参数并且类型为int
4.@annotation(包名.注解名)
@annotation(注解名)

1.2.3通知类型
1.before 前置通知 目标方法执行之前执行.
2.afterReturning 后置通知 目标方法执行之后执行.
3.afterThrowing 异常通知 目标方法执行之后出现异常时执行.
4.after 最终通知 不管什么时候最后都要执行的通知.
5.around 环绕通知 目标方法执行前后都要执行的通知.
说明:around功能最为强大,因为可以控制目标方法的执行

1.2.4AOP整合案例

//@Component		//将类交给spring容器管理
@Aspect
public class DemoAOP {

//切入点+通知   com.jt.service全部类全部方法 任意参数,任意返回值
@Pointcut("execution(* com.jt.service..*.*(..))")
public void pointCut() {
	System.out.println("太TM随意!!!");
}

/**
 * 四大通知: before/afterReturning/afterThrowing/after
 * 主要记录程序的执行状态 一般不加返回值. 都是void
 *	afterReturning除外.
 *  参数中只能添加 JoinPoint
 *
 *around通知最为强大 可以控制目标方法是否执行.
 *	参数中必须添加ProceedingJoinPoint
 */

/**
 * 获取目标对象的name
 */
@Before("pointCut()")
public void before(JoinPoint joinPoint) {
	String targetName = 
			joinPoint.getSignature().getDeclaringTypeName();
	System.out.println("我是一个before通知:"+targetName);
}

//目标方法执行之后
@AfterReturning(pointcut = "pointCut()",returning = "obj")
public Object afterReturn(Object obj) {
	System.out.println(obj);
	System.out.println("我是一个傻傻的后置通知!!!!");
	return obj;
}

//异常通知和后置通知是互斥的
@AfterThrowing(pointcut = "pointCut()",throwing = "thro")
public void afterThrow(Exception thro) {
	System.out.println("我是一个异常通知!!!!!");
	System.out.println(thro.getMessage());
}


@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint) {
	
	Object obj = null;
	try {
		Long startTime = System.currentTimeMillis();
		System.out.println("环绕通知执行前");
		obj = joinPoint.proceed();
		Long endTime = System.currentTimeMillis();
		System.out.println("方式执行时间:"+(endTime - startTime));
	} catch (Throwable e) {
		e.printStackTrace();
		
	}	
	return obj;
}
}

1.2.5AOP图解
1.前置通知
在这里插入图片描述
2.后置通知
在这里插入图片描述
3.环绕通知
在这里插入图片描述
1.3利用AOP实现缓存处理

1.3.1缓存实现准备

1.通知选择   环绕通知
2.切入点表达式  自定义缓存注解,实现该操作.
3.自定义注解CacheFind 

1.3.2编辑自定义注解

@Target(ElementType.METHOD) //该注解对方法有效
@Retention(RetentionPolicy.RUNTIME) //表示运行期有效
public @interface CacheFind {

//保存到redis中 key value  用户可以自己指定,也可以动态生成
public String key() default "";
public int seconds() default 0; //设定超时时间秒
}

1.3.3编辑AOP切面

@Component
@Aspect
public class CacheAOP {

@Autowired(required = false)//必须添加参数
private Jedis jedis;

/**
 * 思考: 是否需要获取注解中的参数.
 * 	需要: 切入点表达式中写注解名称
 * 	不需要: 如果只是做标识,使用包名.注解名
 * 
 * key的生成策略:
 * 	 包名.类名.方法名::第一个参数parentId
 * 
 *   缓存实现业务:
 *   1.从redis中查询数据 获取结果result
 *   2. result==null 用户第一次查询. 执行目标方法
 *   	将返回值结果转为JSON.之后保存到redis.
 *   	根据seconds判断是否超时 redis.set  redis.setex
 *   3. result !=null
 *   	直接将json数据转化为 返回值对象.
 * @param joinPoint
 * @return
 */		
@Around("@annotation(cacheFind)")
public Object around(ProceedingJoinPoint joinPoint,
		CacheFind cacheFind) {
	
	//动态获取key的值
	String key = getKey(joinPoint,cacheFind);
	
	//1.获取缓存数据
	String result = jedis.get(key);
	Object obj = null;
	try {
		if(StringUtils.isEmpty(result)) {
			System.out.println("AOP查询数据库操作!!!");
			//说明用户第一次查询. 执行目标方法
			obj = joinPoint.proceed();
			//2.数据保存到redis中
			String json = ObjectMapperUtil.toJSON(obj);
			if(cacheFind.seconds()>0)
				jedis.setex(key, cacheFind.seconds(), json);
			else 
				jedis.set(key, json);
			
		}else {
			//result为json数据,需要转化为对象
			//目标方法返回值类型 
			Class<?> returnType = getReturnType(joinPoint);
			obj = 
					ObjectMapperUtil.toObj(result, returnType);
			System.out.println("AOP查询缓存!!!!");
		}
	} catch (Throwable e) {
		e.printStackTrace();
		throw new RuntimeException(e);
	}
	
	
	return obj;
	
}


/**
 * 策略: 如果用户自己指定了key,
 * 	    则使用用户的,如果没有指定则动态生成.
 * @param joinPoint
 * @param cacheFind
 * @return
 */
private String getKey(ProceedingJoinPoint joinPoint, CacheFind cacheFind) {
	//1.获取用户自己的参数
	String key = cacheFind.key();
	
	//2.判断key是否有效
	if(!StringUtils.isEmpty(key)) {
		
		return key;
	}
	
	//3.如果用户没有传递,则动态生成  方法的API
	Signature signature = joinPoint.getSignature();
	String className = signature.getDeclaringTypeName();
	String methodName = signature.getName();
	Long arg0 = (Long) joinPoint.getArgs()[0];  //Long
	key = className+"."+methodName+"::"+arg0;
	return key;
}

private Class<?> getReturnType(ProceedingJoinPoint joinPoint) {
	
	MethodSignature signature = (MethodSignature) joinPoint.getSignature(); //方法签名对象
	return signature.getReturnType();
}


//记录一下程序的执行时间 环绕通知

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值