首页 文章 精选 资源 留言

精选列表

搜索[SpringBoot],共4128篇文章
优秀的个人博客,低调大师

SpringBoot 无侵入式实现 API 接口统一 JSON 格式返回

点击上方 一个优秀的废人 ,选择设为星标 优质文章,及时送达 来源:blog.csdn.net/qq_34347620/article/details/102239179 无侵入式统一返回 JSON 格式 其实本没有没打算写这篇博客的,但还是要写一下写这篇博客的起因是因为,现在呆着的这家公司居然没有统一的 API 返回格式?,询问主管他居然告诉我用 HTTP 状态码就够用了(fxxk),天哪 HTTP 状态码真的够用吗? 在仔细的阅读了项目源码后发现,在 API 请求的是居然没有业务异常(黑人问号)。好吧 居然入坑了只能遵照项目风格了,懒得吐槽了。 因为项目已经开发了半年多了,要是全部接口都做修改工作量还是挺大的,只能用这种无侵入式的方案来解决. ❝ 项目源代码: https://github.com/469753862/galaxy-blogs/tree/master/code/responseResult ❞ 定义返回 JSON 格式 后端返回给前端一般情况下使用 JSON 格式,定义如下 {"code":200,"message":"OK","data":{}} code: 返回状态码 message: 返回信息的描述 data: 返回值 定义状态码枚举类 @ToString@GetterpublicenumResultStatus{SUCCESS(HttpStatus.OK,200,"OK"),BAD_REQUEST(HttpStatus.BAD_REQUEST,400,"BadRequest"),INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR,500,"InternalServerError"),;/**返回的HTTP状态码,符合http请求*/privateHttpStatushttpStatus;/**业务异常码*/privateIntegercode;/**业务异常信息描述*/privateStringmessage;ResultStatus(HttpStatushttpStatus,Integercode,Stringmessage){this.httpStatus=httpStatus;this.code=code;this.message=message;}} 状态码和信息以及 http 状态码就能一一对应了便于维护,有同学有疑问了为什么要用到 http 状态码呀,因为我要兼容项目以前的代码,没有其他原因,当然其他同学不喜欢 http 状态码的可以吧源码中 HttpStatus 给删除了 定义返回体类 @Getter@ToStringpublicclassResult<T>{/**业务错误码*/privateIntegercode;/**信息描述*/privateStringmessage;/**返回参数*/privateTdata;privateResult(ResultStatusresultStatus,Tdata){this.code=resultStatus.getCode();this.message=resultStatus.getMessage();this.data=data;}/**业务成功返回业务代码和描述信息*/publicstaticResult<Void>success(){returnnewResult<Void>(ResultStatus.SUCCESS,null);}/**业务成功返回业务代码,描述和返回的参数*/publicstatic<T>Result<T>success(Tdata){returnnewResult<T>(ResultStatus.SUCCESS,data);}/**业务成功返回业务代码,描述和返回的参数*/publicstatic<T>Result<T>success(ResultStatusresultStatus,Tdata){if(resultStatus==null){returnsuccess(data);}returnnewResult<T>(resultStatus,data);}/**业务异常返回业务代码和描述信息*/publicstatic<T>Result<T>failure(){returnnewResult<T>(ResultStatus.INTERNAL_SERVER_ERROR,null);}/**业务异常返回业务代码,描述和返回的参数*/publicstatic<T>Result<T>failure(ResultStatusresultStatus){returnfailure(resultStatus,null);}/**业务异常返回业务代码,描述和返回的参数*/publicstatic<T>Result<T>failure(ResultStatusresultStatus,Tdata){if(resultStatus==null){returnnewResult<T>(ResultStatus.INTERNAL_SERVER_ERROR,null);}returnnewResult<T>(resultStatus,data);}} 因为使用构造方法进行创建对象太麻烦了,我们使用静态方法来创建对象这样简单明了 Result 实体返回测试 @RestController@RequestMapping("/hello")publicclassHelloController{privatestaticfinalHashMap<String,Object>INFO;static{INFO=newHashMap<>();INFO.put("name","galaxy");INFO.put("age","70");}@GetMapping("/hello")publicMap<String,Object>hello(){returnINFO;}@GetMapping("/result")@ResponseBodypublicResult<Map<String,Object>>helloResult(){returnResult.success(INFO);}} 到这里我们已经简单的实现了统一 JSON 格式了,但是我们也发现了一个问题了,想要返回统一的 JSON 格式需要返回Result<Object>才可以,我明明返回 Object 可以了,为什么要重复劳动,有没有解决方法,当然是有的啦,下面我们开始优化我们的代码吧 统一返回 JSON 格式进阶 - 全局处理 (@RestControllerAdvice) 我师傅经常告诉我的一句话: “你就是一个小屁孩,你遇到的问题都已经不知道有多少人遇到过了,你会想到的问题,已经有前辈想到过了。你准备解决的问题,已经有人把坑填了”。是不是很鸡汤,是不是很励志,让我对前辈们充满着崇拜,事实上他对我说的是: “自己去百度”, 这五个大字,其实这五个大字已经说明上明的 B 话了,通过不断的百度和 Google 发现了很多的解决方案. 我们都知道使用 @ResponseBody 注解会把返回 Object 序列化成 JSON 字符串,就先从这个入手吧,大致就是在序列化前把 Object 赋值给Result<Object>就可以了,大家可以观摩 org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice 和 org.springframework.web.bind.annotation.ResponseBody @ResponseBody 继承类 我们已经决定从 @ResponseBody 注解入手了就创建一个注解类继承 @ResponseBody, 很干净什么都没有哈哈,@ResponseResultBody 可以标记在类和方法上这样我们就可以跟自由的进行使用了 @Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE,ElementType.METHOD})@Documented@ResponseBodypublic@interfaceResponseResultBody{} ResponseBodyAdvice 继承类 @RestControllerAdvicepublicclassResponseResultBodyAdviceimplementsResponseBodyAdvice<Object>{privatestaticfinalClass<?extendsAnnotation>ANNOTATION_TYPE=ResponseResultBody.class;/***判断类或者方法是否使用了@ResponseResultBody*/@Overridepublicbooleansupports(MethodParameterreturnType,Class<?extendsHttpMessageConverter<?>>converterType){returnAnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(),ANNOTATION_TYPE)||returnType.hasMethodAnnotation(ANNOTATION_TYPE);}/***当类或者方法使用了@ResponseResultBody就会调用这个方法*/@OverridepublicObjectbeforeBodyWrite(Objectbody,MethodParameterreturnType,MediaTypeselectedContentType,Class<?extendsHttpMessageConverter<?>>selectedConverterType,ServerHttpRequestrequest,ServerHttpResponseresponse){//防止重复包裹的问题出现if(bodyinstanceofResult){returnbody;}returnResult.success(body);}} RestControllerAdvice 返回测试 @RestController@RequestMapping("/helloResult")@ResponseResultBodypublicclassHelloResultController{privatestaticfinalHashMap<String,Object>INFO;static{INFO=newHashMap<String,Object>();INFO.put("name","galaxy");INFO.put("age","70");}@GetMapping("hello")publicHashMap<String,Object>hello(){returnINFO;}/**测试重复包裹*/@GetMapping("result")publicResult<Map<String,Object>>helloResult(){returnResult.success(INFO);}@GetMapping("helloError")publicHashMap<String,Object>helloError()throwsException{thrownewException("helloError");}@GetMapping("helloMyError")publicHashMap<String,Object>helloMyError()throwsException{thrownewResultException();}} 是不是很神奇,直接返回 Object 就可以统一 JSON 格式了,就不用每个返回都返回Result<T>对象了,直接让 SpringMVC 帮助我们进行统一的管理,简直完美 只想看接口哦,helloError 和 helloMyError 是会直接抛出异常的接口,我好像没有对异常返回进行统一的处理哦 统一返回 JSON 格式进阶 - 异常处理 (@ExceptionHandler)) 卧槽,异常处理,差点把这茬给忘了,这个异常处理就有很多方法了,先看看我师傅的处理方式,我刚拿到这个代码的时候很想吐槽,对异常类的处理这么残暴的吗,直接用 PrintWriter 直接输出结果,果然是老师傅,我要是有 100 个异常类,不得要写 100 个 if else 了。赶紧改改睡吧 @ConfigurationpublicclassMyExceptionHandlerimplementsHandlerExceptionResolver{publicModelAndViewresolveException(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,Exceptionex){PrintWriterout=getPrintWrite(response);if(exinstanceofXXXException){out.write(JsonUtil.formatJson(ResultEnum.PAY_ERROR.getCode(),ex.getMessage()));}else{out.write(JsonUtil.formatJson(ResultEnum.FAIL.getCode(),"服务器异常"));}if(null!=out){out.close();}returnmav;}privatePrintWritergetPrintWrite(HttpServletResponseresponse){PrintWriterout=null;try{response.setHeader("Content-type","text/html;charset=UTF-8");response.setCharacterEncoding("UTF-8");out=response.getWriter();}catch(IOExceptione){log.error("PrintWriterisexception",e);}returnout;}} 上面的代码看看还是没有问题的,别学过去哦, 异常处理 @ResponseStatus (不推荐) @ResponseStatus 用法如下,可用在 Controller 类和 Controller 方法上以及 Exception 类上但是这样的工作量还是挺大的 @RestController@RequestMapping("/error")@ResponseStatus(value=HttpStatus.INTERNAL_SERVER_ERROR,reason="Java的异常")publicclassHelloExceptionController{privatestaticfinalHashMap<String,Object>INFO;static{INFO=newHashMap<String,Object>();INFO.put("name","galaxy");INFO.put("age","70");}@GetMapping()publicHashMap<String,Object>helloError()throwsException{thrownewException("helloError");}@GetMapping("helloJavaError")@ResponseStatus(value=HttpStatus.INTERNAL_SERVER_ERROR,reason="Java的异常")publicHashMap<String,Object>helloJavaError()throwsException{thrownewException("helloError");}@GetMapping("helloMyError")publicHashMap<String,Object>helloMyError()throwsException{thrownewMyException();}}@ResponseStatus(value=HttpStatus.INTERNAL_SERVER_ERROR,reason="自己定义的异常")classMyExceptionextendsException{} 全局异常处理 @ExceptionHandler (推荐) 把 ResponseResultBodyAdvice 类进行改造一下,代码有点多了 主要参考了 org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler#handleException () 方法,有空可以看一下 @Slf4j@RestControllerAdvicepublicclassResponseResultBodyAdviceimplementsResponseBodyAdvice<Object>{privatestaticfinalClass<?extendsAnnotation>ANNOTATION_TYPE=ResponseResultBody.class;/**判断类或者方法是否使用了@ResponseResultBody*/@Overridepublicbooleansupports(MethodParameterreturnType,Class<?extendsHttpMessageConverter<?>>converterType){returnAnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(),ANNOTATION_TYPE)||returnType.hasMethodAnnotation(ANNOTATION_TYPE);}/**当类或者方法使用了@ResponseResultBody就会调用这个方法*/@OverridepublicObjectbeforeBodyWrite(Objectbody,MethodParameterreturnType,MediaTypeselectedContentType,Class<?extendsHttpMessageConverter<?>>selectedConverterType,ServerHttpRequestrequest,ServerHttpResponseresponse){if(bodyinstanceofResult){returnbody;}returnResult.success(body);}/***提供对标准SpringMVC异常的处理**@paramexthetargetexception*@paramrequestthecurrentrequest*/@ExceptionHandler(Exception.class)publicfinalResponseEntity<Result<?>>exceptionHandler(Exceptionex,WebRequestrequest){log.error("ExceptionHandler:{}",ex.getMessage());HttpHeadersheaders=newHttpHeaders();if(exinstanceofResultException){returnthis.handleResultException((ResultException)ex,headers,request);}//TODO:2019/10/05galaxy这里可以自定义其他的异常拦截returnthis.handleException(ex,headers,request);}/**对ResultException类返回返回结果的处理*/protectedResponseEntity<Result<?>>handleResultException(ResultExceptionex,HttpHeadersheaders,WebRequestrequest){Result<?>body=Result.failure(ex.getResultStatus());HttpStatusstatus=ex.getResultStatus().getHttpStatus();returnthis.handleExceptionInternal(ex,body,headers,status,request);}/**异常类的统一处理*/protectedResponseEntity<Result<?>>handleException(Exceptionex,HttpHeadersheaders,WebRequestrequest){Result<?>body=Result.failure();HttpStatusstatus=HttpStatus.INTERNAL_SERVER_ERROR;returnthis.handleExceptionInternal(ex,body,headers,status,request);}/***org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler#handleExceptionInternal(java.lang.Exception,java.lang.Object,org.springframework.http.HttpHeaders,org.springframework.http.HttpStatus,org.springframework.web.context.request.WebRequest)*<p>*Asingleplacetocustomizetheresponsebodyofallexceptiontypes.*<p>Thedefaultimplementationsetsthe{@linkWebUtils#ERROR_EXCEPTION_ATTRIBUTE}*requestattributeandcreatesa{@linkResponseEntity}fromthegiven*body,headers,andstatus.*/protectedResponseEntity<Result<?>>handleExceptionInternal(Exceptionex,Result<?>body,HttpHeadersheaders,HttpStatusstatus,WebRequestrequest){if(HttpStatus.INTERNAL_SERVER_ERROR.equals(status)){request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE,ex,WebRequest.SCOPE_REQUEST);}returnnewResponseEntity<>(body,headers,status);}} -END- 如果看到这里,喜欢这篇文章的话,请帮点个好看。微信搜索「一个优秀的废人」,关注后回复「1024」送你一套完整的 java 教程(包括视频)。回复「电子书」送你全编程领域电子书(不只Java)。 本文分享自微信公众号 - 一个优秀的废人(feiren_java)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

