一、SpringBoot概念
通过以前对JavaEE中Spring的学习,Spring越来越复杂化了,而且之前自己在xml文件中真的配置的东西太多了,而且有时候整合第三方框架还会有配置问题,让我前期的开发变得更难了,这也的确是大大降低了我的效率,会很影响后续的学习,太过于占用时间。
之前听过SpringBoot这样一个框架,我也知道,我后续必定会用到这个更为方便的框架,只不过无论如果我都得从基础开始慢慢学起,这样才能对这个对我后续的开发能手------SpringBoot有更深层次的认识。
我看了大概二十几篇的csdn博客学习资料,要么是SpringBoot基础的博客,要么就是构建项目的步骤博客,要么就是各种配置的博客,毕竟每个人说的不一样,最终我知道了SpringBoot并不是用来替代Spring的,而是一个和Spring框架紧密结合、用于提升Spring开发者体验的工具。
而且学习到了它里面有很多常用的第三方配置,SpringBoot应用中这些第三方库几乎能做到零配置,开箱即用。大部分的SpringBoot项目都只写非常少的Java配置代码,就可以极大的增加Java开发人员对业务逻辑的专注。
二、它的优势
2.1 首先我先看了Spring的官方网站 Spring | Home。
可以看到官方对SpringBoot的解释,让我记忆最深刻的就是那个Build Anything,就是创建一切。SpringBoot是要尽可能快地启动并且运行,我们刚开始只需要一些前期配置。
而且我还看到官方两个重要的,分别是
- SpringCloud:Coordinate Anything 协调任何事情。
- SpringCloud Data Flow:Connect everything 连接任何东西。
官网对这两个、加上SpringBoot的解释非常合适,也不难发现,Spring官方对这三个技术非常重视,也就预示着我后续必须抓住这三个重点区学习。
2.2 然后我从自己对SpringBoot优点的理解分析一下。
Boot这个单词是引导,所以Spring Boot旨在帮助开发人员快速搭建Spring框架。Spring Boot继承了Spring框架的所有优势,使得Spring使用起来更方便。
它有以下优点:
- 使用更少的配置文件,而且没有web.xml文件。我只需要添加Configuration注释的类,然后添加用Bean注释的方法,Spring 就会自动加载对象,而且还能像以前一样进行管理。
- 我还可以将Autowired添加到bean方法中,让Spring 自动装入需要的依赖关系中。
- 提供了很多的默认值,可以快速地开始开发。
- 避免了大量的Maven导入,我还从别的博客里面知道了,他们项目开发遇到的问题,就是SpringBoot能解决各版本冲突的问题。
- 使用了JavaConfig,也有助于避免使用XML。
- 没有单独的Web服务器的需求。也就表示我以后都不用再启动Tomcat等任何其他东西了。
- 环境的哪些配置使用某些属性,可以将正在使用的环境传递到应用程序。比如命令行参数配置是-Dspring.profiles.active={enviornment} 。在 加 载 主 应 用 程 序 属 性 文 件 后 , Spring 将 在 (application-{environment}.properties)中加载后续的应用程序属性文件。
- 综上,也就是,能大大地减少开发的复杂程度。
2.3 它还简化了编码、配置、部署
2.3.1 简化编码
之前我创建Web项目的时候,用Spring的话,每次我都必须先在pom.xml里面添加很多的依赖,但是SpringBoot直接就是!!快速启动一个Web容器,我只需要在pom文件中添加个 starter-web 依赖就可以了。
我举个例子
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
点击进入该依赖后可以看到,Spring Boot 这个 starter-web 已经包含了多个依赖,包括之前在 Spring 工程中需要导入的依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<scope>compile</scope>
</dependency>
在这些基础学习后,我发现SpringBoot确实大大简化了编码,不用一个个导入依赖。
2.3.2 简化配置
spring虽然是轻量级,但是他的配置还是太多了,也听网上那些人说这就是配置地域。
各种XML、Annotation让人眼花缭乱,再加上配置一多起来,代码报错了也不好找出错的地方。
新建一个类,不用@Service注解,他就是个普通的类,但是如果加上@Configuration和@Bean注解,就可以让它成为一个Bean交给Spring去管理。
public class TestService {
public String sayHello () {
return "Hello Spring Boot!";
}
}
//对应的配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JavaConfig {
@Bean
public TestService getTestService() {
return new TestService();
}
}
- @Configuration 表示该类是个配置类
- @Bean 表示该方法返回一个Bean
在其他地方如果需要使用该 Bean,和原来一样,直接使用@Resource 注解注入进来就能使用,非常方便。
配置方面:Spring有多个xml和properties,但是SpringBoot只需要一个application.yml。
2.3.3 简化部署
我之前用Spring部署项目时,都得在服务器上部署tomcat,然后把项目打包成war包扔到tomcat里面。
但是现在接触了SpringBoot之后,我根本不需要再去弄什么tomcat,真的很方便。
我查阅了资料,发现是SpringBoot里面有内嵌的tomcat,自己只需要把项目打成jar包,再用 java -jar xxx.jar 直接就可以启动。(注意:环境变量中要有 JDK )
三、开发步骤
3.1 配置它的JDK
3.2 构建SpringBoot工程
- 用IDEA构建
在IDEA里面,点 File,然后 New--> Project,选 Spring Initializr,选JDK版本,Next
到了项目的配置信息部分了。
Group:填企业域名,一般采用域名反装的方式定义,例如使用 com.xu
Artifact:填项目名称,例如工程名 demo,如果项目规模较大这里可能会采用模块名称
Dependencies:添加所需依赖信息,根据情况添加就行
- 官方构建
本来前几次都是用IDEA创建的,后面在别人的博客里看到的用官网构建这个方法,就去试了试,反正第一印象就是界面又简洁又好看,我还挺喜欢的!他那个官方的构建项目网址是 Spring Initializr 或者用这个Cloud Native App Initializer (aliyun.com) 也可以。
大题步骤就是输入SpringBoot版本、Group、Artifact信息、项目依赖,然后点GENERATE。
3.3 maven的配置
点开File,然后settings,搜索maven,把本地maven配置上去。
可以配置一个国内阿里的镜像,这样的话下载maven依赖的速度能快很多。
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>*</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
四、Spring Boot的项目结构
一共三个模块,分别如下:
- src/main/java 路径:主要编写业务程序
- src/main/resources 路径:存放静态文件和配置文件
- src/test/java 路径:主要编写测试程序
Demo01Application:默认情况下会创建的一个启动类,上面有个@SpringBootApplication注解,表示这是SpringBoot的启动类。
注意:后续开发中定义的包必须是主包的子包。就比如说主包如果是com.xu,后面的包名称就是com.xu.biz业务、com.xu.dao等等。
但是我也从网上了解到,进入企业的第一件事就是了解公司的编程规范,到时候以这个为标准就可以。
mport org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController //复合注解@Controller+@ResponseBody,@Controller 用于声明当前类是一个控制器类,而且通过自动组件扫描会成为受管 bean。
@ResponseBody //用于声明当前类方法的返回值会自动转换为 JSON格式
@RequestMapping("/start") //针对当前类中的所有方法声明名空间。定义@RequestMapping 时如果没有属性名称则默认名称为 value
//,value 实际上是 path 属性的别名,只有一个属性 value 时,value 可以省略;
//如果多个属性时则 value 不能省略。如果不定义 method 请求方法时,则所有的请求方法均可匹配
public class StartController {
@RequestMapping("/springboot") //针对方法进行地址映射,这里的请求 URL 地址为/start/springboot,不是/springboot
public String startSpringBoot() {
return "Welcome to the world of Spring Boot!";
}
}
运行一下main方法,启动项目,去浏览器,输入localhost:8080/start/springboot,显示的是Welcome to the world of Spring Boot ! 也就是我的这个项目已经启动成功了。
如果想要修改端口号,不从8080进的话,就在application.properties中用server.port指定想用的端口号。
但是我第一次用这个application.properties的时候,都是下面这样一堆很长很长的,看起来确实很烦人。
我也是继续在网上查,居然还有意外的收获,我查到它可以改成application.yml,是有那种自动缩进形式的。这种形式我读自己的配置文件的话,很容易阅读和理解。读别人的配置文件也会很方便。
就是下面图片这样:
五、SpringBoot返回Json数据
5.1 XML文件的解析
常见的解析工具:
有 DOM4j、JDOM等,除此之外,Java还提出了JAXP规范型的:
- DOM:将标记语言文档一次性加载进入内存中,在内存中形成一颗 DOM 树(服务器端常用)
优点:操作方便,可以对文档进行 CRUD 的所有操作
缺点:一次性加载进入内存形成 DOM 树,非常消耗资源 2. SAX:逐行读取,基于事件驱动(安卓终端常用) * 优点:不消耗资源 * 缺点:只能读取,不能增删改
- SAX:逐行读取,基于事件驱动(安卓终端常用)
优点:不消耗资源
缺点:只能读取,不能增删改
public class Test2 {
public static void main(String[] args) throws Exception {
SAXParserFactory parserFactory = SAXParserFactory.newInstance();
SAXParser saxParser = parserFactory.newSAXParser();
final List<Student> studentList = new ArrayList<Student>();
saxParser.parse("xml02/students.xml", new DefaultHandler() {
private Student student=null;
private int flag=0;
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if ("student".equals(qName)) {
student=new Student();
String id=attributes.getValue("id");//获取当前标签的指定名称的属性
if(id!=null && id.trim().length()>0)
student.setId(id);
}else if("name".equals(qName))
flag=1;
else if("age".equals(qName))
flag=2;
else if("sex".equals(qName))
flag=3;
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
String tmp = new String(ch, start, length);
if(tmp!=null && tmp.trim().length()>0){
if(flag==1)
student.setName(tmp.trim());
else if(flag==2){
Integer kk=Integer.parseInt(tmp.trim());
student.setAge(kk);
}else if(flag==3)
student.setSex(tmp.trim());
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
flag=0;
if("student".equals(qName))
studentList.add(student);
}
});
studentList.forEach(System.out::println);
}
}
前面的学习中,我都是用的xml作为传输数据的交互格式,比如webservice技术,它的解析就有些麻烦。接口和接口之间传输都用的是Json格式,在SpringBoot中接口返回Json格式的数据就比较简单了,他是在Controller中,加上@RestController注解,@RestController是Spring Boot新增的一个复合注解。
以下是底层代码:
Target、Documented、Retention是元注解。
@Target({ElementType.TYPE}) //用于声明注解可以使用在什么地方,Type 表示可以使用在类上
@Retention(RetentionPolicy.RUNTIME) //用于声明注解需要保持到什么阶段,Runtime 表示注解在编译生成的字节码中一直保持到运行时
@Documented
@Controller
@ResponseBody
public @interface RestController {
String value() default "";
}
其中的 @Controller和@ResponseBody可以用一个@RestController代替
- @Controller:用于声明当前类是控制器类
- @ResponseBody:是将返回的数据结构转换为Json格式
所以,使用@RestController 注解就能直接将返回的数据结构转换成Json格式。
SpringBoot里面Json解析技术的框架是jackson,点进pom.xml里面,有一个spring-boot-starter依赖,里面有一个spring-boot-starter-json依赖,里面的代码是:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<scope>compile</scope>
</dependency>
我还学到了SpringBoot会把很多前缀一样的封装到一起,导入前缀的依赖,此依赖下的依赖也会导进来。
5.2 Spring Boot 默认对 Json 的处理
-
5.2.1 我先创建一个User实体类
public class User {
private Long id;
private String username;
private String password;
/* 省略 get、set 和带参构造方法,因为我用了lombok */
}
-
5.2.2 创建Controller类
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/json")
public class JsonController {
@RequestMapping("/user")
public User getUser() {
return new User(1, "徐苗苗", "123456");
}
@RequestMapping("/list")
public List<User> getUserList() {
List<User> userList = new ArrayList<>();
User user1 = new User(1, "徐苗苗", "123456");
User user2 = new User(2, "张三", "123456");
userList.add(user1);
userList.add(user2);
return userList;
}
@RequestMapping("/map")
public Map<String, Object> getMap() {
Map<String, Object> map = new HashMap<>(3);
User user = new User(1, "徐苗苗", "123456");
map.put("作者信息", user);
map.put("博客地址", "xxx");
map.put("CSDN 地址", "https://blog.csdn.net/qq_46258291");
map.put("粉丝数量", 111);
return map;
}
}
-
5.2.3 测试一下结果(不同数据类型返回的json)
写好了接口,分别返回了一个 User 对象、一个 List 集合和一个 Map 集合,其中 Map 集合中的 value 存的是不 同的数据类型。
- 在浏览器中输入:localhost:8080/json/user
返回 json: {"id":1,"username":"徐苗苗","password":"123456"}
- 在浏览器中输入:localhost:8080/json/list
返回 json: [{"id":1, "username":"徐苗苗", "password":"123456"}, {"id":2, "username":"张三", "password":"123456"}]
- 在浏览器中输入:`localhost:8080/json/map
返回 json: {" 作 者 信 息 " : {"id":1, "username":" 徐苗苗 ", "password":"123456"}, "CSDN 地 址 " : "https://blog.csdn.net/qq_46258291", "粉丝数量":111, "博客地址":"xxx"}
可以看出 map 中不管是什么数据类型,都可以转成相应的 json
5.2.4 jackson对null的处理
写代码遇到null也是很常见了,但是有时候我们不希望它给我们返回null,而是比如我们希望返回的是""这种空字符串的样式,下面就是具体的做法:
新建一个jackson的配置类:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import java.io.IOException;
@Configuration //用于声明当前类是一个配置类,最新的注解为@SpringBootConfiguration
public class JacksonConfig {
@Bean //用于在配置类中,表示方法的返回值是一个受管 bean
@Primary //如果多个配置,则以当前的为主
@ConditionalOnMissingBean(ObjectMapper.class) //条件注解,表示如果受管 bean 中没有 ObjectMapper类型的对象,则需要构建
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>() {
@Override
public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
throws IOException {
jsonGenerator.writeString(""); //针对 null 内容输出空字符串
}
});
return objectMapper;
}
}
//然后修改一下上面返回 map 的接口,将几个值改成 null 测试一下:
@RequestMapping("/map")
public Map<String, Object> getMap() {
Map<String, Object> map = new HashMap<>(3);
User user = new User(1, "徐苗苗", "123456");
map.put("作者信息", user);
map.put("博客地址", "xxx");
map.put("CSDN 地址", "https://blog.csdn.net/qq_46258291");
map.put("粉丝数量", 111);
return map;
}
继续在浏览器输入localhost:8080/json/map,就能看到所有的null都变成""了。
- 配置属性不参与映射输出
@JsonIgnore 指定该属性不参与 JSON 字符串的映射
private String password;
- 配置日期类型数据的映射格式
@JsonFormat(pattern = "yyyy-MM-dd")
private Date birth=new Date();
5.2.5 jackson 和 fastJson 的对比
-
性能方面:FastJSON的性能比Jackson更快。FastJSON在序列化和反序列化大量数据时比Jackson更快,尤其在处理简单JSON数据时的性能表现更出色。
-
功能方面:Jackson提供的功能更为全面,支持XML、CSV等格式的数据转换;而FastJSON则更加轻量级,只支持JSON格式的数据转换,但提供了更加灵活的配置方式。
-
安全性方面:FastJSON在过去曾有一些安全漏洞,而Jackson则没有出现过类似的安全问题。
5.3 整合通用mapper的开发方法
它是mybatis的一款增强插件,提供增删改查的操作,不需要自己写sql语句。进一步简化了代码。它也不是为了替代mybatis,而是让mybatis的开发能更方便。
5.3.1 添加依赖
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>4.2.2</version>
</dependency>
5.3.2 建立一个实体类
用通用mapper的注解定义映射关系,就可以自动生成sql语句。
以下是使用规则:
1.表名默认使用类名,驼峰转下划线(只对大写字母进行处理),如 TestUser 默认对应的表名为 test_user
2.表名可以使用@Table(name = "tableName")进行指定,对不符合第一条默认规则的可以通过这种方式指定表 名.
3.字段默认和@Column 一样,都会作为表字段,表字段默认为 Java 对象的 Field 名字驼峰转下划线形式.
4.可以使用@Column(name = "fieldName")指定不符合第 3 条规则的字段名
5.使用@Transient 注解可以忽略字段,添加该注解的字段不会作为表字段使用.
6.建议一定是有一个@Id 注解作为主键的字段,可以有多个@Id 注解的字段作为联合主键.
7.默认情况下,实体类中如果不存在包含@Id 注解的字段,所有的字段都会作为主键字段进行使用(这种效率极 低).
8.实体类可以继承使用,可以参考测试代码中的 tk.mybatis.mapper.model.UserLogin2 类.
9.由于基本类型,如 int 作为实体类字段时会有默认值 0,而且无法消除,所以实体类中建议不要使用基本类型.
10.@NameStyle 注解,用来配置对象名/字段和表名/字段之间的转换方式,该注解优先于全局配置 style,可选值:
normal:使用实体类名/属性名作为表名/字段名
camelhump:这是默认值,驼峰转换为下划线形式
uppercase:转换为大写
lowercase:转换为小写
@Entity //用于声明当前类是一个实体类
@Table(name="tbl_users") // 用于声明当前类所对应的表名称
public class User implements Serializable {
@Id //用于声明标识属性,对应表中的主键
@GeneratedValue(strategy = GenerationType.IDENTITY) //声明主键生成策略
private Long id;
@Column //如果属性名称和列名称不一致,则需要通过@Column 进行配置对应的列名称
private String username;
private String password;
private Date birth;
private Boolean sex;
}
5.3.3 定义 mapper 接口
public interface UserMapper extends BaseMapper<User> {
}
5.3.4 Junit单元测试
@SpringBootTest
class CommonMapperApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
void contextLoads() {
}
@Test
void testCreate(){
User user=new User();
user.setUsername("张三");
user.setPassword("333333");
user.setSex(true);
int len = userMapper.insertSelective(user);
Assertions.assertEquals(1,len);
}
}
5.4 测试工具postman
Postman是一个广泛使用的API开发工具,可以用于测试、调试。
它提供了一种简单易用的方式来创建HTTP请求,并显示服务器的响应。
Postman还提供了许多高级功能,例如自动化测试、API文档生成、协作等,使得API开发变得更加高效和便捷。