springboot二

对spring boot 的一些补充

在具体的应用开发中可以使用 properties 配置文件或者 yaml 配置文件两种一般建议开发中首选 yml 格式的文件,文件后缀可以是 yml 或者 yaml。可以通过格式缩进的方式表达层次结构。解析 yml 格式的文件需要依赖 snakeyaml,这个依赖由 web-starter 依赖引入。

Spring Boot 返回 Json 数据

XML 文件的解析
常见的解析工具有 DOM4j、JDOM 等,为了标准化 XML 文件解析,Java 中提出了 JAXP 规范,使用的解析模型有
1. DOM:将标记语言文档一次性加载进入内存中,在内存中形成一颗 DOM 树(服务器端常用)
* 优点:操作方便,可以对文档进行 CRUD 的所有操作
* 缺点:一次性加载进入内存形成 DOM 树,非常消耗资源
2. 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>(); 因为 SAX 解析并不缓存数据,所以
解析过程中的数据需要自行编程实现存储
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 技术,但是由于 xml 解析比较麻烦,所以现在在项目开发中,在接口与接口之间以及前后端之间数据的传输都使用 Json 格式,在 Spring Boot 中接口返回 Json 格式的数据很简单,在 Controller 中使用@RestController 注解即可返回 Json 格式的数据,@RestController 也是Spring Boot 新增的一个复合注解。
源代码:其中 Target、Retention 和 Documented 是元注解
@Target({ElementType.TYPE}) 用于声明注解可以使用在什么地方,Type 表示可以使用在类上
@Retention(RetentionPolicy.RUNTIME) 用于声明注解需要保持到什么阶段,Runtime 表示注解在编译生成的字节码中一直保持到运行时
@Documented
@Controller
@ResponseBody
public @interface RestController {
String value() default "";
}
可 以看 出@RestController 注 解包 含 了 原来 的 @Controller 和@ResponseBody 注 解, 使 用 过 Spring 对@Controller 注解用于声明当前类是控制器类,@ResponseBody 注解是将返回的数据结构转换为 Json 格式。
所以在默认情况下,使用了@RestController 注解即可将返回的数据结构转换成 Json 格式,Spring Boot 中默认使用的 Json 解析技术框架是 jackson。点开 pom.xml 中的 spring-boot-starter-web 依赖,可以看到一个spring-boot-starter-json 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<scope>compile</scope>
</dependency>
Spring Boot 中对依赖都做了很好的封装,可以看到很多 spring-boot-starter-xxx 系列的依赖,这是 Spring Boot的特点之一,不需要人为去引入很多相关的依赖了,starter-xxx 系列直接都包含了所必要的依赖,所以再次点进去上面这个 spring-boot-starter-json 依赖,可以看到:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-parameter-names</artifactId>
<scope>compile</scope>
</dependency>
到此为止知道了 Spring Boot 中默认使用的 json 解析框架是 jackson。默认的 jackson 框架对常用数据类型的转 Json 处理
 

jackson 中对 null 的处理

在实际项目中,难免会遇到一些 null 值出现,转 json 时是不希望有这些 null 出现的,比如期望所有的 null 在转 json 时都变成""这种空字符串,那怎么做呢?在 Spring Boot 中做一下配置即可,新建一个 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, "张三", null);
map.put("作者信息", user);
map.put("博客地址", "https://www.cnblogs.com/fenglei1/");
map.put("粉丝数量", 0);
return map;
}
复制代码
重启项目再次输入 localhost:8080/json/map,可以看到 jackson 已经将所有 null 字段转成了空字符串了。例如返回数据为{"作者信息":{"id":1,"username":"张三","password":""},"粉丝数量":0,"博客地址":"https://www.cnblogs.com/fenglei1/"}
 
配置属性不参与映射输出
@JsonIgnore 指定该属性不参与 JSON 字符串的映射
private String password;
配置日期类型数据的映射格式
@JsonFormat(pattern = "yyyy-MM-dd")
private Date birth=new Date();
 
 

