SpringBoot是为了简化SSM框架开发过程中繁琐的XML配置,并且SpringBoot内置了小型的Tomcat服务器和相应的jar包,这也解决了我们配置tomcat环境的麻烦。
SpringBoot文档
一切以SpringBoot的官方文档为主,因为SpringBoot的更新迭代快,所以需要关注Spring官网来学习他的新特性,在一些企业中,已经开始使用SpringBoot来代替SSM框架开发流程。
SpringBoot入门
1、环境要求
- jdk8及以上
- Maven3.3以上
- idea(推荐使用)
Maven配置
conf/setting.xml文件<!-- 最新阿里云镜像-->
<mirror>
<id>aliyunmaven</id>
<mirrorOf>*</mirrorOf>
<name>aliyunmaven</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
<!-- 使用jdk设置 -->
<profile>
<id>jdk-1.8</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.8</jdk>
</activation>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
</properties>
</profile>
<!-- 设置本地仓库 -->
<localRepository>D:\enviroment\apache-maven-3.6.3\maven-repo</localRepository>
2、项目搭建
创建Maven项目,在porm.xml中配置以下依赖 ```xmlorg.springframework.boot spring-boot-starter-parent 2.4.1
<a name="eY5H7"></a>
## 3、创建主程序
在java文件夹下创建主程序,作为springboot项目启动的开关
```java
//这是一个Springboot应用
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class,args);
}
}
4、编写Controller业务类
原先的注解配置
@Controller
public class HelloController {
@ResponseBody
@RequestMapping("/")
public String handle01(){
return "Hello,SpringBoot!";
}
}
Controller:返回的是页面,用于处理请求
Spring4以后,后来的注解
@RestController
public class HelloController {
@RequestMapping("/")
public String handle01(){
return "Hello,SpringBoot!";
}
}
引入了新注解RestController,它是Controller和ReSponseBody配合使用的自定义注解
5、结果
SpringBoot简化配置
可以在配置文件中,修改springboot的配置,以键值对的形式来改变,极大的简化了我们的配置。
在SpringBoot官方文档中,我们可以设置以下的配置信息
SpringBoot简化部署
以jar打包方式进行目标服务器运行
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
SpringBoot特性
依赖管理
父项目依赖管理
子项目如果依赖父项目的依赖,则会继承父项目的依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.1</version>
</parent>
spring-boot-starter-parent依赖的父依赖为spring-boot-dependencies
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.4.1</version>
</parent>
spring-boot-starter-parent中声明了各种版本号的依赖,自动版本仲裁机制
starter场景启动器
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
在springboot中配置了各种场景启动器:如jdbc,消息队列,redis等,引入依赖的格式是spring-boot-starter-*
各种场景启动器都会依赖于
spring-boot-starter
第三方starter
*-spring-boot-starter:如这样格式的starter,则为第三方简化开发的场景启动器
修改版本号
由于spring-boot-dependencies中声明了各版本的依赖,所以一般不需要声明版本依赖,如果是非版本仲裁的依赖,则需要自己声明。
如果声明的版本号不是自己需要的,则可以通过maven仓库修改版本号,如修改mysql驱动版本
<properties>
<mysql.vesrion>5.1.47</mysql.vesrion>
</properties>
自动配置
starter-web中自动引入tomcat
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.4.1</version>
<scope>compile</scope>
</dependency>
自动引入了springmvc的全套主键
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.2</version>
<scope>compile</scope>
</dependency>
springboot配好了所有web场景的组件
- 如文件上传,编码方式,视图解析等常用组件
默认包结构:主程序所在的包及其所有子包的组件都会被扫描进来
- 改变扫描路径,设置@SpringBootApplication(scanBasePackages = “com.learn”)
- 或者使用ComponentScan
正常配置下@SpringBootApplication(scanBasePackages = "com.learn")
等同于
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.learn")
报错
改变扫描路径
各种配置都有默认值
- 默认的最终配置的都是映射到某个类上
- 而配置文件中配置的值都会绑定到相应类上,这个类最终会在容器中创建对象
- 按需加载自动配置项
- 只有引入了相应的场景启动器,springboot才会把相应场景加载进来
底层注解
spring配置
实体类
package com.learn.springboot.bean;
public class User {
private String name;
private int 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;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
容器配置bean对象信息:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="User" class="com.learn.springboot.bean.User">
<property name="name" value="张三"></property>
<property name="age" value="18"></property>
</bean>
</beans>
SpringBoot配置
package com.learn.springboot.config;
import com.learn.springboot.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration //告诉SpringBoot这是一个配置类
public class MyConfig {
@Bean //给容器添加组件,以方法名作为id,以返回类型作为class。
public User user01(){
//返回的容器实例
return new User("张三",18);
}
}
这是SpringBoot添加组件的过程:
- Configuration申明配置类
- Bean申明配置bean对象函数
- 返回相应类型示例
Configuration注解
@Configuration(proxyBeanMethods = true)
proxyBeanMethods属性表示是否开启代理对象。
- 如果为true,则上文中的MyConfig类是spring中的cglib代理的增强类,因此我们获取到的bean对象的代理对象,是单实例的。
- 如果是false,则MyConfig类是默认类型,我们获得的bean是多实例的。
public class MainApplication {
public static void main(String[] args) {
//IOC容器
ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
MyConfig bean = run.getBean(MyConfig.class);
System.out.println(bean);
User user = bean.user01();
User user1 = bean.user01();
System.out.println(user==user1);
}
}
当proxyBeanMethods = true时
当proxyBeanMethods = false时
可以看到,我们两次比较user和user1对象,得到的结果都是不同的,并且,bean对象相应class也是不同的。
全配置和轻量配置
- Full(proxyBeanMethods = true)
- Lite(proxyBeanMethods = false)
注:全配置和轻量配置主要解决的是JaveBean依赖问题,如果是true,是单实例情况,这被依赖的组件不会被多次创建,是多实例的。
如果User中有其他类,例如
@Configuration(proxyBeanMethods = true) //告诉SpringBoot这是一个配置类
public class MyConfig {
@Bean //给容器添加组件,以方法名作为id,以返回类型作为class。
public User user01(){
User user = new User("张三", 18);
user.setPet(tom());
//返回的容器实例
return user;
}
@Bean
public Pet tom(){
return new Pet("tom");
}
}
主程序
public class MainApplication {
public static void main(String[] args) {
//IOC容器
ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
MyConfig bean = run.getBean(MyConfig.class);
System.out.println(bean);
User user = bean.user01();
Pet tom = bean.tom();
System.out.println(user.getPet()==tom);
}
}
当proxyBeanMethods = true,结果
当proxyBeanMethods = false,结果
可以看到,全配置和轻量配置的结果是不同的。
全配置和轻量配置的使用时间:
- 当存在bean依赖时,使用full模式,保证依赖的正确性。
- 当配置类中,没有bean依赖时,用lite模式,容器能够快速启动,减少判断
Import注解
给容器创建bean实例。
注解内容 ```java @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Import { Class<?>[] value(); }
Import注解中的value属性,是一个class对象的数组。
<a name="Bmdkp"></a>
#### 实例展示
**配置类**
```java
@Import({User.class})
@Configuration(proxyBeanMethods = false) //告诉SpringBoot这是一个配置类
public class MyConfig {
@Bean //给容器添加组件,以方法名作为id,以返回类型作为class。
public User user01(){
User user = new User("张三", 18);
//返回的容器实例
return user;
}
}
主程序
@SpringBootApplication(scanBasePackages = "com.learn")
public class MainApplication {
public static void main(String[] args) {
//IOC容器
ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
MyConfig bean = run.getBean(MyConfig.class);
String[] user = run.getBeanNamesForType(User.class);
for (String use : user) {
System.out.println(use);
}
}
}
结果
我们可以看到,我们使用@Import({User.class}),在lite模式下,自动为我们创建了一个全类名的bean。
注:实测,采用@Import({User.class,User.class})方式,或proxyBeanMethods = true 这样都只会创建一个全类名的bean。
Conditional注解
实例演示
ConditionalOnBean
@Configuration(proxyBeanMethods = true) //告诉SpringBoot这是一个配置类
public class MyConfig {
@Bean
public Pet tom(){
return new Pet("tom");
}
@ConditionalOnBean(name="tom")
@Bean //给容器添加组件,以方法名作为id,以返回类型作为class。
public User user01(){
User user = new User("张三", 18);
//返回的容器实例
return user;
}
}
如果配置类中没有name=tom的bean实例的话,则user01也不初始化。
主程序
@SpringBootApplication(scanBasePackages = "com.learn")
public class MainApplication {
public static void main(String[] args) {
//IOC容器
ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
boolean user01 = run.containsBean("user01");
System.out.println("容器中是否有user01:"+user01);
}
}
结果
注:配置类是从上往下顺序生成bean实例的,ConditionalOnBean查找标注类的上方是否存在name=tom的bean实例,因此,如果tom实例函数定义在user01下方的话,也会找不到tom实例,从而不生成user01实例。
ConditionalOnClass不需要按顺序编写。
ImportResource注解
实例演示
在resources文件夹下写入一个xml文件。
beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user02" class="com.learn.springboot.bean.User">
<property name="name" value="张三"></property>
<property name="age" value="18"></property>
</bean>
</beans>
有个user02的bean实例。
配置类
@ImportResource("classpath:beans.xml")
@Configuration(proxyBeanMethods = true) //告诉SpringBoot这是一个配置类
public class MyConfig {
@Bean //给容器添加组件,以方法名作为id,以返回类型作为class。
public User user01(){
User user = new User("张三", 18);
//返回的容器实例
return user;
}
}
在配置类中申明注解@ImportResource(“classpath:beans.xml”)。
主程序
@SpringBootApplication(scanBasePackages = "com.learn")
public class MainApplication {
public static void main(String[] args) {
//IOC容器
ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
String[] user = run.getBeanNamesForType(User.class);
for (String use : user) {
System.out.println(use);
}
}
}
结果
可以看到beans.xml中的user02也在容器实例化了。
ConfigurationProperties注解
将properties文件中的信息加入到bean中,并实现实例化。
实例演示
properties文件
myuser.name = springboot
myuser.age = 17
实体类
实体类写入注解加入到容器中,并且去配置文件中寻找前缀为myuser的变量
@Component
@ConfigurationProperties(prefix="myuser")
public class User {
private String name;
private int age;
private Pet pet;
public Pet getPet() {
return pet;
}
public void setPet(Pet pet) {
this.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;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
控制类
@RestController
public class HelloController {
@Autowired
User user;
@RequestMapping("/user")
public User user01(){
return user;
}
}
在服务器页面中查看信息
可以看到,配置文件中的信息添加到容器中去了。
第二种实现方式
使用EnableConfigurationProperties和ConfigurationProperties来实现读取配置类信息
实体类
@ConfigurationProperties(prefix="myuser")
public class User {
private String name;
private int age;
private Pet pet;
public Pet getPet() {
return pet;
}
public void setPet(Pet pet) {
this.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;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
配置类
@Configuration(proxyBeanMethods = false) //告诉SpringBoot这是一个配置类
@EnableConfigurationProperties(User.class)
public class MyConfig {
}
注:发现一个很怪的问题,当容器中有两个User时,自动装配无法装配上的,且如果能装配上,则需要在@Autowired
@Qualifier(“myuser-com.learn.springboot.bean.User”)
下声明该bean的名字。暂时还没找到解决方法,无法对EnableConfigurationProperties下的bean重新命名。