CGB2005 0821 JAVA17

AOP复习
在这里插入图片描述
在这里插入图片描述
实现原理
CGLIB继承 父子关系实现is a
JDK耦合 组合 兄弟关系实现 has a
在这里插入图片描述
开始AOP拓展
在这里插入图片描述

将日志文件信息写入到数据库

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
有目标 如下图
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
和数据库打交道的还是数据层
得把数据层写一些
dao层
在这里插入图片描述

mapper
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
回到切面
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
先写无参 后面需要什么再定义什么
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

回到切面上面
doAround
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
上面log日志的才是aop
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
自己想到的问题
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

不涉及到aop 所以自动依赖注入和手动 都不一样的
在这里插入图片描述
自己去复现
1.模仿上面的概念点

@Autowired
	private SysLogService sysLogService;
	/**记录用户行为的正常信息*/
	private void saveUserLog(ProceedingJoinPoint jp,long time) {
		//1.获取用户行为日志
		String ip=IPUtils.getIpAddr();
		String username="cgb2005";//将来是登录用户的用户名
		Date createdTime=new Date();
		//获取操作名
		String operation="operation";
		String method=getTargetClassMethod(jp);
		String params=Arrays.toString(jp.getArgs());
		//2.封装用户行为日志(在内容中通过对象去实现)
		SysLog log=new SysLog();
		log.setIp(ip);
		log.setUsername(username);
		log.setCreatedTime(createdTime);
		log.setOperation(operation);
		log.setMethod(method);
    	log.setParams(params);
		log.setTime(time);
		//3.将用户行为日志持久化(将内存中对象信息写入到数据库)
		sysLogService.saveObject(log);
	}
	
	/**获取目标方法上@ReqiredLog注解中定义的operation名字*/
	private String getOperation(ProceedingJoinPoint jp) {
		//1.获取目标对象类型
		//2.获取目标方法对象
		//3.获取方法上的reqiredLog注解对象
		//4.获取注解中的operation的值.
		return null;
	}

在这里插入图片描述
2.dao层
在这里插入图片描述
在这里插入图片描述
3.service层
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如下图 去补充注解
在这里插入图片描述
ip
在这里插入图片描述
5.controller
不需要
6.html 客户端
在这里插入图片描述
获取注解对象方法
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
如下图 拿到更多
在这里插入图片描述
如下图 只取到了接口的注解方法 需要的是接口的实现类
在这里插入图片描述
改了配置文件之后 true之后
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
想一个稳定的实现
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
为了实现
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
改成细密度
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
然后想要一个json格式的字符串
在这里插入图片描述
json读出来好处理
都得加上注解
声明式侵入式
在这里插入图片描述
在这里插入图片描述
key:value 格式

自己实现
在这里插入图片描述
在这里插入图片描述
实现类
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
日志管理界面记录日志信息 并且以json串格式呈现
在这里插入图片描述
查询都是目标方法
在这里插入图片描述
操作没显示注释的名称
应该是注解方面的问题

@Autowired
	private SysLogService sysLogService;
	/**记录用户行为的正常信息*/
	private void saveUserLog(ProceedingJoinPoint jp,long time)throws Exception {
		//1.获取用户行为日志
		String ip=IPUtils.getIpAddr();
		String username="cgb2005";//将来是登录用户的用户名
		Date createdTime=new Date();
		//获取操作名
		String operation=getOperation(jp);
		String method=getTargetClassMethod(jp);
		//String params=Arrays.toString(jp.getArgs());//普通格式字符串
		String params=new ObjectMapper().writeValueAsString(jp.getArgs());//json格式的参数
		//2.封装用户行为日志(在内容中通过对象去实现)
		SysLog log=new SysLog();
		log.setIp(ip);
		log.setUsername(username);
		log.setCreatedTime(createdTime);
		log.setOperation(operation);
		log.setMethod(method);
    	log.setParams(params);
		log.setTime(time);
		//3.将用户行为日志持久化(将内存中对象信息写入到数据库)
		sysLogService.saveObject(log);
	}
	/**获取目标方法上@ReqiredLog注解中定义的operation名字*/
	private String getOperation(ProceedingJoinPoint jp)throws Exception {
		//1.获取目标对象类型
		Class<?> targetCls=jp.getTarget().getClass();
		//2.获取目标方法对象
		//2.1获取方法签名信息
		MethodSignature ms=(MethodSignature)jp.getSignature();
		//2.2获取方法对象
		//假如底层配置为jdk代理,则method指向接口中的抽象方法对象.
		//假如底层配置为CGLIB代理,则这个method指向具体目标对象中的方法对象
	    //Method method=ms.getMethod();
	    //假如希望无论是jdk代理还是cglib代理,我们让method变量指向的都是目标对象中的方法对象,那如何实现?
		Method method=targetCls.getDeclaredMethod(ms.getName(), ms.getParameterTypes());
		//3.获取方法上的reqiredLog注解对象
		RequiredLog requiredLog=method.getAnnotation(RequiredLog.class);
		//4.获取注解中的operation的值.
		if(requiredLog==null)return "operation";
		return requiredLog.operation();
	}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
