Springboot2
发布时间:2023-04-13 12:23:50
来源:腾讯云
参考:https://www.yuque.com/leifengyang/springboot2
参考:https://www.bilibili.com/video/BV19K4y1L7MT?p=1&vd_source=0c3c1f43c75954a15fba4e42c1d7883e
【资料图】
1. HelloWorld
项目结构
1. pom.xml
4.0.0 com.example springboot-test 1.0-SNAPSHOT pom springboot-01-hello org.springframework.boot spring-boot-starter-parent 2.3.4.RELEASE org.springframework.boot spring-boot-starter-web 8 8 UTF-8 org.springframework.boot spring-boot-maven-plugin
2. MainApplication
package com.example;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;/** * 主程序类 * @SpringBootApplication:这是一个springboot应用 */@SpringBootApplicationpublic class MainApplication { public static void main(String[] args) { SpringApplication.run(MainApplication.class, args); }}
3. HelloController
package com.example.controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class HelloController { @RequestMapping("/hello") public String home() { return "Hello world"; }}
4. 启动服务,浏览器访问
2. Springboot特点
2.1 依赖管理
2.1.1 父项目做依赖管理
org.springframework.boot spring-boot-starter-parent 2.3.4.RELEASE 点进spring-boot-starter-parent看到: org.springframework.boot spring-boot-dependencies 2.3.4.RELEASE 点进spring-boot-dependencies看到: 几乎声明了所有开发中常用的依赖的版本号
2.1.2 开发导入starter场景启动器
会见到很多spring-boot-starter-*: *就代表某种场景 只要引入starter,这个场景的所有常规需要的依赖我们都自动引入SpringBoot所有支持的场景:https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter见到的 *-spring-boot-starter: 第三方为我们提供的简化开发的场景启动器。 所有场景启动器最底层的依赖 org.springframework.boot spring-boot-starter 2.3.4.RELEASE compile
2.1.3 无需关注版本号,自动版本仲裁
spring-boot-dependencies几乎声明了所有开发中常用的依赖的版本号,引入依赖默认都可以不写版本=>自动版本仲裁2.1.4 可以修改版本号
引入非版本仲裁的jar,要写版本号查看spring-boot-dependencies里面规定当前依赖的版本用的 key,版本不一致=>在当前项目里面重写配置 5.1.43
2.2 自动配置
2.2.1 自动配好Tomcat
引入Tomcat依赖 org.springframework.boot spring-boot-starter-tomcat 2.3.4.RELEASE compile
配置Tomcat ???2.2.2 自动配好SpringMVC
引入SpringMVC全套组件:spring-boot-starter-web引入自动配好SpringMVC常用组件(功能)2.2.3 自动配好Web常见功能
SpringBoot帮我们配置好了所有web开发的常见场景
dispatcherServlet字符编码:characterEncodingFilter文件上传:multipartResolver2.2.4 默认的包结构
主程序所在包及其下面的所有子包里面的组件都会被默认扫描进来无需以前的包扫描配置想要改变扫描路径,@SpringBootApplication(scanBasePackages="com.example")或者@ComponentScan 指定扫描路径@SpringBootApplication等同于@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan("com.example")
2.2.5 各种配置拥有默认值
application.properties:默认配置最终都是映射到某个类上,如:MultipartPropertiesapplication.properties配置文件的值最终会绑定每个类上,这个类会在容器中创建对象2.2.6 按需加载所有自动配置项
非常多的starter引入了哪些场景这个场景的自动配置才会开启SpringBoot所有的自动配置功能都在 spring-boot-autoconfigure 包里面package com.example;import org.springframework.boot.SpringApplication;import org.springframework.boot.SpringBootConfiguration;import org.springframework.boot.autoconfigure.EnableAutoConfiguration;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.ConfigurableApplicationContext;import org.springframework.context.annotation.ComponentScan;/**@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan("com.example")等价于@SpringBootApplication(scanBasePackages="com.example") 默认扫描主程序所在的包*/@SpringBootApplicationpublic class MainApplication { public static void main(String[] args) { //1、返回我们IOC容器 ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args); //2、查看容器里面的组件 String[] names = run.getBeanDefinitionNames(); for (String name : names) { System.out.println("组件:" + name); } }}
3. 自动配置注解
3.1 @Configuration
Full模式与Lite模式项目结构Userpackage com.example.bean;public class User { private String name; private int age; private Pet pet; public User() { } public User(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Pet getPet() { return pet; } public void setPet(Pet pet) { this.pet = pet; } @Override public String toString() { return "User{" + "name="" + name + "\"" + ", age=" + age + ", pet=" + pet + "}"; }}
Petpackage com.example.bean;public class Pet { private String name; public Pet() { } public Pet(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Pet{" + "name="" + name + "\"" + "}"; }}
MainConfigpackage com.example.config;import com.example.bean.Pet;import com.example.bean.User;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configuration(proxyBeanMethods=true) // 告诉springboot这是一个配置类public class MainConfig { @Bean public User userBean(){ User u = new User("zhangsan", 18); u.setPet(petBean()); return u; } @Bean public Pet petBean(){ Pet p = new Pet("cat"); return p; }}
MainApplicationpackage com.example;import com.example.bean.Pet;import com.example.bean.User;import com.example.config.MainConfig;import org.springframework.boot.SpringApplication;import org.springframework.boot.SpringBootConfiguration;import org.springframework.boot.autoconfigure.EnableAutoConfiguration;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.ConfigurableApplicationContext;import org.springframework.context.annotation.ComponentScan;/**@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan("com.example")等价于@SpringBootApplication(scanBasePackages="com.example") 默认扫描主程序所在的包*/@SpringBootApplicationpublic class MainApplication { public static void main(String[] args) { //1、返回我们IOC容器 ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args); //2、查看容器里面的组件 String[] names = run.getBeanDefinitionNames(); for (String name : names) { System.out.println("组件:" + name); } // 3. 从容器中获取组件 User user = (User) run.getBean("userBean"); User user2 = (User) run.getBean("userBean"); System.out.println("组件默认是单例的:" + (user==user2)); // 4. 配置类也是组件 MainConfig bean = run.getBean(MainConfig.class); System.out.println("配置类本身也是组件:" + bean); // 5. proxyBeanMethods = true, mainConfig就是被增强的代理对象 // springboot总会检查组件是否在容器中存在 // 保持组件单实例 // 外部无论对配置类中的这个组件注册方法调用多少次,获取的都是之前注册容器中的单实例对象 User user3 = bean.userBean(); User user4 = bean.userBean(); System.out.println("主配置是否为代理对象: "+(user3==user4)); // full: proxyBeanMethods=true => 应用场景: 解决组件依赖 // lite: proxyBeanMethods=false => 配置类在容器中不会保存代理对象,在外边调用方法,每次都会产生一个新对象 // Full: 保证每个@Bean方法被调用多少次返回的组件都是单实例的。类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式 // Lite: 每个@Bean方法被调用多少次返回的组件都是新创建的。类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断 // 组件依赖必须使用Full模式默认。其他默认是否Lite模式 Pet pet = (Pet) run.getBean("petBean"); System.out.println("proxyBeanMethods组件依赖" + (user.getPet()==pet)); }}
3.2 @Bean、@Component、@Controller、@Service、@Repository
项目结构MainConfigpackage com.example.config;import com.example.bean.Pet;import com.example.bean.User;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class MainConfig {}
Userpackage com.example.bean;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.stereotype.Component;import javax.annotation.Resource;@Component("userBean")public class User { private String name; private int age; @Autowired private Pet pet; public User() { } public User(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Pet getPet() { return pet; } public void setPet(Pet pet) { this.pet = pet; } @Override public String toString() { return "User{" + "name="" + name + "\"" + ", age=" + age + ", pet=" + pet + "}"; }}
Petpackage com.example.bean;import org.springframework.stereotype.Component;@Component("petBean")public class Pet { private String name; public Pet() { } public Pet(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Pet{" + "name="" + name + "\"" + "}"; }}
MainApplicationpackage com.example;import com.example.bean.Pet;import com.example.bean.User;import com.example.config.MainConfig;import org.springframework.boot.SpringApplication;import org.springframework.boot.SpringBootConfiguration;import org.springframework.boot.autoconfigure.EnableAutoConfiguration;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.ConfigurableApplicationContext;import org.springframework.context.annotation.ComponentScan;/** * @SpringBootConfiguration * @EnableAutoConfiguration * @ComponentScan("com.example") 等价于@SpringBootApplication(scanBasePackages="com.example") * 默认扫描主程序所在的包 */@SpringBootApplicationpublic class MainApplication { public static void main(String[] args) { //1、返回我们IOC容器 ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args); //2、查看容器里面的组件 String[] names = run.getBeanDefinitionNames(); for (String name : names) { System.out.println("组件:" + name); } // 3. 从容器中获取组件 User user = (User) run.getBean("userBean"); User user2 = (User) run.getBean("userBean"); System.out.println("组件默认是单例的:" + (user == user2)); // 4. 配置类也是组件 MainConfig bean = run.getBean(MainConfig.class); System.out.println("配置类本身也是组件:" + bean); // 5. 组件依赖 Pet pet = (Pet) run.getBean("petBean"); System.out.println("组件依赖" + (user.getPet() == pet)); }}
3.3 @Import、@Conditional、@ImportResource、@ConfigurationProperties
@Import:给容器中自动创建出指定类型的组件、默认组件的名字就是全类名
@Conditional:条件装配,满足Conditional指定的条件,则进行组件注入
@ImportResource:原生配置文件引入
@ConfigurationProperties:配置文件中的配置项绑定给组件的属性
项目结构MainApplicationpackage com.example;import ch.qos.logback.core.db.DBHelper;import com.example.bean.Pet;import com.example.bean.User;import com.example.config.MainConfig;import org.springframework.boot.SpringApplication;import org.springframework.boot.SpringBootConfiguration;import org.springframework.boot.autoconfigure.EnableAutoConfiguration;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.ConfigurableApplicationContext;import org.springframework.context.annotation.ComponentScan;import java.util.Arrays;/** @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan("com.example") 等价于@SpringBootApplication(scanBasePackages="com.example") 默认扫描主程序所在的包 *//** * @Import:给容器中自动创建出指定类型的组件、默认组件的名字就是全类名 * @Conditional:条件装配,满足Conditional指定的条件,则进行组件注入 * @ImportResource:原生配置文件引入 * @ConfigurationProperties:配置文件中的配置项绑定给组件的属性 */@SpringBootApplicationpublic class MainApplication { public static void main(String[] args) { //1、返回我们IOC容器 ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args); //2、查看容器里面的组件 String[] names = run.getBeanDefinitionNames(); for (String name : names) { System.out.println("组件:" + name); } // 3. 获取组件 Boolean flag = run.containsBean("petBean"); System.out.println("容器中是否存在petBean组件:" + flag); Boolean flag2 = run.containsBean("userBean"); System.out.println("容器中是否存在userBean组件:" + flag2); // 4. 容器中组件数量 System.out.println("组件数量:" + run.getBeanDefinitionCount()); // 167 }}
mainApplication.xml
Userpackage com.example.bean;public class User { private String name; private int age; private Pet pet; public User() { } public User(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Pet getPet() { return pet; } public void setPet(Pet pet) { this.pet = pet; } @Override public String toString() { return "User{" + "name="" + name + "\"" + ", age=" + age + ", pet=" + pet + "}"; }}
Petpackage com.example.bean;public class Pet { private String name; public Pet() { } public Pet(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Pet{" + "name="" + name + "\"" + "}"; }}
Carpackage com.example.bean;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.stereotype.Component;// @Component // MainConfig中使用了@EnableConfigurationProperties(Car.class),则不用@Component@ConfigurationProperties(prefix = "car")public class Car { private String brand; private double price; public Car() { } public Car(String brand, double price) { this.brand = brand; this.price = price; } public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } @Override public String toString() { return "Car{" + "brand="" + brand + "\"" + ", price=" + price + "}"; }}
MainConfigpackage com.example.config;import ch.qos.logback.core.db.DBHelper;import com.example.bean.Car;import com.example.bean.Pet;import com.example.bean.User;import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;import org.springframework.boot.context.properties.EnableConfigurationProperties;import org.springframework.context.annotation.*;// @ConditionalOnBean(name="petBean") // 有petBean类里的组件注册才生效,否则都不生效@EnableConfigurationProperties(Car.class)// @EnableConfigurationProperties(Car.class)的作用:// 1. 开启Car配置绑定功能// 2. 把这个Car这个组件自动注册到容器中@ImportResource("classpath:mainApplication.xml")@Import({User.class, DBHelper.class})@Configuration(proxyBeanMethods=true) // 告诉springboot这是一个配置类public class MainConfig { @ConditionalOnBean(name="petBean") // 容器中存在petBean才执行userBean的组件注册 @Bean public User userBean(){ User u = new User("zhangsan", 18); u.setPet(petBean()); return u; } // @Bean public Pet petBean(){ Pet p = new Pet("cat"); return p; }}
4. 自动配置原理
4.1 引导加载自动配置类
@SpringBootApplication主要组成:@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan( excludeFilters = {@Filter( type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class}), @Filter( type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class})})public @interface SpringBootApplication {}
4.1.1 @SpringBootConfiguration
@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Configuration // SpringBootConfiguration用来注解 配置类 => MainApplication是一个配置类(核心配置类)public @interface SpringBootConfiguration { @AliasFor( annotation = Configuration.class ) boolean proxyBeanMethods() default true;}
4.1.2 @ComponentScan:包扫描,指定要扫描哪些
两个自定义扫描器4.1.3 @EnableAutoConfiguration
@AutoConfigurationPackage@Import({AutoConfigurationImportSelector.class})public @interface EnableAutoConfiguration {}
@AutoConfigurationPackage@Import({AutoConfigurationPackages.Registrar.class}) // 给容器中导入一个组件// 指定了默认的包规则public @interface AutoConfigurationPackage {}// 利用Registrar给容器中导入一系列组件// 将指定的MainApplication所在包下的所有组件导入进来
@Import({AutoConfigurationImportSelector.class})1. 利用getAutoConfigurationEntry(annotationMetadata)给容器中批量导入一些组件2. 调用List configurations = getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要导入到容器中的配置类3. 利用工厂加载 Map> loadSpringFactories(@Nullable ClassLoader classLoader)得到所有的组件4. 从META-INF/spring.factories位置来加载一个文件。 默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件 spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面也有META-INF/spring.factories 文件里面写死了spring-boot一启动就要给容器中加载的所有配置类
4.2 按需开启自动配置项
spring.factories@Conditional1. 虽然我们127个场景的所有自动配置启动的时候默认全部加载。 META-INF/spring.factories文件中:xxxxAutoConfiguration2. 按照条件装配规则(@Conditional),最终会按需配置。 eg. @ConditionalOnClass({Gson.class})
4.3 修改默认配置
@Bean@ConditionalOnBean(MultipartResolver.class) //容器中有这个类型组件@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) //容器中没有这个名字 multipartResolver 的组件public MultipartResolver multipartResolver(MultipartResolver resolver) { //给@Bean标注的方法传入了对象参数,这个参数的值就会从容器中找。 //SpringMVC multipartResolver。防止有些用户配置的文件上传解析器不符合规范// Detect if the user has created a MultipartResolver but named it incorrectlyreturn resolver;}
SpringBoot默认会在底层配好所有的组件。但是如果用户自己配置了以用户的优先
@Bean@ConditionalOnMissingBeanpublic CharacterEncodingFilter characterEncodingFilter() {}
总结:
SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。xxxxProperties里面拿。xxxProperties和配置文件进行了绑定生效的配置类就会给容器中装配很多组件(pom.xml配置了)只要容器中有这些组件,相当于这些功能就有了定制化配置用户直接自己@Bean替换底层的组件用户去看这个组件是获取的配置文件什么值就去修改。xxxxxAutoConfiguration ---> 组件 ---> xxxxProperties里面拿值 ----> application.properties
4,4 最佳实践
引入场景依赖https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter查看自动配置了哪些(选做)自己分析,引入场景对应的自动配置一般都生效了配置文件中debug=true开启自动配置报告。Negative(不生效)\Positive(生效)是否需要修改参照文档修改配置项https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html#common-application-properties自己分析。xxxxProperties绑定了配置文件的哪些。自定义加入或者替换组件@Bean、@Component。。。自定义器 XXXXXCustomizer;例子resources目录下放一张jpg图片配置文件中添加配置项:spring.banner.image.location=classpath:timg.jpg
4.5 简化开发
4.5.1 lombok
引入依赖 org.projectlombok lombok
idea安装插件使用@ToString // toString@Data // getter、setter@AllArgsConstructor // 全参构造器@NoArgsConstructor // 无参构造器@Slf4j // 日志
例子package com.example.bean;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import lombok.ToString;@ToString // toString@Data // getter、setter@AllArgsConstructor // 全参构造器@NoArgsConstructor // 无参构造器public class LombokBean { private String str; private int i;}
import com.example.bean.LombokBean;import lombok.extern.slf4j.Slf4j;import org.junit.Test;@Slf4jpublic class LombokBeanTest { @Test public void testLombokBean(){ LombokBean o = new LombokBean(); o.setStr("abc"); o.setI(10); System.out.println(o); log.info(o.toString()); }}