【Spring Boot系列学习】01.入门helloworld

目录

一、springboot初识概念

二、入门程序搭建

1.intellij idea搭建入门程序

1.1新建项目

1.2目录结构:

1.3源码及说明

1.4.Hello world

三、SpringApplication执行流程

四、项目启动方式

五、Springboot 返回JSON数据及返回数据封装

1.springboot默认json解析框架jackson

1.1.jackson数据类型转换

1.2.jackson对null的处理

2.使用阿里巴巴FastJson的设置

2.1.fastJson和jackson比较

2.2.在springboot项目中使用

2.3.使用 fastJson 处理 null

2.4.使用 fastJson 实现类型转换

3.封装统一返回的数据结构



一、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的处理

在实际项目中,我们难免会遇到一些 null 值出现,我们转 json 时,是不希望有这些 null 出现的,比如期望所有的 null 在转 json 时都变成 "" 这种空字符串, Spring Boot 中,做一 下配置即可,新建一个 jackson 的配置类:
@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比较

选项
fastJsonjackson
上手难易程度容易中等
高级特性支持中等丰富
官方文档、Example支持中文英文
处理json速度略快
关于 fastJson jackson 的对比,网上有很多资料可以查看,主要是根据自己实际项目情况来选择合适的框架。从扩展上来看, fastJson 没有 jackson 灵活,从速度或者上手难度来看, fastJson 可以考虑。
 

2.2.在springboot项目中使用

1.依赖导入

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.35</version><!--版本可根据需要修改-->
</dependency>

2.需要使用时调用即可

2.3.使用 fastJson 处理 null

使用 fastJson 时,对 null 的处理和 jackson 有些不同,需要继承 WebMvcConfigurationSupport 类,然后覆盖 configureMessageConverters 方法,在方法中,我们可以选择对要实现 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.封装统一返回的数据结构

在实际项目中,除了要封装数据之外,往往需要在返回的 json 中添加一些其他信息,比如返回一些状态码 code ,返回一些 msg 给调用者,这样调用者可以根据 code 或者 msg 做一些逻辑判断。所以在实际项目中,需要封装一个统一的 json 返回结构存储返回信息。
 
3.1.定义统一的 json 结构
由于封装的 json 数据的类型不确定,所以在定义统一的 json 结构时,我们需要用到泛型。统一的 json 结构中属性包括数据、状态码、提示信息即可,构造方法可以根据实际业务需求做相应的添加即可,一般来说,应该有默认的返回结构,也应该有用户指定的返回结构
@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中应用

由于 JsonResult 使用了泛型,所以所有的返回值类型都可以使用该统一结构,在具体的场景将泛型替换成具体的数据类型即可,非常方便,也便于维护。在实际项目中,还可以继续封装,比如状态码和提示信息可以定义一个枚举类型,以后只需要维护这个枚举类型中的数据即可。
 
在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/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值