晚上回看``

//@Pointcut("bean(sysUserServiceImpl)")
	@Pointcut("@annotation(com.cy.pj.common.annotation.RequiredLog)")

不懂怎么区分
晚上分析错误
错误代码

package com.cy.pj.common.aspect;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import com.cy.pj.common.IPUtils;
import com.cy.pj.common.annotation.RequiredLog;
import com.cy.pj.sys.pojo.SysLog;
import com.cy.pj.sys.service.SysLogService;

import lombok.extern.slf4j.Slf4j;
/**
 * @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));
			//5.返回目标方法执行结果
			return result;//目标方法的返回结果
		}catch(Throwable e) {
			log.error("目标方法执行时出现了异常:{}",e.getMessage());
			throw e;
		}
	}
	@Autowired
	private SysLogService sysLogService;
	/**记录用户行为的正常信息*/
	private void saveUserLog(ProceedingJoinPoint jp,long time) {
		//1.获取用户行为日志
		String ip=IPUtils.getIpAddr();
		String username="cgb2005";//将来是登录用户的用户名
		Date createdTime=new Date();
		//获取操作名
		String operation="operation";
		String method=getTargetClassMethod(jp);
		String params=Arrays.toString(jp.getArgs());
		//2.封装用户行为日志(在内容中通过对象去实现)
		SysLog log=new SysLog();
		log.setIp(ip);
		log.setUsername(username);
		log.setCreatedTime(createdTime);
		log.setOperation(operation);
		log.setMethod(method);
    	log.setParams(params);
		log.setTime(time);
		//3.将用户行为日志持久化(将内存中对象信息写入到数据库)
		sysLogService.saveObject(log);
	}
	
	/**获取目标方法上@ReqiredLog注解中定义的operation名字*/
	private String getOperation(ProceedingJoinPoint jp)throws Exception {
		//1.获取目标对象类型
		Class<?> targetCls=jp.getTarget().getClass();
		//2.获取目标方法对象
		//2.1获取方法签名信息
		MethodSignature ms=(MethodSignature)jp.getSignature();
		//2.2获取方法对象
		//假如底层配置为jdk代理,则method指向接口中的抽象方法对象.
		//假如底层配置为CGLIB代理,则这个method指向具体目标对象中的方法对象
	    //Method method=ms.getMethod();
	    //假如希望无论是jdk代理还是cglib代理,我们让method变量指向的都是目标对象中的方法对象,那如何实现?
		Method method=targetCls.getDeclaredMethod(ms.getName(), ms.getParameterTypes());
		//3.获取方法上的reqiredLog注解对象
		RequiredLog requiredLog=method.getAnnotation(RequiredLog.class);
		//4.获取注解中的operation的值.
		if(requiredLog==null)return "operation";
		return requiredLog.operation();
	}
	/**获取目标方法的全限定名(目标类全名+方法名)*/
	private String getTargetClassMethod(ProceedingJoinPoint jp) {
		//1.获取目标对象类型
		Class<?> targetCls=jp.getTarget().getClass();
		//2.获取目标对象类型的类全名
		String targetClsName=targetCls.getName();
		//3.获取目标方法名
		//3.1获取方法签名(方法签名对象中封装了方法相关信息)
		MethodSignature ms=(MethodSignature)jp.getSignature();
		//3.2基于方法签名获取方法名
		String methodName=ms.getName();
		//4.构建方法的全限定名并返回
		return targetClsName+"."+methodName;
	}
	
	
}








