明天咱们来说说 spring 中的切面 Aspect,这是 Spring 的一大劣势。面向切面编程往往让咱们的开发更加低耦合,也大大减少了代码量,同时呢让咱们更专一于业务模块的开发,把那些与业务无关的货色提取进来,便于前期的保护和迭代。
视频看的能源节点的视频,整顿了这篇博客作为笔记,当前温习时候看看,也分享给有须要的小伙伴。
视频资源:https://www.bilibili.com/vide…
相干概念
1. 切面(Aspect):
首先要了解‘切’字,须要把对象设想成一个立方体,传统的面向对象变成思维,类定义实现之后(封装)。每次实例化一个对象,对类定义中的成员变量赋值,就相当于对这个立方体进行了一个定义,定义实现之后,就等着被应用,等着被回收。
面向切面编程则是指,对于一个咱们曾经封装好的类,咱们能够在编译期间或在运行期间,对其进行切割,把立方体切开,在原有的办法外面增加(织入)一些新的代码,对原有的办法代码进行一次加强解决。而那些加强局部的代码,就被称之为切面,如上面代码实例中的通用日志解决代码,常见的还有事务处理、权限认证等等。
2. 切入点(PointCut):
要对哪些类中的哪些办法进行加强,进行切割,指的是被加强的办法。即要切哪些货色。
3. 连接点(JoinPoint):
晓得了要切哪些办法后,剩下的就是什么时候切,在原办法的哪一个执行阶段退出减少代码,这个就是连接点。如办法调用前,办法调用后,产生异样时等等。
4. 告诉(Advice):
告诉被织入办法,改如何被加强。定义切面的具体实现。那么这外面就波及到一个问题,空间(切哪里)和工夫(什么时候切,在何时退出减少代码),空间咱们曾经晓得了就是切入点中定义的办法,而什么时候切,则是连接点的概念。
5. 指标对象(Target Object):
被一个或多个切面所告诉的对象,即为指标对象。
6. AOP 代理对象(AOP Proxy Object):
AOP 代理是 AOP 框架所生成的对象,该对象是指标对象的代理对象。代理对象可能在指标对象的根底上,在相应的连接点上调用告诉。
7. 织入(Weaving):
将切面切入到指标办法之中,使指标办法失去加强的过程被称之为织入。
AOP 必须的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
日志实体类
package com.space.aspect.bo;
import lombok.Data;
/**
* 系统日志 bo
*/
@Data
public class SysLogBO {
private String className;
private String methodName;
private String params;
private Long exeuTime;
private String remark;
private String createDate;
}
日志 service
package com.space.aspect.service;
import com.space.aspect.bo.SysLogBO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class SysLogService {public boolean save(SysLogBO sysLogBO){
// 这里就不做具体实现了
log.info(sysLogBO.getParams());
return true;
}
}
定义日志注解
package com.space.aspect.anno;
import java.lang.annotation.*;
/**
* 定义系统日志注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {String value() default "";
}
申明切面,实现日志记录
以上 4 点咱们的筹备工作曾经实现。接下来就是重点了
package com.space.aspect.aspect;
import com.google.gson.Gson;
import com.space.aspect.anno.SysLog;
import com.space.aspect.bo.SysLogBO;
import com.space.aspect.service.SysLogService;
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.stereotype.Component;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* 系统日志切面
*/
@Aspect // 应用 @Aspect 注解申明一个切面
@Component
public class SysLogAspect {
@Autowired
private SysLogService sysLogService;
/**
* 这里咱们应用注解的模式
* 当然,咱们也能够通过切点表达式间接指定须要拦挡的 package, 须要拦挡的 class 以及 method
* 切点表达式: execution(...)
*
* execution(public * *(..)) 任意的公共办法
* execution(* set*(..))以 set 结尾的所有的办法
* execution(* com.LoggerApply.*(..))com.LoggerApply 这个类里的所有的办法
* execution(* com.annotation.*.*(..))com.annotation 包下的所有的类的所有的办法
* execution(* com.annotation..*.*(..))com.annotation 包及子包下所有的类的所有的办法
* execution(* com.annotation..*.*(String,?,Long)) com.annotation 包及子包下所有的类的有三个参数,第一个参数为 String 类型,第二个参数为任意类型,第三个参数为 Long 类型的办法
* execution(@annotation(com.lingyejun.annotation.Lingyejun))
*/
@Pointcut("@annotation(com.space.aspect.anno.SysLog)")
public void logPointCut() {}
/**
* 盘绕告诉 @Around,当然也能够应用 @Before (前置告诉) @After (后置告诉)
* @param point
* @return
* @throws Throwable
*/
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {long beginTime = System.currentTimeMillis();
Object result = point.proceed();
long time = System.currentTimeMillis() - beginTime;
try {saveLog(point, time);
} catch (Exception e) { }
return result;
}
/**
* 保留日志
*/
private void saveLog(ProceedingJoinPoint joinPoint, long time) {MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
SysLogBO sysLogBO = new SysLogBO();
sysLogBO.setExeuTime(time);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
sysLogBO.setCreateDate(dateFormat.format(new Date()));
SysLog sysLog = method.getAnnotation(SysLog.class);
if(sysLog != null){
// 注解上的形容
sysLogBO.setRemark(sysLog.value());
}
// 申请的 类名、办法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
sysLogBO.setClassName(className);
sysLogBO.setMethodName(methodName);
// 申请的参数
Object[] args = joinPoint.getArgs();
try{List<String> list = new ArrayList<String>();
for (Object o : args) {list.add(new Gson().toJson(o));
}
sysLogBO.setParams(list.toString());
}catch (Exception e){ }
sysLogService.save(sysLogBO);
}
}
测试
package com.space.aspect.controller;
import com.space.aspect.anno.SysLog;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {@SysLog("测试")
@GetMapping("/test")
public String test(@RequestParam("name") String name){return name;}
}
这样,咱们就胜利实现了应用 Aspect 实现切面记录日志。