• [技术干货] 高并发场景下的 HttpClient 优化方案,QPS大大提升!【转】
    1 背景我们有个业务,会调用其他部门提供的一个基于http的服务,日调用量在千万级别。使用了httpclient来完成业务。之前因为qps上不去,就看了一下业务代码,并做了一些优化,记录在这里。先对比前后:优化之前,平均执行时间是250ms;优化之后,平均执行时间是80ms,降低了三分之二的消耗,容器不再动不动就报警线程耗尽了,清爽~2 分析项目的原实现比较粗略,就是每次请求时初始化一个httpclient,生成一个httpPost对象,执行,然后从返回结果取出entity,保存成一个字符串,最后显式关闭response和client。我们一点点分析和优化:2.1 httpclient反复创建开销httpclient是一个线程安全的类,没有必要由每个线程在每次使用时创建,全局保留一个即可。2.2 反复创建tcp连接的开销tcp的三次握手与四次挥手两大裹脚布过程,对于高频次的请求来说,消耗实在太大。试想如果每次请求我们需要花费5ms用于协商过程,那么对于qps为100的单系统,1秒钟我们就要花500ms用于握手和挥手。又不是高级领导,我们程序员就不要搞这么大做派了,改成keep alive方式以实现连接复用!2.3 重复缓存entity的开销原本的逻辑里,使用了如下代码:HttpEntityentity=httpResponse.getEntity();String response = EntityUtils.toString(entity);这里我们相当于额外复制了一份content到一个字符串里,而原本的httpResponse仍然保留了一份content,需要被consume掉,在高并发且content非常大的情况下,会消耗大量内存。并且,我们需要显式的关闭连接,ugly。3 实现按上面的分析,我们主要要做三件事:一是单例的client,二是缓存的保活连接,三是更好的处理返回结果。一就不说了,来说说二。提到连接缓存,很容易联想到数据库连接池。httpclient4提供了一个PoolingHttpClientConnectionManager 作为连接池。接下来我们通过以下步骤来优化:3.1 定义一个keep alive strategy关于keep-alive,本文不展开说明,只提一点,是否使用keep-alive要根据业务情况来定,它并不是灵丹妙药。还有一点,keep-alive和time_wait/close_wait之间也有不少故事。在本业务场景里,我们相当于有少数固定客户端,长时间极高频次的访问服务器,启用keep-alive非常合适再多提一嘴,http的keep-alive 和tcp的KEEPALIVE不是一个东西。回到正文,定义一个strategy如下:ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy() { @Override public long getKeepAliveDuration(HttpResponse response, HttpContext context) { HeaderElementIterator it = new BasicHeaderElementIterator (response.headerIterator(HTTP.CONN_KEEP_ALIVE)); while (it.hasNext()) { HeaderElement he = it.nextElement(); String param = he.getName(); String value = he.getValue(); if (value != null && param.equalsIgnoreCase ("timeout")) { return Long.parseLong(value) * 1000; } } return 60 * 1000;//如果没有约定,则默认定义时长为60s }};3.2 配置一个PoolingHttpClientConnectionManagerPoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();connectionManager.setMaxTotal(500);connectionManager.setDefaultMaxPerRoute(50);//例如默认每路由最高50并发,具体依据业务来定也可以针对每个路由设置并发数。3.3 生成httpclienthttpClient = HttpClients.custom() .setConnectionManager(connectionManager) .setKeepAliveStrategy(kaStrategy) .setDefaultRequestConfig(RequestConfig.custom().setStaleConnectionCheckEnabled(true).build()) .build();注意:使用setStaleConnectionCheckEnabled方法来逐出已被关闭的链接不被推荐。更好的方式是手动启用一个线程,定时运行closeExpiredConnections 和closeIdleConnections方法,如下所示。public static class IdleConnectionMonitorThread extends Thread { private final HttpClientConnectionManager connMgr; private volatile boolean shutdown; public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr) { super(); this.connMgr = connMgr; } @Override public void run() { try { while (!shutdown) { synchronized (this) { wait(5000); // Close expired connections connMgr.closeExpiredConnections(); // Optionally, close connections // that have been idle longer than 30 sec connMgr.closeIdleConnections(30, TimeUnit.SECONDS); } } } catch (InterruptedException ex) { // terminate } } public void shutdown() { shutdown = true; synchronized (this) { notifyAll(); } } }3.4 使用httpclient执行method时降低开销这里要注意的是,不要关闭connection。推荐程序员摸鱼地址:https://www.yoodb.com/slack-off/home.html一种可行的获取内容的方式类似于,把entity里的东西复制一份:res = EntityUtils.toString(response.getEntity(),"UTF-8");EntityUtils.consume(response1.getEntity());但是,更推荐的方式是定义一个ResponseHandler,方便你我他,不再自己catch异常和关闭流。在此我们可以看一下相关的源码:public <T> T execute(final HttpHost target, final HttpRequest request, final ResponseHandler<? extends T> responseHandler, final HttpContext context) throws IOException, ClientProtocolException { Args.notNull(responseHandler, "Response handler"); final HttpResponse response = execute(target, request, context); final T result; try { result = responseHandler.handleResponse(response); } catch (final Exception t) { final HttpEntity entity = response.getEntity(); try { EntityUtils.consume(entity); } catch (final Exception t2) { // Log this exception. The original exception is more // important and will be thrown to the caller. this.log.warn("Error consuming content after an exception.", t2); } if (t instanceof RuntimeException) { throw (RuntimeException) t; } if (t instanceof IOException) { throw (IOException) t; } throw new UndeclaredThrowableException(t); } // Handling the response was successful. Ensure that the content has // been fully consumed. final HttpEntity entity = response.getEntity(); EntityUtils.consume(entity);//看这里看这里 return result;}可以看到,如果我们使用resultHandler执行execute方法,会最终自动调用consume方法,而这个consume方法如下所示:public static void consume(final HttpEntity entity) throws IOException { if (entity == null) { return; } if (entity.isStreaming()) { final InputStream instream = entity.getContent(); if (instream != null) { instream.close(); } }}可以看到最终它关闭了输入流。4 其他通过以上步骤,基本就完成了一个支持高并发的httpclient的写法,下面是一些额外的配置和提醒:4.1 httpclient的一些超时配置CONNECTION_TIMEOUT是连接超时时间,SO_TIMEOUT是socket超时时间,这两者是不同的。连接超时时间是发起请求前的等待时间;socket超时时间是等待数据的超时时间。HttpParams params = new BasicHttpParams();//设置连接超时时间Integer CONNECTION_TIMEOUT = 2 * 1000; //设置请求超时2秒钟 根据业务调整Integer SO_TIMEOUT = 2 * 1000; //设置等待数据超时时间2秒钟 根据业务调整 //定义了当从ClientConnectionManager中检索ManagedClientConnection实例时使用的毫秒级的超时时间//这个参数期望得到一个java.lang.Long类型的值。如果这个参数没有被设置,默认等于CONNECTION_TIMEOUT,因此一定要设置。Long CONN_MANAGER_TIMEOUT = 500L; //在httpclient4.2.3中我记得它被改成了一个对象导致直接用long会报错,后来又改回来了 params.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, CONNECTION_TIMEOUT);params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, SO_TIMEOUT);params.setLongParameter(ClientPNames.CONN_MANAGER_TIMEOUT, CONN_MANAGER_TIMEOUT);//在提交请求之前 测试连接是否可用params.setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, true); //另外设置http client的重试次数,默认是3次;当前是禁用掉(如果项目量不到,这个默认即可)httpClient.setHttpRequestRetryHandler(newDefaultHttpRequestRetryHandler(0,false));4.2 如果配置了nginx的话,nginx也要设置面向两端的keep-alive现在的业务里,没有nginx的情况反而比较稀少。nginx默认和client端打开长连接而和server端使用短链接。注意client端的keepalive_timeout和keepalive_requests参数,以及upstream端的keepalive参数设置,这三个参数的意义在此也不再赘述。以上就是我的全部设置。通过这些设置,成功地将原本每次请求250ms的耗时降低到了80左右,效果显著。JAR包如下:<!-- httpclient --><dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.6</version></dependency>代码如下://Basic认证private static final CredentialsProvider credsProvider = new BasicCredentialsProvider();//httpClientprivate static final CloseableHttpClient httpclient;//httpGet方法private static final HttpGet httpget;//private static final RequestConfig reqestConfig;//响应处理器private static final ResponseHandler<String> responseHandler;//jackson解析工具private static final ObjectMapper mapper = new ObjectMapper(); static { System.setProperty("http.maxConnections","50"); System.setProperty("http.keepAlive", "true"); //设置basic校验 credsProvider.setCredentials( new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM), new UsernamePasswordCredentials("", "")); //创建http客户端 httpclient = HttpClients.custom() .useSystemProperties() .setRetryHandler(new DefaultHttpRequestRetryHandler(3,true)) .setDefaultCredentialsProvider(credsProvider) .build(); //初始化httpGet httpget = new HttpGet(); //初始化HTTP请求配置 reqestConfig = RequestConfig.custom() .setContentCompressionEnabled(true) .setSocketTimeout(100) .setAuthenticationEnabled(true) .setConnectionRequestTimeout(100) .setConnectTimeout(100).build(); httpget.setConfig(reqestConfig); //初始化response解析器 responseHandler = new BasicResponseHandler();}/* * 功能:返回响应 * @author zhangdaquan * @param [url] * @return org.apache.http.client.methods.CloseableHttpResponse * @exception */public static String getResponse(String url) throws IOException { HttpGet get = new HttpGet(url); String response = httpclient.execute(get,responseHandler); return response;} /* * 功能:发送http请求,并用net.sf.json工具解析 * @author zhangdaquan * @param [url] * @return org.json.JSONObject * @exception */public static JSONObject getUrl(String url) throws Exception{ try { httpget.setURI(URI.create(url)); String response = httpclient.execute(httpget,responseHandler); JSONObject json = JSONObject.fromObject(response); return json; } catch (IOException e) { e.printStackTrace(); } return null;} /* * 功能:发送http请求,并用jackson工具解析 * @author zhangdaquan * @param [url] * @return com.fasterxml.jackson.databind.JsonNode * @exception */public static JsonNode getUrl2(String url){ try { httpget.setURI(URI.create(url)); String response = httpclient.execute(httpget,responseHandler); JsonNode node = mapper.readTree(response); return node; } catch (IOException e) { e.printStackTrace(); } return null;}/* * 功能:发送http请求,并用fastjson工具解析 * @author zhangdaquan * @param [url] * @return com.fasterxml.jackson.databind.JsonNode * @exception */public static com.alibaba.fastjson.JSONObject getUrl3(String url){ try { httpget.setURI(URI.create(url)); String response = httpclient.execute(httpget,responseHandler); com.alibaba.fastjson.JSONObject jsonObject = com.alibaba.fastjson.JSONObject.parseObject(response); return jsonObject; } catch (IOException e) { e.printStackTrace(); } return null;}
  • [技术干货] MVC小知识
    (1) 什么是MVC?       MVC是一种设计思想,根据职责不同将程序中的组件分成以下3个部分。       V(View视图):负责与用户交互。将数据展现,或者是接收数据       M(Model模型):负责业务处理。业务模型,数据模型       C(Controller控制器):负责协同模型和视图工作。视图有请求调用模型处理,模型处理完毕调用视图响应。(2)为什么使用MVC?      MVC是一个非常优秀的设计思想,基于该思想架构程序,可以提高程序的结构灵活性,便于日后维护、扩展和升级。注意:下面内容助于理解:1)  一个模型可以被多个视图共享模型只负责输出数据,不关心数据的表现形式,同一仹数据,可以使用多个不同的视图展现给用户。模型只负责处理数据,不关心是谁在调用,可以使用多种不同的界面来调用模型。2)  方便测试    模型一般使用java 类来开发,在开发完成之后,可以立即测试。如果业务逻辑直接写在servlet里面,则需要部署在服务器上面才能测试,比较麻烦。3)  组件复用    控制器可以做成一个通用的模块。4)  代码好维护,利于分工协作。    按照 mvc 的思想,可以对程序迚行分层,一般划分成表示层(包括 v,c)、业务层(m中的业务逻辑部分)、持久层(m中的数据访问逻辑部分)。下一层的代码发生改变,只要接口不变,不会影响到上一层的代码。mvc的缺点1)  采用 mvc 以后,会增加代码量,相应的开发周期以及开发的成本会相应增加。2)  使用 mvc,需要良好的设计。如果设计不当,会增加开发的难度。在表示层Servlet中调用业务层代码的接口,当业务层发生改变时不影响Servelt ;在业务层Service中调用DAO的接口,DAO发生改变不影响Service和其上层 结论一般来说,如果一个程序需要良好的架构,需要良好的代码的可维护性及可扩展性,需要使用mvc思想来架构。反之,则不必使用。
  • [技术干货] AJAX开发小知识
    AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。AJAX 不是新的编程语言,而是一种使用现有标准的新方法。AJAX 是与服务器交换数据并更新部分网页的艺术,在不重新加载整个页面的情况下。AJAX 不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。AJAX编程步骤?1) 获得 XmlHttpRequest对象2) 使用 XmlHttpRequest向服务器发请求。                  a.发送get请求:                  /* open(请求方式,请求地址,同步/异步)  * 请求方式: get/post   * 请求地址:如果是get请求,请求参数添加到地址之后。                 * 比如  check_user.do?username=zs                 * 同步/异步:true 表示异步。*/xhr.open('get','check_user.do',true);                  b. 发送 post 请求:xhr.open('post','check_username.do',true); //#必须添加一个消息头content-type     xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");3). 在服务器端,处理请求。4).在监听器当中,处理服务器返回的响应。xhr.onreadystatechange=function(){  //编写相应的处理代码  if(xhr.readyState == 4){    //只有 readyState 等亍 4,xhr 才完整地接收到了服务器返回的数据。    //获得文本数据    var txt = xhr.responseText;    //获得一个xml dom对象。    var xml = xhr.responseXML;    //dom操作、更新页面  }   };5.xhr.send(null)AJAX技术的优点?   1.页面无刷新   2.不打断用户的操作,用户的体验好   3.按需获取数据,浏览器和服务器之间数据的传输量减少   4.是一个标准技术,不需要下载任何的插件   5.可以利用客户端(浏览器)的计算能力
  • Swagger 介绍和使用
    使用 Swagger 你只需要按照它的规范去定义接口及接口相关的信息,就可以做到生成接口文档,以及在线接口调试页面官网: swagger.io/ Knife4j 是为 Java MVC 框架集成 Swagger 生成 Api 文档的增强解决方案  使用方式 1. 导入坐标 <dependency>     <groupId>com.github.xiaoymin</groupId>     <artifactId>knife4j-spring-boot-starter</artifactId> </dependency> 2. 在配置类中加入 knife4j 相关配置     /**      * 通过knife4j生成接口文档      * @return      */     @Bean     public Docket docket() {         ApiInfo apiInfo = new ApiInfoBuilder()                 .title("苍穹外卖项目接口文档")                 .version("2.0")                 .description("苍穹外卖项目接口文档")                 .build();         Docket docket = new Docket(DocumentationType.SWAGGER_2)                 .apiInfo(apiInfo)                 .select()                 .apis(RequestHandlerSelectors.basePackage("com.sky.controller"))                 .paths(PathSelectors.any())                 .build();         return docket;     } 3. 设置静态资源映射,否则接口文档页面无法访问     /**      * 设置静态资源映射      * @param registry      */     protected void addResourceHandlers(ResourceHandlerRegistry registry) {         registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");         registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");     } 常用注解  查看效果 启动项目后访问如下地址: localhost:8080/doc.html  成功 附完整配置类代码: package com.sky.config;import com.sky.interceptor.JwtTokenAdminInterceptor;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;import springfox.documentation.builders.ApiInfoBuilder;import springfox.documentation.builders.PathSelectors;import springfox.documentation.builders.RequestHandlerSelectors;import springfox.documentation.service.ApiInfo;import springfox.documentation.spi.DocumentationType;import springfox.documentation.spring.web.plugins.Docket;/** * 配置类,注册web层相关组件 */@Configuration@Slf4jpublic class WebMvcConfiguration extends WebMvcConfigurationSupport { @Autowired private JwtTokenAdminInterceptor jwtTokenAdminInterceptor; /** * 注册自定义拦截器 * * @param registry */ protected void addInterceptors(InterceptorRegistry registry) { log.info("开始注册自定义拦截器..."); registry.addInterceptor(jwtTokenAdminInterceptor) .addPathPatterns("/admin/**") .excludePathPatterns("/admin/employee/login"); } /** * 通过knife4j生成接口文档 * @return */ @Bean public Docket docket() { log.info("准备生成接口文档..."); ApiInfo apiInfo = new ApiInfoBuilder() .title("苍穹外卖项目接口文档") .version("2.0") .description("苍穹外卖项目接口文档") .build(); Docket docket = new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo) .select() .apis(RequestHandlerSelectors.basePackage("com.sky.controller")) .paths(PathSelectors.any()) .build(); return docket; } /** * 设置静态资源映射 * @param registry */ protected void addResourceHandlers(ResourceHandlerRegistry registry) { log.info("开始设置静态资源映射..."); registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); }} 转载自https://learnku.com/articles/85427
  • [技术干货] Spring家族
    大家应该都知道,按照出现的顺序,spring全家桶大概包含了spring、springmvc、springboot以及springcloud,从开胃小菜spring到满汉全席springcloud,spring全家桶可谓Java工程师的必备大餐,那么,我们不妨先来看看,spring全家桶是如何从光杆司令spring发展到如今的庞大家族的。 想要精通Spring的你,不妨来翻一翻这份大神整理出来的367页PDF,我想这应该是对“Spring家族”最完美的诠释了。  Key1:攻克Spring5 ①Spring-手绘脑图 (基本概念+AOP+事务管理+IOC+MVC+Spring类等) ②Spring5高级编程 ③Spring源码解析 第一部分:核心实现(Spring整体架构和环境搭建+容器的基本实现+默认标签的解析+自定义标签的解析+bean的加载+容器的功能扩展+AOP) 第二部分:企业应用(数据库连接JDBC+整合MyBatis+事务+SpringMVC+远程服务+Spring消息) ④Spring源码笔记 (Spring概述+核心思想+手写实现IOC和AOP+SpringIOC应用+SpringIOC源码深度剖析+SpringAOP应用+SpringAOP源码深度剖析) ⑤Spring面试题(高级应用篇) 什么是Spring 框架?Spring 框架有哪些主要模块? 使用 Spring 框架能带来哪些好处? 什么是控制反转(IOC)?什么是依赖注入? 请解释下 Spring 框架中的 IoC? BeanFactory 和 ApplicationContext 有什么区别? Spring 有几种配置方式? 如何用基于 XML 配置的方式配置 Spring? 如何用基于 Java 配置的方式配置 Spring? 怎样用注解的方式配置 Spring? 请解释 Spring Bean 的生命周期? Spring Bean 的作用域之间有什么区别? 什么是 Spring inner beans? Spring 框架中的单例 Beans 是线程安全的么? 请举例说明如何在 Spring 中注入一个 Java Collection? 如何向 Spring Bean 中注入一个 Java.util.Properties? 请解释 Spring Bean 的自动装配? 请解释一下自动装配模式的区别? 如何开启基于注解的自动装配? 请举例解释@Required 注解? 请举例解释@Autowired 注解? 请举例说明@Qualifier 注解? 构造方法注入和设值注入有什么区别? Spring 框架中有哪些不同类型的事件? FileSystemResource 和 ClassPathResource 有何区别? Spring 框架中都用到了哪些设计模式? 开发中主要使用 Spring 的什么技术 ? 简述 AOP 和 IOC 概念 AOP 在 Spring 中如何配置 Bean ? IOC 容器对 Bean 的生命周期 答案: Key2:攻克Spring Boot ①Spring Boot-手绘脑图 ②Spring Boot实战 (入门+开发的第一个应用程序+自定义配置+测试+Groovy与Spring Boot CLI+在Spring Boot中使用Grails+深入Actuator+部署Spring Boot应用程序) ③Spring Boot 学习笔记-核心部分 (Spring Boot入门+配置文件+日志+Web开发+Docker+SpringBoot与数据访问+启动配置原理+自定义starter) ④Spring Boot面试题(高级应用篇) 什么是 Spring Boot? Spring Boot 有哪些优点? 什么是 JavaConfig? 如何重新加载 Spring Boot 上的更改,而无需重新启动服务器? Spring Boot 中的监视器是什么? 如何在 Spring Boot 中禁用 Actuator 端点安全性? 如何在自定义端口上运行 Spring Boot 应用程序? YAML 是一种人类可读的数据序列化语言。它通常用于配置文件。 如何实现 Spring Boot 应用程序的安全性? 如何集成 Spring Boot 和 ActiveMQ? 如何使用 Spring Boot 实现分页和排序? 什么是 Swagger?你用 Spring Boot 实现了它吗? 什么是 Spring Profiles? 什么是 Spring Batch? 什么是 FreeMarker 模板? 如何使用 Spring Boot 实现异常处理? 您使用了哪些 starter maven 依赖项? 什么是 CSRF 攻击? 什么是 WebSockets? 什么是 AOP? 什么是 Apache Kafka? 我们如何监视所有 Spring Boot 微服务? 答案: Key3:攻克Spring MVC ①Spring MVC-手绘脑图 ②Spring MVC源码分析与实践 (Spring框架+模型2和MVC模式+SpringMVC介绍+基于注解的控制器+数据绑定和表单标签库+转换器和格式化+验证器+表达式语言+JSTL+国际化+上传文件+下载文件+应用测试) ③Spring MVC学习笔记 ④Spring MVC面试题(高级应用篇) 什么是 SpringMVC? 说说Spring MVC 的优点 SpringMVC 工作原理 SpringMVC 流程 SpringMvc 的控制器是不是单例模式,如果是,有什么问题,怎么解决? 如果你也用过 struts2.简单介绍下 springMVC 和 struts2 的区别有哪些? SpingMVC 中的控制器的注解一般用哪个,有没有别的注解可以替代? @RequestMapping 注解用在类上面有什么作用? 怎么样把某个请求映射到特定的方法上面? 如果在拦截请求中,我想拦截 get 方式提交的方法,怎么配置? 怎么样在方法里面得到 Request,或者 Session? 我想在拦截的方法里面得到从前台传入的参数,怎么得到? 如果前台有很多个参数传入,并且这些参数都是一个对象的,那么怎么样快速得到这个对象? SpringMVC 中函数的返回值是什么? SpringMVC 怎么样设定重定向和转发的? SpringMVC 用什么对象从后台向前台传递数据的? SpringMVC 中有个类把视图和数据都合并的一起的,叫什么? 怎么样把 ModelMap 里面的数据放入 Session 里面? SpringMVC 怎么和 AJAX 相互调用的? 当一个方法向 AJAX 返回特殊对象,比如 Object,List 等,需要做什么处理? SpringMVC 里面拦截器是怎么写的? 讲下 SpringMVC 的执行流程  面试: Key4:攻克Spring Cloud ①Spring Cloud-手绘脑图 ②Spring Cloud参考指南 ③Spring Cloud学习笔记  第一篇:基础服务篇(微服务与SpringCloud+服务发现+配置中心+客户端负载均衡+熔断器+Zuul+网关新选择+调用链追踪+加密管理+公共子项目) 第二篇:任务与消息篇(消息驱动+消息总线+批处理) 第三篇:微服务实战篇(利用Docker进行编排与整合) ④Spring Cloud面试题(高级应用篇) 什么是 Spring Cloud? 使用 Spring Cloud 有什么优势? 服务注册和发现是什么意思?Spring Cloud 如何实现? 负载平衡的意义什么? 什么是 Hystrix?它如何实现容错? 什么是 Hystrix 断路器?我们需要它吗? 什么是 Netflix Feign?它的优点是什么? 什么是 Spring Cloud Bus?我们需要它吗? ———————————————— 原文链接:https://blog.csdn.net/weixin_66896902/article/details/126464926 
  • [技术干货] spring全家桶
    1、spring是一个容器、生态、框架 Spring框架为开发提供了一系列的解决方案,比如利用控制反转的核心特性,并通过依赖注入实现控制反转来实现管理对象生命周期容器化,利用面向切面编程进行声明式的事务管理,整合多种持久化技术管理数据访问,提供大量优秀的Web框架方便开发等等。Spring框架具有控制反转(IOC)特性,IOC旨在方便项目维护和测试,它提供了一种通过Java的反射机制对Java对象进行统一的配置和管理的方法。Spring框架利用容器管理对象的生命周期,容器可以通过扫描XML文件或类上特定Java注解来配置对象,开发者可以通过依赖查找或依赖注入来获得对象。Spring框架具有面向切面编程(AOP)框架,SpringAOP框架基于代理模式,同时运行时可配置;AOP框架主要针对模块之间的交叉关注点进行模块化。Spring框架下的事务管理、远程访问等功能均可以通过使用SpringAOP技术实现。  spring首先是一个框架,在我们整个开发流程中,所有的框架生产几乎全都依赖于spring,spring帮我们起到了一个IOC容器的作用,用来承载整体的bean对象,帮我们进行了对象的创建到销毁的整个生命周期的管理。在使用spring的时候可以使用配置文件也可以使用注解的方式来进行相关实现,在程序启动后,把配置文件或者注解定义好的那些bean对象转换成beanDefination,要完成整个beanDefination的解析到加载的过程,获得完整的对象之后,下一步要对beanDefination进行实例化操作,在进行实例化的时候最简单的方式是使用反射的方式来创建对象,当对象创建完成之后,之后要实现Aware、BeanPostProcessor接口的一些操作,初始化对象的一些操作  1.1IOC容器(存放bean对象) IOC(Inversion of Control): IOC容器控制管理bean对象,实现解耦。 控制翻转,其根本是依赖注入(Dependecy Injection),不会直接创建对象,只是把对象声明出来,在代码 中不直接与对象和服务进行连接,但是在配置文件中描述了哪一项组件需要哪一项服务,容器将他们组合起来。在一般的IOC场景中容器创建了所有的对象,并设置了必要的属性将他们联系在一起,等到需要使用的时候才把他们声明出来,使用注解就更方便了,容器会自动根据注 解把对象组合起来。  是我们在使用spring中最重要的一个方面,spring容器里面放的是一个一个的bean对象,spring在启动时通过BeanDefinitionReader读取XML配置文件或者注解(bean的定义信息)获取bean对象:  下图说明: BeanDefinitionReader将配置文件信息解析成BeanDefinition(有$占位符), BeanFactoryPostProcessor将此信息在解析成完整的BeanDefinition对象(将信息传入,去掉$占位符) 1 2 3  1.1.2 bean生命周期(复杂) 1)Spring启动,查找并加载需要被Spring管理的bean,进行Bean的实例化,在堆开辟新空间,使用反射实现对象的实例化  2)Bean实例化后对将Bean的引入和值注入到Bean的属性中  3)如果Bean实现了BeanNameAware接口的话,Spring将Bean的Id传递给setBeanName()方法  4)如果Bean实现了BeanFactoryAware接口的话,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入  5)如果Bean实现了ApplicationContextAware接口的话,Spring将调用Bean的setApplicationContext()方法,将bean所在应用上下文引用传入进来。  6)如果Bean实现了BeanPostProcessor接口,Spring就将调用他们的postProcessBeforeInitialization()方法。  7)如果Bean 实现了InitializingBean接口,Spring将调用他们的afterPropertiesSet()方法。类似的,如果bean使用init-method声明了初始化方法,该方法也会被调用  8)如果Bean 实现了BeanPostProcessor接口,Spring就将调用他们的postProcessAfterInitialization()方法。  9)此时,Bean已经准备就绪,可以被应用程序使用了。他们将一直驻留在应用上下文中,直到应用上下文被销毁。  10)如果bean实现了DisposableBean接口,Spring将调用它的destory()接口方法,同样,如果bean使用了destory-method 声明销毁方法,该方法也会被调用。  参考文档:https://www.cnblogs.com/javazhiyin/p/10905294.html  1.1.3 bean生命周期(简单): 实例化 Instantiation 属性赋值 Populate 初始化 Initialization 销毁 Destruction 1)实例化(Instantiation) //实例化是指Bean 从Bean到Object Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);  2)属性赋值(Populate)  3)初始化(Initialization) 初始化前: org.springFrameWork.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization 初始化中: org.springFrameWork.bean.InitializingBean#afterPropertiesSet 初始化后:org.springFrameWork.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization  4)销毁  参考文档:https://www.jianshu.com/p/1dec08d290c1  1.2 AOP面向切面编程 AOP:面向切面编程,底层实现是动态代理(JDK、CGlib)  web层级设计中,web层->网关层->服务层->数据层,每一层之间也是一个切面。编程中,对象与对象之间,方法与方法之间,模块与模块之间都是一个个切面。 将方法注入到接口调用的某个地方(切点)。  1.2.1 相关概念: 1)Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。 2)Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。 3)Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。 4)Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。 5)Target(目标对象):织入 Advice 的目标对象.。 6)Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程  参考文档:https://blog.csdn.net/q982151756/article/details/80513340  1.3 JdbcTemplate JdbcTemplate是Spring对JDBC的封装,目的是使JDBC更加易于使用。JdbcTemplate是Spring的一部分。JdbcTemplate处理了资源的建立和释放。  在JdbcTemplate中执行SQL语句的方法大致分为3类: 1)execute:可以执行所有SQL语句,一般用于执行DDL语句。 2)update:用于执行INSERT、UPDATE、DELETE等DML语句。 3)queryXxx:用于DQL数据查询语句。  1.4 事物 1.4.1、事物的特性 1.4.1.1、原子性(Atomicity): 事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。  1.4.1.2、一致性(Consistency): 一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏。  1.4.1.3、隔离性(Isolation): 可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。  1.4.1.4、持久性(Durability): 一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。  1.4.2、spring事物配置方式 1)编程式事物管理:是侵入性事务管理,使用TransactionTemplate或者直接使用PlatformTransactionManager,对于编程式事务管理,Spring推荐使用TransactionTemplate。 2)声明式事物管理:建立在AOP之上,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,执行完目标方法之后根据执行的情况提交或者回滚。  声明式事务管理要优于编程式事务管理:声明式事务属于无侵入式,不会影响业务逻辑的实现,只需要在配置文件中做相关的事务规则声明或者通过注解的方式,便可以将事务规则应用到业务逻辑中。  1.4.3、事物的隔离级别  参考文档:https://www.cnblogs.com/mseddl/p/11577846.html  1.5 循环依赖 循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。  Spring中循环依赖场景有: (1)构造器的循环依赖 (2)field属性的循环依赖。  1.5.1、检测是否存在循环依赖 Bean在创建的时候可以给该Bean打标,如果递归调用回来发现正在创建中的话,即说明了循环依赖了。  1.5.2、spring解决循环依赖 Spring为了解决单例的循环依赖问题,使用了三级缓存。  这三级缓存分别指: singletonFactories : 单例对象工厂的cache earlySingletonObjects :提前暴光的单例对象的Cache singletonObjects:单例对象的cache。  参考文档:https://blog.csdn.net/u010853261/article/details/77940767  2、SpringMVC 2.1、springMVC简介 M:model,模型层,模型就是数据 V:view,网页、jsp,显示数据 C:controller,控制层,控制器的作用就是把不同的数据(Model),显示在不同的视图(View)上,Servlet 扮演的就是这样的角色。  Spring MVC主要由DispatcherServlet、处理器映射、处理器(控制器)、视图解析器、视图组成。 他的两个核心是两个核心: 1)处理器映射:选择使用哪个控制器来处理请求 2)视图解析器:选择结果应该如何渲染  2.1.1、运行原理  (1) Http请求:客户端请求提交到DispatcherServlet。 (2) 寻找处理器:由DispatcherServlet控制器查询一个或多个HandlerMapping,找到处理请求的Controller。 (3) 调用处理器:DispatcherServlet将请求提交到Controller。 (4)调用业务处理和返回结果:Controller调用业务逻辑处理后,返回ModelAndView。 (5)处理视图映射并返回模型: DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图。 (6) Http响应:视图负责将结果显示到客户端。  2.1.2、SpringMVC接口解释 (1)DispatcherServlet接口: Spring提供的前端控制器,所有的请求都有经过它来统一分发。在DispatcherServlet将请求分发给Spring Controller之前,需要借助于Spring提供的HandlerMapping定位到具体的Controller。  (2)HandlerMapping接口: 能够完成客户请求到Controller映射。  (3)Controller接口: 需要为并发用户处理上述请求,因此实现Controller接口时,必须保证线程安全并且可重用。 Controller将处理用户请求,这和Struts Action扮演的角色是一致的。一旦Controller处理完用户请求,则返回ModelAndView对象给DispatcherServlet前端控制器,ModelAndView中包含了模型(Model)和视图(View)。 从宏观角度考虑,DispatcherServlet是整个Web应用的控制器;从微观考虑,Controller是单个Http请求处理过程中的控制器,而ModelAndView是Http请求过程中返回的模型(Model)和视图(View)。  (4)ViewResolver接口: Spring提供的视图解析器(ViewResolver)在Web应用中查找View对象,从而将相应结果渲染给客户。  2.1.3、DispatcherServlet: 是整个Spring MVC的核心。它负责接收HTTP请求组织协调Spring MVC的各个组成部分。其主要工作有以下三项: (1)截获符合特定格式的URL请求。 (2)初始化DispatcherServlet上下文对应WebApplicationContext,并将其与业务层、持久化层的WebApplicationContext建立关联。 (3)初始化Spring MVC的各个组成组件,并装配到DispatcherServlet中。  参考文档:https://blog.csdn.net/jianyuerensheng/article/details/51258942  3、springBoot 3.1springboot简介 用来简化spring初始搭建的过程,将配置文件转换成注解的形式,方便迅速。Spring Boot 提供了大量开箱即用(out-of-the-box)的依赖模块,例如 spring-boot-starter-redis、spring-boot-starter-data-mongodb 和 spring-boot-starter-data-elasticsearch 等。这些依赖模块为 Spring Boot 应用提供了大量的自动配置,使得 Spring Boot 应用只需要非常少量的配置甚至零配置,便可以运行起来。  3.2、特点 1)独立运行的 Spring 项目 Spring Boot 可以以 jar 包的形式独立运行,Spring Boot 项目只需通过命令“ java–jar xx.jar” 即可运行。 2) 内嵌 Servlet 容器 Spring Boot 使用嵌入式的 Servlet 容器(例如 Tomcat、Jetty 或者 Undertow 等),应用无需打成 WAR 包 。 3) 提供 starter 简化 Maven 配置 Spring Boot 提供了一系列的“starter”项目对象模型(POMS)来简化 Maven 配置。 4) 提供了大量的自动配置 Spring Boot 提供了大量的默认自动配置,来简化项目的开发,开发人员也通过配置文件修改默认配置。 5) 自带应用监控 Spring Boot 可以对正在运行的项目提供监控。 6) 无代码生成和 xml 配置 Spring Boot 不需要任何 xml 配置即可实现 Spring 的所有配置。 参考文档:http://c.biancheng.net/spring_boot/overview.html  4、springCloud Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的开发便利性简化了分布式系统的开发,比如服务发现、服务网关、服务路由、链路追踪等。Spring Cloud 并不重复造轮子,而是将市面上开发得比较好的模块集成进去,进行封装,从而减少了各模块的开发成本。换句话说:Spring Cloud 提供了构建分布式系统所需的“全家桶”。  4.1、优点: 1)集大成者,Spring Cloud 包含了微服务架构的方方面面。 2)约定优于配置,基于注解,没有配置文件。 3)轻量级组件,Spring Cloud 整合的组件大多比较轻量级,且都是各自领域的佼佼者。 4)开发简便,Spring Cloud 对各个组件进行了大量的封装,从而简化了开发。 5)开发灵活,Spring Cloud 的组件都是解耦的,开发人员可以灵活按需选择组件。  4.2、Spring Cloud 模块的相关介绍: Eureka:服务注册中心,用于服务管理。 Ribbon:基于客户端的负载均衡组件,默认的负载策略是轮询。 Hystrix:容错框架,能够防止服务的雪崩效应。 Feign:Web 服务客户端,能够简化 HTTP 接口的调用。 Zuul:核心是过滤器,API 网关,提供路由转发、请求过滤等功能,是一个基于 JVM 路由和服务端的负载均衡器。 Config:分布式配置管理。 Sleuth:服务跟踪。 Stream:构建消息驱动的微服务应用程序的框架。 Bus:消息代理的集群消息总线。  4.2.1、 服务治理 Eureka 和 Zookeeper 区别: Eureka 是基于 AP 原则构建的,而 ZooKeeper 是基于 CP 原则构建的。  注: 在分布式系统领域有个著名的 CAP 定理,即 C 为数据一致性;A 为服务可用性;P 为服务对网络分区故障的容错性。这三个特性在任何分布式系统中都不能同时满足,最多同时满足两个。  服务治理就是服务的自动化管理,其核心是服务的自动注册与发现 1)服务注册:服务实例将自身服务信息注册到注册中心 2)服务发现:服务实例通过注册中心,获取注册到其中的服务实例的信息,通过这些信息去请求题目提供的服务 3)服务剔除:服务注册中心将出问题的服务自动剔除可使用服务列表,使其不会被调用 参考文档:http://c.biancheng.net/spring_cloud/ 5、SSM Java 企业开发框架 SSM,即 Spring、SpringMVC、MyBatis 。 Spring框架:是一个轻量级 Java 开发框架,主要是为了解决企业应用开发的复杂性而创建的。 SpringMVC框架:SpringMVC 分离了 控制器、模型对象、分派器,让我们更容易进行开发定制 MyBatis框架:是一个 Java 持久层框架,用于操作数据库,消除了几乎所有的 JDBC 代码,使用简单的 XML 或 注解即可完成数据库操作。 ————————————————       原文链接:https://blog.csdn.net/weixin_47268011/article/details/118188608 
  • [技术干货] springcloud综述之springcloud全家桶
    1.1 系统架构演变随着互联网的发展,网站应用的规模也在不断的扩大,进而导致系统架构也在不断的进行变化。系统架构大体经历了下面几个过程: 单体应用架构—>垂直应用架构—>分布式架构—>SOA架构—>微服务架构,当然还有悄然兴起的Service Mesh(服务网格化)。接下来我们就来了解一下每种系统架构是什么样子的, 以及各有什么优缺点。1.1.1 单体应用架构web项目,然后部署到一台tomcat服务器上优点:项目架构简单,小型项目的话, 开发成本低项目部署在一个节点上, 维护方便缺点:全部功能集成在一个工程中,对于大型项目来讲不易开发和维护项目模块之间紧密耦合,单点容错率低无法针对不同模块进行针对性优化和水平扩展1.1.2 垂直应用架构所谓的垂直应用架构,就是将原来的一个应用拆成互不相干的几个应用,以提升效率。这样拆分完毕之后,一旦某个应用用户访问量变大,只需要增加对应应用节点就可以了,而无需增加其他节点。优点:系统拆分实现了流量分担,解决了并发问题,而且可以针对不同模块进行优化和水扩展一个系统的问题不会影响到其他系统,提高容错率缺点:系统之间相互独立, 无法进行相互调用系统之间相互独立, 会有重复的开发任务1.1.3 分布式架构分布式系统架构,它把工程拆分成表现层和服务层两个部分,服务层中包含业务逻辑。表现层只需要处理和页面的交互,业务逻辑都是调用服务层的服务来实现。优点:抽取公共的功能为服务层,提高代码复用性缺点:系统间耦合度变高,调用关系错综复杂,难以维护1.1.4 SOA架构增加一个调度中心对集群进行实时管理。此时,用于资源调度和治理中心(SOA Service OrientedArchitecture,面向服务的架构)是关键。优点:使用注册中心解决了服务间调用关系的自动调节缺点:服务间会有依赖关系,一旦某个环节出错会影响较大( 服务雪崩 )服务关心复杂,运维、测试部署困难1.1.5 微服务架构微服务架构在某种程度上是面向服务的架构SOA继续发展的下一步,它更加强调服务的"彻底拆分"。优点:服务原子化拆分,独立打包、部署和升级,保证每个微服务清晰的任务划分,利于扩展微服务之间采用Restful等轻量级http协议相互调用缺点:分布式系统开发的技术成本高(容错、分布式事务等)1.2 微服务架构介绍微服务架构, 简单的说就是将单体应用进一步拆分,拆分成更小的服务,每个服务都是一个可以独立运行的项目。1.2.1 微服务架构的常见问题一旦采用微服务系统架构,就势必会遇到这样几个问题:这么多小服务,如何管理他们?(服务治理 注册中心[服务注册 发现 剔除])这么多小服务,他们之间如何通讯?(restful rpc)这么多小服务,客户端怎么访问他们?(网关)这么多小服务,一旦出现问题了,应该如何自处理?(容错)这么多小服务,一旦出现问题了,应该如何排错? (链路追踪)对于上面的问题,是任何一个微服务设计者都不能绕过去的,因此大部分的微服务产品都针对每一个问题提供了相应的组件来解决它们。常见的微服务组件及发展趋势1.2.2 微服务架构的常见概念1.2.2.1 服务治理服务治理就是进行服务的自动化管理,其核心是服务的自动注册与发现。服务注册:服务实例将自身服务信息注册到注册中心。服务发现:服务实例通过注册中心,获取到注册到其中的服务实例的信息,通过这些信息去请求它们提供的服务。服务剔除:服务注册中心将出问题的服务自动剔除到可用列表之外,使其不会被调用到。1.2.2.2 服务调用在微服务架构中,通常存在多个服务之间的远程调用的需求。目前主流的远程调用技术有基于HTTP的RESTful接口以及基于TCP的RPC协议。(Representational State Transfer)这是一种HTTP调用的格式,更标准,更通用,无论哪种语言都支持http协议RPC(Remote Promote Call)一种进程间通信方式。允许像调用本地服务一样调用远程服务。RPC框架的主要目标就是让远程服务调用更简单、透明。RPC框架负责屏蔽底层的传输方式、序列化方式和通信细节。开发人员在使用的时候只需要了解谁在什么位置提供了什么样的远程服务接口即可,并不需要关心底层通信细节和调用过程。区别与联系比较项 RESTful RPC通讯协议 HTTP 一般使用TCP性能 略低 较高灵活度 高 低应用 微服务架构 SOA架构1.2.2.3 服务网关随着微服务的不断增多,不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求,如果让客户端直接与各个微服务通信可能出现:客户端需要调用不同的url地址,增加难度在一定的场景下,存在跨域请求的问题每个微服务都需要进行单独的身份认证针对这些问题,API网关顺势而生。API网关直面意思是将所有API调用统一接入到API网关层,由网关层统一接入和输出。一个网关的基本功能有:统一接入、安全防护、协议适配、流量管控、长短链接支持、容错能力。有了网关之后,各个API服务提供团队可以专注于自己的的业务逻辑处理,而API网关更专注于安全、流量、路由等问题。1.2.2.4 服务容错在微服务当中,一个请求经常会涉及到调用几个服务,如果其中某个服务不可用,没有做服务容错的话,极有可能会造成一连串的服务不可用,这就是雪崩效应。我们没法预防雪崩效应的发生,只能尽可能去做好容错。服务容错的三个核心思想是:不被外界环境影响不被上游请求压垮不被下游响应拖垮1.2.2.5 链路追踪随着微服务架构的流行,服务按照不同的维度进行拆分,一次请求往往需要涉及到多个服务。互联网应用构建在不同的软件模块集上,这些软件模块,有可能是由不同的团队开发、可能使用不同的编程语言来实现、有可能布在了几千台服务器,横跨多个不同的数据中心。因此,就需要对一次请求涉及的多个服务链路进行日志记录,性能监控即链路追踪1.2.3 微服务架构的常见解决方案1.2.3.1 ServiceCombApache ServiceComb,前身是华为云的微服务引擎 CSE (Cloud Service Engine) 云服务,是全球首个Apache微服务顶级项目。它提供了一站式的微服务开源解决方案,致力于帮助企业、用户和开发者将企业应用轻松微服务化上云,并实现对微服务应用的高效运维管理。1.2.3.2 SpringCloudSpring Cloud是一系列框架的集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring Cloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。SpringCloud提供的全生态的分布式组件支持,netflex系列服务注册发现:Eureka(其他选择: Consul,Zookeeper,Nacos,Etcd)负载均衡:Robbin(声明式调用:Fegin)断路器:Hystrix网关:Zuul2,Spring Cloud Gateway配置中心:Spring Cloud Config监控:Spring Boot Admin,Spring Boot Actuator链路跟踪:Spring Cloud Sleuth三、组件介绍1、Eureka(1)体系划分Eureka是SpringCloud官方推荐的服务注册发现组件。Eureka的角色和Dubbo中Zookeeper的角色类似,体系划分如下,业务上可以分为:服务注册中心、服务提供者、服务消费者。代码逻辑上分为:Eureka Server 和 Eureka Client。(2)结构原理两台Eureka服务注册中心构成的服务注册中心的主从复制集群;然后服务提供者向注册中心进行注册、续约、下线服务等;服务消费者向Eureka注册中心拉去服务列表并维护在本地;然后服务消费者根据从Eureka服务注册中心获取的服务列表选取一个服务提供者进行消费服务。(3)Eureka 集群(三节点,两两注册)从图中可以看出 Eureka Server 集群相互之间通过 Replicate 来同步数据,相互之间不区分主节点和从节点,所有的节点都是平等的。在这种架构中,节点通过彼此互相注册来提高可用性,每个节点需要添加一个或多个有效的 serviceUrl 指向其他节点。如果某台 Eureka Server 宕机,Eureka Client 的请求会自动切换到新的 Eureka Server 节点。当宕机的服务器重新恢复后,Eureka 会再次将其纳入到服务器集群管理之中。当节点开始接受客户端请求时,所有的操作都会进行节点间复制,将请求复制到其它 Eureka Server 当前所知的所有节点中。Eureka Server 集群之间的状态是采用异步方式同步的,所以不保证节点间的状态一定是一致的,不过基本能保证最终状态是一致的(Eureka保证AP,Zookeeper强调CP)。2、Robbin(1)客户端的软负载Robbin是springcloud的LB调用组件,提供客户端的软件负载均衡。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随即连接等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法。Robbin 提供的客户端软负载,是SpringCloud微服务的典型特征之一。(2)负载均衡策略轮询(RoundRobin),以轮询的方式依次将请求调度不同的服务器,即每次调度执行i = (i + 1) mod n,并选出第i台服务器。随机(Random),随机选择状态为UP的Server。加权响应时间(WeightedResponseTime),根据相应时间分配一个weight,相应时间越长,weight越小,被选中的可能性越低。区域感知轮询(ZoneAvoidanceRule),复合判断server所在区域的性能和server的可用性选择server。(3)核心组件Ribbon的核心组件(均为接口类型)有以下几个,ServerList,用于获取地址列表。它既可以是静态的(提供一组固定的地址),也可以是动态的(从注册中心中定期查询地址列表)。ServerListFilter,仅当使用动态ServerList时使用,用于在原始的服务列表中使用一定策略过虑掉一部分地址。IRule,选择一个最终的服务地址作为LB结果。选择策略有轮询、根据响应时间加权、断路器(当Hystrix可用时)等。Ribbon在工作时首选会通过ServerList来获取所有可用的服务列表,然后通过ServerListFilter过虑掉一部分地址,最后在剩下的地址中通过IRule选择出一台服务器作为最终结果。3、FeginFegin是一个声明式Http端调用,集成了Robbin的负载均衡功能,同时声明式调用更加方便(只需要简单的注解即可)。简单的可以理解为:Spring Cloud Feign 的出现使得Eureka和Ribbon的使用更加简单。(1)Fegin接口示例a、启动类 @EnableFeignClients 注解@SpringBootApplication@EnableEurekaClient@EnableFeignClients // 启用fegin声明式调用public class FeginComsumerApplication {public static void main(String[] args) {SpringApplication.run(FeginComsumerApplication.class, args);}}b、声明一个调用的Feign接口,@Service@FeignClient(name = "name-service")public interface NameService {@RequestMapping(value = "/getName", method = RequestMethod.GET)public String getName();}c、服务端提供接口实现@RequestMapping(value = "/getName", method = RequestMethod.GET) public String getName(){return "hello world";}d、fegin声明是调用@Autowiredprivate NameServiceClient feginNameServiceClient;@RequestMapping(value = "/getName", method= RequestMethod.GET)public String getName(){return feginNameServiceClient.getName();}(2)Fegin 的类加载流程通过主类上的EnableFeignClients 注解开启FeignClient;根据Feign 的规则实现接口,并加上FeignClient注解,供调用的地方注入调用;程序启动后,会扫描所有FeignClient 注解的类,并将这些信息注入到IOC 容器中;当b中接口被调用时,通过jdk代理,以及反射(Spring处理注解的方式),来生成具体的RequestTemplateRequestTemplate 生成ReqestRequest 交给httpclient处理,这里的httpclient 可以是OkHttp,也可以是HttpUrlConnection 或者HttpClient最后Client被封装到LoadBalanceClient类,这个类结合Ribbon 实现负载均衡。(3)Fegin的原理4、HystrixHystrix 是springcloud生态的断路器(隔离、限流、降级),主要是用来预防服务雪崩的现象,剔除掉分布式系统中某些挂掉或请求过慢的服务节点。Hystrix 是一个帮助解决分布式系统中超时处理和容错的类库, 拥有保护系统的能力。(1)隔离、限流、降级Hystrix断路器有两种隔离策略:信号量隔离(默认)和线程池隔离。信号量模式从始至终都只有请求线程自身,是同步调用模式,不支持超时调用,不支持直接熔断,由于没有线程的切换,开销非常小。线程池模式可以支持异步调用,支持超时调用,支持直接熔断,存在线程切换,开销大。信号量隔离:常用于获取共享资源的场景中,比如计算机连接了两个打印机,那么初始的信号量就是2,被某个进程或线程获取后减1,信号量为0后,需要获取的线程或进程进入资源等待状态。Hystrix的处理有些不同,其不等待,直接返回失败。线程池隔离:采用的就是jdk的线程池,其默认选用不使用阻塞队列的线程池,例如线程池大小为10,如果某时刻10个线程均被使用,那么新的请求将不会进入等待队列,而是直接返回失败,起到限流的作用。此外,其还引入了一个断路器机制,当断路器处于打开状态时,直接返回失败或进入降级流程。断路器打开和关闭的触发流程为:当总的请求数达到可阈值HystrixCommandProperties.circuitBreakerRequestVolumeThreshold(),或总的请求失败百分比达到了阈值HystrixCommandProperties.circuitBreakerErrorThresholdPercentage(),这时将断路器的状态由关闭设置为打开。当断路器打开时,所有的请求均被短路,在经过指定休眠时间窗口后,让下一个请求通过(断路器被认为是半开状态)。如果请求失败,断路器进入打开状态,并进入新的休眠窗口;否则进入关闭状态。(2)Hystrix 的整体处理流程流程如上图所示,Hystrix框架通过命令模式来实现方法粒度上的服务保障,主要涉及HystrixCommand和HystrixObservableCommand类,前者提供同步的execute和异步的queue方法,后者提供立即执行observe和延迟执行toObservable的回调方法。此外,实际项目中通常不会使用Hystrix集成的本地缓存。5、Spring Cloud GatewaySpring Cloud Gateway 是springcloud全新推出的第二代微服务网关,用来替代Zuul。gateway实现了服务转发、熔断、限流、权限校验等功能,有测评显示,性能比Zuul要好不少。(1)相关概念Route(路由):这是网关的基本构建块。它由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配。Predicate(断言):这是一个 Java 8 的 Predicate。输入类型是一个 ServerWebExchange。我们可以使用它来匹配来自 HTTP 请求的任何内容,例如 headers 或参数。Filter(过滤器):这是org.springframework.cloud.gateway.filter.GatewayFilter的实例,我们可以使用它修改请求和响应。(2)请求流程spring cloud gateway处理request请求的流程如下图,我们先看gateway的构成:一个netty server,一个netty client,Route(包含Predicate和Filter)。在gateway中最重要的应该是Route(Netty Server和Client已经封装好了),它由RouteLocatorBuilder构建,内部包含Predicate和Filter。流程:即在最前端,启动一个netty server(默认端口为8080)接受请求,然后通过Routes(每个Route由Predicate(等同于HandlerMapping)和Filter(等同于HandlerAdapter))处理后通过Netty Client发给响应的微服务。(3)yml配置server.port: 8082spring:application:name: gatewaycloud:gateway:routes:- id: path_routeuri: http://localhost:8000order: 0predicates:- Path=/foo/**filters:- StripPrefix=1上面给出了一个根据请求路径来匹配目标uri的例子,如果请求的路径为/foo/bar,则目标uri为 http://localhost:8000/bar。如果上面例子中没有加一个StripPrefix=1过滤器,则目标uri 为http://localhost:8000/foo/bar,StripPrefix过滤器是去掉一个路径。6、Spring Boot AdminSpring Boot Admin 是springcloud提供的监控组件,用于管理和监控SpringBoot各个微服务。Admin通过注册中心(如Eureka)来监控各个节点的状态。可以结合 Spring Boot Actuator 使用,常用的监控数据有,显示健康状况显示详细信息,例如JVM和内存指标micrometer.io指标数据源指标缓存指标查看jvm系统和环境属性监控 server端需要 @EnableAdminServer 注解,client端只需要配置好yaml文件就好了,admin会通过注册中心获取各个服务节点的状态。management:endpoints:web:exposure:include: '*'endpoint:health:show-details: ALWAYS7、Spring Cloud ConfigSpring Cloud Config 是springcloud的分布式配置中心组件。分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件。在Spring Cloud中,有分布式配置中心组件Spring Cloud Config ,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中。SpringCloudConfig分为服务端(Config Server)和客户端(Config Client),服务端负责将git(svn)中存储的配置文件发布成REST接口,客户端可以从服务端REST接口获取配置。但客户端并不能主动感知到配置的变化,从而主动去获取新的配置, 它需要每个客户端通过POST方法触发各自的/refresh,SpringCloudBus就通过一个轻量级消息代理连接分布式系统的节点。Config Server用于配置属性的存储,存储的位置可以为Git仓库、SVN仓库、本地文件等,Config Client用于服务属性的读取。spring cloud config是将配置保存在git/svn上,依赖git每次push后,触发webhook回调,最终触发spring cloud bus(消息总线),然后由消息总线通知相关的应用。1.2.3.3 SpringCloud AlibabaSpring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。SpringCloud Alibaba介绍Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式 应用服务。依托 Spring Cloud Alibaba,您只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里微服务解决方案,通过阿里中间件来迅速搭建分布式应用系统。1.3.1 主要功能服务限流降级:默认支持 WebServlet、WebFlux, OpenFeign、RestTemplate、Spring Cloud Gateway, Zuul, Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。服务注册与发现:适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。分布式配置管理:支持分布式系统中的外部化配置,配置更改时自动刷新。消息驱动能力:基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。分布式事务:使用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。阿里云对象存储:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。分布式任务调度:提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有Worker(schedulerx-client)上执行。阿里云短信服务:覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。组件Sentinel: 把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。Nacos: 一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。RocketMQ: 一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。Dubbo: Apache Dubbo™ 是一款高性能 Java RPC 框架。Seata: 阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。Alibaba Cloud ACM: 一款在分布式架构环境中对应用配置进行集中管理和推送的应用配置中心产品。Alibaba Cloud OSS: 阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。Alibaba Cloud SchedulerX: 阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。Alibaba Cloud SMS: 覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。具体技术点(约定>配置>编码)maven:3.6.0这样做的好处就是: 如果有多个子项目都引用同一样的依赖,则可以避免在每个使用的子项目里都声明一个版本号,这样想升级或切换到另一个版本时,只需在顶层父容器里更新,而不需要一个一个子项目的修改l;另外如果某个子项目需要另外的一个版本,只需声明version版本数据库:MySQL 5.7持久层: SpingData Jpa其他: SpringCloud Alibaba 技术栈安全框架:Spring Security Spring Cloud Oauth2分布式任务调度:elastic-job持久层框架:MyBatis、通用Mapper4、Mybatis_PageHelper数据库连接池日志管理:Logback 前端框架:Vue全家桶以及相关组件三方服务: 邮件服务、阿里云短信服务、七牛云文件服务、钉钉机器人服务、高德地图API————————————————原文链接:https://blog.csdn.net/liuerchong/article/details/108374777
  • [技术干货] 一文读懂SpringCloud全家桶
    一、云原生应用 SpringCloud是对Springboot使用的分布式解决方案,适合分布式、中大型的项目架构开发,现在也逐渐成为Java服务端的主流框架。使用Spring Cloud开发的应用程序非常适合在Docker和PaaS(比如Pivotal Cloud Foundry)上部署,所以又叫做云原生应用(Cloud Native Application)。云原生可以简单地理解为面向云环境的软件架构。 springcloud 微服务架构, 1、优点 服务拆分粒度更细,有利于资源重复利用,有利于提高开发效率 可以更精准的制定优化服务方案,提高系统的可维护性 微服务架构采用去中心化思想,服务之间采用Restful等轻量级通讯,比ESB更轻量 适于互联网时代,产品迭代周期更短 2、缺点 微服务过多,治理成本高,不利于维护系统 分布式系统开发的成本高(容错,分布式事务等)对团队挑战大 二、全家桶 SpringCloud提供的全生态的分布式组件支持, 服务注册发现:Eureka(其他选择: Consul,Zookeeper,Nacos,Etcd) 负载均衡:Robbin(声明式调用:Fegin) 断路器:Hystrix 网关:Zuul2,Spring Cloud Gateway 配置中心:Spring Cloud Config 监控:Spring Boot Admin,Spring Boot Actuator 链路跟踪:Spring Cloud Sleuth 三、组件介绍 1、Eureka (1)体系划分 Eureka是SpringCloud官方推荐的服务注册发现组件。Eureka的角色和Dubbo中Zookeeper的角色类似,体系划分如下, 业务上可以分为:服务注册中心、服务提供者、服务消费者。 代码逻辑上分为:Eureka Server 和 Eureka Client。  (2)结构原理 看图说话, 两台Eureka服务注册中心构成的服务注册中心的主从复制集群; 然后服务提供者向注册中心进行注册、续约、下线服务等; 服务消费者向Eureka注册中心拉去服务列表并维护在本地; 然后服务消费者根据从Eureka服务注册中心获取的服务列表选取一个服务提供者进行消费服务。 (3)Eureka 集群(三节点,两两注册) 从图中可以看出 Eureka Server 集群相互之间通过 Replicate 来同步数据,相互之间不区分主节点和从节点,所有的节点都是平等的。在这种架构中,节点通过彼此互相注册来提高可用性,每个节点需要添加一个或多个有效的 serviceUrl 指向其他节点。 如果某台 Eureka Server 宕机,Eureka Client 的请求会自动切换到新的 Eureka Server 节点。当宕机的服务器重新恢复后,Eureka 会再次将其纳入到服务器集群管理之中。当节点开始接受客户端请求时,所有的操作都会进行节点间复制,将请求复制到其它 Eureka Server 当前所知的所有节点中。 Eureka Server 集群之间的状态是采用异步方式同步的,所以不保证节点间的状态一定是一致的,不过基本能保证最终状态是一致的(Eureka保证AP,Zookeeper强调CP)。 2、Robbin (1)客户端的软负载 Robbin是springcloud的LB调用组件,提供客户端的软件负载均衡。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随即连接等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法。 Robbin 提供的客户端软负载,是SpringCloud微服务的典型特征之一。 (2)负载均衡策略 轮询(RoundRobin),以轮询的方式依次将请求调度不同的服务器,即每次调度执行i = (i + 1) mod n,并选出第i台服务器。 随机(Random),随机选择状态为UP的Server。 加权响应时间(WeightedResponseTime),根据相应时间分配一个weight,相应时间越长,weight越小,被选中的可能性越低。 区域感知轮询(ZoneAvoidanceRule),复合判断server所在区域的性能和server的可用性选择server。 (3)核心组件 Ribbon的核心组件(均为接口类型)有以下几个, ServerList,用于获取地址列表。它既可以是静态的(提供一组固定的地址),也可以是动态的(从注册中心中定期查询地址列表)。 ServerListFilter,仅当使用动态ServerList时使用,用于在原始的服务列表中使用一定策略过虑掉一部分地址。 IRule,选择一个最终的服务地址作为LB结果。选择策略有轮询、根据响应时间加权、断路器(当Hystrix可用时)等。 Ribbon在工作时首选会通过ServerList来获取所有可用的服务列表,然后通过ServerListFilter过虑掉一部分地址,最后在剩下的地址中通过IRule选择出一台服务器作为最终结果。 3、Fegin Fegin是一个声明式Http端调用,集成了Robbin的负载均衡功能,同时声明式调用更加方便(只需要简单的注解即可)。简单的可以理解为:Spring Cloud Feign 的出现使得Eureka和Ribbon的使用更加简单。  (1)Fegin接口示例  a、启动类@EnableFeignClients 注解  @SpringBootApplication @EnableEurekaClient @EnableFeignClients // 启用fegin声明式调用 public class FeginComsumerApplication {      public static void main(String[] args) {         SpringApplication.run(FeginComsumerApplication.class, args);     }  } b、声明一个调用的Feign接口,  @Service @FeignClient(name = "name-service") public interface NameService {      @RequestMapping(value = "/getName", method = RequestMethod.GET)     public String getName();  } c、服务端提供接口实现  @RequestMapping(value = "/getName", method = RequestMethod.GET) public String getName(){     return "hello world"; } d、fegin声明是调用 @Autowired private NameServiceClient feginNameServiceClient; @RequestMapping(value = "/getName", method= RequestMethod.GET) public String getName(){     return feginNameServiceClient.getName(); } (2)Fegin 的类加载流程 通过主类上的EnableFeignClients 注解开启FeignClient; 根据Feign 的规则实现接口,并加上FeignClient注解,供调用的地方注入调用; 程序启动后,会扫描所有FeignClient 注解的类,并将这些信息注入到IOC 容器中; 当b中接口被调用时,通过jdk代理,以及反射(Spring处理注解的方式),来生成具体的RequestTemplate RequestTemplate 生成Reqest Request 交给httpclient处理,这里的httpclient 可以是OkHttp,也可以是HttpUrlConnection 或者HttpClient 最后Client被封装到LoadBalanceClient类,这个类结合Ribbon 实现负载均衡。 (3)Fegin的原理 4、Hystrix Hystrix 是springcloud生态的断路器(隔离、限流、降级),主要是用来预防服务雪崩的现象,剔除掉分布式系统中某些挂掉或请求过慢的服务节点。Hystrix是一个帮助解决分布式系统中超时处理和容错的类库, 拥有保护系统的能力。 (1)隔离、限流、降级 Hystrix断路器有两种隔离策略:信号量隔离(默认)和线程池隔离。 信号量模式从始至终都只有请求线程自身,是同步调用模式,不支持超时调用,不支持直接熔断,由于没有线程的切换,开销非常小。 线程池模式可以支持异步调用,支持超时调用,支持直接熔断,存在线程切换,开销大。 信号量隔离:常用于获取共享资源的场景中,比如计算机连接了两个打印机,那么初始的信号量就是2,被某个进程或线程获取后减1,信号量为0后,需要获取的线程或进程进入资源等待状态。Hystrix的处理有些不同,其不等待,直接返回失败。 线程池隔离:采用的就是jdk的线程池,其默认选用不使用阻塞队列的线程池,例如线程池大小为10,如果某时刻10个线程均被使用,那么新的请求将不会进入等待队列,而是直接返回失败,起到限流的作用。 此外,其还引入了一个断路器机制,当断路器处于打开状态时,直接返回失败或进入降级流程。断路器打开和关闭的触发流程为:当总的请求数达到可阈值HystrixCommandProperties.circuitBreakerRequestVolumeThreshold(),或总的请求失败百分比达到了阈值HystrixCommandProperties.circuitBreakerErrorThresholdPercentage(),这时将断路器的状态由关闭设置为打开。当断路器打开时,所有的请求均被短路,在经过指定休眠时间窗口后,让下一个请求通过(断路器被认为是半开状态)。如果请求失败,断路器进入打开状态,并进入新的休眠窗口;否则进入关闭状态。 (2)Hystrix 的整体处理流程 流程如上图所示,Hystrix框架通过命令模式来实现方法粒度上的服务保障,主要涉及HystrixCommand和HystrixObservableCommand类,前者提供同步的execute和异步的queue方法,后者提供立即执行observe和延迟执行toObservable的回调方法。此外,实际项目中通常不会使用Hystrix集成的本地缓存。 5、Spring Cloud Gateway Spring Cloud Gateway 是springcloud全新推出的第二代微服务网关,用来替代Zuul。gateway实现了服务转发、熔断、限流、权限校验等功能,有测评显示,性能比Zuul要好不少。 (1)相关概念 Route(路由):这是网关的基本构建块。它由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配。 Predicate(断言):这是一个 Java 8 的 Predicate。输入类型是一个 ServerWebExchange。我们可以使用它来匹配来自 HTTP 请求的任何内容,例如 headers 或参数。 Filter(过滤器):这是org.springframework.cloud.gateway.filter.GatewayFilter的实例,我们可以使用它修改请求和响应。 (2)请求流程 spring cloud gateway处理request请求的流程如下图, 我们先看gateway的构成:一个netty server,一个netty client,Route(包含Predicate和Filter)。在gateway中最重要的应该是Route(Netty Server和Client已经封装好了),它由RouteLocatorBuilder构建,内部包含Predicate和Filter。 **流程:**即在最前端,启动一个netty server(默认端口为8080)接受请求,然后通过Routes(每个Route由Predicate(等同于HandlerMapping)和Filter(等同于HandlerAdapter))处理后通过Netty Client发给响应的微服务。  (3)yml配置 server.port: 8082 spring:   application:     name: gateway   cloud:     gateway:       routes:         - id: path_route           uri: http://localhost:8000           order: 0           predicates:             - Path=/foo/**           filters:             - StripPrefix=1 上面给出了一个根据请求路径来匹配目标uri的例子,如果请求的路径为/foo/bar,则目标uri为http://localhost:8000/bar。如果上面例子中没有加一个StripPrefix=1过滤器,则目标uri 为http://localhost:8000/foo/bar,StripPrefix过滤器是去掉一个路径。  6、Spring Boot Admin Spring Boot Admin 是springcloud提供的监控组件,用于管理和监控SpringBoot各个微服务。Admin通过注册中心(如Eureka)来监控各个节点的状态。可以结合Spring Boot Actuator 使用,常用的监控数据有,  显示健康状况 显示详细信息,例如 JVM和内存指标 micrometer.io指标 数据源指标 缓存指标 查看jvm系统和环境属性 监控 server端需要 @EnableAdminServer 注解,client端只需要配置好yaml文件就好了,admin会通过注册中心获取各个服务节点的状态。  management:   endpoints:     web:       exposure:         include: '*'   endpoint:     health:       show-details: ALWAYS 7、Spring Cloud Config Spring Cloud Config 是springcloud的分布式配置中心组件。 分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件。在Spring Cloud中,有分布式配置中心组件Spring Cloud Config ,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中。 SpringCloudConfig分为服务端(Config Server)和客户端(Config Client),服务端负责将git(svn)中存储的配置文件发布成REST接口,客户端可以从服务端REST接口获取配置。但客户端并不能主动感知到配置的变化,从而主动去获取新的配置, 它需要每个客户端通过POST方法触发各自的/refresh,SpringCloudBus就通过一个轻量级消息代理连接分布式系统的节点。 Config Server用于配置属性的存储,存储的位置可以为Git仓库、SVN仓库、本地文件等,Config Client用于服务属性的读取。 spring cloud config是将配置保存在git/svn上,依赖git每次push后,触发webhook回调,最终触发spring cloud bus(消息总线),然后由消息总线通知相关的应用。 ————————————————  原文链接:https://blog.csdn.net/m0_67401660/article/details/124465286 
  • [技术干货] SpringBoot之容器
    容器功能 1.1 组件添加 法一: @Configuration  /**  * 1、配置类里面使用@Bean标注在方法上给容器注册组件,默认也是单实例的  * 2、配置类本身也是组件  * 3、proxyBeanMethods:代理bean的方法        (这是SpringBoot2对SpringBoot很大的不同)  *      Full(proxyBeanMethods = true)、【保证每个@Bean方法被调用多少次返回的组件都是单实例的】  *      Lite(proxyBeanMethods = false)【每个@Bean方法被调用多少次返回的组件都是新创建的】  *      组件依赖必须使用Full模式默认。其他默认是否Lite模式  *  *  *  */ @Configuration(proxyBeanMethods = false) //告诉SpringBoot这是一个配置类 == 配置文件 public class MyConfig {      /**      * Full:外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象      * @return      */     @Bean //给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例     public User user01(){         User zhangsan = new User("zhangsan", 18);         //user组件依赖了Pet组件         zhangsan.setPet(tomcatPet());         return zhangsan;     }      @Bean("tom")   //给容器中添加组件。以方法名作为组件的id。     public Pet tomcatPet(){         return new Pet("tomcat");     } } 法二:@Bean、@Component、@Controller、@Service、@Repository 法一的方法新一些,这个方法老一些,这个方法是将注解标于要添加到bean中的那些类的上面。而法一是统一在MyConfig类中添加组件。 将注解t注解在一个类上,表示将此类标记为Spring容器中的一个Bean。 Spring为Controller-Service-Dao的分层模型分别提供了@Controller、@Service、@Repository注解,除此之外的组件使用@Component注解 其他注解1: @ComponentScan、@Import @ComponentScan 的作用就是根据定义的扫描路径(否则扫描路径是主配置类的同级或下级目录),把符合扫描规则的类装配到spring容器中 @import的好处是可以引入外部类(非自己定义的类) * 4、@Import({User.class, DBHelper.class})  *      给容器中自动创建出这两个类型的组件、默认组件的名字就是全类名  *  *  *  */  @Import({User.class, DBHelper.class}) @Configuration(proxyBeanMethods = false) //告诉SpringBoot这是一个配置类 == 配置文件 public class MyConfig { } 其他注解2: @Configuration (这个注解重要) 该注解放在配置类上表示,当容器中满足条件时,配置类中的组件才生效; 放在配置方法上的时候,表示的意思是当满足条件的时候配置方法才生效;  1.2 原生配置文件引入 @ImportResource 指以.xml结尾的配置文件,通过@ImportResource导入后SpringBoot进行解析,完成对应的组件注册 位置:在主配置类的上方。主要是为了兼容第三方,注入IOC。 @ImportResource("classpath:beans.xml") public class MyConfig {} 1.3 配置绑定 @ConfigurationProperties (常见) 场景例子:我们习惯将经常爱变化的东西写在.properties配置文件中,比如与数据库相关的信息(连接池、URL等)配置到配置文件中,为了方便我们会将配置文件中的内容解析到JavaBean中。 配置绑定:两种方式 @Component + @ConfigurationProperties(prefix=“mycar”)声明在要绑定的类的上方 @ConfigurationProperties(prefix=“mycar”)声明在要绑定的类的上方;在配置类的上方声明@EnableConfigurationProperties(Car.class),开启对应类的配置绑定功能,并把Car这个组件自动注入到容器中; /**  * 只有在容器中的组件,才会拥有SpringBoot提供的强大功能,所以先用@Component将Car绑定到容器中  */ @Component @ConfigurationProperties(prefix = "mycar") public class Car {      private String brand;     private Integer price;      public String getBrand() {         return brand;     }      public void setBrand(String brand) {         this.brand = brand;     }      public Integer getPrice() {         return price;     }      public void setPrice(Integer price) {         this.price = price;     }      @Override     public String toString() {         return "Car{" +                 "brand='" + brand + '\'' +                 ", price=" + price +                 '}';     } }` //一般使用这种方式 @EnableConfigurationProperties(Car.class) // 开启 Car 的属性配置并自动注入到容器中 public class MyConfiguration { @ConfigurationProperties(prefix = "mycar") ———————————————— 原文链接:https://blog.csdn.net/qq_44901346/article/details/119703806 
  • [技术干货] SpringBoot中的容器
    一、容器功能 1、组件添加 (1)主要注解 @Configuration 告诉SpringBoot这是一个配置类 == 配置文件 注意:spring5.2以后@Configuration多了一个属性proxyBeanMethods,默认为true  @Configuration(proxyBeanMethods = true) proxyBeanMethods:代理bean的方法  Full(proxyBeanMethods = true)、【保证每个@Bean方法被调用多少次返回的组件都是单实例的】 外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象  Lite(proxyBeanMethods = false)【每个@Bean方法被调用多少次返回的组件都是新创建的】  组件依赖必须使用Full模式默认。其他默认是否Lite模式 ● Full模式与Lite模式 ○ 最佳实战 ■ 配置 类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断 ■ 配置类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式 @Bean 给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例 配置类里面使用@Bean标注在方法上给容器注册组件,默认是单实例的 配置类本身也是组件  (2) 基本使用 bean包: Pet类:  /**  * 宠物  */ public class Pet {     private String name;      public String getName() {         return name;     }      public void setName(String name) {         this.name = name;     }      public Pet(String name) {         this.name = name;     }      public Pet() {     }      @Override     public String toString() {         return "Pet{" +                 "name='" + name + '\'' +                 '}';     } }  User类:  /* 用户  */ public class User {     private String name;     private Integer age;      public User() {     }      public User(String name, Integer age) {         this.name = name;         this.age = age;     }      public String getName() {         return name;     }      public void setName(String name) {         this.name = name;     }      public Integer getAge() {         return age;     }      public void setAge(Integer age) {         this.age = age;     }      @Override     public String toString() {         return "User{" +                 "name='" + name + '\'' +                 ", age=" + age +                 '}';     } } config包: MyConfig类  @Configuration(proxyBeanMethods = false)//告诉Spring这是一个配置类 public class MyConfig {     @Bean//给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例     public User user01(){         return  new User("zhangsan",18);     }     @Bean("tom")     public Pet tomcatPet(){         return new Pet("tomcat");     } } controller包: MainApplication类  @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan("com") public class MainApplication {     public static void main(String[] args) {         //1、返回我们IOC容器         ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);          //2、查看容器里面的组件         String[] names = run.getBeanDefinitionNames();         for (String name : names) {             System.out.println(name);         }         //3、从容器中获取组件         MyConfig bean = run.getBean(MyConfig.class);         System.out.println(bean);         //如果@Configuration(proxyBeanMethods = true)代理对象调用方法。SpringBoot总会检查这个组件是否在容器中有。         //保持组件单实例         User user = bean.user01();         User user1 = bean.user01();         System.out.println("组件为:"+(user == user1));     }  } 结果 (3)补充 @Import 给容器导入一个组件 必须写在容器中的组件上   * @Import({User.class, DBHelper.class})  *      给容器中自动创建出这两个类型的组件、默认组件的名字就是全类名  *  *  *  */  @Import({User.class, DBHelper.class}) @Configuration(proxyBeanMethods = false) //告诉SpringBoot这是一个配置类 == 配置文件 public class MyConfig { } @Configuration测试代码如下  @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan("com") public class MainApplication {     public static void main(String[] args) {         //1、返回我们IOC容器         ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);          //2、查看容器里面的组件         String[] names = run.getBeanDefinitionNames();         for (String name : names) {             System.out.println(name);         }         //3、从容器中获取组件         MyConfig bean = run.getBean(MyConfig.class);         System.out.println(bean);         //如果@Configuration(proxyBeanMethods = true)代理对象调用方法。SpringBoot总会检查这个组件是否在容器中有。         //保持组件单实例         User user = bean.user01();         User user1 = bean.user01();         System.out.println("组件为:"+(user == user1));          //5、获取组件         String[] beanNamesForType = run.getBeanNamesForType(User.class);         System.out.println("======");         for (String s : beanNamesForType) {             System.out.println(s);         }         DBHelper bean1 = run.getBean(DBHelper.class);         System.out.println(bean1);      }  } @Conditional 条件装配:满足Conditional指定的条件,则进行组件注入  ConditionalOnBean:当容器中存在指定的bean组件时才干某些事情 ConditionalOnMissingBean:当容器中不存在指定的bean组件时才干某些事情 ConditionalOnClass:当容器中有某个类时才干某些事情 ConditionalOnResource:当项目的类路径存在某个资源时,才干什么事  =====================测试条件装配========================== @Configuration(proxyBeanMethods = false) //告诉SpringBoot这是一个配置类 == 配置文件 //@ConditionalOnBean(name = "tom") @ConditionalOnMissingBean(name = "tom") public class MyConfig {     @Bean //给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例     public User user01(){         User zhangsan = new User("zhangsan", 18);         //user组件依赖了Pet组件         zhangsan.setPet(tomcatPet());         return zhangsan;     }      @Bean("tom22")     public Pet tomcatPet(){         return new Pet("tomcat");     } }  测试: public static void main(String[] args) {         //1、返回我们IOC容器         ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);          //2、查看容器里面的组件         String[] names = run.getBeanDefinitionNames();         for (String name : names) {             System.out.println(name);         }          boolean tom = run.containsBean("tom");         System.out.println("容器中Tom组件:"+tom);          boolean user01 = run.containsBean("user01");         System.out.println("容器中user01组件:"+user01);          boolean tom22 = run.containsBean("tom22");         System.out.println("容器中tom22组件:"+tom22);      }  2、原生配置文件引入(xml文件引入) @ImportResource 导入资源  @ImportResource("classpath:beans.xml")//导入spring的配置文件 @Import({User.class, DBHelper.class}) @Configuration(proxyBeanMethods = false)//告诉Spring这是一个配置类 @ConditionalOnMissingBean(name = "tom") public class MyConfig { ======================beans.xml========================= <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"        xmlns:context="http://www.springframework.org/schema/context"        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">      <bean id="haha" class="com.atguigu.boot.bean.User">         <property name="name" value="zhangsan"></property>         <property name="age" value="18"></property>     </bean>      <bean id="hehe" class="com.atguigu.boot.bean.Pet">         <property name="name" value="tomcat"></property>     </bean> </beans> 测试  ======================测试=================         boolean haha = run.containsBean("haha");         boolean hehe = run.containsBean("hehe");         System.out.println("haha:"+haha);//true         System.out.println("hehe:"+hehe);//true 3.配置绑定 如何使用Java读取到properties文件中的内容,并且把它封装到JavaBean中,以供随时使用; (1)@Component + @ConfigurationProperties properties文件 /**  * 只有在容器中的组件,才会拥有SpringBoot提供的强大功能  */ @Component @ConfigurationProperties(prefix = "mycar") public class Car {      private String brand;     private Integer price;      public String getBrand() {         return brand;     }      public void setBrand(String brand) {         this.brand = brand;     }      public Integer getPrice() {         return price;     }      public void setPrice(Integer price) {         this.price = price;     }      @Override     public String toString() {         return "Car{" +                 "brand='" + brand + '\'' +                 ", price=" + price +                 '}';     } } (2)@EnableConfigurationProperties + @ConfigurationProperties @Configuration(proxyBeanMethods = false) //告诉SpringBoot这是一个配置类 == 配置文件 @ConditionalOnMissingBean(name = "tom") @ImportResource("classpath:beans.xml") //@EnableConfigurationProperties(Car.class) //1、开启Car配置绑定功能 //2、把这个Car这个组件自动注册到容器中 ———————————————— 原文链接:https://blog.csdn.net/spade_Kwo/article/details/121778319 
  • [技术干货] Spring Boot之容器功能
    一.Spring 注入组件的注解 @Component、@Controller、 @Service、@Repository 说明: 这些在 Spring 中的传统注解仍然有效,通过这些注解可以给容器注入组件 二.@Configuration 在 SpringBoot, 通过 @Configuration 创建配置类来注入组件 1.代码演示 1.1JavaBean--》Monster.java ublic class Monster {       private Integer id;     private String name;     private Integer age;     private String skill;       public Monster(Integer id, String name, Integer age, String skill) {         this.id = id;         this.name = name;         this.age = age;         this.skill = skill;     }       public Monster() {     }       public Integer getId() {         return id;     }       public void setId(Integer id) {         this.id = id;     }       public String getName() {         return name;     }       public void setName(String name) {         this.name = name;     }       public Integer getAge() {         return age;     }       public void setAge(Integer age) {         this.age = age;     }       public String getSkill() {         return skill;     }       public void setSkill(String skill) {         this.skill = skill;     }       @Override     public String toString() {         return "Monster{" +                 "id=" + id +                 ", name='" + name + '\'' +                 ", age=" + age +                 ", skill='" + skill + '\'' +                 '}';     } }  1.2配置类 /**  * @author 海绵hong  * @version 1.0  * <p>  * 解读  * 1. @Configuration 标识这是一个配置类, 等价于配置文件  * 2. 程序员可以通过@Bean 注解注入bean对象到容器  * 3. 当一个类被 @Configuration 标识,该类-Bean 也会注入容器  */ @Configuration public class BeanConfig {       /**      * 解读      * 1. @Bean : 给容器添加组件, 就是Monster bean      * 2. monster01() : 默认 你的方法名monster01 作为Bean的名字/id      * 3. Monster : 注入类型, 注入bean的类型是Monster      * 4. new Monster(200,"牛魔王",500,"疯魔拳") 注入到容器中具体的Bean信息      * 5. @Bean(name = "monster_nmw") : 在配置、注入Bean指定名字/id monster_nmw      * 6. 默认是单例注入      * 7. 通过 @Scope("prototype")  可以每次返回新的对象,就多例.      */     //@Bean(name = "monster_nmw")     @Bean     //@Scope("prototype")     public Monster monster01() {         return new Monster(200, "牛魔王", 500, "疯魔拳");     }   }  1.3执行代码         //启动springboot应用程序/项目         ConfigurableApplicationContext ioc =                 SpringApplication.run(MainApp.class, args);     //  ===演示 @Configuration start ====                  Monster monster01 = ioc.getBean("monster01", Monster.class);         Monster monster02 = ioc.getBean("monster01", Monster.class);                  System.out.println("monster01--" + monster01 + " " + monster01.hashCode());         System.out.println("monster02--" + monster02 + " " + monster02.hashCode());           //===演示 @Configuration end ==== 2.@Configuration 注意事项和细节 1. 配置类本身也是组件, 因此也可以获取 , 测试 修改 MainApp.java     public static void main(String[] args) {         ConfigurableApplicationContext ioc =                 SpringApplication.run(MainApp.class, args); //解读 //1. ioc.getBean("monster01", Monster.class) 是从 BeanConfig 配置类/容器获取 bean 实例 //2. 默认是单列模式, 所以 monster01 == monster02 //获取 BeanConfig 配置类的组件/bean 实例         Monster monster01 = ioc.getBean("monster01", Monster.class);         System.out.println(monster01);         Monster monster02 = ioc.getBean("monster01", Monster.class);         System.out.println(monster01 == monster02); //解读 //配置类本身也是组件, 因此也可以获取         BeanConfig beanConfig = ioc.getBean(BeanConfig.class);         System.out.println("beanConfig= " + beanConfig);     }  2. SpringBoot2 新增特性: proxyBeanMethods 指定 Full 模式 (全模式)和 Lite 模式(简化模式) 1. proxyBeanMethods:代理bean的方法  (1) Full(proxyBeanMethods = true)、【保证每个@Bean方法被调用多少次返回的组件都是单实例的, 是代理方式】  (2) Lite(proxyBeanMethods = false)【每个@Bean方法被调用多少次返回的组件都是新创建的, 是非代理方式】  (3) 特别说明: proxyBeanMethods 是在 调用@Bean方法 才生效,因此,需要先获取BeanConfig 组件,再调用方法 而不是直接通过 SpringBoot 主程序得到的容器来获取bean, 注意观察直接通过ioc.getBean() 获取Bean, proxyBeanMethods 值并没有生效  (4) 如何选择: 组件依赖必须使用Full模式默认。如果不需要组件依赖使用 Lite模  (5) Lite模 也称为轻量级模式,因为不检测依赖关系,运行速度快 @Configuration(proxyBeanMethods = false) public class BeanConfig {         //===演示@Configuration(proxyBeanMethods = xxx) start           //1. 先得到BeanConfig组件         BeanConfig beanConfig = ioc.getBean(BeanConfig.class);         Monster monster_01 = beanConfig.monster01();         Monster monster_02 = beanConfig.monster01();           System.out.println("monster_01-" + monster_01 + " " + monster_01.hashCode());         System.out.println("monster_02-" + monster_02 + " " + monster_02.hashCode());           //特别说明: proxyBeanMethods 是在 调用@Bean方法 才生效,因此,需要先获取BeanConfig 组件,再调用方法         //1. 而不是直接通过 SpringBoot 主程序得到的容器来获取bean, 注意观察直接通过ioc.getBean() 获取Bean, proxyBeanMethods 值并没有生效           Monster monster01 = ioc.getBean("monster01", Monster.class);         Monster monster02 = ioc.getBean("monster01", Monster.class);         System.out.println("monster01-" + monster01 + " " + monster01.hashCode());         System.out.println("monster02-" + monster02 + " " + monster02.hashCode());           //===演示@Configuration(proxyBeanMethods = xxx) end  3. 配置类可以有多个 , 就和 Spring 可以有多个 ioc 配置文件是一个道理 三.@Import 在 SpringBoot, 通过 @Import 来注入组件(和@Configuration一样都是注入bean) 1.创建两个JavaBean类 Cat+Dog 2.注入类 /**  * 解读  * 1. @Import 代码 可以看到,可以指定 class的数组, 可以注入指定类型的Bean  * public @interface Import {  *  *          Class<?>[] value()}  *  * 2. 通过@Import 方式注入了组件, 默认组件名字/id就是对应类型的全类名  */   @Import({Dog.class, Cat.class}) 3.测试注解的使用   //启动springboot应用程序/项目         ConfigurableApplicationContext ioc =                 SpringApplication.run(MainApp.class, args);    //===测试@Import 使用 start           Dog dogBean = ioc.getBean(Dog.class);         Cat catBean = ioc.getBean(Cat.class);         System.out.println("dogBean--" + dogBean);         System.out.println("catBean--" + catBean);           //===测试@Import 使用 end 四.@Conditional 1.@Conditional 介绍 1. 条件装配:满足 Conditional 指定的条件,则进行组件注入 2. @Conditional 是一个根注解,下面有很多扩展注解 2.应用实例   1. 要求 : 演示在 SpringBoot, 如何通过 @ConditionalOnBean 来注入组件 2. 只有在容器中有 name = monster_nmw 组件时,才注入 dog01, 代码如图     @Bean     /**      * 解读      * 1. @ConditionalOnBean(name = "monster_nmw") 表示      * 2. 当容器中有一个Bean , 名字是monster_nmw (类型不做约束), 就注入dog01这个Dog bean      * 3. 如果没有 名字是monster_nmw Bean 就不注入dog01这个Dog bean      * 4. 还有很多其它的条件约束注解,小伙伴可以自己测试      *      * 5. @ConditionalOnMissingBean(name = "monster_nmw") 表示在容器中,      * 没有 名字/id 为 monster_nmw 才注入dog01这个Bean      *      * 6. @ConditionalOnBean(name = "monster_nmw") 也可以放在配置类      * 表示对该配置类的所有要注入的组件,都进行条件约束.      *      */     @ConditionalOnBean(name = "monster_nmw")     //@ConditionalOnMissingBean(name = "monster_nmw")     public Dog dog01() {         return new Dog();     }  五.@ImportResource 1.作用: 原生配置文件引入 , 也就是可以直接导入 Spring 传统的 beans.xml ,可以认 为是 SpringBoot 对 Spring 容器文件的兼容 .(原来依靠借口或者类来导入,现在可以使用该注解进行判断) 2.@ImportResource 应用实例  @Configuration //导入beans.xml - 就可以获取到beans.xml 中配置bean @ImportResource(locations = {"classpath:beans.xml","classpath:beans02.xml"})//配置两个bean文件 public class BeanConfig3 { }         //===测试@Import 使用 start           Dog dogBean = ioc.getBean(Dog.class);         Cat catBean = ioc.getBean(Cat.class);         System.out.println("dogBean--" + dogBean);         System.out.println("catBean--" + catBean);           //===测试@Import 使用 end 六.配置绑定 一句话:使用 Java 读取到 SpringBoot 核心配置文件 application.properties 的内容, 并且把它封装到 JavaBean 中 1.代码演示 #设置Furn的属性k-v #前面的 furn01 是用于指定/区别不同的绑定对象, 这样可以再绑定Furn bean属性值时 #通过furn01 前缀进行区分 #furn01.id 中的id 就是你要绑定的 Furn bean的属性名 furn01.id=100 furn01.name=TV furn01.price=1000.9 @Component @ConfigurationProperties(prefix = "furn01") public class Furn {     private Integer id;     private String name;     private Double price;     } 会读取核心配置文件的信息,然后放入容器 2.配置绑定还有第 2 种方式 注意 : 注销 @Component 需 要 在 BeanConfig.java( 说 明 : 也 可 以 是 其 它 配 置 类 ) 配 置 @EnableConfigurationProperties ( Furn . class ), 否则会提示错误 //@EnableConfigurationProperties(Furn.class)解读 //1、开启 Furn 配置绑定功能 //2、把 Furn 组件自动注册到容器中 @EnableConfigurationProperties(Furn.class) public class BeanConfig { } 海绵的思路(可能错误): 将@Component注解标识掉之后,就不会去读取到容器中,但是在控制器中@EnableConfigurationProperties(Furn.class)被添加,那么底层应该就是这样一个思路:将这个启用配置属性明确配置那个类的信息,然后在那个对应的JavaBean类中有@ConfigurationProperties(prefix = "furn01")这个配置就可以读取到,并且直接将这个信息发送到了控制器中,所有在之后添加了那个注解之后便没有报错  3.注意事项和细节 1. 如果 application.properties 有中文 , 需要转成 unicode 编码写入 , 否则出现乱码 #设置属性 k-v furn01.id=100 furn01.name=soft_chair\u6c99\u53d1!! furn01.price=45678.9 2. 使用 @ConfigurationProperties (prefix = "furn01" ) 会提示如下信息 , 但是不会影响使用 3. 解决 @ConfigurationProperties(prefix = "furn01") 提示信息, 在 pom.xml 增加依赖, 即可  <dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-configuration-processor</artifactId>     <optional>true</optional> </dependency> ———————————————— 原文链接:https://blog.csdn.net/weixin_54107527/article/details/127870247 
  • [技术干货] Spring容器详细介绍
    一、Spring容器是什么,有什么作用? Spring容器是Spring核心部分,Spring容器的主要作用是帮java程序员处理大量繁琐的任务。使得程序员只需要用代码实现自己所关注的事情。 程序员是不用创建对象的,也不用创建对象的关联。只需要从配置的信息中告诉Spring容器。 而且他还能管理对象的全生命周期。 例如: 我们知道权限和用户服务是有关联的。我们只需要提供给Spring容器这四个类的代码,用注解或配置文件告诉Spring这四个服务之间的关联,关系。 Spring在启动时就会创建好这些对象,并且建立他们之间的关联,我们可以从Spring中拿到这些对象,很方便的使用他。 二 、写配置信息的方法 xml    缺点:无法检查错误。 Java代码 注解 使用java代码 和 注解 的优点:我们可以使用很多工具来测试发现错误。 但是 还是会有很多配置要我们去写 这时候可以用Springboot Springboot优点:大量采用默认配置,帮助开发者高效的构建Spring应用。 如何用注释告诉Spring容器? 1.注解(这四个注解功能都一样,知识帮助开发者区分用途) @component{"boss_setter"}普通对象 @controller{""}处理http的response,request对象 表示是一个控制器对象 @Service{" "}服务层的对象 @Repository数据存储层的对象 三、Spring容器 BeanFactory ApplicationContext 两者区别:  ApplicationContext是BeanFactory的派生容器。 Application在BeanFactory原有功能的基础上还有面向实际的 高级功能 国际化接口 ,MessageSource,ResourceLoader(可以去加载外面资源的接口),ApplicationEvent的publish这样一些 应用事件发布接口。  最大的区别是 ApplicationContext在启动时就把所有的对象都创建对象。 ———————————————— 原文链接:https://blog.csdn.net/Victor_e/article/details/126528788 
  • [技术干货] 理解Spring容器、BeanFactory和ApplicationContext
    理解Spring容器、BeanFactory和ApplicationContext初学Spring时一定会对它的容器概念有所困惑,对context应用上下文感到无法理解,我这里参考他人的博客以及自己的理解做了一些总结,主要给自己的Java学习之路做一些总结性工作,另外希望给需要帮助的人提供一些指南。下面图中【实线】为继承extends,【虚线】为实现implements。一. spring容器理解spring容器可以理解为生产对象(OBJECT)的地方,在这里容器不只是帮我们创建了对象那么简单,它负责了对象的整个生命周期--创建、装配、销毁。而这里对象的创建管理的控制权都交给了Spring容器,所以这是一种控制权的反转,称为IOC容器,而这里IOC容器不只是Spring才有,很多框架也都有该技术。二. BeanFactory和ApplicationContext之间的关系BeanFactory和ApplicationContext是Spring的两大核心接口,而其中ApplicationContext是BeanFactory的子接口。它们都可以当做Spring的容器,Spring容器是生成Bean实例的工厂,并管理容器中的Bean。在基于Spring的Java EE应用中,所有的组件都被当成Bean处理,包括数据源,Hibernate的SessionFactory、事务管理器等。生活中我们一般会把生产产品的地方称为工厂,而在这里bean对象的地方官方取名为BeanFactory,直译Bean工厂(com.springframework.beans.factory.BeanFactory),我们一般称BeanFactory为IoC容器,而称ApplicationContext为应用上下文。Spring的核心是容器,而容器并不唯一,框架本身就提供了很多个容器的实现,大概分为两种类型:一种是不常用的BeanFactory,这是最简单的容器,只能提供基本的DI功能;一种就是继承了BeanFactory后派生而来的ApplicationContext(应用上下文),它能提供更多企业级的服务,例如解析配置文本信息等等,这也是ApplicationContext实例对象最常见的应用场景。三. BeanFactory详情介绍Spring容器最基本的接口就是BeanFactory。BeanFactory负责配置、创建、管理Bean,它有一个子接口ApplicationContext,也被称为Spring上下文,容器同时还管理着Bean和Bean之间的依赖关系。spring Ioc容器的实现,从根源上是beanfactory,但真正可以作为一个可以独立使用的ioc容器还是DefaultListableBeanFactory,因此可以这么说,DefaultListableBeanFactory 是整个spring ioc的始祖。BeanFactory结构.png接口介绍:1.BeanFactory接口: 是Spring bean容器的根接口,提供获取bean,是否包含bean,是否单例与原型,获取bean类型,bean 别名的方法 。它最主要的方法就是getBean(String beanName)。2.BeanFactory的三个子接口: * HierarchicalBeanFactory:提供父容器的访问功能 * ListableBeanFactory:提供了批量获取Bean的方法 * AutowireCapableBeanFactory:在BeanFactory基础上实现对已存在实例的管理3.ConfigurableBeanFactory:主要单例bean的注册,生成实例,以及统计单例bean4.ConfigurableListableBeanFactory:继承了上述的所有接口,增加了其他功能:比如类加载器,类型转化,属性编辑器,BeanPostProcessor,作用域,bean定义,处理bean依赖关系, bean如何销毁…5.实现类DefaultListableBeanFactory详细介绍:实现了ConfigurableListableBeanFactory,实现上述BeanFactory所有功能。它还可以注册BeanDefinition接口详细介绍请参考:揭秘BeanFactory四. ApplicationContext介绍如果说BeanFactory是Sping的心脏,那么ApplicationContext就是完整的身躯了。ApplicationContext结构图.pngApplicationContext类结构树.pngApplicationContext常用实现类作用AnnotationConfigApplicationContext从一个或多个基于java的配置类中加载上下文定义,适用于java注解的方式。ClassPathXmlApplicationContext从类路径下的一个或多个xml配置文件中加载上下文定义,适用于xml配置的方式。FileSystemXmlApplicationContext从文件系统下的一个或多个xml配置文件中加载上下文定义,也就是说系统盘符中加载xml配置文件。AnnotationConfigWebApplicationContext专门为web应用准备的,适用于注解方式。XmlWebApplicationContext从web应用下的一个或多个xml配置文件加载上下文定义,适用于xml配置方式。Spring具有非常大的灵活性,它提供了三种主要的装配机制:在XMl中进行显示配置在Java中进行显示配置隐式的bean发现机制和自动装配*组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean。*自动装配(autowiring):Spring自动满足bean之间的依赖。(使用的优先性: 3>2>1)尽可能地使用自动配置的机制,显示配置越少越好。当必须使用显示配置bean的时候(如:有些源码不是由你来维护的,而当你需要为这些代码配置bean的时候),推荐使用类型安全比XML更加强大的JavaConfig。最后只有当你想要使用便利的XML命名空间,并且在JavaConfig中没有同样的实现时,才使用XML。代码示例:通过xml文件将配置加载到IOC容器中<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><!--若没写id,则默认为com.test.Man#0,#0为一个计数形式--><bean id="man" class="com.test.Man"></bean></beans>public class Test {public static void main(String[] args) {//加载项目中的spring配置文件到容器//ApplicationContext context = new ClassPathXmlApplicationContext("resouces/applicationContext.xml");//加载系统盘中的配置文件到容器ApplicationContext context = new FileSystemXmlApplicationContext("E:/Spring/applicationContext.xml");//从容器中获取对象实例Man man = context.getBean(Man.class);man.driveCar();}}通过java注解的方式将配置加载到IOC容器//同xml一样描述bean以及bean之间的依赖关系@Configurationpublic class ManConfig {@Beanpublic Man man() {return new Man(car());}@Beanpublic Car car() {return new QQCar();}}public class Test {public static void main(String[] args) {//从java注解的配置中加载配置到容器ApplicationContext context = new AnnotationConfigApplicationContext(ManConfig.class);//从容器中获取对象实例Man man = context.getBean(Man.class);man.driveCar();}}隐式的bean发现机制和自动装配/*** 这是一个游戏光盘的实现*///这个简单的注解表明该类回作为组件类,并告知Spring要为这个创建bean。@Componentpublic class GameDisc implements Disc{@Overridepublic void play() {System.out.println("我是马里奥游戏光盘。");}}不过,组件扫描默认是不启用的。我们还需要显示配置一下Spring,从而命令它去寻找@Component注解的类,并为其创建bean。@Configuration@ComponentScanpublic class DiscConfig {}我们在DiscConfig上加了一个@ComponentScan注解表示在Spring中开启了组件扫描,默认扫描与配置类相同的包,就可以扫描到这个GameDisc的Bean了。这就是Spring的自动装配机制。除了提供BeanFactory所支持的所有功能外ApplicationContext还有额外的功能默认初始化所有的Singleton,也可以通过配置取消预初始化。继承MessageSource,因此支持国际化。资源访问,比如访问URL和文件。事件机制。同时加载多个配置文件。以声明式方式启动并创建Spring容器。注:由于ApplicationContext会预先初始化所有的Singleton Bean,于是在系统创建前期会有较大的系统开销,但一旦ApplicationContext初始化完成,程序后面获取Singleton Bean实例时候将有较好的性能。也可以为bean设置lazy-init属性为true,即Spring容器将不会预先初始化该bean。参考文章(一部分在上述链接中,这里就不加了):BeanFactory和ApplicationContextSpring容器BeanFactory和ApplicationContext对比Spring基础篇——Spring容器和应用上下文理解ApplicationContext探究Spring 装配bean的三种方式(注解,xml,自动装配)
  • [技术干货] spring容器与对象
    一. spring容器理解spring容器可以理解为生产对象(OBJECT)的地方,在这里容器不只是帮我们创建了对象那么简单,它负责了对象的整个生命周期--创建、装配、销毁。而这里对象的创建管理的控制权都交给了Spring容器,所以这是一种控制权的反转,称为IOC容器,而这里IOC容器不只是Spring才有,很多框架也都有该技术。二. BeanFactory和ApplicationContext之间的关系BeanFactory和ApplicationContext是Spring的两大核心接口,而其中ApplicationContext是BeanFactory的子接口。它们都可以当做Spring的容器,Spring容器是生成Bean实例的工厂,并管理容器中的Bean。在基于Spring的Java EE应用中,所有的组件都被当成Bean处理,包括数据源,Hibernate的SessionFactory、事务管理器等。生活中我们一般会把生产产品的地方称为工厂,而在这里bean对象的地方官方取名为BeanFactory,直译Bean工厂(com.springframework.beans.factory.BeanFactory),我们一般称BeanFactory为IoC容器,而称ApplicationContext为应用上下文。Spring的核心是容器,而容器并不唯一,框架本身就提供了很多个容器的实现,大概分为两种类型:一种是不常用的BeanFactory,这是最简单的容器,只能提供基本的DI功能;一种就是继承了BeanFactory后派生而来的ApplicationContext(应用上下文),它能提供更多企业级的服务,例如解析配置文本信息等等,这也是ApplicationContext实例对象最常见的应用场景。三. BeanFactory详情介绍Spring容器最基本的接口就是BeanFactory。BeanFactory负责配置、创建、管理Bean,它有一个子接口ApplicationContext,也被称为Spring上下文,容器同时还管理着Bean和Bean之间的依赖关系。spring Ioc容器的实现,从根源上是beanfactory,但真正可以作为一个可以独立使用的ioc容器还是DefaultListableBeanFactory,因此可以这么说,DefaultListableBeanFactory 是整个spring ioc的始祖。BeanFactory结构.png接口介绍:1.BeanFactory接口: 是Spring bean容器的根接口,提供获取bean,是否包含bean,是否单例与原型,获取bean类型,bean 别名的方法 。它最主要的方法就是getBean(String beanName)。2.BeanFactory的三个子接口: * HierarchicalBeanFactory:提供父容器的访问功能 * ListableBeanFactory:提供了批量获取Bean的方法 * AutowireCapableBeanFactory:在BeanFactory基础上实现对已存在实例的管理3.ConfigurableBeanFactory:主要单例bean的注册,生成实例,以及统计单例bean4.ConfigurableListableBeanFactory:继承了上述的所有接口,增加了其他功能:比如类加载器,类型转化,属性编辑器,BeanPostProcessor,作用域,bean定义,处理bean依赖关系, bean如何销毁…5.实现类DefaultListableBeanFactory详细介绍:实现了ConfigurableListableBeanFactory,实现上述BeanFactory所有功能。它还可以注册BeanDefinition接口详细介绍请参考:揭秘BeanFactory四. ApplicationContext介绍如果说BeanFactory是Sping的心脏,那么ApplicationContext就是完整的身躯了。ApplicationContext结构图.pngApplicationContext类结构树.pngApplicationContext常用实现类作用AnnotationConfigApplicationContext从一个或多个基于java的配置类中加载上下文定义,适用于java注解的方式。ClassPathXmlApplicationContext从类路径下的一个或多个xml配置文件中加载上下文定义,适用于xml配置的方式。FileSystemXmlApplicationContext从文件系统下的一个或多个xml配置文件中加载上下文定义,也就是说系统盘符中加载xml配置文件。AnnotationConfigWebApplicationContext专门为web应用准备的,适用于注解方式。XmlWebApplicationContext从web应用下的一个或多个xml配置文件加载上下文定义,适用于xml配置方式。Spring具有非常大的灵活性,它提供了三种主要的装配机制:在XMl中进行显示配置在Java中进行显示配置隐式的bean发现机制和自动装配*组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean。*自动装配(autowiring):Spring自动满足bean之间的依赖。(使用的优先性: 3>2>1)尽可能地使用自动配置的机制,显示配置越少越好。当必须使用显示配置bean的时候(如:有些源码不是由你来维护的,而当你需要为这些代码配置bean的时候),推荐使用类型安全比XML更加强大的JavaConfig。最后只有当你想要使用便利的XML命名空间,并且在JavaConfig中没有同样的实现时,才使用XML。代码示例:通过xml文件将配置加载到IOC容器中<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><!--若没写id,则默认为com.test.Man#0,#0为一个计数形式--><bean id="man" class="com.test.Man"></bean></beans>public class Test {public static void main(String[] args) {//加载项目中的spring配置文件到容器//ApplicationContext context = new ClassPathXmlApplicationContext("resouces/applicationContext.xml");//加载系统盘中的配置文件到容器ApplicationContext context = new FileSystemXmlApplicationContext("E:/Spring/applicationContext.xml");//从容器中获取对象实例Man man = context.getBean(Man.class);man.driveCar();}}通过java注解的方式将配置加载到IOC容器//同xml一样描述bean以及bean之间的依赖关系@Configurationpublic class ManConfig {@Beanpublic Man man() {return new Man(car());}@Beanpublic Car car() {return new QQCar();}}public class Test {public static void main(String[] args) {//从java注解的配置中加载配置到容器ApplicationContext context = new AnnotationConfigApplicationContext(ManConfig.class);//从容器中获取对象实例Man man = context.getBean(Man.class);man.driveCar();}}隐式的bean发现机制和自动装配/*** 这是一个游戏光盘的实现*///这个简单的注解表明该类回作为组件类,并告知Spring要为这个创建bean。@Componentpublic class GameDisc implements Disc{@Overridepublic void play() {System.out.println("我是马里奥游戏光盘。");}}不过,组件扫描默认是不启用的。我们还需要显示配置一下Spring,从而命令它去寻找@Component注解的类,并为其创建bean。@Configuration@ComponentScanpublic class DiscConfig {}我们在DiscConfig上加了一个@ComponentScan注解表示在Spring中开启了组件扫描,默认扫描与配置类相同的包,就可以扫描到这个GameDisc的Bean了。这就是Spring的自动装配机制。除了提供BeanFactory所支持的所有功能外ApplicationContext还有额外的功能默认初始化所有的Singleton,也可以通过配置取消预初始化。继承MessageSource,因此支持国际化。资源访问,比如访问URL和文件。事件机制。同时加载多个配置文件。以声明式方式启动并创建Spring容器。注:由于ApplicationContext会预先初始化所有的Singleton Bean,于是在系统创建前期会有较大的系统开销,但一旦ApplicationContext初始化完成,程序后面获取Singleton Bean实例时候将有较好的性能。也可以为bean设置lazy-init属性为true,即Spring容器将不会预先初始化该bean。原文链接:https://blog.csdn.net/s573626822/article/details/109599329
  • [技术干货] Spring详解 容器及实例化
    1. Spring容器1.1 容器的定义我们先看官网中的一句话:The org.springframework.context.ApplicationContext interface represents the Spring IoC container and is responsible for instantiating, configuring, and assembling the beans.翻译下来大概就是:Spring IOC容器就是一个org.springframework.context.ApplicationContext的实例化对象容器负责了实例化,配置以及装配一个bean那么我们可以说:从代码层次来看:Spring容器就是一个实现了ApplicationContext接口的对象,从功能上来看: Spring 容器是 Spring 框架的核心,是用来管理对象的。容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁。1.2 容器工作方式我们直接看官网上的一张图片,如下:Spring容器通过我们提交的pojo类以及配置元数据产生一个充分配置的可以使用的系统这里说的配置元数据,实际上我们就是我们提供的XML配置文件,或者通过注解方式提供的一些配置信息2. Spring Bean2.1 Spring Bean实例化从官网上来看,主要有以下三种方法构造方法通过静态工厂方法通过实例工厂方法这三种例子,官网都有具体的演示,这里就不再贴了,我们通过自己查阅部分源码,来验证我们在官网得到的结论,然后通过debug等方式进行验证。我们再从代码的角度进行一波分析,这里我们直接定位到org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance这个方法中,具体定位步骤不再演示了,大家可以通过形如下面这段代码:ClassPathXmlApplicationContext cc =// 这里我们通过xml配置实例化一个容器new ClassPathXmlApplicationContext("classpath:application.xml");MyServiceImpl luBan = (MyServiceImpl) cc.getBean("myServiceImpl");直接main方法运行,然后在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance这个方法的入口打一个断点,如图:接下来我们对这个方法进行分析,代码如下:protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {// 1.获取这个bean的class属性,确保beanDefinition中beanClass属性已经完成解析// 我们通过xml从<bean>标签中解析出来的class属性在刚刚开始的时候必定是个字符串Class<?> beanClass = resolveBeanClass(mbd, beanName);// 省略异常判断代码.....// 2.通过beanDefinition中的supplier实例化这个beanSupplier<?> instanceSupplier = mbd.getInstanceSupplier();if (instanceSupplier != null) {return obtainFromSupplier(instanceSupplier, beanName);}// 3.通过FactoryMethod实例化这个beanif (mbd.getFactoryMethodName() != null) {return instantiateUsingFactoryMethod(beanName, mbd, args);}// 4.下面这段代码都是在通过构造函数实例化这个Bean,分两种情况,一种是通过默认的无参构造,一种 是通过推断出来的构造函数boolean resolved = false;boolean autowireNecessary = false;if (args == null) {synchronized (mbd.constructorArgumentLock) {if (mbd.resolvedConstructorOrFactoryMethod != null) {resolved = true;autowireNecessary = mbd.constructorArgumentsResolved;}}}if (resolved) {if (autowireNecessary) {return autowireConstructor(beanName, mbd, null, null);}else {return instantiateBean(beanName, mbd);}}// Candidate constructors for autowiring?Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {return autowireConstructor(beanName, mbd, ctors, args);}// Preferred constructors for default construction?ctors = mbd.getPreferredConstructors();if (ctors != null) {return autowireConstructor(beanName, mbd, ctors, null);}// No special handling: simply use no-arg constructor.return instantiateBean(beanName, mbd);}我们主要关注进行实例化的几个方法:通过BeanDefinition中的instanceSupplier直接获取一个实例化的对象。这个instanceSupplier属性我本身不是特别理解,在xml中的标签以及注解的方式都没有找到方式配置这个属性。后来在org.springframework.context.support.GenericApplicationContext这个类中找到了以下两个方法经过断点测试,发现这种情况下,在实例化对象时会进入上面的supplier方法。下面是测试代码:public static void main(String[] args) {// AnnotationConfigApplicationContext是GenericApplicationContext的一个子类AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();ac.registerBean("service", Service.class,Service::new);ac.refresh();System.out.println(ac.getBean("service"));}这个方法一般不常用,平常我们也使用不到,就不做过多探究,笔者认为,这应该是Spring提供的一种方便外部扩展的手段,让开发者能够更加灵活的实例化一个bean。接下来我们通过不同的创建bean的手段,来分别验证对象的实例化方法通过@compent,@Service等注解的方式测试代码:public class Main {public static void main(String[] args) {// 通过配置类扫描AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);System.out.println(ac.getBean(Service.class));}}@Componentpublic class Service {}可以发现,代码执行到最后一行,同时我们看代码上面的注释可以知道,当没有进行特殊的处理的时候,默认会使用无参构造函数进行对象的实例化通过普通XML的方式(同@compent注解,这里就不赘诉了)通过@Configuration注解的方式测试代码:public class Main {public static void main(String[] args) {// 通过配置类扫描AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);// 这里将测试对象换为config即可,同时记得将条件断点更改为beanName.equlas("config")System.out.println(ac.getBean(config.class));}}同样,断点也进入最后一行通过@Bean的方式测试代码:@Configuration@ComponentScan("com.dmz.official")public class Config {@Beanpublic Service service(){return new Service();}}public class Main {public static void main(String[] args) {AnnotationConfigApplicationContext ac =new AnnotationConfigApplicationContext(Config.class);System.out.println(ac.getBean("service"));}}可以发现,通过@Bean方法创建对象时,Spring底层是通过factoryMethod的方法进行实例化对象的。Spring会在我们需要实例化的这个对象对应的BeanDefinition中记录factoryBeanName是什么(在上面的例子中factoryBeanName就是config),同时会记录这个factoryBean中创建对象的factoryMethodName是什么,最后通过factoryBeanName获取一个Bean然后反射调用factoryMethod实例化一个对象。这里我们需要注意几个概念:这里所说的通过静态工厂方式通过factoryBeanName获取一个Bean,注意,这个Bean,不是一个FactoryBean。也就是说不是一个实现了org.springframework.beans.factory.FactoryBean接口的Bean。至于什么是FactoryBean我们在后面的文章会认真分析提到了一个概念BeanDefinition,它就是Spring对自己所管理的Bean的一个抽象。不懂可以暂且跳过,后面有文章会讲到。通过静态工厂方法的方式测试代码:public static void main(String[] args) {ClassPathXmlApplicationContext cc =new ClassPathXmlApplicationContext("application.xml");System.out.println(cc.getBean("service"));}<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><!-- <bean id="myServiceImpl" class="com.dmz.official.service.Service"/>--><!-- the factory bean, which contains a method called get() --><bean id="myFactoryBean" class="com.dmz.official.service.MyFactoryBean"><!-- inject any dependencies required by this locator bean --></bean><!-- 测试实例工厂方法创建对象--><bean id="clientService"factory-bean="myFactoryBean"factory-method="get"/><!--测试静态工厂方法创建对象--><bean id="service"class="com.dmz.official.service.MyFactoryBean"factory-method="staticGet"/></beans>可以发现,这种情况也进入了instantiateUsingFactoryMethod方法中。通过静态工厂方法这种方式特殊之处在于,包含这个静态方法的类,不需要实例化,不需要被Spring管理。Spring的调用逻辑大概是:通过<bean>标签中的class属性得到一个Class对象通过Class对象获取到对应的方法名称的Method对象最后反射调用Method.invoke(null,args)因为是静态方法,方法在执行时,不需要一个对象。通过实例工厂方法的方式测试代码(配置文件不变):public static void main(String[] args) {ClassPathXmlApplicationContext cc =new ClassPathXmlApplicationContext("application.xml");System.out.println(cc.getBean("clientService"));}还是执行的这个方法。这个方法的执行过程我断点跟踪了以后,发现跟@Bean方式执行的流程是一样的。这里也不再赘述了。到这里,这段代码我们算结合官网大致过了一遍。其实还遗留了以下几个问题:Spring是如何推断构造函数的?我们在上面验证的都是无参的构造函数,并且只提供了一个构造函数Spring是如何推断方法的?不管是静态工厂方法,还是实例工厂方法的方式,我们都只在类中提供了一个跟配置匹配的方法名,假设我们对方法进行了重载呢?要说清楚这两个问题需要比较深入的研究代码,同时进行测试。我们在官网学习过程中,暂时不去强求这类问题。这里提出来是为了在源码学习过程中,我们可以带一定目的性去阅读。实例化总结:对象实例化,只是得到一个对象,还不是一个完全的Spring中的Bean,我们实例化后的这个对象还没有完成依赖注入,没有走完一系列的声明周期,这里需要大家注意Spring官网上指明了,在Spring中实例化一个对象有三种方式:构造函数实例工厂方法静态工厂方法我自己总结如下结论:Spring通过解析我们的配置元数据,以及我们提供的类对象得到一个Beanfinition对象。通过这个对象可以实例化出一个java bean对象。主要流程如图:这篇文章到这里就结束了,主要学习了Spring官网中的1.2,1.3两小节。下篇文章,我们开始学习1.4中的知识。主要涉及到依赖注入的一些内容,也是我们Spring中非常重要的一块内容哦原文链接:https://blog.csdn.net/fedorafrog/article/details/112785956
总条数:764 到第
上滑加载中