正确代码

package com.cy.pj.common.aspect;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import com.cy.pj.common.IPUtils;
import com.cy.pj.common.annotation.RequiredLog;

import com.cy.pj.sys.pojo.SysLog;
import com.cy.pj.sys.service.SysLogService;
import com.fasterxml.jackson.databind.ObjectMapper;

import lombok.extern.slf4j.Slf4j;
/**
 * @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));
			//5.返回目标方法执行结果
			return result;//目标方法的返回结果
		}catch(Throwable e) {
			log.error("目标方法执行时出现了异常:{}",e.getMessage());
			throw e;
		}
	}
	@Autowired
	private SysLogService sysLogService;
	/**记录用户行为的正常信息*/
	private void saveUserLog(ProceedingJoinPoint jp,long time)throws Exception {
		//1.获取用户行为日志
		String ip=IPUtils.getIpAddr();
		String username="cgb2005";//将来是登录用户的用户名
		Date createdTime=new Date();
		//获取操作名
		String operation=getOperation(jp);
		String method=getTargetClassMethod(jp);
		//String params=Arrays.toString(jp.getArgs());//普通格式字符串
		String params=new ObjectMapper().writeValueAsString(jp.getArgs());//json格式的参数
		//2.封装用户行为日志(在内容中通过对象去实现)
		SysLog log=new SysLog();
		log.setIp(ip);
		log.setUsername(username);
		log.setCreatedTime(createdTime);
		log.setOperation(operation);
		log.setMethod(method);
    	log.setParams(params);
		log.setTime(time);
		//3.将用户行为日志持久化(将内存中对象信息写入到数据库)
		sysLogService.saveObject(log);
	}
	/**获取目标方法上@ReqiredLog注解中定义的operation名字*/
	private String getOperation(ProceedingJoinPoint jp)throws Exception {
		//1.获取目标对象类型
		Class<?> targetCls=jp.getTarget().getClass();
		//2.获取目标方法对象
		//2.1获取方法签名信息
		MethodSignature ms=(MethodSignature)jp.getSignature();
		//2.2获取方法对象
		//假如底层配置为jdk代理,则method指向接口中的抽象方法对象.
		//假如底层配置为CGLIB代理,则这个method指向具体目标对象中的方法对象
	    //Method method=ms.getMethod();
	    //假如希望无论是jdk代理还是cglib代理,我们让method变量指向的都是目标对象中的方法对象,那如何实现?
		Method method=targetCls.getDeclaredMethod(ms.getName(), ms.getParameterTypes());
		//3.获取方法上的reqiredLog注解对象
		RequiredLog requiredLog=method.getAnnotation(RequiredLog.class);
		//4.获取注解中的operation的值.
		if(requiredLog==null)return "operation";
		return requiredLog.operation();
	}
	/**获取目标方法的全限定名(目标类全名+方法名)*/
	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;
	}
}








事务管理
在这里插入图片描述
数据层控制事务 mybatis
两个操作放在一个事务中执行

想法就是 加一个注解
在这里插入图片描述
如上图 作用 在方法执行之前开始事务 方法结束之后提交事务
晚上11.25分 回看事务
示例的错误点

加入错误的点
if(rows>0)
	throw new ServiceException("关系数据保存失败 检验事务开没开启 然后查询验证");

在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
尝试自我实现回归和事务
模拟
在这里插入图片描述

添加错误代码 先不加事务
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
错误经过不显示 拦截
回看代码
在这里插入图片描述
加了事务注解
在这里插入图片描述
在这里插入图片描述
加了事务处理注解
在这里插入图片描述
结束
在这里插入图片描述

类上定义了的注解 方法上也定义了注解
方法上的优先级更高

