在实际业务开发过程当中,有很多公共的功能。针对这些功能,可以自己开发公共的starter模块
命名
对于自己开发的starter模块,一般使用xxx-spring-boot-starter,而spring自己生态圈的starter,一般都是spring-boot-starter-xxx格式命名
maven依赖(pom.xml)
<!-- 版本自己定 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
属性类
package com.leewan.vue.starter.properties;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "leewan.vue")
public class LeewanVueProperties {
private String rootPath;
public String getRootPath() {
return rootPath;
}
public void setRootPath(String rootPath) {
this.rootPath = rootPath;
}
}
通过@ConfigurationProperties注解,注入属性
配置类(最核心)
package com.leewan.vue.starter.config;
import com.leewan.vue.starter.condition.ConditionalHasProperties;
import com.leewan.vue.starter.controller.LeewanPageController;
import com.leewan.vue.starter.pageContext.ClassPathPageContext;
import com.leewan.vue.starter.pageContext.FilePageContext;
import com.leewan.vue.starter.pageContext.PageContext;
import com.leewan.vue.starter.properties.LeewanVueProperties;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ResourceUtils;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.lang.reflect.Method;
import java.net.URL;
@Configuration
//开启LeewanVueProperties配置对象,如不配置,则无法自动注入LeewanVueProperties类
@EnableConfigurationProperties(LeewanVueProperties.class)
//条件注解
@ConditionalHasProperties(propertyName = "leewan.vue.root-path")
public class LeewanVueConfiguration {
@Autowired
private LeewanVueProperties properties;
@Bean
public PageContext pageContext() {
String rootPath = properties.getRootPath();
if(rootPath.startsWith(ResourceUtils.CLASSPATH_URL_PREFIX)) {
String path = rootPath.substring(ResourceUtils.CLASSPATH_URL_PREFIX.length());
URL url = Thread.currentThread().getContextClassLoader().getResource(path);
if(ResourceUtils.isJarURL(url)) {
return new ClassPathPageContext(path);
}else {
return new FilePageContext(rootPath);
}
}else {
return new FilePageContext(rootPath);
}
}
@Bean("leewanPageController")
public LeewanPageController pageController(){
return new LeewanPageController();
}
@Bean
public ApplicationContextAware aware(){
return new ApplicationContextAware() {
@Override
public void setApplicationContext(ApplicationContext application) throws BeansException {
//手动注册controller
RequestMappingHandlerMapping handlerMapping = application.getBean(RequestMappingHandlerMapping.class);
try {
Method method=handlerMapping.getClass().getSuperclass().getSuperclass().
getDeclaredMethod("detectHandlerMethods",Object.class);
method.setAccessible(true);
method.invoke(handlerMapping, "leewanPageController");
} catch (Exception e) {
e.printStackTrace();
}
}
};
}
}
@EnableConfigurationProperties(LeewanVueProperties.class)
表示开启LeewanVueProperties配置对象,否则无法注入LeewanVueProperties对象
后续使用IDE(如idea,eclipse)编辑yml文件时,会根据LeewanVueProperties进行自动提示
条件注解
有些starter虽然引入,但实际运行过程中 需要满足一些特定条件才会运行。所以还需要条件注解来进行判断。springboot自带了很多条件注解,如@ConditionalOnProperty、@ConditionalOnBean等。
但是对于一些比较复杂的业务场景,使用spring自带的条件注解无法满足需求时,我们还可以自定义注解。 自定义注解一般需要两个类,一是注解,一是具体的条件匹配实现类
注解类
package com.leewan.vue.starter.condition;
import org.springframework.context.annotation.Conditional;
//配置条件匹配类
@Conditional(HasPropertiesCondition.class)
public @interface ConditionalHasProperties {
String propertyName() default "";
}
条件匹配实现类
需要实现org.springframework.context.annotation.Condition类,
通过AnnotatedTypeMetadata可以获取注解类里的一些配置
通过conditionContext可以获取上下文一些配置参数
package com.leewan.vue.starter.condition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.StringUtils;
import java.util.Map;
public class HasPropertiesCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Map<String, Object> annotationAttributes = annotatedTypeMetadata.getAnnotationAttributes(ConditionalHasProperties.class.getName());
String propertyName = (String) annotationAttributes.get("propertyName");
String property = conditionContext.getEnvironment().getProperty(propertyName);
if(StringUtils.hasText(property)){
return true;
}
return false;
}
}
配置文件
最后需要告诉spring 你的这个starter的自动配置类是哪个。通过配置文件进行配置
1、resources目录下 新建META-INF目录
2、新建文件spring.factories
3、在spring.factories中
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.leewan.vue.starter.config.LeewanVueConfiguration
一切搞定