▶AOP实现日志的记录

简短的前言

AOP是面向切面编程思想,用于日志处理,事务处理,权限处理,缓存处理等等(是基于OCP[开闭原则]在不改变原有代码的情况下,对现有功能进行功能扩展)。
:如果没有AOP,又要基于OCP原则实现功能扩展有两种方式
1.通过继承方式,原有功能不变,在子类增加特性功能扩展。
2.通过组合方式,对业务层实现类的接口增加特性功能扩展。

AOP底层两种原理

1.被代理对象如果实现接口,底层默认采用JDK动态代理机制为目标对象创建代理对象(目标类和代理类会实现共同接口)。
2.被代理对象如果没有实现接口,则底层默认采用CGLIB代理机制为目标对象创建代理对象(默认创建的代理类会继承目标对象类型)。
说明:
Spring boot2.x 中AOP现在默认使用的CGLIB代理,假如需要使用JDK动态代理可以在配置文件(applicatiion.properties)中进行如下配置:

    spring.aop.proxy-target-class=false

1.添加依赖

	  <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-aop</artifactId>
      </dependency>

2.创建日志切面处理类

/**
 * @Aspect 注解修饰的类通常认为一个切面对象类型
 * 切面对象是对扩展业务的封装,它通常会在内部声明
 * 如下几个部分.
 * 1)实现扩展业务的方法(一般会称为通知-advice)
 * 2)切入扩展业务的点(一般会称为切入点-PointCut)
 */
//@Order(1)
@Aspect
@Service
public class SysLogAspect {//日志切面
	  /**
	   * @Around注解修饰方法为一个环绕通知,其目的
	   * 是在目标业务方法执行之前和之后都可以进行
	   * 扩展业务的处理
	   * 其中:
	   * 1)bean(sysUserServiceImpl) 为切入点表达式,
	   * 表示sysUserServiceImpl对象中所有业务方法执行
	   * 时都会执行@Around注解修饰的方法
	   * @param jp 连接点(封装了要执行的目标方法信息)
	   * @return
	   * @throws Throwable
	   */
	  //@Around("bean(sysUserServiceImpl)")
	  //@Around("bean(*ServiceImpl)")
	  //@annotation()为细粒度的切入点表达式定义方式
	  @Around("@annotation(com.cy.pj.common.annotation.RequiredLog)")
	  public Object aroundMethod(ProceedingJoinPoint jp) 
	      throws Throwable{
		  System.out.println("LogAspect:开始记录日志");
		  //1.目标业务执行之前的记录
		  long t1=System.currentTimeMillis();
		  //2.执行目标业务(底层通过反射执行目标方法)
		  Object result=jp.proceed();
		  //3.目标业务执行之后的记录
		  long t2=System.currentTimeMillis();
		  System.out.println("目标业务执行时长:"+(t2-t1));
		  saveObject(jp,(t2-t1));
		  //4.返回目标业务的执行结果
		  return result;
	  }
	  @Autowired
	  private SysLogDao sysLogDao;
	  private void saveObject(ProceedingJoinPoint jp,long time)throws Exception {
		//1.获取要保存的日志信息
		//1.1获取登陆用户(没问题)
		SysUser user=(SysUser)SecurityUtils.getSubject().getPrincipal();
		//1.2获取方法签名(此对象封装了我们要执行的目标方法信息)
		Signature s=jp.getSignature();
		System.out.println(s.getClass().getName());//MethodSignature
		MethodSignature ms=(MethodSignature)s;
		//1.2.1获取目标对象(要执行的业务层对象)
		Class<?> targetClass=jp.getTarget().getClass();
		//1.2.2基于目标业务对象获取要执行的目标方法
		//?思考(为什么要获取此方法呢)
		Method targetMethod=targetClass.getDeclaredMethod(
				    ms.getName(),
				    ms.getParameterTypes());
		//1.2.3获取方法上定义的注解内容(定义的操作名)
		RequiredLog requiredLog=
		targetMethod.getDeclaredAnnotation(RequiredLog.class);
		String operation=requiredLog.value();
		//1.2.4获取目标对象方法的全称(类全名+方法名)
		String targetClassName=targetClass.getName();
		String targetMethodName=targetClassName+"."+targetMethod.getName();
		//1.3获取方法执行时的实际参数
		String params=Arrays.toString(jp.getArgs());
		//2.封装日志信息
		SysLog log=new SysLog();
		log.setUsername(user.getUsername());
		log.setIp(IPUtils.getIpAddr());
		log.setOperation(operation);
		log.setMethod(targetMethodName);
		log.setParams(params);
		log.setTime(time);
		log.setCreatedTime(new Date());
		//3.将日志信息写入到数据库
		sysLogDao.insertObject(log);
	  }
}

代码内注解的总结:

@Aspect 注解用于标识此类为一个AOP横切面对象
@Pointcut 注解用于定义本类中的切入点,本案例中切入点表达式用的是bean表达式,这个表达式以bean开头,bean括号中的内容为一个spring管理的某个bean对象的id。
@Around用于定义一个环绕通知(满足切入点表达式的核心业务方法执行之前和之后执行的一个操作)

术语增强:
切面:用于封装扩展业务的一个类的对象。
通知:切面扩展业务中的一个操作(方法)。

切面表达式

指示符 作用
bean 应用于类级别,实现粗粒度的控制:(用于匹配指定bean id的的方法执行)