类的注解 更详细
在这里插入图片描述
在这里插入图片描述
@Transactional注解,则方法上的注解一般用于事务特性的定义。
@Transactional 常用属性应用说明:
timeout:事务的超时时间,默认值为-1,表示没有超时显示。如果配置了具体时间,则超过该时间限制但事务还没有完成,则自动回滚事务。这个时间的记录方式是在事务开启以后到sql语句执行之前。
read-only:指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置read-only为true。对添加,修改,删除业务read-only的值应该为false。
rollback-for:用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔。
no-rollback- for: 抛出no-rollback-for 指定的异常类型,不回滚事务。
isolation事务的隔离级别,默认值采用 DEFAULT。当多个事务并发执行时,可能会出现脏读,不可重复读,幻读等现象时,但假如不希望出现这些现象可考虑修改事务的隔离级别(但隔离级别越高并发就会越小,性能就会越差)

在这里插入图片描述

在这里插入图片描述
方法上的优先级更高

ps 这个注解表表明出现任何错误都会回滚在这里插入图片描述
在这里插入图片描述

@Transactional(timeout =60,rollbackFor =  Throwable.class,
                           noRollbackFor = ServiceException.class //service层出现的异常
                           )

这里则是 遇到ServiceException类异常 不会滚
晚上 阿里文档 看异常规范

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
http://blog.itpub.net/26736162/viewspace-2638951/
事务的隔离级别
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
没写事务管理代码 却有事务管理逻辑 就是因为有AOP
在这里插入图片描述
在这里插入图片描述
环绕通知
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如果没有事务创建新事务, 如果当前有事务参与当前事务, Spring 默认的事务传播行为是PROPAGATION_REQUIRED,它适合于绝大多数的情况。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
事务传播特性测试
切面
在这里插入图片描述
接口实现类
在这里插入图片描述
事务传播特性 当前会被挂起
在这里插入图片描述
在这里插入图片描述
如上图只读不可看了
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以在这样做
必须要两个impl
在这里插入图片描述

事务加注解=声明式事务

Spring 声明式事务是 Spring 最核心,最常用的功能

。由于 Spring 通过 IOC 和 AOP的功能非常透明地实现了声明式事务的功能,对于一般的开发者基本上无须了解 Spring声明式事务的内部细节,仅需要懂得如何配置就可以了。但对于中高端开发者还需要了解其内部机制。

通过代码去解决业务
代码会变 业务 逻辑不会变
初学者 业务驱动业务

AOP异步操作 串行改并行

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
线程 被阻塞了 效率肯定比较慢 同一线程去写
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
写日志依然有阻塞(5s) 但是不影响显示
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一万个并发量 10g内存没了
底层必须不停地 gc 还要会内存溢出
线程对象得到重用
项目实践
问题:内存溢出
在这里插入图片描述
在这里插入图片描述
模拟并发 雷哥电脑无了
自我实现 模拟延时操作
1.在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

如上二图 日志会有延迟(5s)
在这里插入图片描述
讲解
在这里插入图片描述
并发量高 租用阿里云的平台
在这里插入图片描述
在这里插入图片描述
线程切换也很耗时
异步就是两个线程同时操作
在这里插入图片描述
1.启动异步
在这里插入图片描述
log日志面
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
启动类就是通知方法
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
说起“并行”自然离不开“异步”
晚上再看一次

缓存
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如下图 删除之后还要数据 是cache的数据
需要在方法中清除
在这里插入图片描述
在这里插入图片描述

如上图 和day19号同理
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

0821 晚总结

注解和获取日志对应操作方法名 @RequiredLog(operation = “xxxxxx”)
切入点(Pointcut):
@Pointcut(“bean(sysUserServiceImpl)”)
@Pointcut("@annotation(com.cy.pj.common.annotation.RequiredLog)")
理解用户管理页面的切面方法 回看切面理解的图
切入点 遗忘
首先了解什么是切入点

在这里插入图片描述
拿实际切面类去校对学习
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

bean表达式

一般应用于类级别,实现粗粒度的切入点定义,案例分析:
bean(“userServiceImpl”)指定一个userServiceImpl类中所有方法。
bean("*ServiceImpl")指定所有后缀为ServiceImpl的类中所有方法。
说明:bean表达式内部的对象是由spring容器管理的一个bean对象,表达式内部的名字应该是spring容器中某个bean的name。