优秀的个人博客,低调大师

SpringBoot系列教程之Bean加载顺序之错误使用姿势辟谣

在网上查询 Bean 的加载顺序时,看到了大量的文章中使用@Order注解的方式来控制 bean 的加载顺序,不知道写这些的博文的同学自己有没有实际的验证过,本文希望通过指出这些错误的使用姿势,让观文的小伙伴可以知道@Order的具体的应用场景 I. 环境搭建 创建一个 maven 项目,pom 文件如下(具体的项目代码,可以在文末获取) <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.7</version> <relativePath/> <!-- lookup parent from update --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <build> <pluginManagement> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </pluginManagement> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> II. 错误姿势 下面我们会介绍两种典型注解的错误使用姿势,一个@Order,一个@AutoConfigureOrder I. @Order err.case1: 类上添加 Order 注解 一种常见的错误观点是在类上添加这个 Order 注解,就可以指定 bean 之间的初始化顺序,order 值越小,则优先级越高,接下来我们实际测试一下,是否如此 我们创建两个 DemoBean, 指定不同的 Order 顺序 @Order(4) @Component public class BaseDemo1 { private String name = "base demo 1"; public BaseDemo1() { System.out.println(name); } } @Order(3) @Component public class BaseDemo2 { private String name = "base demo 2"; public BaseDemo2() { System.out.println(name); } } 根据前面的观点,orde 值小的优先级高,那么 BaseDemo2 应该先被初始化,实际测试一下,输出如下 err.case2: 配置类中 Bean 声明方法上添加@Order Bean 除了上面的自动扫描之外,还有一种方式就是通过@Bean注解,下面我们演示一下在配置类中指定 bean 加载顺序的错误 case 同样我们新建两个测试 bean public class BaseDemo3 { private String name = "base demo 3"; public BaseDemo3() { System.out.println(name); } } public class BaseDemo4 { private String name = "base demo 4"; public BaseDemo4() { System.out.println(name); } } 接下来在配置类中定义 bean @Configuration public class ErrorDemoAutoConf { @Order(2) @Bean public BaseDemo3 baseDemo3() { return new BaseDemo3(); } @Order(1) @Bean public BaseDemo4 baseDemo4() { return new BaseDemo4(); } } 同样的,如果@Order注解有效,那么BaseDemo4应该先被初始化 从上面的实际测试输出可以看出,@Order 注解在上面的方式中也不生效,如果有兴趣的同学可以试一下,将上面配置类中的两个方法的顺序颠倒一下,会发现BaseDemo4先加载 err.case3: @Order 注解修饰配置类 这也是一种常见的错误 case,认为@Order 注解是用来指定配置类的加载顺序的,然而真的是这样么? 我们创建两个测试的配置类 @Order(1) @Configuration public class AConf { public AConf() { System.out.println("AConf init!"); } } @Order(0) @Configuration public class BConf { public BConf() { System.out.println("BConf init"); } } 如果@Order 注解生效,那么 BConf 配置类会优先初始化,那么我们实测一下 从上面的结果可以看出,并不是 BConf 先被加载;当然这种使用姿势,实际上和第一种错误 case,并没有什么区别,配置类也是 bean,前面不生效,这里当然也不会生效 那么是不是我们的理解不对导致的呢,实际上这个@Order放在配置类上之后,是这个配置类中定义的 Bean 的优先于另一个配置类中定义的 Bean 呢? 同样的我们测试下这种 case,我们定义三个 bean,两个 conf public class Demo1 { private String name = "conf demo bean 1"; public Demo1() { System.out.println(name); } } public class Demo2 { private String name = "conf demo bean 2"; public Demo2() { System.out.println(name); } } public class Demo3 { private String name = "conf demo bean 3"; public Demo3() { System.out.println(name); } } 然后我们将 Demo1, Demo3 放在一个配置中,Demo2 放在另外一个配置中 @Order(2) @Configuration public class AConf1 { @Bean public Demo1 demo1() { return new Demo1(); } @Bean public Demo3 demo3() { return new Demo3(); } } @Order(1) @Configuration public class BConf1 { @Bean public Demo2 demo2() { return new Demo2(); } } 如果@Order 注解实际上控制的是配置类中 Bean 的加载顺序,那么 BConf1 中的 Bean 应该优先加载,也就是说 Demo2 会优先于 Demo1, Demo3,实际测试一下,输出如 上面的输出结果和我们预期的并不一样,所以@Order注解来决定配置类的顺序也是不对的 2. @AutoConfigureOrder 从命名来看,这个注解是用来指定配置类的顺序的,然而对于这个注解的错误使用也是非常多的,而大多的错误使用在于没有真正的了解到它的使用场景 接下来我们来演示一下错误的使用 case 在工程内新建两个配置类,直接使用注解 @Configuration @AutoConfigureOrder(1) public class AConf2 { public AConf2() { System.out.println("A Conf2 init!"); } } @Configuration @AutoConfigureOrder(-1) public class BConf2 { public BConf2() { System.out.println("B conf2 init!"); } } 当注解生效时,BConf 会优先级加载 从输出结果来看,和我们预期的不一样;那么这个注解是不是作用于配置类中的 Bean 的顺序,而不是配置类本身呢? 同样的我们设计一个 case 验证一下 public class DemoA { private String name = "conf demo bean A"; public DemoA() { System.out.println(name); } } public class DemoB { private String name = "conf demo bean B"; public DemoB() { System.out.println(name); } } public class DemoC { private String name = "conf demo bean C"; public DemoC() { System.out.println(name); } } 对应的配置类 @Configuration @AutoConfigureOrder(1) public class AConf3 { @Bean public DemoA demoA() { return new DemoA(); } @Bean public DemoC demoC() { return new DemoC(); } } @Configuration @AutoConfigureOrder(-1) public class BConf3 { @Bean public DemoB demoB() { return new DemoB(); } } 如果 DemoB 后被加载,则说明上面的观点是错误的,实测结果如下 所以问题来了,@AutoConfigureOrder这个注解并不能指定配置类的顺序,还叫这个名,干啥?存粹是误导人不是!!! 接下来我们看一下@Order和@AutoConfigureOrder的正确使用方式 III. 使用说明 1. @Order 先看一下这个注解的官方注释 {@code @Order} defines the sort order for an annotated component. Since Spring 4.0, annotation-based ordering is supported for many kinds of components in Spring, even for collection injection where the order values of the target components are taken into account (either from their target class or from their {@code @Bean} method). While such order values may influence priorities at injection points, please be aware that they do not influence singleton startup order which is an orthogonal concern determined by dependency relationships and {@code @DependsOn} declarations (influencing a runtime-determined dependency graph). 最开始 Order 注解用于切面的优先级指定;在 4.0 之后对它的功能进行了增强,支持集合的注入时,指定集合中 bean 的顺序 并且特别指出了,它对于但实例的 bean 之间的顺序,没有任何影响;这句话根据我们上面的测试也可以验证 接下来我们需要看一下通过@Order 注解来注入集合时,指定顺序的场景 首先我们定义两个 Bean 实现同一个接口,并添加上@Order注解 public interface IBean { } @Order(2) @Component public class AnoBean1 implements IBean { private String name = "ano order bean 1"; public AnoBean1() { System.out.println(name); } } @Order(1) @Component public class AnoBean2 implements IBean { private String name = "ano order bean 2"; public AnoBean2() { System.out.println(name); } } 然后再一个测试 bean 中,注入IBean的列表,我们需要测试这个列表中的 Bean 的顺序是否和我们定义的@Order规则一致 @Component public class AnoTestBean { public AnoTestBean(List<IBean> anoBeanList) { for (IBean bean : anoBeanList) { System.out.println("in ano testBean: " + bean.getClass().getName()); } } } 根据我们的预期, anoBeanList 集合中,anoBean2 应该在前面 根据上面的输出,也可以看出列表中的顺序和我们预期的一致,并且 AnoOrderBean1与 AnoOrderBean2 的加载顺序和注解没有关系 2. @AutoConfigureOrder 这个注解用来指定配置文件的加载顺序,然而前面的测试中并没有生效,那么正确的使用姿势是怎样的呢? @AutoConfigureOrder适用于外部依赖的包中 AutoConfig 的顺序,而不能用来指定本包内的顺序 为了验证上面的说法,我们再次新建两个工程,并指定自动配置类的顺序 工程一配置如下: @AutoConfigureOrder(1) @Configuration @ComponentScan(value = {"com.git.hui.boot.order.addition"}) public class AdditionOrderConf { public AdditionOrderConf() { System.out.println("additionOrderConf init!!!"); } } 注意自动配置类如要被正确加载,需要在工程的 /META-INF/spring.factories文件中定义 org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.git.hui.boot.order.addition.AdditionOrderConf 工程二的配置如下: @Configuration @AutoConfigureOrder(-1) @ComponentScan("com.git.hui.boot.order.addition2") public class AdditionOrderConf2 { public AdditionOrderConf2() { System.out.println("additionOrderConf2 init!!!"); } } 然后我们在项目内部添加一个配置 @AutoConfigureOrder(10) @Configuration public class OrderConf { public OrderConf() { System.out.println("inner order conf init!!!"); } } 因为注解适用于外部依赖包中的自动配置类的顺序,所以上面三个配置类中,正确的话 AdditionOrderConf2 在 AdditionOrderConf1 之前;而 OrderConf 并不会收到注解的影响,默认环境下,内部定义的配置类会优于外部依赖,从下面的输出也可以佐证我们说明(当然为了验证确实如次,还应该调整下两个外部工程配置类的顺序,并观察下加载顺序是否随之改变,我们这里省略掉了) IV. 小结 本篇主要介绍了网上对@Order和@AutoConfigureOrder常见的错误使用姿势,并给出了正确的使用 case。 下面用简单的几句话介绍一下正确的姿势 @Order注解不能指定 bean 的加载顺序,它适用于 AOP 的优先级,以及将多个 Bean 注入到集合时,这些 bean 在集合中的顺序 @AutoConfigureOrder指定外部依赖的 AutoConfig 的加载顺序(即定义在/META-INF/spring.factories文件中的配置 bean 优先级),在当前工程中使用这个注解并没有什么鸟用 同样的 @AutoConfigureBefore和 @AutoConfigureAfter这两个注解的适用范围和@AutoConfigureOrder一样 0. 项目 工程:https://github.com/liuyueyi/spring-boot-demo 源码模块: - https://github.com/liuyueyi/spring-boot-demo/blob/master/spring-boot/008-beanorder - https://github.com/liuyueyi/spring-boot-demo/blob/master/spring-boot/008-beanorder-addition - https://github.com/liuyueyi/spring-boot-demo/blob/master/spring-boot/008-beanorder-addition2 1. 一灰灰 Blog 尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现 bug 或者有更好的建议,欢迎批评指正,不吝感激 下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛 一灰灰 Blog 个人博客 https://blog.hhui.top 一灰灰 Blog-Spring 专题博客 http://spring.hhui.top

