1. starter工程的命名
starter 是一个开箱即用的组件,减少不必要的重复代码,重复配置。
例如,
redis
的 starter,spring-boot-starter-data-redis
Spring
官方定义的 starter 通常命名遵循的格式为 spring-boot-starter-{name}。
例如 spring-boot-starter-web
。
非官方 starter 命名应遵循 {name}-spring-boot-starter 的格式。
例如,dubbo-spring-boot-starter
。
2. 需求
定义
@AspectLog
注解,用于标注需要打印执行时间的方法
3. 实现
3.1 创建一个SpringBoot项目
这里项目名称为
aspectlog-spring-boot-starter
3.2 导入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
关于 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
大概意思就是:写 starter
时,在 pom
中配置 spring-boot-configuration-processor
,在编译时会自动收集配置类的条件,写到一个 META-INF/spring-configuration-metadata.
properties
文件中
3.3 编写自动配置逻辑
3.3.1 各种 Conditions
Conditions | 描述 |
@ConditionalOnBean | 在存在某个 bean 的时候 |
@ConditionalOnMissingBean | 不存在某个 bean 的时候 |
@ConditionalOnClass | 当前 classpath 可以找到某个类型的类时 |
@ConditionalOnMissingClass | 当前 classpath 不可以找到某个类型的类时 |
@ConditionalOnResource | 当前 classpath 是否存在某个资源文件 |
@ConditionalOnProperty | 当前 jvm 是否包含某个系统属性为某个值 |
@ConditionalOnWebApplication | 当前 spring context 是 web 应用程序才加载 |
@ConditionalOnNotWebApplication | 不是web应用才加载 |
这里选用 @ConditionalOnProperty
。即配置文件中的属性aspectLog.enable=true,才加载我们的配置类。
3.3.2 定义 @AspectLog 注解
该注解用于标注需要打印执行时间的方法
package org.example.aspectlog.interfaces; import java.lang.annotation.*; /** * 定义AspectLog注解,该注解用于标注需要打印执行时间的方法。 */ ElementType.METHOD) (RetentionPolicy.RUNTIME) ( public @interface AspectLog { }
3.3.3 定义配置文件类
package org.example.aspectlog.configuration; import org.springframework.boot.context.properties.ConfigurationProperties; prefix = AspectLogProperties.PREFIX) (public class AspectLogProperties { public static final String PREFIX = "aspectlog"; private boolean enable; public boolean isEnable() { return enable; } public void setEnable(boolean enable) { this.enable = enable; } }
3.3.4 定义自动配置类
package org.example.aspectlog.configuration; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.core.PriorityOrdered; exposeProxy = true, proxyTargetClass = true) (AspectLogProperties.class) ( prefix = AspectLogProperties.PREFIX, name = "enable", havingValue = "true", matchIfMissing = true) (public class AspectLogAutoConfiguration implements PriorityOrdered { private static final Logger LOGGER = LoggerFactory.getLogger(AspectLogAutoConfiguration.class); "@annotation(org.example.aspectlog.interfaces.AspectLog)") ( public Object isOpen(ProceedingJoinPoint pjp) throws Throwable { String taskName = pjp.getSignature().toString().substring(pjp.getSignature().toString().indexOf(" "), pjp.getSignature().toString().indexOf("(")); taskName = taskName.trim(); long time = System.currentTimeMillis(); Object result = pjp.proceed(); LOGGER.info("method:{} run:{} ms", taskName, (System.currentTimeMillis() - time)); return result; } public int getOrder() { // 保证事务等切面先执行 return Integer.MAX_VALUE; } }
上面 @ConditionalOnProperty
中的参数说明
prefix = AspectLogProperties.PREFIX,即AspectLogProperties中常量属性PREFIX="aspectlog"
name = "enable",name的名称为enable时
havingValue = "true",上面的name为enable的值为true时才开启
matchIfMissing = true,匹配不到对应的属性的时也开启
即:
当配置文件有aspectLog.enable=true时开启,如果配置文件没有设置aspectLog.enable也开启。
3.4 配置 META-INF/spring.factories
META-INF/spring.factories是spring的工厂机制,在这个文件中定义的类,都会被自动加载。多个配置使用逗号分割,换行用\
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.example.aspectlog.configuration.AspectLogAutoConfiguration
3.5 打包测试
3.5.1 目录结构
3.5.2 mvn install到本地
3.5.3 其他项目中引用测试
3.5.3.1 pom引用
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.example</groupId> <artifactId>aspectlog-spring-boot-starter</artifactId> <version>1.0.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
3.5.3.2 测试代码
"/test") (public class TestController { "/test") ( public String test () throws InterruptedException { return "success"; } }
3.5.3.3 测试结果