Spring Boot 的项目属性配置

在使用框架时需要用到一些配置的信息,这些信息可能在测试环境和生产环境下会有不同的配置,后面根据实际业务情况有可能还会做修改,针对这种情况不能将这些配置在代码中写死,最好就是写到配置文件中。
比如可以把这些信息写到 application.yml 文件中。
在具体应用中实际上 application.properties 和 application.yml 都可以使用,并允许同时使用。如果同时进行配置,且配置冲突,则 properties 优先于 yml
1. 少量配置信息的情形
例如在微服务架构中,最常见的就是某个服务需要调用其他服务来获取其提供的相关信息,那么在该服务的配置文件中需要配置被调用的服务地址,比如在当前服务里需要调用订单微服务获取订单相关的信息,假设订单服务的端口号是 8002,那可以做配置:
server:
port: 8001
# 配置微服务的地址
url: # 自定义的订单微服务的地址。不是系统预定义的配置,所以不会出现任何提示
orderUrl: http://localhost:8002
然后在业务代码中如何获取到这个配置的订单服务地址呢?可以使用@Value 注解来解决。在对应的类中加上一个属性,在属性上使用@Value 注解即可获取到配置文件中的配置信息
@RestController
@RequestMapping("/test")
public class ConfigController {
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigController.class);
@Value("${url.orderUrl}")
其中的内容为 SpEL 即 Spring 表达式语言,可以获取配置文件中 url.orderUrl的值
private String orderUrl;
@RequestMapping("/config")
public String testConfig() {
LOGGER.info("=====获取的订单服务地址为:{}", orderUrl);
return "success";
}
}
@Value 注解上通过${key}即可获取配置文件中和 key 对应的 value 值。启动一下项目在浏览器中输入localhost:8080/test/config 请求服务后,可以看到控制台会打印出订单服务的地址:
=====获取的订单服务地址为:http://localhost:8002
说明成功获取到了配置文件中的订单微服务地址,在实际项目中也是这么用的,后面如果因为服务器部署的原因,需要修改某个服务的地址,那么只要在配置文件中修改即可。
2. 多个配置信息的情形
这里再引申一个问题,随着业务复杂度的增加,某个模块可能需要调用多个微服务获取不同的信息,那么就需要在配置文件中配置多个微服务的地址。可是在需要调用这些微服务的代码中,如果这样一个个去使用@Value 注解引入相应的微服务地址的话,太过于繁琐,也不科学。所以在实际项目中,业务繁琐,逻辑复杂的情况下,需要考虑封装一个或多个配置类。举个例子:假如在当前服务中,某个业务需要同时调用订单微服务、用户微服务和购物车微服务,分别获取订
单、用户和购物车相关信息,然后对这些信息做一定的逻辑处理。那么在配置文件中,需要将这些微服务的地址都配置好
url: 配置多个微服务的地址
orderUrl: http://localhost:8002 订单微服务的地址,注意 key 值不能重复,但是允许 key 对应的值是集合类型
userUrl: http://localhost:8003 用户微服务的地址
shoppingUrl: http://localhost:8004 购物车微服务的地址
复制代码
@Component 定义受管 bean,否则 Spring 无法注入配置参数值
@ConfigurationProperties(prefix = "url") 用于读取配置信息,声明配置以 url 开头。需要解析的 key 是以 url. 开始的
public class MicroServiceUrl { 专门用于存储配置信息的类
private String orderUrl; 其中的属性名称和 key 对应的除去 url.部分之外的内容一致,例如这里对应
url.orderUrl
private String userUrl;
private String shoppingUrl;
// 省去 get 和 set 方法
}
复制代码
使用@ConfigurationProperties 注解并且使用 prefix 来指定一个前缀,然后该类中的属性名就是配置中去掉前缀后的名字,一一对应即可。即:前缀名+属性名就是配置文件中定义的 key。同时,该类上需要加@Component
注解,把该类作为组件放到 Spring 容器中,让 Spring 去管理,使用的时候直接注入即可。需要注意的是,使用@ConfigurationProperties 注解需要导入它的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
到此为止将配置写好了,接下来写个 Controller 来测试一下。直接通过@Resource 注解将刚刚写好配置类注入进来即可使用了,非常方便。
 