优秀的个人博客,低调大师

springboot:框架想学好,属性配置和使用你都明白了吗?

Spring Boot 属性配置和使用 简单的说,Spring Boot 就是允许通过外部配置让你在不同的环境使用同一应用程序的代码,或许说就是可以通过配置文件来注入属性或者修改默认的配置。Spring Boot 支持多种外部配置方式 这些方式优先级如下: 命令行参数来自java:comp/env的JNDI属性Java系统属性(System.getProperties())操作系统环境变量RandomValuePropertySource配置的random.*属性值jar包外部的application-{profile}.properties或application.yml(带spring.profile)配置文件jar包内部的application-{profile}.properties或application.yml(带spring.profile)配置文件jar包外部的application.properties或application.yml(不带spring.profile)配置文件jar包内部的application.properties或application.yml(不带spring.profile)配置文件@Configuration注解类上的@PropertySource通过SpringApplication.setDefaultProperties指定的默认属性命令行参数 通过java -jar app.jar --name="Spring" --server.port=9090方式来传递参数。参数用--xxx=xxx的形式传递。可以使用的参数可以是我们自己定义的,也可以是Spring Boot中默认的参数。 -很多人可能会关心如web端口如何配置这样的问题,这些都是Spring Boot中提供的参数,部分可用参数如下: LOGGING logging.path=/var/logs logging.file=myapp.log logging.config= # location of config file (default classpath:logback.xml for logback) logging.level.*= # levels for loggers, e.g. "logging.level.org.springframework=DEBUG" (TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF) # EMBEDDED SERVER CONFIGURATION (ServerProperties) server.port=8080 server.address= # bind to a specific NIC server.session-timeout= # session timeout in seconds server.context-parameters.*= # Servlet context init parameters, e.g. server.context-parameters.a=alpha server.context-path= # the context path, defaults to '/' server.servlet-path= # the servlet path, defaults to '/' 复制代码注意:命令行参数在app.jar的后面!可以通过SpringApplication.setAddCommandLineProperties(false)禁用命令行配置。操作系统环境变量 配置过JAVA_HOME的应该都了解这一个。这里需要注意的地方,有些OS可以不支持使用.这种名字,如server.port,这种情况可以使用SERVER_PORT来配置。应用配置文件(.properties或.yml) 在配置文件中直接写: name=Isea533 server.port=8080.yml格式的配置文件如: name: Isea533 server: port: 8080当有前缀的情况下,使用.yml格式的配置文件更简单。关于.yml配置文件用法请看这里注意:使用.yml时,属性名的值和冒号中间必须有空格,如name: Isea533正确,name:Isea533就是错的。属性配置文件的位置 spring会从classpath下的/config目录或者classpath的根目录查找application.properties或application.yml。/config优先于classpath根目录@PropertySource 这个注解可以指定具体的属性配置文件,优先级比较低。SpringApplication.setDefaultProperties 例如:SpringApplication application = new SpringApplication(Application.class); Map defaultMap = new HashMap(); defaultMap.put("name", "Isea-Blog"); //还可以是Properties对象 application.setDefaultProperties(defaultMap); application.run(args);复制代码应用(使用)属性 @Value(“${xxx}”) 这种方式是最简单的,通过@Value注解可以将属性值注入进来。@ConfigurationProperties Spring Boot 可以方便的将属性注入到一个配置对象中。例如:my.name=Isea533 my.port=8080 my.servers[0]=dev.bar.com my.servers[1]=foo.bar.com复制代码对应对象:@ConfigurationProperties(prefix="my") public class Config { private String name; private Integer port; private List servers = new ArrayList(); public String geName(){ return this.name; } public Integer gePort(){ return this.port; } public List getServers() { return this.servers; } }复制代码Spring Boot 会自动将prefix="my"前缀为my的属性注入进来。Spring Boot 会自动转换类型,当使用List的时候需要注意在配置中对List进行初始化!Spring Boot 还支持嵌套属性注入,例如: name=isea533 jdbc.username=root jdbc.password=root ...对应的配置类:@ConfigurationProperties public class Config { private String name; private Jdbc jdbc; class Jdbc { private String username; private String password; //getter... } public Integer gePort(){ return this.port; } public Jdbc getJdbc() { return this.jdbc; }复制代码}jdbc开头的属性都会注入到Jdbc对象中。在@Bean方法上使用@ConfigurationProperties 例如: @ConfigurationProperties(prefix = "foo") @Bean public FooComponent fooComponent() { ... }Spring Boot 会将foo开头的属性按照名字匹配注入到FooComponent对象中。这种方式比较少见,我们在整合mybatis那章结合Druid的配置来具体演示。属性占位符 例如: app.name=MyApp app.description=${app.name} is a Spring Boot application可以在配置文件中引用前面配置过的属性(优先级前面配置过的这里都能用)。通过如${app.name:默认名称}方法还可以设置默认值,当找不到引用的属性时,会使用默认的属性。由于${}方式会被Maven处理。如果你pom继承的spring-boot-starter-parent,Spring Boot 已经将maven-resources-plugins默认的${}方式改为了@ @方式,例如@name@。如果你是引入的Spring Boot,你可以修改使用其他的分隔符通过属性占位符还能缩短“命令行”参数 例如修改web默认端口需要使用--server.port=9090方式,如果在配置中写上: server.port=${port:8080}那么就可以使用更短的--port=9090,当不提供该参数的时候使用默认值8080。属性名匹配规则 例如有如下配置对象:@Component @ConfigurationProperties(prefix="person") public class ConnectionSettings { private String firstName; }复制代码firstName可以使用的属性名如下:person.firstName,标准的驼峰式命名person.first-name,虚线(-)分割方式,推荐在.properties和.yml配置文件中使用PERSON_FIRST_NAME,大写下划线形式,建议在系统环境变量中使用需要java学习路线图的私信笔者“java”领取哦!另外喜欢这篇文章的可以给笔者点个赞,关注一下,每天都会分享Java相关文章!还有不定时的福利赠送,包括整理的学习资料,面试题,源码等~~

资源下载

更多资源
Mario,低调大师唯一一个Java游戏作品

Mario,低调大师唯一一个Java游戏作品

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Oracle Database,又名Oracle RDBMS

Oracle Database,又名Oracle RDBMS

Oracle Database,又名Oracle RDBMS,或简称Oracle。是甲骨文公司的一款关系数据库管理系统。它是在数据库领域一直处于领先地位的产品。可以说Oracle数据库系统是目前世界上流行的关系数据库管理系统,系统可移植性好、使用方便、功能强,适用于各类大、中、小、微机环境。它是一种高效率、可靠性好的、适应高吞吐量的数据库方案。

Eclipse(集成开发环境)

Eclipse(集成开发环境)

Eclipse 是一个开放源代码的、基于Java的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。幸运的是,Eclipse 附带了一个标准的插件集,包括Java开发工具(Java Development Kit,JDK)。

Sublime Text 一个代码编辑器

Sublime Text 一个代码编辑器

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。