目录
一、springboot初识概念
Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。就是spring boot其实不是什么新的框架,它默认配置了很多框架的使用方式,就像maven整合了所有的jar包,spring boot整合了所有的框架(不知道这样比喻是否合适)。
Spring Boot不是一门新技术。从本质上来说,Spring Boot就是Spring,它做了那些没有它你也会去做的Spring Bean配置。它使用“习惯优于配置”(项目中存在大量的配 置,此外还内置了一个习惯性的配置,让你无需手动进行配置)的理念让你的项目快速运行起来。使用Spring Boot很容易创建一个独立运行(运行jar,内嵌Servlet容器),准生产级别的基于Spring框架的项目,使用Spring Boot可以不用或者只需要很少的Spring配置。
- 自动配置:针对很多Spring应用程序常见的应用功能,Spring Boot能自动提供相关配置
- 起步依赖:告诉Spring Boot需要什么功能,它就能引入需要的库。
- 命令行界面:这是Spring Boot的可选特性,借此你只需写代码就能完成完整的应用程序,无需传统项目构建。
- Actuator:让你能够深入运行中的Spring Boot应用程序,一套究竟。
二、入门程序搭建
1.intellij idea搭建入门程序
1.1新建项目
菜单栏选择File菜单-->New-->Project...-->Spring Initializr,Project SDK即选择JDK的版本,Choose Initializer Service URL选择Default:http://start.spring.io。
选择项目需要的依赖,及springboot的版本,选择web依赖即可。
此时该项目就可以启动了。
1.2目录结构:
main/java:可以新建package,存放类文件;
main/resources:
static:用于存放静态资源,如js、css、image等;
template:用于存放模板文件;
application.properties:用于配置项目运行所需要的配置信息。
springboot01Application类文件,一般存放在main/java的根目录下,作为该项目的启动类。
1.3源码及说明
package com.bj.study;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/*
*该注解该注解指定项目为springboot,由此类当作程序入口
*自动装配 web 依赖的环境
*/
@SpringBootApplication
public class Springboot01Application {
public static void main(String[] args) {
SpringApplication.run(Springboot01Application.class, args);
}
}
1.4.Hello world
在启动类上添加注解@RestController,使该类成为一个Controller。使用@RestController注解的控制器类中的方法都会以json的格式返回。同时新增一个方法,配置其访问路径。
package com.bj.study;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@RestController
@SpringBootApplication
public class Springboot01Application {
public static void main(String[] args) {
SpringApplication.run(Springboot01Application.class, args);
}
@RequestMapping(value = "/",produces = "text/plain;charset=UTF-8")
String index(){
return "Hello Spring Boot!";
}
}
启动项目,打开浏览器访问即可。
注解@SpringBootApplication说明
@SpringBootApplication是一个组合注解,其源码如下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
}
组合了@SpringBootConfiguration、@EnableAutoConfiguration以及@ComponentScan。
我们在开发的过程中如果不使用@SpringBootApplication,则可以组合使用这三个注解。
@SpringBootConfiguration实际上等同于@Configuration注解,表明这个类是一个配置类,
@EnableAutoConfiguration则表示让Spring Boot根据类路径中的jar包依赖为当前项目进行自动配置,将所有符合自动配置条件的bean定义加载到IoC容器。借助@Import的支持,收集和注册特定场景相关的bean定义
自动配置幕后英雄:SpringFactoriesLoader:功能就是从指定的配置文件META-INF/spring.factories加载配置
自动配置就变成了:从classpath中搜寻所有的META-INF/spring.factories配置文件,并将其中org.springframework.boot.autoconfigure.EnableutoConfiguration对应的配置项通过反射(Java Refletion)实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,然后汇总为一个并加载到IoC容器
@ComponentScan表示自动扫描并加载符合条件的组件,可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。
使用@SpringBootApplication注解时,系统会去入口类的同级包以及下级包中去扫描实体类,因此我们建议入口类的位置在根目录下。
三、SpringApplication执行流程
SpringApplication的run方法的实现是我们本次旅程的主要线路,该方法的主要流程大体可以归纳如下:
1) 如果我们使用的是SpringApplication的静态run方法,那么,这个方法里面首先要创建一个SpringApplication对象实例,然后调用这个创建好的SpringApplication的实例方法。在SpringApplication实例初始化的时候,它会提前做几件事情:
- 根据classpath里面是否存在某个特征类(org.springframework.web.context.ConfigurableWebApplicationContext)来决定是否应该创建一个为Web应用使用的ApplicationContext类型。
- 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer。
- 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener。
- 推断并设置main方法的定义类。
2) SpringApplication实例初始化完成并且完成设置后,就开始执行run方法的逻辑了,方法执行伊始,首先遍历执行所有通过SpringFactoriesLoader可以查找到并加载的SpringApplicationRunListener。调用它们的started()方法,告诉这些SpringApplicationRunListener,“嘿,SpringBoot应用要开始执行咯!”。
3) 创建并配置当前Spring Boot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile)。
4) 遍历调用所有SpringApplicationRunListener的environmentPrepared()的方法,告诉他们:“当前SpringBoot应用使用的Environment准备好了咯!”。
5) 如果SpringApplication的showBanner属性被设置为true,则打印banner。
6) 根据用户是否明确设置了applicationContextClass类型以及初始化阶段的推断结果,决定该为当前SpringBoot应用创建什么类型的ApplicationContext并创建完成,然后根据条件决定是否添加ShutdownHook,决定是否使用自定义的BeanNameGenerator,决定是否使用自定义的ResourceLoader,当然,最重要的,将之前准备好的Environment设置给创建好的ApplicationContext使用。
7) ApplicationContext创建好之后,SpringApplication会再次借助Spring-FactoriesLoader,查找并加载classpath中所有可用的ApplicationContext-Initializer,然后遍历调用这些ApplicationContextInitializer的initialize(applicationContext)方法来对已经创建好的ApplicationContext进行进一步的处理。
8) 遍历调用所有SpringApplicationRunListener的contextPrepared()方法。
9) 最核心的一步,将之前通过@EnableAutoConfiguration获取的所有配置以及其他形式的IoC容器配置加载到已经准备完毕的ApplicationContext。
10) 遍历调用所有SpringApplicationRunListener的contextLoaded()方法。
11) 调用ApplicationContext的refresh()方法,完成IoC容器可用的最后一道工序。
12) 查找当前ApplicationContext中是否注册有CommandLineRunner,如果有,则遍历执行它们。
13) 正常情况下,遍历执行SpringApplicationRunListener的finished()方法、(如果整个过程出现异常,则依然调用所有SpringApplicationRunListener的finished()方法,只不过这种情况下会将异常信息一并传入处理)
去除事件通知点后,整个流程如下:
四、项目启动方式
Spring Boot项目启动方式,分别是:
1.入口类的main方法运行
2.使用命令 mvn spring-boot:run”在命令行启动该应用,IDEA中该命令在如下位置:双击即可启动
3.将项目打包成jar,然后找到jar包位置,命令行中打开,通过java -jar 包名 的方式启动
总结:
使用spring boot可以非常方便、快速搭建项目,使我们不用关心框架之间的兼容性,适用版本等各种问题,我们想使用任何东西,仅仅添加一个配置就可以,所以使用sping boot非常适合构建微服务。
五、Springboot 返回JSON数据及返回数据封装
在项目开发中,接口与接口之间,前后端之间数据的传输更多的使用json格式,在springboot入门helloworld中,控制器类中添加注解@RestController即可返回json格式的数据。该注解是对Spring注解@Controller和@ResponseBody注解的封装简化,所以在springboot中默认使用了@RestController注解即可将返回的数据结构转换为json格式。
1.springboot默认json解析框架jackson
在pom.xml文件中,点击打开 spring-boot-starter-web依赖,可以看到一个 spring-boot-starter-json 依赖,再次点击进入,即可看到json解析框架为jackson。
1.1.jackson数据类型转换
springboot默认json框架jackson对常用的数据结构如对象(object),列表(list),键值对(map对象)都支持转换成相应的json格式。
1.2.jackson对null的处理
@Configuration
public class JacksonConfig {
@Bean
@Primary
@ConditionalOnMissingBean(ObjectMapper.class)
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("");
}
});
return objectMapper;
}
}
2.使用阿里巴巴FastJson的设置
2.1.fastJson和jackson比较
选项
| fastJson | jackson |
---|---|---|
上手难易程度 | 容易 | 中等 |
高级特性支持 | 中等 | 丰富 |
官方文档、Example支持 | 中文 | 英文 |
处理json速度 | 略快 | 快 |
2.2.在springboot项目中使用
1.依赖导入
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.35</version><!--版本可根据需要修改-->
</dependency>
2.需要使用时调用即可
2.3.使用 fastJson 处理 null
@Configuration
public class fastJsonConfig extends WebMvcConfigurationSupport {
/*** 使用阿里 FastJson 作为JSON MessageConverter
* @param converters
*/
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
FastJsonConfig config = new FastJsonConfig();
config.setSerializerFeatures(
// 保留map空的字段
SerializerFeature.WriteMapNullValue,
// 将String类型的null转成""
SerializerFeature.WriteNullStringAsEmpty,
// 将Number类型的null转成0
SerializerFeature.WriteNullNumberAsZero,
// 将List类型的null转成[]
SerializerFeature.WriteNullListAsEmpty,
// 将Boolean类型的null转成false
SerializerFeature.WriteNullBooleanAsFalse,
// 避免循环引用
SerializerFeature.DisableCircularReferenceDetect);
converter.setFastJsonConfig(config);
converter.setDefaultCharset(Charset.forName("UTF-8"));
List<MediaType> mediaTypeList = new ArrayList<>();
// 解决中文乱码问题,相当于在Controller上的@RequestMapping中加了个属性produces = "application/json"
mediaTypeList.add(MediaType.APPLICATION_JSON);
converter.setSupportedMediaTypes(mediaTypeList);
converters.add(converter);
}
}
2.4.使用 fastJson 实现类型转换
对于普通类型,可直接转换,无需转换器,在前面示例中已经运用到;
但对于date等类型,因无法确定格式,需要我们增加转换器。
1.若为添加依赖需先添加依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.35</version<!--版本根据需要进行修改-->
</dependency>
2.创建配置管理类
@Configuration
public class WebConfig {
@Bean
public HttpMessageConverters fastJsonHttpMessageConverters() {
FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig);
HttpMessageConverter<?> converter = fastJsonHttpMessageConverter;
return new HttpMessageConverters(converter);
}
}
3.应用示例
3.1实体类
public class Role implements Serializable {
private String id;
private String name;
private String available;
private Date createDate;
}
3.2控制器类
@Controller
@RequestMapping("fastjson")
public class FastJsonController {
@RequestMapping("/test")
@ResponseBody
public Role test() {
Role role = new Role();
role.setId("roleid01");
role.setName("jack");
role.setAvailable("1");
role.setCreateDate(new Date());
return role;
}
}
4.启动访问,此时可看到未经过转换器,日期类型的数据未转换
5.关联配置消息转换器
在实体类中,修改如下代码
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date createDate;
再次启动访问
3.封装统一返回的数据结构
@Data
public class ResultVo<T> {
/**错误码*/
private String code;
/**返回的信息*/
private String msg;
/** 记录总条数,可以不添加该字段*/
private Long count;
/**具体内容*/
private T data;
//可以添加一些构造方法
}
public class ResultVoUtil {
public static ResultVo success(Object o){
ResultVo resultVo = new ResultVo();
resultVo.setCode(0);
resultVo.setMsg("成功");
resultVo.setData(o);
return resultVo;
}
public static ResultVo success(){
return success(null);
}
public static ResultVo error(String code,String message){
ResultVo resultVo = new ResultVo();
resultVo.setCode(code);
resultVo.setMsg(message);
return resultVo;
}
public static ResultVo success(Object o, long count){
ResultVo resultVo = success(o);
resultVo.setCount(count);
return resultVo;
}
public static ResultVo success(long count){
ResultVo resultVo = success();
resultVo.setCount(count);
return resultVo;
}
public static ResultVo result(Map<String, String> map){
ResultVo resultVo = new ResultVo();
resultVo.setCode(Integer.parseInt(map.get("errcode")));
resultVo.setMsg(map.get("errmsg"));
resultVo.setData(null);
return resultVo;
}
}
3.2.在controller中应用
@RestController
@RequestMapping("/buyer/order")
@Slf4j
public class BuyerOrderController {
@Autowired
private OrderMasterService orderMasterService;
@Autowired
private BuyerService buyerService;
/**创建订单
* @param orderForm 表单数据及验证
* @param bindingResult 表单数据验证结果
* @return
*/
@PostMapping("/create")
public ResultVo<Map<String,String>> create(@Valid OrderForm orderForm, BindingResult bindingResult){
if (bindingResult.hasErrors()){
log.error("【创建订单】,参数不正确,orderForm={}",orderForm);
throw new SellException(ResultEnum.PARAM_ERR.getCode(),bindingResult.getFieldError().getDefaultMessage());
}
OrderDto orderDto = OrderFormToOrderDtoConverter.convert(orderForm);
if (CollectionUtils.isEmpty(orderDto.getOrderDetails())){
log.error("【创建订单】,购物车不能为空,orderDetail={}",orderDto.getOrderDetails());
throw new SellException(ResultEnum.CART_NOT_NULL);
}
orderDto.setCreateTime(new Date());
OrderDto createResult = orderMasterService.create(orderDto);
Map<String, String> map = new HashMap<>();
map.put("orderId", createResult.getId());
return ResultVoUtil.success(map);
}
//订单列表
@GetMapping("list")
public ResultVo<List<OrderDto>> list(@RequestParam("openId") String openId,
@RequestParam(value = "page", defaultValue = "0") Integer page,
@RequestParam(value = "size", defaultValue = "10") Integer size){
if(StringUtils.isEmpty(openId)){
log.error("【查询订单列表】,openId为空");
throw new SellException(ResultEnum.OPENID_IS_NULL);
}
PageRequest pageRequest = PageRequest.of(page,size);
Page<OrderDto> orderDtoPage = orderMasterService.findList(openId,pageRequest);
return ResultVoUtil.success(orderDtoPage.getContent());
}
//订单详情
@GetMapping("/detail")
public ResultVo<List<OrderDto>> detail(@RequestParam("openId") String openId,
@RequestParam(value = "orderId") String orderId){
if(StringUtils.isEmpty(openId)){
log.error("【查询订单详情】,openId为空");
throw new SellException(ResultEnum.OPENID_IS_NULL);
}
if(StringUtils.isEmpty(orderId)){
log.error("【查询订单详情】,订单号为空");
throw new SellException(ResultEnum.ORDER_IS_NULL);
}
OrderDto orderDto = buyerService.findOrderOne(openId, orderId);
return ResultVoUtil.success(orderDto);
}
//取消订单
@PostMapping("/cancel")
public ResultVo cancel(@RequestParam("openId") String openId,
@RequestParam(value = "orderId") String orderId){
if(StringUtils.isEmpty(openId)){
log.error("【查询订单详情】,openId为空");
throw new SellException(ResultEnum.OPENID_IS_NULL);
}
if(StringUtils.isEmpty(orderId)){
log.error("【查询订单详情】,订单号为空");
throw new SellException(ResultEnum.ORDER_IS_NULL);
}
buyerService.updateOrder(openId,orderId, OrderUpdateEnum.CANCEL.getCode());
return ResultVoUtil.success();
}
}
参考:
https://blog.csdn.net/ityouknow/column/info/15149/2
http://tengj.top/categories/Spring-Boot%E5%B9%B2%E8%B4%A7%E7%B3%BB%E5%88%97/