欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

AOP

程序员文章站 2022-07-12 14:29:50
...

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
aaa@qq.com(包名.注解名)
@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.前置通知
AOP
2.后置通知
AOP
3.环绕通知
AOP
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();
}


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

}