复制代码
@RestController
@RequestMapping("/test")
public class TestController {
private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);
@Resource 可以使用 Autowired 按照类型自动装配,也可以使用 Resource 按照名称自动装配
private MicroServiceUrl microServiceUrl;
@RequestMapping("/config")
public String testConfig() {
LOGGER.info("=====获取的订单服务地址为:{}", microServiceUrl.getOrderUrl());
LOGGER.info("=====获取的用户服务地址为:{}", microServiceUrl.getUserUrl());
LOGGER.info("=====获取的购物车服务地址为:{}", microServiceUrl.getShoppingUrl());
return "success";
}
}
再次启动项目,请求一下可以看到,控制台打印出如下信息,说明配置文件生效,同时正确获取配置文件内容:
=====获取的订单服务地址为:http://localhost:8002 =====获取的订单服务地址为:http://localhost:8002 =====获取的用户服务地址为:http://localhost:8003 =====获取的购物车服务地址为:http://localhost:8004
复制代码
复制代码
注意:使用@Value 获取配置数据和通过配置参数 bean 的方式获取配置数据,两者不互斥,可以同时使用
@RestController
@RequestMapping("/test")
public class TestController {
@Value("${url.orderUrl}")
private String orderURL;
@Autowired
private MicroServiceUrl microServiceUrl;
private static final Logger logger= LoggerFactory.getLogger(TestController.class);
@GetMapping("/params")
public String params(){
logger.warn("========="+orderURL);
logger.error("========="+microServiceUrl.getOrderUrl());
logger.error("========="+microServiceUrl.getUserUrl());
return "success";
}
}
复制代码
3. 指定项目配置文件
在实际项目中,一般有两个环境:开发环境和生产环境。开发环境中的配置和生产环境中的配置往往不同,比如环境、端口、数据库、相关地址等。实际上不可能在开发环境调试好之后,部署到生产环境后,又要将配置信息全部修改成生产环境上的配置,这样太麻烦,也不科学。最好的解决方法就是开发环境和生产环境都有一套对用的配置信息,然后当在开发时,指定读取开发环境的配置,当将项目部署到服务器上之后,再指定去读取生产环境的配置。
新建两个配置文件:application-dev.yml 和 application-prod.yml,分别用来对开发环境和生产环境进行相关配
置。这里为了方便分别设置两个访问端口号,开发环境用 8001,生产环境用 8002.
# 开发环境配置文件
server:
port: 8001
# 生产环境配置文件
 
server:
port: 8002
然后在 application.yml 文件中指定读取哪个配置文件即可。比如在开发环境下指定读取 applicationn-dev.yml
文件
spring:
  profiles:
    active:
    - dev
这样就可以在开发的时候,指定读取 application-dev.yml 文件,访问的时候使用 8001 端口,部署到服务器后,只需要将 application.yml 中指定的文件改成 application-pro.yml 即可,然后使用 8002 端口访问,非常方便。
注意:这里不是相互覆盖定义,而是只有一个配置文件生效,具体哪个配置生效却决于 active 的值。例如 active为 abc,则自动查询对应的 application-abc.yml 文件。
4. 总结
这里主要了解 Spring Boot 中在业务代码中读取相关配置,包括单一配置和多个配置项,在微服务中,这种情况非常常见,往往会有很多其他微服务需要调用,所以封装一个配置类来接收这些配置是个很好的处理方式。
除此之外,例如数据库相关的连接参数等,也可以放到一个配置类中,其他遇到类似的场景,都可以这么处理。最后了解开发环境和生产环境配置的快速切换方式,省去了项目部署时,诸多配置信息的修改。
posted @   fenglei1  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~
点击右上角即可分享
微信分享提示