bean(“userServiceImpl”))指定userServiceImpl类中所有方法
bean("*ServiceImpl")指定所有的后缀为serviceImpl的类的所有方法

说明:bean表达式内部的对象是由spring容器管理的一个bean对象,表达式内部的内部的名字应该时spring容器中某个bean的key.

within 粗粒度的控制:用于匹配指定包名下类型内的方法执行

within(“aop.service.UserServiceImpl”)指定aop.service目录下的UserServiceImpl类中的方法
within(“aop.service.*”)指定aop.service目录下的类中的方法(只包括当前目录下的类)
within(“aop.service…*”)指定aop.service目录包含所有子目录中的类

execution 细粒度的控制:用于进行细粒度方法匹配执行具体业务
|

execution(void aop.service.UserServiceImpl.addUser())只匹配方法
execution(void aop.service.PersonServiceImpl.addUser(String))方法参数必须为字符串

|execution(* aop.service….(…)) |万能配置

@annotation 细粒度的控制:用于匹配指定注解修饰的方法执行 RequestLog为自定义的注解
|

@annotation(com.jt.common.anno.RequestLog)指定一个需要实现增强功能的方法

AOP中的五种通知类型

1)前置通知 (@Before) 方法执行之前执行
2)返回通知 (@AfterReturning) 方法return之后执行
3)异常通知 (@AfterThrowing) 方法出现异常之后执行
4)后置通知 (@After) : 又称之为最终通知(finally)
5)环绕通知 (@Around) :重点掌握
其结构如下:

Try{
   @Before
   核心业务
   @AfterReturning
}catch(Exception e){
   ….
   @AfterThrowing
}finally{
   ….
   @After
}

如上四个一起使用时可以直接使用@Around通知替换

五种通知类型案例分析

@Service
@Aspect
public class SysTimeAspect {
	@Pointcut("bean(sysUserServiceImpl)")
	public void doTime(){}

	@Before("doTime()")
	public void doBefore(JoinPoint jp){
		System.out.println("time doBefore()");
	}
	@After("doTime()")
	public void doAfter(){
		System.out.println("time doAfter()");
	}
	/**核心业务正常结束时执行
	 * 说明:假如有after,先执行after,再执行returning*/
	@AfterReturning("doTime()")
	public void doAfterReturning(){
		System.out.println("time doAfterReturning");
	}
	/**核心业务出现异常时执行
                 说明:假如有after,先执行after,再执行Throwing*/
	@AfterThrowing("doTime()")
	public void doAfterThrowing(){
		System.out.println("time doAfterThrowing");
	}
	@Around("doTime()")
	public Object doAround(ProceedingJoinPoint jp)
			throws Throwable{
		System.out.println("doAround.before");
		Object obj=jp.proceed();
		System.out.println("doAround.after");
		return obj;
	}
}

其中:环绕通知的优先级最高.

切面执行顺序配置

注解方式顺序配置需要借助@Order注解

@Order(1)
@Aspect
@Component
public class TxManager {}

注解方式顺序配置

@Order(2)
@Aspect
@Component
public class SysLogAspect {}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring AOP一个强大的框架,可以帮助我们实现各种切面,其包括日志记录。下面是实现日志记录的步骤: 1. 添加Spring AOP依赖 在Maven或Gradle添加Spring AOP依赖。 2. 创建日志切面 创建一个用于记录日志切面。这个切面可以拦截所有需要记录日志方法。在这个切面,我们需要使用@Aspect注解来声明这是一个切面,并使用@Pointcut注解来定义哪些方法需要被拦截。 ```java @Aspect @Component public class LoggingAspect { @Pointcut("execution(* com.example.demo.service.*.*(..))") public void serviceMethods() {} @Around("serviceMethods()") public Object logServiceMethods(ProceedingJoinPoint joinPoint) throws Throwable { // 获取方法名,参数列表等信息 String methodName = joinPoint.getSignature().getName(); Object[] args = joinPoint.getArgs(); // 记录日志 System.out.println("Method " + methodName + " is called with args " + Arrays.toString(args)); // 执行方法 Object result = joinPoint.proceed(); // 记录返回值 System.out.println("Method " + methodName + " returns " + result); return result; } } ``` 在上面的代码,我们使用了@Around注解来定义一个环绕通知,它会在拦截的方法执行前后执行。在方法执行前,我们记录了该方法的名称和参数列表,然后在方法执行后记录了该方法的返回值。 3. 配置AOP 在Spring的配置文件配置AOP。首先,我们需要启用AOP: ```xml <aop:aspectj-autoproxy/> ``` 然后,我们需要将创建的日志切面添加到AOP: ```xml <bean id="loggingAspect" class="com.example.demo.aspect.LoggingAspect"/> <aop:config> <aop:aspect ref="loggingAspect"> <aop:pointcut id="serviceMethods" expression="execution(* com.example.demo.service.*.*(..))"/> <aop:around method="logServiceMethods" pointcut-ref="serviceMethods"/> </aop:aspect> </aop:config> ``` 在上面的代码,我们将创建的日志切面声明为一个bean,并将其添加到AOP。我们还定义了一个切入点,并将其与日志切面方法进行关联。 4. 测试 现在,我们可以测试我们的日志记录功能了。在我们的业务逻辑,所有匹配切入点的方法都会被拦截,并记录它们的输入和输出。我们可以在控制台看到这些日志信息。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值