最重要@annotation表达式(重点)

@annotaion表达式应用于方法级别,实现细粒度的切入点表达式定义,案例分析
@annotation(anno.RequiredLog) 匹配有此注解描述的方法。
@annotation(anno.RequiredCache) 匹配有此注解描述的方法。
其中:RequiredLog为我们自己定义的注解,当我们使用@RequiredLog注解修饰业务层方法时,系统底层会在执行此方法时进行日扩展操作。

课堂练习:定义一Cache相关切面,使用注解表达式定义切入点,并使用此注解对需要使用cache的业务方法进行描述,代码分析如下:

package com.cy.pj.common.aspect;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import com.cy.pj.common.IPUtils;
import com.cy.pj.common.annotation.RequiredLog;

import com.cy.pj.sys.pojo.SysLog;
import com.cy.pj.sys.service.SysLogService;
import com.fasterxml.jackson.databind.ObjectMapper;

import lombok.extern.slf4j.Slf4j;
/**
 * @Aspect 注解描述的类为spring容器中的一个切面对象类型(此类型中封装切入点与通知方法)
 * 1)切入点:(要执行拓展业务的方法的集合)
 * 2)通知方法:封装了在切入点方法上要执行的拓展业务方法.
 */
/*order的值越小,优先级越高
order如果不标注数字,默认最低优先级,因为其默认值是int最大值
该注解等同于实现Ordered接口getOrder方法,并返回数字。
*/
//@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));
			//5.返回目标方法执行结果
			return result;//目标方法的返回结果
		}catch(Throwable e) {
			log.error("目标方法执行时出现了异常:{}",e.getMessage());
			throw e;
		}
	}
	@Autowired
	private SysLogService sysLogService;
	/**记录用户行为的正常信息*/
	private void saveUserLog(ProceedingJoinPoint jp,long time)throws Exception {
		//1.获取用户行为日志
		String ip=IPUtils.getIpAddr();
		String username="cgb2005";//将来是登录用户的用户名
		Date createdTime=new Date();
		//获取操作名
		String operation=getOperation(jp);
		String method=getTargetClassMethod(jp);
		//String params=Arrays.toString(jp.getArgs());//普通格式字符串
		String params=new ObjectMapper().writeValueAsString(jp.getArgs());//json格式的参数
		//2.封装用户行为日志(在内容中通过对象去实现)
		SysLog log=new SysLog();
		log.setIp(ip);
		log.setUsername(username);
		log.setCreatedTime(createdTime);
		log.setOperation(operation);
		log.setMethod(method);
    	log.setParams(params);
		log.setTime(time);
		//3.将用户行为日志持久化(将内存中对象信息写入到数据库)
		
		
		//sysLogService.saveObject(log);
		new Thread() {
			public void run() {
				sysLogService.saveObject(log);
			}
		}.start();
		
	}
	/**获取目标方法上@ReqiredLog注解中定义的operation名字*/
	private String getOperation(ProceedingJoinPoint jp)throws Exception {
		//1.获取目标对象类型
		Class<?> targetCls=jp.getTarget().getClass();
		//2.获取目标方法对象
		//2.1获取方法签名信息
		MethodSignature ms=(MethodSignature)jp.getSignature();
		//2.2获取方法对象
		//假如底层配置为jdk代理,则method指向接口中的抽象方法对象.
		//假如底层配置为CGLIB代理,则这个method指向具体目标对象中的方法对象
	    //Method method=ms.getMethod();
	    //假如希望无论是jdk代理还是cglib代理,我们让method变量指向的都是目标对象中的方法对象,那如何实现?
		Method method=targetCls.getDeclaredMethod(ms.getName(), ms.getParameterTypes());
		//3.获取方法上的reqiredLog注解对象
		RequiredLog requiredLog=method.getAnnotation(RequiredLog.class);
		//4.获取注解中的operation的值.
		if(requiredLog==null)return "operation";
		return requiredLog.operation();
	}
	/**获取目标方法的全限定名(目标类全名+方法名)*/
	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;
	}
}








周日下午 看多一次异步和自我实现一次注解cache

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值