前面写了一篇AOP自定义注解实现接口请求日志记录,可以记录接口请求日志。但是代码与项目耦合度太高,其他项目也需要的话,不利于移植!此时我们就可以把它写成一个starter,需要时在pom文件中添加依赖就可以了。
创建maven项目
在使用spring-boot-starter,会发现,有的项目名称是 XX-spring-boot-starter,有的是
spring-boot-starter-XX,这个项目的名称有什么讲究呢?
从springboot官方文档摘录如下:
Do not start your module names with spring-boot, even if you use a different Maven groupId. We may offer official support for the thing you auto-configure in the future.As a rule of thumb, you should name a combined module after the starter.
从这段话可以看出spring-boot-starter命名的潜规则:
spring-boot-starter-XX是springboot官方的starter
XX-spring-boot-starter是第三方扩展的starter
创建名为aspectlog-spring-boot-starter的项目,pom文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.jiangchh</groupId>
<artifactId>aspectlog-spring-boot-starter</artifactId>
<version>1.0.0</version>
<properties>
<java.version>1.8</java.version>
<mybatis-plus.version>2.1.8</mybatis-plus.version>
<mybatisplus-spring-boot-starter.version>1.0.5</mybatisplus-spring-boot-starter.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- MyBatis plus增强和springboot的集成-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatisplus-spring-boot-starter</artifactId>
<version>${mybatisplus-spring-boot-starter.version}</version>
</dependency>
</dependencies>
</project>
关于spring-boot-configuration-processor的说明,引自springBoot官方文档:
Spring Boot uses an annotation processor to collect the conditions on auto-configurations in a metadata file ( META-INF/spring-autoconfigure-metadata.properties ). If that file is present, it is used to eagerly filter auto-configurations that do not match, which will improve startup time. It is recommended to add the following dependency in a module that contains auto-configurations:org.springframework.bootspring-boot-autoconfigure-processortrue
简单说就是:写starter时,在pom中配置spring-boot-autoconfigure-processor,在编译时会自动收集配置类的条件,写到一个META-INF/spring-autoconfigure-metadata.properties中。
自动配置逻辑
各种condition
类型 | 注解 | 说明 |
---|---|---|
Class Conditions类条件注解 | @ConditionalOnClass | 当前classpath下有指定类才加载 |
@ConditionalOnMissingClass | 当前classpath下无指定类才加载 | |
Bean ConditionsBean条件注解 | @ConditionalOnBean | 当期容器内有指定bean才加载 |
@ConditionalOnMissingBean | 当期容器内无指定bean才加载 | |
Property Conditions环境变量条件注解(含配置文件) | @ConditionalOnProperty | prefix 前缀name 名称havingValue 用于匹配配置项值matchIfMissing 没找指定配置项时的默认值 |
ResourceConditions 资源条件注解 | @ConditionalOnResource | 有指定资源才加载 |
Web Application Conditionsweb条件注解 | @ConditionalOnWebApplication | 是web才加载 |
@ConditionalOnNotWebApplication | 不是web才加载 | |
SpEL Expression Conditions | @ConditionalOnExpression | 符合SpEL 表达式才加载 |
- 定义AspectLog注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface AspectLog {
/**
* 接口描述
*/
String desc() default "";
}
- 定义配置文件对应类
@ConfigurationProperties("aspectlog")
public class AspectLogProperties {
private boolean enable;
public boolean isEnable() {
return enable;
}
public void setEnable(boolean enable) {
this.enable = enable;
}
}
- 定义自动配置类
@Aspect
@EnableAspectJAutoProxy(exposeProxy = true, proxyTargetClass = true)
@Configuration
@ConditionalOnProperty(prefix = "aspect-log", name = "enable",matchIfMissing = true)
@EnableConfigurationProperties(AspectLogProperties.class)
public class AspectLogAutoConfiguration implements PriorityOrdered {
protected Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private SysOperateLogDao sysOperateLogDao;
/*Logger logger = LoggerFactory.getLogger(LogAdvice.class);*/
/**
* 切入点
*/
@Pointcut("@annotation(com.digihealth.icu.aspectLog.AspectLog)")
public void logAop(){}
/**
* 环绕通知
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("logAop()")
public Object arround(ProceedingJoinPoint joinPoint) throws Throwable {
//请求开始时间戳
long begin = System.currentTimeMillis();
Date startTime = new Date();
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
AspectLog aspectLog = method.getAnnotation(AspectLog.class);
Object result = joinPoint.proceed();
//请求结束时间戳
long end = System.currentTimeMillis();
//结束时间
Date endTime = new Date();
SysOperateLog sysOperateLog = new SysOperateLog();
sysOperateLog.setId(GenerateSequenceUtil.generateSequenceNo())
.setUrl(request.getRequestURL().toString())
.setTpye(request.getMethod())
.setMethod(signature.getDeclaringTypeName()+"."+signature.getName())
.setIp(request.getRemoteAddr())
//.setInParam(JSON.toJSONString(joinPoint.getArgs()))
//.setOutParam(JSON.toJSONString(result))
.setStartTime(startTime)
.setTimeConsum(Long.toString(end-begin))
.setEndTime(endTime)
.setDescription(aspectLog.desc());
sysOperateLogDao.insert(sysOperateLog);
return result;
}
@Override
public int getOrder() {
//保证事务等切面先执行
return Integer.MAX_VALUE;
}
}
配置类简要说明:
@ConditionalOnProperty(prefix = “aspect-log”, name = “enable”,matchIfMissing = true)
prefix:配置属性名称的前缀
name:数组,配置属性完整名称或部分名称,可与prefix组合使用,组成完整的配置属性名称,与value不可同时使用
matchIfMissing:缺少该配置属性时是否可以加载。如果为true,没有该配置属性时也会正常加载;反之则不会生效
- META-INF/spring.factories
META-INF/spring.factories是spring的工厂机制,在这个文件中定义的类,都会被自动加载。多个配置使用逗号分割,换行用\
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.digihealth.icu.aspectLog.AspectLogAutoConfiguration
- 打包测试
在IDEA中,进行mvn intall
打包完成后,在其他项目中的pom中引入进行测试
源码地址