简介
我们在项目中经常需要把工具抽象成jar的形式,让所有的项目都可以使用,但是如果仅仅是util直接放到jar包中,打包就可以了。但是很多时候不仅仅是util,还有依赖springboot的东西,需要jar包可以和springboot整合,这时候这个jar怎么去创建和打包呢?
我已经给大家做了一个简单的项目,项目的结构很简单,就一个切面完成开关动态获取配置的操作。项目是一个完整的可配置的项目。大家可以参考
项目地址:https://github.com/chunlaiqingke/config-switcher/tree/master
流程介绍
- 1.首先创建一个maven的项目,这里就不介绍了,如果不会可以百度,可以留言,我教你啊~~。
- 2.pom配置,肯定得是jar的包
<?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 http://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.2.11.RELEASE</version>
</parent>
<groupId>com.handsome</groupId>
<artifactId>config-switcher</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<spring.version>2.2.11.RELEASE</spring.version>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
<!--<optional>true</optional>-->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<exclusions>
<exclusion>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-core</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-openapi</artifactId>
<version>1.1.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<compilerVersion>${java.version}</compilerVersion>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.1</version>
</plugin>
</plugins>
</build>
</project>
- 3.创建注解
这个注解是加载springboot的启动类上的,用来整合jar包的关键
@import注解导入ConfigSwitcherBeanDefinitionRegistrar这个类,这个类中需要把jar包中依赖spring容器的类注册到容器中
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import(ConfigSwitcherBeanDefinitionRegistrar.class)
public @interface EnableConfigSwitcher {
}
- 创建注册类ConfigSwitcherBeanDefinitionRegistrar
public class ConfigSwitcherBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
AbstractBeanDefinition switcherAspectBD = BeanDefinitionBuilder.genericBeanDefinition(SwitcherAspect.class).getBeanDefinition();
beanDefinitionRegistry.registerBeanDefinition("switcherAspect", switcherAspectBD);
AbstractBeanDefinition qConfigSwitcherProperties = BeanDefinitionBuilder.genericBeanDefinition(ApolloSwitcherProperties.class).getBeanDefinition();
beanDefinitionRegistry.registerBeanDefinition("qConfigSwitcherProperties", qConfigSwitcherProperties);
AbstractBeanDefinition switcherContextListenerBD = BeanDefinitionBuilder.genericBeanDefinition(SwitcherContextListener.class).getBeanDefinition();
beanDefinitionRegistry.registerBeanDefinition("switcherContextListener", switcherContextListenerBD);
}
}
到这来你需要整合的部分都已经配置完了,这样打包,就可以配置到spring项目中了。
下面在介绍一下我的那个项目的结构
添加spring的ApplicationListener来监听容器启动完成事件
这个类是监听spring启动后去apollo添加新增的开关配置
public class SwitcherContextListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
AbstractApplicationContext applicationContext = (AbstractApplicationContext)event.getApplicationContext();
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
//apollo只支持单个配置的新增,所以这里只能循环去操作
for (String bdName : beanDefinitionNames) {
Class<?> type = applicationContext.getType(bdName);
appendUpdate(bdName, findMethod(bdName, type, Switcher.class));
}
}
/**
* @param methodsWithAnnotation
* @return
*/
private void appendUpdate(String bdName, List<Method> methodsWithAnnotation){
if(!CollectionUtils.isEmpty(methodsWithAnnotation)) {
for (Method method : methodsWithAnnotation) {
//这里要用spring的反射去拿,而不可以自己使用字节码去拿,拿不到
Switcher switcherAnno = AnnotationUtils.findAnnotation(method, Switcher.class);
SwitcherDefinition switcherDefinition = new SwitcherDefinition();
switcherDefinition.setState(switcherAnno.state());
switcherDefinition.setExpiry(switcherAnno.expiry());
switcherDefinition.setEffective(switcherAnno.effective());
ApolloSwitcherProperties.appendUpdate(bdName + "." + method.getName(), JacksonUtil.toJSONString(switcherDefinition));
}
ApolloSwitcherProperties.releaseNamespace();
}
}
/**
* qconfig中没有的就是新配置,需要append到qconfig中去的
* @return
*/
private boolean configContains(String bdName, Method method){
return ApolloSwitcherProperties.containsKey(bdName + "." + method.getName());
}
/**
* 获取需要加到config中的方法
* jdk1.8+
* @param clazz
* @param annotationClazz
* @return
*/
private List<Method> findMethod(String bdName, Class<?> clazz, Class<? extends Annotation> annotationClazz){
//继承的方法不算
Method[] methods = clazz.getDeclaredMethods();
return Arrays.stream(methods)
.filter(m -> AnnotationUtils.findAnnotation(m, annotationClazz) != null && !configContains(bdName, m))
.collect(Collectors.toList());
}
}
让开关在调用的时候生效的切面
@Aspect
public class SwitcherAspect {
@Autowired
private AbstractApplicationContext applicationContext;
@Around("execution(* com.handsome.*.*(..)) || @annotation(com.handsome.switcher.annotation.Switcher)")
public Object execut(ProceedingJoinPoint joinPoint) throws Throwable {
try {
String methodName = joinPoint.getSignature().getName();
String className = joinPoint.getTarget().getClass().getSimpleName();
//首字母改为小写
String bdName = lower(className);
//检查应用是否启动
if(!applicationContext.isRunning()){
return joinPoint.proceed();
}
//从qconfig中拿取结果
SwitcherDefinition configSwitcher = ApolloSwitcherProperties.getConfigSwitcher(bdName + "." + methodName);
//todo 过期时间和生效时间,现在过期时间和生效时间还没有实现,只实现了状态字段
if(configSwitcher != null){
return configSwitcher.isState();
}
ConfigSwitcherLogger.errorLog("没有此方法的开关配置,方法名:" + bdName + "." + methodName, new RuntimeException("no this method config"));
return joinPoint.proceed();
} catch (Throwable throwable) {
throw throwable;
}
}
private String lower(String className) {
if(StringUtils.isEmpty(className)){
return className;
}
return className.substring(0,1).toLowerCase()+className.substring(1);
}
}
到此就是这个项目的所有内容~~
有什么问题加群留言(qq群:574948729)或者公众号留言