spring boot Aop面向切面

前置通知(Before):在目标方法被调用之前调用通知功能
后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么
返回通知(After-returning):在目标方法成功执行之后调用通知
异常通知(After-throwing):在目标方法抛出异常后调用通知
环绕通知(Around):通知包裹了被通知的方法,在被通知的方
法调用之前和之后执行自定义的行为

引入依赖

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
<dependency>
        <groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-aop</artifactId>
	<version>2.3.2.RELEASE</version>
</dependency>


配置1:
spring.aop.proxy-target-class: true#true表示系统底层会基于CGLIB方式为目标对象创建代理对象
代理对象为目标对象的子类对象,在控制层可以直接使用目标类来注入

配置2:
spring.aop.proxy-target-class: false#false表示系统底层会基于JDK方式为目标对象创建代理对象
代理对象为目标对象的兄弟对象,不能使用目标类注入,需要通过接口来注入

/**
 * @Aspect 注解描述的类为spring容器中的一个切面对象类型(此类型中封装切入点与通知方法) 1)切入点:(要执行拓展业务的方法的集合)
 *         2)通知方法:封装了在切入点方法上要执行的拓展业务方法.
 */
//@Order(1)
@Slf4j
@Aspect
@Component
public class SysLogAspect {
	// private static final Logger log=LoggerFactory.getLogger(SysLogAspect.class);
	/**
	 * @Pointcut 注解用于描述切入点(在哪些点上执行拓展业务)
	 *           bean(bean对象名字):为一种切入点表达式(这个表达式中定义了哪个或哪些bean对象的方法要进行功能扩展).
	 *           例如,bean(sysUserServiceImpl)表达式表示名字为sysUserServiceImpl的bean对象中所有方法的集合为切入点,
	 *           也就是说这个sysUserServiceImpl对象中的任意方法执行时都要进行功能扩展.
	 */
	@Pointcut("bean(sysUserServiceImpl)")
	// @Pointcut("@annotation(com.cy.pj.common.annotation.RequiredLog)")
	public void doLogPointCut() {
	}// 此方法内部不需要写具体实现(方法的方法名也是任意)

	/**
	 * Around注解描述的方法为一个通知方法(服务增益方法),此方法内部可以做服务增益(拓展业务),@Around注解
	 * 内部要指定切入点表达式,在此切入点表达式对应的切入点方法上做功能扩展.
	 * 
	 * @param jp 表示连接点,连接点是动态确定的,用于封装正在执行的切入点方法(目标方法)信息.
	 * @return 目标方法的执行结果
	 * @throws Throwable 通知方法执行过程中出现的异常
	 */
	@Around("doLogPointCut()")
	public Object doLogAround(ProceedingJoinPoint jp) throws Throwable {
		// 1.记录方法开始执行时间
		try {
			long t1 = System.currentTimeMillis();
			log.info("start:{}", t1);
			// 2.执行目标方法
			Object result = jp.proceed();// 最终(中间还可以调用本类其它通知或其它切面的通知)会调用目标方法
			// 3.记录方法结束执行时间
			long t2 = System.currentTimeMillis();
			log.info("after:{}", t2);
			// String targetClassMethod = getTargetClassMethod(jp);
			// log.info("{}目标方法执行耗时:{}", targetClassMethod, (t2 - t1));
			// 4.将正常行为日志信息写入到数据库
			// saveUserLog(jp, (t2 - t1));// new Thread(){}.start()
			// 5.返回目标方法执行结果
			return result;// 目标方法的返回结果
		} catch (Throwable e) {
			log.error("目标方法执行时出现了异常:{}", e.getMessage());
			throw e;
		}
	}

}
/**获取目标方法的全限定名(目标类全名+方法名)*/
	private String getTargetClassMethod(ProceedingJoinPoint jp) {
		// 1.获取目标对象类型
		Class<?> targetCls = jp.getTarget().getClass();
		// 2.获取目标对象类型的类全名
		String targetClsName = targetCls.getName();
		// 3.获取目标方法名
		// 3.1获取方法签名(方法签名对象中封装了方法相关信息)
		// Signature s=jp.getSignature();
		MethodSignature ms = (MethodSignature) jp.getSignature();
		// 3.2基于方法签名获取方法名
		// String methodName=s.getName();
		String methodName = ms.getName();
		// 4.构建方法的全限定名并返回
		return targetClsName + "." + methodName;
	}
posted @ 2020-08-31 20:42  岁月染过的梦  阅读(278)  评论(0)    收藏  举报