Spring Boot 解决快启动与运行Spring项目的问题,Spring配置依赖比较繁琐。它不是对Spring功能上的增强,而是提供了一种快速使用Spring的方式。
- 自动配置:项目启动时,配置自动选择
- 起步依赖:使用Maven项目对象模型,将一些搭配使用的包打包在一起
- 其他:提供一些大型项目中常用的非功能特性,如嵌入式服务器
起步依赖原理
1. spring-boot-starter-parent
定义了各类技术的版本信息,组合了一套最优搭配的功能版本。在各种starts中定义了该功能所需的做标记he,大部分版本信息来自父工程,因此并不会导致版本冲突
2. spring-boot-starter-web
Sprint Boot 配置文件
定义
properties:键值对形式,参数=值。是默认的配置文件
yml / yaml:参数.小参数: 空格 (冒号后面是有空格的)
- yml 以数据为核心,比传统的xml方式更简洁,不用写标签;比简介的properties更完整,可以看到上层参数
- 大小写敏感;数据值前有空格;使用缩进代表层级关系,且缩进不允许用 tab,只能用空格(IDEA无需关注);#代表空格
- 数据格式
#对象
person:
name: zhangsan
#或者使用行内写法,少用
person: {name: zhangsan}
#数组
address:
- bejing #带空格
- shanghai
#行内写法
address:{bejing,shanghai}
#纯量(常量)
msg: 'hello \n word' #使用单引号,忽略转义字符,直接输出\n
msg: "hello \n word" #使用双引号,识别转义字符,\n识别为换行
- 参数引用:${ arg }
三种配置文件优先级:properties>yml>yaml。若配置文件中配置了相同的参数,则读取优先级更高的
读取
- @Value:使用注解在代码的变量中引用,如:@Value("${name}") private String name;。单个引用,数量较多时比较繁杂
- Environment对象:environment对象+Autowired注解。只需注入一次就可以全局使用。如:@Autowired private Environment environment; 引用参数时直接使用其getProperty即可
- @CpnfigurationProperties:将一个实体类作为配置文件类。实体类的变量对应配置文件中的参数。但要注意,使用这种方法获取参数时,名字需要完整,如果想获取二级参数,需要在添加前缀,前缀就是一级参数名,例如获取person下的所有参数,应写成这样:@ConfigurationProperties(prefix = "person")。这样在使用get方法时,就会准确定位到想要的参数
补充知识:
@Component:确保被Spring Boot所识别,是一个Been
添加@CpnfigurationProperties时配置文件会报红,添加以下依赖即可
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<scope>true</scope>
</dependency>
profile配置
properties方式
当存在多个配置文件时,需要激活后方可使用,否则就使用默认的,可以看到,未激活时项目启动显示:
2021-12-26 14:23:02.165 INFO 1420 --- [ main] c.e.s.SpringbootProfileApplication : No active profile set, falling back to default profiles: default
而当需要另一个配置文件生效时(激活),在application.properties文件中配置:spring.profiles.active=dev 即可。这里的dev就是配置文件的标记。当配置多个配置文件时,文件命名应按照如下格式,application后应该使用-来区分环境:
application.properties application-dev.properties application-prd.properties application-test.properties
标记某配置文件后,读取配置时就会选择所标记的文件,而启动项目时,也会显示日志:
2021-12-26 14:26:47.815 INFO 7808 --- [ main] c.e.s.SpringbootProfileApplication : The following profiles are active: dev
yml方式
yml内部使用 --- 三条短杠为分隔符将一个yml文件分为多个部分,环境名使用spring.profiles:xxx 来区分,激活时用spring.profile.active:xxx。这样使得在一个yml配置文件中就存在不同的配置环境
补充:
激活方式②:虚拟机命令激活:在右上角配置一栏选择设置配置文件,并在VM options中填写:-Dspring.profiles.active=xxx 这样也可以激活配置文件,并且优先级比配置文件要高
激活方式③:命令行激活:在运行jar包时带入外部参数:--spring.profiles.active=prd 即可切换不同的环境
综上所述,boot项目的配置文件既有内部的,也有外部的。因此他们的加载顺序自然是不同的。这里列举一些内部位置:
- file:./config/:当前项目下的/config目录——即在当前项目的根目录下创建一个config目录,里面的配置文件是优先级最高的
- file:./:当前项目的根目录 ——即当前项目根目录,在IDEA页面使用Project File页面新建一个配置文件,这个配置文件的优先级是2,仅次于1
- classpath:/config/:classpath下的/config/目录:即resource/config/目录下的配置文件,/config文件本是没有的,但创建后,里面的配置文件优先级就比外部根目录的要高了
- classpath/:classpath的根目录——即resource下的application.properties文件,默认的配置文件(yml也是)
- 注意:由于file:./config/和file:/下的文件并不符合maven的结构,所以不会打进jar包
外部位置。官方定义了十多个外部可引用的位置,但实际用到的却不多:
- 命令行(会比上面的4、5优先级高)
- 在运行jar包时指定启动参数 E:\Work\Workspace\springboot-config\target>java -jar springboot-config-0.0.1-SNAPSHOT.jar --server.port=8079
- 也可以在运行jar包时指定外部配置文件 E:\Work\Workspace\springboot-config\target>java -jar springboot-config-0.0.1-SNAPSHOT.jar --spring.config.location=e://application.properties
- 将配置文件直接放在于jar包同级的目录下,则会自动读取
- 建立与jar包同级的/config/application.properties目录文件,也可以读取到
E:\Work\Workspace\springboot-config\target>java -jar springboot-config-0.0.1-SNAPSHOT.jar --spring.config.location=e://application.properties . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.6.2) 2021-12-26 15:41:46.188 INFO 4932 --- [ main] c.e.s.SpringbootConfigApplication : Starting SpringbootConfigApplication v0.0.1-SNAPSHOT using Java 1.8.0_181 on DESKTOP-FPG6BLH with PID 4932 (E:\Work\Workspace\springboot-config\target\springboot-config-0.0.1-SNAPSHOT.jar started by Administrator in E:\Work\Workspace\springboot-config\target) 2021-12-26 15:41:46.201 INFO 4932 --- [ main] c.e.s.SpringbootConfigApplication : No active profile set, falling back to default profiles: default 2021-12-26 15:41:47.706 INFO 4932 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8078 (http)
……
springboot设置这么多配置的原因就是在项目启动后如果配置错误,可以使用外部配置来替代内部配置。而无需修改内部配置
(说起来,如果配置有误,难道不要重启项目才会读取吗?既然都重启了,为啥不直接改呢(可能是热部署模式下可以不用重启项目就可以读取新配置吧!))
整合Junit
整合框架需要注意的点
- 该打的注解不能少,Service层需要加@Service,测试主类需要加@SpringBootTest,并指定对应的主类:@SpringBootTest(classes = SpringbootJunitApplication.class)
- 如果测试所在的包与主类所在的包是同一级或者在它的子包,就无需使用SpringBootTest去指定主类
- 由快速构建出来的Spring Boot项目,它的测试包和主类包是一致的(flag:个人还未验证)
整合MyBatis
(补充)
SprintBoot切换内置服务器
配置类:EmbeddedWebServerFactoryCustomizerAutoConfiguration
注:可在pom.xml右键选择diagrams查看编辑里面的内容
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <!-- 排除tomcat依赖 --> <exclusions> <exclusion> <artifactId>spring-boot-starter-tomcat</artifactId> <groupId>org.springframework.boot</groupId> </exclusion> </exclusions> </dependency> <!-- 加入jetty依赖 --> <dependency> <artifactId>spring-boot-starter-jetty</artifactId> <groupId>org.springframework.boot</groupId> </dependency>
选择性的创建Bean:Condition 注解
在Sprintboot中获取Bean
//启动SpringBoot,获取Ioc容器 ConfigurableApplicationContext context = SpringApplication.run(SprintbootMyUnApplication.class, args); //当字节码文件存在时,创建bean后在这里可以获取到userbaen Object user = context.getBean("user"); System.out.println(user);
介绍:条件接口——Condition
@FunctionalInterface
//实现该接口并重写matches方法,自定义判断条件
public interface Condition {
//返回值是一个boolean值,为true则创建bean,为false不创建
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
实现条件接口,并重写方法:
/** * 自定义的条件类,根据类是否存在决定是否创建bean * 实现Condition接口,重写matches方法,并在里面进行逻辑判断 * 返回boolean值,true则创建,false不创建 */ public class ClassCondition implements Condition { /** * @param context 上下文对象,用于获取环境,IOC容器,Classloader对象 * @param metadata 注解元对象,用于获取注解定义的属性值 * @return */ @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { boolean flag = true; //1.根据是否导入指定依赖坐标来判断是否创建User Bean // try { // //判断对应文件是否存在 // Class<?> aClass = Class.forName("redis.clients.jedis.Jedis"); // } catch (ClassNotFoundException e) { // flag = false; // } //2.导入通过注解属性值val指定坐标创建bean Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(ConditionOnClass.class.getName()); String[] val = (String[])annotationAttributes.get("val"); try { for (String className : val) { //判断对应文件是否存在 Class<?> aClass = Class.forName(className); } } catch (ClassNotFoundException e) { flag = false; } return flag; } }
sprintboot自动配置包:org.sprintbootframework.boot.autoconfigure,其中的condition包内有多个ConditionOnxxxx的类,就是按照各种条件来决定是否创建Bean的方法
按照properties内是否存在某属性的键值对来检测是否创建该bean
@Bean @ConditionalOnProperty(name = "xiaozhou", havingValue = "zzz") //当name和value的值存在时,才会创建这个bean
//获取baen的时候以这个方法名为准
public User user(){
return new User(); }
总结:
- 定义条件类:自定义实现Condition接口,重写matches方法,在matches方法中进行逻辑判断,返回boolean值
- 判断条件:在初始化Bean时,使用@Conditional(条件类.class)注解
自动配置的关键:Enable注解
Springboot中提供了很多Enable开头的注解,这些注解都是用于动态启动某些功能的,而底层原理是使用@Import注解导入一些类,实现Bean的动态加载
@EnableAutoConfiguration
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import({AutoConfigurationImportSelector.class}) //使用import注解导入配置类,实现Bean的动态加载 public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<?>[] exclude() default {}; String[] excludeName() default {}; }
手动扫描包,让SpringBoot添加其为Bean扫描范围
/** * ComponentScan扫描范围是当前引导类所在包以及其子包 * 如果不是,可使用以下方法获取bean * 1.在当前启动类上添加@ComponentScan("包名“) * 2.使用@Import注解加载类,这些类都会被Sprint创建,并放入Ioc容器 * 3.对Import注解进行封装,让sprintboot识别到并加载进来 */ @SpringBootApplication //@ComponentScan("com.yz.config") //@Import(UserConfig.class) @EnableUser //在EnableUser中封装Import public class SprintbootMyUnApplication {
………………
}
Enbale注解
import com.yz.config.UserConfig; import org.springframework.context.annotation.Import; import java.lang.annotation.*; //这三个是Import的元注解 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(UserConfig.class) //在这里添配置类 public @interface EnableUser { }