springboot基础
1. 项目创建
(1) idea自动创建
idea要2022及以上版本,因为spring3需要的java版本至少是17
选择需要的依赖
完成后就可以自动创建springboot项目
(2) 手动创建
手动创建springboot项目
- 创建普通maven项目
- 引入springboot的依赖
- 创建springboot的启动类
①创建普通maven项目
② 引入springboot依赖
springboot相当于帮助我们完成了spring中复杂的配置,我们在创建springboot时直接继承springboot的父坐标就行
2. 配置文件使用
在使用idea创建项目时,会自动创建相关的配置文件,而且这个配置文件springboot项目会自动识别到
具体有哪些配置可以在官方文档中查看----配置项查看
(1) 配置文件格式
(2) 配置文件书写
对于数组类型的数据
properties文件中,不能直接填写中文,会导致乱码,但yml文件支持中文编码
c格式
zhangsan.hobby[0] = lanqiu0
zhangsan.hobby[1] = lanqiu01
zhangsan.hobby[2] = lanqiu02
yml格式
zhangsan:
hobby:
- lanqiu0
- lanqiu1
- lanqiu2
(3) 值的获取
使用Value注解
使用ConfigurationProperties 注解
3. bean的管理
(1) bean的扫描
在spring中bean的扫描是通过在配置文件中或者通过注解引入扫描器完成的。
标签:
<context:component-scan basepackage="com.itheima"/>
注解:
@ComponentScan(basePackages="com.itheima")
而在springboot中已经自己定义好了扫描器,默认的扫描范围是启动类所在的包,在包之外的bean无法被扫描到。
主要原因是@SpringBootApplicatio注解
是组合注解,里面包含了配置注解@ComponentScan
如果想要扫描启动类所在包之外的东西,我们可以在启动类中自己加上@ComponentScan
注解
!!!遵循约定优于配置,一般不会手动加,代码写在启动类对应包下即可!!!
@ComponentScan(value = "springbootquickstart")
@SpringBootApplication
public class SpringbootquickstartApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootquickstartApplication.class, args);
}
}
(2) bean的注册
一般的方法是通过注解来告诉框架这个类对象由spring来管理
注解 | 说明 | 位置 |
---|---|---|
@Component | 声明bean的基础注解 | 不属于以下三类时,用此注解 |
@Controller | @Component的衍生注解 | 标注在控制器类上 |
@Service | @Component的衍生注解 | 标注在业务类上 |
@Repository | @Component的衍生注解 | 标注在数据访问类上(由于与mybatis整合,用的少) |
springboot中很少使用配置文件,所以用配置文件注册bean的方法就不说了。
以上的方法需要把注解加在源码对应的类上面,但是对于外来引入的包,不能修改源码,这种情况下就不能通过上面的几个注解来对bean进行注册。
@Bean注解
@bean
注解主要用在方法上面,告诉spring把这个方法返回的对象放入spring容器中,对象的 id 默认为方法的名字(也可以在Bean注解中的value属性设置id)。
一般bean
注解放在@Configuration
注解的配置类中,来统一管理bean对象的注册。
package springbootquickstart.springbootquickstart.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springbootquickstart.springbootquickstart.pojo.User;
@Configuration
public class commonconfig {
// 默认的id是方法名,需要指定bean的id就使用@Bean(value = "ID")
@Bean(value = "lisi")
public User guser(){
return new User();
}
//如果方法的内部需要使用到ioc容器中已经存在的bean对象,那么只需要在方法上声明即可,spring会自动的注入
@Bean(value = "lisi")
public User guser(Home home){
System.out.println(home);
return new User();
}
}
@Import注解
当配置类在启动类包之外时,可以通过在启动类之上添加@Import
注解来导入配置类(spring会自动导入配置类里面的bean)
当需要导入多个配置类时可以给注解传入数组参数
@Import({config1.class, config2.class, ... , confign.class})
上面的导入方式会让启动类变得臃肿,一般方法是通过实现ImportSelect接口的selectimports方法(这个方法返回值就是String数组,数组里面保存配置类的全类名)
具体实现
通过这种方法减少了启动类的代码,但是配置还是写死在了代码中,不利于维护,所以需要把各个配置类的类名写入到一个配置文件中,从配置文件中读取。
在resources文件夹中创建common.imports保存配置类的全类名。
然后通过代码读取common.imports
package config;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
public class conmonImportSelect implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
List<String> imports = new ArrayList<>();
InputStream is = conmonImportSelect.class.getClassLoader().getResourceAsStream("common.imports");
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line = null;
try {
while ((line = br.readLine()) != null){
imports.add(line);
System.out.println(line);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
if (br != null){
br.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return imports.toArray(new String[0]);
}
}
自定义组合注解减少配置类注解
最好,如果启动类之上的注解太多,可以使用自定义组合注解的方式减少启动类的代码
注解一般放在名为anno的包下面。像这种自定义的组合注解一般命名也是命名为EnableXXX
package springbootquickstart.springbootquickstart.anno;
import org.springframework.context.annotation.Import;
import springbootquickstart.springbootquickstart.config.conmonImportSelect;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(conmonImportSelect.class)
public @interface EnableCommonConfig {
}
这样在启动类之上加上这个注解就行
package springbootquickstart.springbootquickstart;
import springbootquickstart.springbootquickstart.anno.EnableCommonConfig;
import springbootquickstart.springbootquickstart.config.conmonImportSelect;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Import;
import springbootquickstart.springbootquickstart.pojo.User;
@SpringBootApplication
@EnableCommonConfig
public class SpringbootquickstartApplication {
public static void main(String[] args) {
ApplicationContext applicationContext = SpringApplication.run(SpringbootquickstartApplication.class, args);
System.out.println(applicationContext.getBean(User.class));
System.out.println(applicationContext.getBean("lisi"));
}
}
(3) 条件注册
bean对象值初始化
① 在创建bean对象的方法中给对象赋值
这种方法把值写死在了代码里,不利于维护。我们可以使用第2节学到的配置文件来传值(使用Value注解和ConfigurationProperties 注解)
user:
name1: nihao
age: 18
使用Value注解对应读取代码为
@Configuration
public class commonconfig {
// 默认的id是方法名,需要指定bean的id就使用@Bean(value = "ID")
@Bean
public User guser(@Value("${user.name1}") String name, @Value("${user.age}") int age){
System.out.println(name);
User user = new User(name, age);
return user;
}
}
上面使用配置文件初始化中存在一些问题,比如配置文件中不存在对应的属性就会发生报错。
而且存在一些情况,就是依赖关系,如果A存在,B才会创建。或者一些排斥情况,A存在则B就不能创建。
对以上的问题,springboot提供了@Conditional注解和很多对应的子注解来解决上面的问题。
注解 | 说明 |
---|---|
@ConditionalOnProperty | 配置文件中存在对应的属性,才声明该bean |
@Conditional0nMissingBean | 当不存在当前类型的bean时,才声明该bean |
@ConditionalOnClass | 当前环境存在指定的这个类时,才声明该bean |
@ConditionalOnProperty注解
需要给定参数perfix用于定位配置文件的值前缀
value数组用于指示必须要的属性,如果value中的属性在配置文件中没有,这不会创建这个bean;
@Configuration
public class commonconfig {
// 默认的id是方法名,需要指定bean的id就使用@Bean(value = "ID")
@ConditionalOnProperty(prefix = "user",value = {"name", "age"})
@Bean
public User guser(@Value("${user.name1}") String name, @Value("${user.age}") int age){
System.out.println(name);
User user = new User(name, age);
return user;
}
}
@Conditional0nMissingBean
当不存在参数中类型bean时,才声明该bean
参数可以是单个.class。也可以是 .class数组
@Configuration
public class commonconfig {
// 默认的id是方法名,需要指定bean的id就使用@Bean(value = "ID")
// @ConditionalOnProperty(prefix = "user",value = {"name", "age"})
@ConditionalOnMissingBean(Address.class)
@Bean
public User guser(@Value("${user.name1}") String name, @Value("${user.age}") int age){
System.out.println(name);
User user = new User(name, age);
return user;
}
}
@ConditionalOnClass
在没有参数里面的类时创建bean。注意这里是没有类,而不是对象。没有类是只文件里面压根没有,没有对象是有代码只不过没创建
参数主要有两个都是数组类型的,name和value。区别在于name传入的是类的全限定名字。value传入的是.class。
测试的话
@Configuration
public class commonconfig {
// 默认的id是方法名,需要指定bean的id就使用@Bean(value = "ID")
// @ConditionalOnProperty(prefix = "user",value = {"name", "age"})
ConditionalOnClass(name = "org.springframework.web.servlet.DispatcherServlet";
@Bean
public User guser(@Value("${user.name1}") String name, @Value("${user.age}") int age){
System.out.println(name);
User user = new User(name, age);
return user;
}
}
在没有引入springboot中对应的web依赖的时候是不存在DispatcherServlet这个类的,这时是不会创建对应的bean的。
引入对应的依赖后,就能创建对应的类。
注意,不要用user.name做键值,比如会读取计算机自己的用户名
4. springboot自动配置
源码跟踪
自动配置需求:在jar包中要有配置类,自动配置类,imports配置文件,在imports配置文件中加入自动配置类的全限定名称。
面试问题:Springboot中如何实现自动配置
自定义starts
在实际开发中,经常会定义一些公共组件,提供给各个项目团队使用。而在SpringBoot的项目中,一般会将这些公共组件封装为SpringBoot 的starter.
主要有两个模块
一个自动配置功能模块,这里需要实现自动配置类那些
依赖管理模型是进行依赖管理的,需要引入自动配置模型(一般情况下会把依赖管理模块中引入的依赖再重新引入一般,便于管理)。这个模块只需要pom配置文件就行
自定义一个mybatis的starter
(1) 创建 dmybatis-spring-boot-autoconfigure 模块,提供自动配置功能,并自定义配置文件 META-NF/spring/xxx.imports
①创建模块
注意这里创建普通的mavne工程就行,创建spring的话也一样需要修改配置文件
②完善目录
③引入依赖
<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.example</groupId>
<artifactId>springbootlearing</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>dmybatis-spring-boot-autoconfigure</artifactId>
<packaging>jar</packaging>
<name>dmybatis-spring-boot-autoconfigure</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- spring 起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>3.3.3</version>
</dependency>
<!-- jdbc起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>3.3.3</version>
</dependency>
<!-- mybatis依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.13</version>
</dependency>
<!-- mybatis和spring的结合依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
④ 在config包下实现自动配置类
类上要加AutoConfiguration 注解,说明这个类是自动配置类,类里面需要的bean方法上一定要加@Bean注解
方法中需要的对象可以直接当参数传入,spring会执行自动注入
package org.example.config;
import org.apache.ibatis.annotations.Mapper;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurationPackage;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.context.annotation.Bean;
import javax.sql.DataSource;
import java.util.List;
import java.util.Map;
@AutoConfiguration //表示当前是一个自动配置类
public class MybatisAutoConfig {
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(BeanFactory beanFactory){
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
//包扫描的范围:启动类所在的包
List<String> packages = AutoConfigurationPackages.get(beanFactory);
mapperScannerConfigurer.setBasePackage(packages.get(0));
//扫描注解
mapperScannerConfigurer.setAnnotationClass(Mapper.class);
return mapperScannerConfigurer;
}
}
⑤在resourece中创建 META-NF/spring/xxx.imports配置文件
METE-NF文件夹名字那些一定不能随便取,不然找不到路径
imports文件的名字太长,记不住就到目录下面找
创建并写入自动配置类的全类名
至此,自动配置类完成,下面写依赖管理模块
(2) 创建 dmybatis-spring-boot-starter模块,在starter中引入自动配置模块
① 创建模型
②引入依赖
这里需要引入上面完成的自动配置模块的坐标,为了方便管理还需要引入自动配置模块里面所引入的依赖的坐标
代码
<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.example</groupId>
<artifactId>springbootlearing</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>dmybatis-spring-boot-starter</artifactId>
<packaging>jar</packaging>
<name>dmybatis-spring-boot-starter</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- 自动配置模块依赖-->
<dependency>
<groupId>org.example</groupId>
<artifactId>dmybatis-spring-boot-autoconfigure</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- spring 起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>3.3.3</version>
</dependency>
<!-- jdbc起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>3.3.3</version>
</dependency>
<!-- mybatis依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.13</version>
</dependency>
<!-- mybatis和spring的结合依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
到这依赖管理也完成了
(3) 整理项目结构
为了减少冗余代码,删除不需要的文件,减少jar包的大小
到此构建已经全部完成
如果要使用这个starter只需要引入自动配置的坐标即可
<dependency>
<groupId>org.example</groupId>
<artifactId>dmybatis-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
(4) 使用自己构建的mybatis-strater
自己构建的starter功能和mybatis的starter是一样的,mybatis的starter使用和这个过程是一样的。
这节相当于是springboot整合mybatis
①创建模块
② 引入要用的依赖
<?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 https://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>3.3.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>springboot-mybatis</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-mybatis</name>
<description>springboot-mybatis</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.3.0</version>
</dependency>
<!-- 自己定义的mybatis依赖-->
<dependency>
<groupId>org.example</groupId>
<artifactId>dmybatis-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
<!-- <dependency>-->
<!-- <groupId>org.mybatis.spring.boot</groupId>-->
<!-- <artifactId>mybatis-spring-boot-starter</artifactId>-->
<!-- <version>3.0.3</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
在配置文件中配置数据源
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://39.103.208.97:3306/ssm
username: supuser
password: 123456
③在spring的配置文件中配置数据源
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ssm
username: supuser
password: 123456
到这可以说已经整合好了mybatis框架
④ 创建pojo包下的实体类接收结果
表信息
注意,实体属性名要和表中列名一样(如果不一样需要重映射,如果表里面采用下划线命名,实体类里面可以用小驼峰命名,如user_id在实体类里面是userId)
⑤ 创建mapper包下对应的mapper接口用于读取数据库
package com.example.springbootmybatis.mapper;
import com.example.springbootmybatis.pojp.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface UserMapper {
@Select("select * from user where user_id = #{id}")
public User findById(Integer id);
}
有了这个接口spring会自动创建一个userMapper对象来操作数据库
⑥创建service包下对应的服务接口,以及接口的实现
接口中定义要实现的服务
package com.example.springbootmybatis.service;
import com.example.springbootmybatis.pojp.User;
public interface UserServce {
public User findById(Integer id);
}
在service包下创建子包Imp用于实现对应的服务接口
package com.example.springbootmybatis.service.impl;
import com.example.springbootmybatis.mapper.UserMapper;
import com.example.springbootmybatis.pojp.User;
import com.example.springbootmybatis.service.UserServce;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImp implements UserServce {
@Autowired
private UserMapper userMapper;
@Override
public User findById(Integer id) {
System.out.println(id);
User user = userMapper.findById(id);
System.out.println(user.toString());
return user;
}
}
直接调用UserServiceImp 中的对应方法就能执行对应的功能。
参考:
Java获取properties文件中的数组/list类型
SpringBoot3+Vue3全套视频教程
为什么读取配置文件中的user.name会得到计算机名
idea中配置maven没提示怎么处理