• [技术干货] 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
  • [技术干货] 什么是Spring?Spring容器介绍
    一、什么是spring? spring是2003年兴起的一个轻量级的java开发框架,它是为了解决企业应用开发的复杂性而创建的。 spring是一个容器,用于降低代码间的耦合度,根据不同情况(不同代码),采用IoC(用于主业务逻辑解耦合)和AOP(用于系统级服务与主业务逻辑解耦合)两种技术进行解耦合。 上面是spring架构图,每一块代表一个jar包,例如第一块由5个jar包组成。 二、spring的特点 1.非侵入式 spring框架的API不会在业务逻辑上出现,即业务逻辑是POJO(java原生类,不需要依赖任何额外引入的jar)。由于业务逻辑中没有spring的API,所以业务逻辑可以从spring框架迅速移植到其他框架。  2.容器 spring作为一个容器,可以管理对象的生命周期、对象与对象之间的依赖关系。可以通过配置文件,来定义对象,以及设置其与其他对象的依赖关系。 3.IoC 控制反转,即实例的创建不是由调用者完成,而是由spring容器完成,并注入调用者。容器在对象初始化时不等对象请求就主动将依赖传递给它。 4.AOP 面向切面编程,是一种编程思想,是面对对象编程OOP的补充。很多框架都实现了对AOP编程思想的实现。spring也提供了面向切面编程的丰富支持,允许分离应用的业务逻辑与系统级服务(如日志和事务管理)进行开发。 我们可以把日志、安全和事务管理等服务理解成一个“切面”,以前都是直接把这些服务写到业务逻辑代码当中,这样有两点不好:(1)业务逻辑代码不纯净,不便于阅读理解。(2)这些服务是会被业务逻辑反复使用的,完全可以剥离出来做到复用。AOP就是解决这个问题的,它可以把这些服务剥离出来形成一个“切面”,然后将“切面”动态的织入到业务逻辑中,让业务逻辑能够享受到此“切面”的服务。 三、spring与IoC IoC是一种思想,指将传统上由程序代码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。控制反转就是对对象控制权的转移,将程序代码本身反转到了外部容器。 IoC是一个概念,是一种思想,其实现方式多种多样。当前比较流行的实现方式有两种: (1)依赖查找(DL):容器提供调用接口,程序代码则需提供查找的路径。 (2)依赖注入(DI):不做定位查询,所有工作由容器自动完成。(但是对象不能重名)。 3.1 spring的第一个程序 首先准备好spring的jar包和spring的依赖包,下面需要的jar包都是从这里来的。 必须:spring的4个基础jar包 如果需要测试,导入junit 如果需要记录日志,导入log4j和apache-commons中的logging  jar包。logging是一个适配器,它使得框架可以使用任何日志技术,而不局限于log4j等。 到这里需要的jar包就准备好了。 1)创建一个java project 2)建立服务类及其实现类  public interface SomeService {     void doSome(); } import com.hnu.service.SomeService;   public class SomeServiceImpl implements SomeService {       @Override     public void doSome() {         System.out.println("执行doSome方法!");     }   }  3)在src目录下建立applicationContext.xml(取什么名字都行,官方推荐这个名字)。xml文件需要约束,可以从spring官方jar包中的spring-framework-4.2.1.RELEASE\docs\spring-framework-reference\html这个文件夹下找到xsd-configuration.html这个文件,里面有spring的约束文件格式。 建立xml文件: <?xml version="1.0" encoding="UTF-8"?> <xml-body>   </xml-body> 添加约束:  <?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 definitions here -->   </beans> 添加自动提示(补充):拷贝http://www.springframework.org/schema/beans/spring-beans.xsd,点击window->preferences->XML Catalog 点击添加,弹出如下窗口,Key type选择URI,Key填上面复制的spring-bean的约束地址http://www.springframework.org/schema/beans/spring-beans.xsd,Location选择spring官方jar包中的spring-bean约束spring-framework-4.2.1.RELEASE\schema\beans\spring-beans-4.2.xsd ,选择4.2版本的,因为我们用的spring也是4.2的。 点击确定,然后关掉xml再打开,就有自动提示了。 4)注册bean <?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 definitions here -->     <!--注册Service -->     <bean id="myService" class="com.hnu.service.implSomeServiceImpl" />   </beans>   5)获取容器对象,实现注入: import org.junit.Test; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; import org.springframework.core.io.ClassPathResource; import com.hnu.service.SomeService; import com.hnu.service.impl.SomeServiceImpl; public class MyTest {          @Test     public void test01(){         SomeService service = new SomeServiceImpl();         service.doSome();     }          @Test     public void test02(){         //1.从类路径下找配置文件         ApplicationContext ac =new ClassPathXmlApplicationContext("applicationContext.xml");                  //2.从项目路径下找配置文件         //ApplicationContext ac =new FileSystemXmlApplicationContext("applicationContext.xml");                  //3.从指定盘符下找配置文件         //ApplicationContext ac =new FileSystemXmlApplicationContext("d:/applicationContext.xml");         SomeService service = (SomeService) ac.getBean("myService");         service.doSome();     }               @Test     public void test05(){         //另外一中容器BeanFactory         BeanFactory bf =new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));       //BeanFactory bf =new XmlBeanFactory(new FileSystemResource("applicationContext.xml"));         SomeService service = (SomeService) bf.getBean("myService");         service.doSome();     }     } ApplicationContext的getBean()方法是继承的BeanFactory的。 ApplicationContext和BeanFactory容器的区别: 1)ApplicationContext容器在进行初始化时,会将其中的所有的Bean(对象)进行创建          缺点:占用系统资源          优点:响应速度快 2)BeanFactory容器中的对象,在容器初始化时并不会被创建,而是在真正获取该对象时才被创建          缺点:相对来说响应速度慢          优点:不多占用系统资源 目前计算机资源越来约廉价,我们一般都习惯用空间换时间,所以一般第一种方式用的较多一点。 连接spring源码操作(补充): alt+鼠标左键点击ApplicationContext类,弹出如下窗口: 选择External location ,Encoding选择UTF-8,根据上面的提示找到spring-context-4.2.1的源码,一般在spring-framework-4.2.1.RELEASE\libs里面。点击ok即可连上源码。 Ctrl+T可以查看类或接口的继承关系,如下: Ctrl+O可以查看类或接口的结构 ————————————————             原文链接:https://blog.csdn.net/cqf949544506/article/details/80894673 
  • [技术干货] Spring介绍
    1. Spring是什么? Spring是一个开放源代码的设计层面框架,解决业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。  Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。  2. Spring框架特点 Spring是一个轻量级且非侵入式的开源框架 。 Spring是包含并管理应用对象的配置和生命周期的一种容器。 Spring是可以将简单的组件配置、组合成为复杂的应用的框架。 Spring通过控制反转(IoC) 促进了低耦合。 Spring提供了面向切面编程(AOP) 的丰富支持,允许通过分离应用的业务逻辑与系统级服务进行内聚性的开发。 3. Spring框架组成 Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式。  组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:  Spring Core(核心容器):核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC)模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。  Spring Context(上下文):Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。  Spring AOP(面向切片):通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能 , 集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理任何支持 AOP的对象。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中。  Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。  Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。  Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。  Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。  重要拓展:Spring Boot与Spring Cloud Spring Boot 是 Spring 的一套快速配置脚手架,可以基于Spring Boot 快速开发单个微服务。 Spring Cloud是基于Spring Boot实现的。 Spring Boot专注于快速、方便集成的单个微服务个体,Spring Cloud关注全局的服务治理框架。 Spring Boot使用了约束优于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置 , Spring Cloud很大的一部分是基于Spring Boot来实现,Spring Boot可以离开Spring Cloud独立使用开发项目,但是Spring Cloud离不开Spring Boot,属于依赖的关系。 SpringBoot在SpringClound中起到了承上启下的作用,如果你要学习SpringCloud必须要学习SpringBoot。 4. 控制反转(IOC) 控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转(IOC)后将对象的创建转移给第三方调用者,控制反转可认为是获得依赖对象的方式反转。  通俗来讲,以前所有东西都是由程序去进行控制创建(代码写死,直接实例化对象), 而现在是由调用者自行控制创建对象(由调用者按需求传入对象),把主动权交给了调用者,程序不用去管怎么创建,怎么实现了,它只负责提供一个接口。 4.1 Spring IOC容器 官方解释: 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 IOC容器就是一个实现了ApplicationContext接口的对象, 从功能上来看: Spring 容器是 Spring 框架的核心,是用来管理对象的。容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁。 4.1.1 Spring IOC容器如何工作 IoC是Spring框架的核心内容,可以通过使用XML配置,也可以使用注解来实现了IoC,新版本的Spring也可以零配置实现IoC。 Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。 采用XML方式配置Bean的时候,Bean的定义信息是和实现分离开的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。  4.2 如何实例化Bean 构造方法 通过静态工厂方法 通过实例工厂方法  简单分析下创建bean实例的方法  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实例化这个bean         Supplier<?> instanceSupplier = mbd.getInstanceSupplier();         if (instanceSupplier != null) {             return obtainFromSupplier(instanceSupplier, beanName);         }                  // 3.通过FactoryMethod实例化这个bean         if (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);     }  通过上述代码可以发现实例化bean的方法主要分为三类:  1. 通过instanceSupplier获取,这个方法一般不常用,平常我们也使用不到,就不做过多探究,笔者认为,这应该是Spring提供的一种方便外部扩展的手段,让开发者能够更加灵活的实例化一个bean。 2. FactoryMethod,通过断点调试可以发现@compent、@Service、普通XML的方式(同@compent注解)、@Configuration等方式创建bean都是走instantiateUsingFactoryMethod(beanName, mbd, args)方法,即通过工厂方法实例化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>  通过静态工厂方法这种方式特殊之处在于,包含这个静态方法的类,不需要实例化,不需要被Spring管理。Spring的调用逻辑大概是: 通过<bean>标签中的class属性得到一个Class对象 通过Class对象获取到对应的方法名称的Method对象 最后反射调用Method.invoke(null,args)获取bean 3. 构造方法。 4.3 实例化总结 1. 对象实例化,只是得到一个对象,还不是一个完全的Spring中的Bean,我们实例化后的这个对象还没有完成依赖注入,没有走完一系列的声明周期,这里需要大家注意。 2. Spring官网上指明了,在Spring中实例化一个对象有三种方式: 构造函数 实例工厂方法 静态工厂方法 5. 依赖注入(DI) 5.1 依赖注入概念 依赖注入(Dependency Injection,DI):  依赖 : 指Bean对象的创建依赖于容器。 注入 : 指Bean对象所依赖的资源 , 由容器来设置和装配。 5.2 依赖注入方式 依赖注入主要分为两种方式:  构造函数注入 Setter方法注入 5.3 代码测试 public class Main02 {     public static void main(String[] args) {         AnnotationConfigApplicationContext ac = new              // config类主要完成对类的扫描             AnnotationConfigApplicationContext(Config.class);         Service service = (Service) ac.getBean("service");         service.test();     } }   // setter方法注入 @Component public class Service {       private LuBanService luBanService;       public Service() {         System.out.println("service create");     }       public void test(){         System.out.println(luBanService);     }       // 通过autowired指定使用set方法完成注入     @Autowired     public void setLuBanService(LuBanService luBanService) {         System.out.println("注入luBanService by setter");         this.luBanService = luBanService;     } }   // 构造方法注入 @Component public class Service {       private LuBanService luBanService;          public Service() {         System.out.println("service create by no args constructor");     }          // 通过Autowired指定使用这个构造函数,否则默认会使用无参     @Autowired     public Service(LuBanService luBanService) {         System.out.println("注入luBanService by constructor with arg");         this.luBanService = luBanService;         System.out.println("service create by constructor with arg");     }       public void test(){         System.out.println(luBanService);     } }   @Component public class LuBanService {     LuBanService(){         System.out.println("luBan create ");     } }  上述代码中,@Autowired 注解加在setter 方法和属性字段上的区别: 1. 直接添加@Autowired注解到字段上,不需要提供setter方法也能完成注入。以上面的例子来说,Spring会通过反射获取到Service中luBanService这个字段,然后通过反射包的方法,Filed.set(Service,luBanService)这种方式来完成注入。 2. 将@Autowired添加到setter方法时,我们可以通过断点看一下方法的调用栈,如下: 对于这种方式来说,最终是通过Method.invoke(object,args)的方式来完成注入的,这里的method对象就是我们的setter方法。 同时采用构造注入加属性注入会怎么样呢? Spring虽然能在构造函数里完成属性注入,但是这属于实例化对象阶段做的事情,那么在后面真正进行属性注入的时候,属性注入会将构造函数注入的属性覆盖。 总结:构造函数注入和属性注入使用官方推荐: 1. 构造函数注入跟setter方法注入可以混用,对于一些强制的依赖,我们最好使用构造函数注入,对于一些可选依赖我们可以采用setter方法注入。 2. Spring团队推荐使用构造函数的方式完成注入。但是对于一些参数过长的构造函数,Spring是不推荐的。 5.3 方法注入 5.3.1 Bean的作用域 类别    说明 Singleton 在Spring IoC容器中仅存在一个Bean实例,Bean以单例方式存在,Spring IoC容器中所有对象默认是单例的 Prototype 原型模式,每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行new XxxBean() Request 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境 Session 同一个Http Session 共享一个Bean,不同的Session 使用不用Bean,仅适用于WebApplicationContext环境 5.3.2 方法注入作用 有了上面Bean的作用域基础知识之后,就能理解方法注入的作用。  @Component public class MyService {       @Autowired     private LuBanService luBanService;       public void test(int a){         luBanService.addAndPrint(a);     }   }   @Component // 原型对象 @Scope("prototype") public class LuBanService {     int i;       LuBanService() {         System.out.println("luBan create ");     }     // 每次将当前对象的属性i+a然后打印     public void addAndPrint(int a) {         i+=a;         System.out.println(i);     } }   public class Main02 {     public static void main(String[] args) {         AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);         MyService service = (MyService) ac.getBean("myService");         service.test(1);         service.test(2);         service.test(3);     } }  如上述代码,我们目的是每次都获取到不同的 LuBanService 对象,然而实际是每次调用到的LuBanService是同一个对象。当然,这也很好理解,因为在依赖注入阶段我们就完成了LuBanService的注入,之后我们在调用测试方法时,不会再去进行注入,所以我们一直使用的是同一个对象。 方法注入的作用就是解决此问题,每次使用Bean时都重新去获取。 实现方式: 1. 通过注入上下文(applicationContext对象)获取Bean  // 实现org.springframework.context.ApplicationContextAware接口 @Component public class MyService implements ApplicationContextAware {       private ApplicationContext applicationContext;       public void test(int a) {         LuBanService luBanService = ((LuBanService) applicationContext.getBean("luBanService"));         luBanService.addAndPrint(a);     }       @Override     public void setApplicationContext(@Nullable ApplicationContext applicationContext) throws BeansException {         this.applicationContext = applicationContext;     } }   // 直接注入上下文 @Component public class MyService{     @Autowired     private ApplicationContext applicationContext;       public void test(int a) {         LuBanService luBanService = ((LuBanService) applicationContext.getBean("luBanService"));         luBanService.addAndPrint(a);     } } 2. 通过@LookUp的方式(也分为注解跟XML两种方式,这里只演示注解的)  @Component public class MyService{     public void test(int a) {         LuBanService luBanService = lookUp();         luBanService.addAndPrint(a);     }       @Lookup     public LuBanService lookUp(){         return null;     } } 3. 方法注入 之 replace-method(使用很少) 5.4 依赖注入总结 Spring中的依赖注入就是属性注入。 我们知道一个对象由两部分组成:属性+行为(方法),可以说Spring通过属性注入+方法注入的方式掌控的整个bean。 属性注入跟方法注入都是Spring提供给我们用来处理Bean之间协作关系的手段。 属性注入有两种方式:构造函数,Setter方法。 方法注入(LookUp Method跟Replace Method)需要依赖动态代理完成。 方法注入对属性注入进行了一定程度上的补充,因为属性注入的情况下,原型对象可能会失去原型的意义。 6. 自动注入 Spring可以自动注入互相协作的bean之间的依赖。自动注入有以下两个好处: 自动注入能显著的减少我们指定属性或构造参数的必要,即可以不用依赖注入的方式为类注入相关依赖对象。 自动装配可以随着对象的演化更新配置。例如,如果需要向类添加依赖项,则可以自动满足该依赖项,而不需要修改配置。因此,自动装配在开发过程中特别有用,但是当我们的代码库变的稳定时,自动装配也不会影响我们将装配方式切换到精确注入。 自动注入四种模型: 模型    解释 no     这是目前Spring默认的注入模型,也可以说默认情况下Spring是关闭自动注入,必须要我们通过setter方法或者构造函数完成依赖注入,并且Spring也不推荐修改默认配置。建议我们使用精确的方式注入依赖。 byName     这种方式,我们为了让Spring完成自动注入需要提供两个条件: 提供setter方法 如果需要注入的属性为xxx,那么setter方法命名必须是setXxx,也就是说,命名必须规范 在找不到对应名称的bean的情况下,Spring也不会报错,只是不会给我们完成注入。 byType     测试代码跟之前唯一不同的就是修改配置autowire="byType",这里我们测试以下三种异常情况: 找不到合适类型的bean,发现不报异常,同时不进行注入 找到了多个合适类型的bean,Spring会直接报错Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.dmz.official.service.DmzService' available: expected single matching bean but found 2: dmzService,dmzService2 set方法中有两个参数,切两个参数都能找到唯一一个类型符合的bean,不报异常,也不进行注入 另外需要说明的是,我在测试的过程,将set方法仅仅命名为set,像这样public void set(DmzService dmzService),这种情况下Spring也不会进行注入。 我们可以发现,对于这两种注入模型都是依赖setter方法完成注入的,并且对setter方法命名有一定要求(只要我们平常遵从代码书写规范,一般也不会踩到这些坑)。第一,不能有多个参数;第二,不能仅仅命名为set constructor    当我们使用这种注入模型时,Spring会根据构造函数查找有没有对应参数名称的bean,有的话完成注入(跟前文的byName差不多),如果根据名称没找到,那么它会再根据类型进行查找,如果根据类型还是没找到,就会报错。 自动注入的缺陷: 1. 精确注入会覆盖自动注入。并且我们不能注入基本数据类型,字符串,Class类型(这些数据的数组也不行)。而且这是Spring故意这样设计的; 2. 自动注入不如精确注入准确。而且我们在使用自动注入时,对象之间的依赖关系不明确; 3. 对于一些为Spring容器生成文档的工具,无法获取依赖关系; 4. 容器中的多个bean定义可能会与自动注入的setter方法或构造函数参数指定的类型匹配。对于数组、集合或映射实例,这可能不会产生什么问题。但是,对于期望单个值的依赖项,我们无法随意确定到底有谁进行注入。如果没有唯一的bean定义可用,则会抛出异常。 补充一些注解的知识: @Autowired 与 @Qualifier  1. @Autowired(required=false) 说明:false,对象可以为null;true,对象必须存对象,不能为null。 2. @Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配。 3. @Qualifier不能单独使用。 @Resource 1. @Resource如有指定的name属性,先按该属性指定的name进行byName方式查找装配;其次再进行默认的byName方式进行装配。 2. 如果以上都不成功,则按byType的方式自动装配。 3. 都不成功,则报异常。 @Autowired与@Resource异同: 1. @Autowired与@Resource都可以用来装配bean。都可以写在属性上,或写在setter方法上。 2. @Autowired 默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用。 3. @Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。 4. 它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。 ———————————————— 原文链接:https://blog.csdn.net/m0_38033199/article/details/126949283 
  • [技术干货] Spring Boot和Spring的区别
    一、SpringBoot的介绍 Spring是一个开放源代码的设计层面框架,解决业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。 Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。  简而言之,Spring框架为开发Java应用程序提供了全面的基础架构支持。它包含一些很好的功能,列如依赖注入和开箱即用的等模块。 Spring框架优点: Spring是一个轻量级的、非入侵式的框架。 控制反转(IOC),面向切面编程(AOP)。 支持事务的处理,对框架整合的支持。 总结:Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架。 这些模块可以大大缩短应用程序的开发时间。例如,在Java Web开发的早期阶段,我们需要编写大量的重复代码来将记录插入到数据源中。但是通过使用Spring JDBC模块的JDBCTemplate,我们可以将它简化为只需几个简单配置或者几行代码。 二、SpringBoot的介绍 拓展 1、Spring Boot框架基本上是Spring框架的扩展,它消除了设置Spring应用程序所需的复杂例行配置 2、学习SpringBoot需要掌握Spring与SpringMVC 它的目标和Spring的目标是一致的,为更快,更高效的开发生态系统铺平了道路。以下是Spring Boot中的一些功能: 通过starter这一个依赖,以简化构建和复杂的应用程序配置。 可以直接main函数启动,嵌入式web服务器,避免了应用程序部署的复杂性,Metrics度量,Helth check健康检查和外部化配置。 尽可能的自动化配置Spring功能。 三、Spring与SpringBoot的比较 Maven依赖     首先,让我们看一下使用Spring创建Web应用程序所需的最小依赖项: 与Spring不同,Spring Boot只需要一个依赖项来启动和运行Web应用程序:  在构建期间,所有其他依赖项将自动添加到最终归档中,如果构建项目时没有勾选该依 赖的功能在构建期间不会有该依赖需要在配置中手动添加。 Spring Boot为不同的Spring模块提供许多依赖项,比喻我们常用的:  spring-boot-starter-data-jpa spring-boot-starter-security spring-boot-starter-test spring-boot-starter-web spring-boot-starter-thymeleaf ———————————————— 原文链接:https://blog.csdn.net/weixin_49983304/article/details/124643580 
  • [技术干货] Spring和Spring Boot的区别
    Spring和Spring Boot都是Java开发的框架,用于简化和加速Java应用程序的开发。  Spring是一个开源的应用程序框架,它提供了一个综合的编程和配置模型,用于构建Java应用程序。Spring框架提供了许多功能,如依赖注入、面向切面编程、事务管理等,它也可以与其他框架和技术进行集成,如Hibernate、MVC框架等。Spring框架需要开发人员进行大量的配置和编写XML文件。  Spring Boot是一个开源的微服务框架,它是在Spring框架的基础上构建的。Spring Boot的目标是简化Spring应用程序的配置和部署。它提供了自动配置、起步依赖和可嵌入式服务器等功能,使得开发人员能够快速地创建独立的、可部署的Spring应用程序。Spring Boot大大减少了配置的工作量,开发人员只需要少量的代码就能够创建一个功能完整的Spring应用程序。  总而言之,Spring是一个全功能的应用程序框架,适用于大型的、复杂的应用程序开发,而Spring Boot则是一个更轻量级、更简化的框架,适用于快速、简单的应用程序开发。  Spring和Spring Boot是Java开发中常用的两个框架,它们在功能和实现上存在一些区别。以下是它们之间的一些主要区别:  配置方式:在Spring中,开发人员需要手动配置大量的组件,如数据源、事务管理器、Web服务器等。相比之下,Spring Boot通过自动配置方式,可以根据classpath中的依赖关系自动配置很多组件,简化了应用程序的配置过程。 依赖管理:Spring Boot采用了“约定优于配置”的思想,提供了一系列的starter依赖。这些依赖包含了所需的依赖和默认配置,使得开发者可以快速开始一个新的项目,而Spring则需要手动管理依赖。 微服务支持:Spring Boot的设计初衷是支持微服务的快速开发。因此,它提供了很多针对微服务的功能,例如服务注册与发现、负载均衡、断路器等。而Spring则需要通过额外的组件来实现这些功能。 项目构建:Spring Boot可以使用内嵌的Tomcat或Jetty服务器,无需部署到外部服务器,可以更加方便地构建独立的可执行JAR包或WAR包。而Spring则需要部署到外部服务器。 操作简化:Spring Boot提供了极其快速和简化的操作,让Spring开发者快速上手。它提供了Spring运行的默认配置,为通用Spring项目提供了很多非功能性特性。 总的来说,Spring Boot是Spring框架的扩展,它简化了Spring应用程序的配置和开发流程,为更快、更高效的开发生态系统铺平了道路。 ———————————————— 原文链接:https://blog.csdn.net/qq_23997827/article/details/135697093 
  • [技术干货] 在 Spring Boot 中使用 MyBatis-Plus 进行批量操作数据
    引言 MyBatis-Plus 是 MyBatis 的增强工具包,提供了许多便捷的功能来简化 MyBatis 的使用。在实际项目中,我们经常需要进行批量的数据操作,例如批量插入、批量更新和批量删除。本文将介绍如何在 Spring Boot 中使用 MyBatis-Plus 进行这些批量操作。 Mybatis-Plus是一个Mybatis(opens new window)的增强工具,在Mybatis的基础上只做增强不做改变,为简化开发。 Mybatis-Plus的优势 无侵入:只做增强不做改变,引入它不会对现有工程产生影响。 损耗小:启动即会自动注入基本CURD,性能基本无损耗,直接面向对象操作。 强大的CRUD操作:内置通用Mapper、通用Service,仅仅通过少量配置即可实现单表大部分CRUD操作,更有强大的条件构造器,满足各类使用需求。 支持Lambda形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写 错。 支持主键自动生成:支持多达4种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题。 支持ActiveRecord模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作。 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )。 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用。 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询。 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、 Postgre、SQLServer 等多种数据库。 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询。 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作。  1. 准备工作 首先,确保你的 Spring Boot 项目中已经添加了 MyBatis-Plus 的依赖。在 Maven 项目中,可以在 pom.xml 文件中添加以下依赖:  <dependency>     <groupId>com.baomidou</groupId>     <artifactId>mybatis-plus-boot-starter</artifactId>     <version>最新版本</version> </dependency> 2. 批量插入数据 @Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {       public void saveBatchUsers(List<User> userList) {         saveBatch(userList);     } } 在上述代码中,saveBatch 方法接收一个实体列表作为参数,然后执行批量插入操作。UserMapper 是你的 MyBatis Mapper 接口,User 是你的实体类。  3. 批量更新数据 @Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {       public void updateBatchUsers(List<User> userList) {         updateBatchById(userList);     } } updateBatchById 方法同样接收一个实体列表参数,执行批量更新操作。  4. 批量删除数据 @Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {       public void removeUsersByIds(List<Long> userIds) {         removeByIds(userIds);     } } removeByIds 方法接收一个主键 ID 的列表,执行批量删除操作。  5. 总结 通过 MyBatis-Plus 提供的 saveBatch、updateBatchById 和 removeByIds 方法,我们可以非常方便地实现在 Spring Boot 项目中的批量操作数据。这些方法简化了 SQL 的编写,提高了开发效率,同时也遵循了 MyBatis-Plus 的约定大于配置的设计理念。  在实际应用中,根据业务需求和数据量,选择合适的批量操作方法可以有效提高系统性能,减少数据库交互次数。希望本文能帮助你更好地利用 MyBatis-Plus 进行批量操作数据的开发。 ————————————————   原文链接:https://blog.csdn.net/qq_28068311/article/details/134559225 
  • [技术干货] Mybatis-plus批量操作
    前言 Mybatis-Plus是一个Mybatis(opens new window)的增强工具,在Mybatis的基础上只做增强不做改变,为简化开发。   Mybatis-Plus的优势 有:无侵入:只做增强不做改变,引入它不会对现有工程产生影响。 损耗小:启动即会自动注入基本CURD,性能基本无损耗,直接面向对象操作。 强大的CRUD操作:内置通用Mapper、通用Service,仅仅通过少量配置即可实现单表大部分CRUD操作,更有强大的条件构造器,满足各类使用需求。 支持Lambda形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写 错。 支持主键自动生成:支持多达4种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题。 支持ActiveRecord模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作。 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )。 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用。 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询。 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、 Postgre、SQLServer 等多种数据库。 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询。 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作。    使用Mybatis-plus可以很方便的实现批量新增和批量修改,不仅比自己写foreach遍历方便很多,而且性能也更加优秀。但是Mybatis-plus官方提供的批量修改和批量新增都是根据id来修改的,有时候我们需求其他字段,所以就需要我们自己修改一下。  一、批量修改         在Mybatis-plus的IService接口中有updateBatchById方法,我们常用以下方法根据id批量修改数据。      @Transactional(rollbackFor = Exception.class)     default boolean updateBatchById(Collection<T> entityList) {         return updateBatchById(entityList, DEFAULT_BATCH_SIZE);     }       @Transactional(rollbackFor = Exception.class)     @Override     public boolean updateBatchById(Collection<T> entityList, int batchSize) {         String sqlStatement = getSqlStatement(SqlMethod.UPDATE_BY_ID);         return executeBatch(entityList, batchSize, (sqlSession, entity) -> {             MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();             param.put(Constants.ENTITY, entity);             sqlSession.update(sqlStatement, param);         });     } 但是当我们想根据其他字段批量修改数据时,该方法就无能为力了。所以我们就可以根据第二个updateBatchById方法在自己的service类里面写一个新的批量新增方法。      public boolean updateBatchByColumn(Collection<?> entityList, String idCard) {         String sqlStatement = getSqlStatement(SqlMethod.UPDATE);         return executeBatch(entityList, (sqlSession, entity) -> {             LambdaUpdateWrapper<SysUser> updateWrapper = Wrappers.<SysUser>lambdaUpdate()                     .eq(SysUser::getIdCard, idCard);             Map<String, Object> param = CollectionUtils.newHashMapWithExpectedSize(2);             param.put(Constants.ENTITY, entity);             param.put(Constants.WRAPPER, updateWrapper);             sqlSession.update(sqlStatement, param);         });     } 注意sqlStatement是使用的SqlMethod.UPDATE,SysUser对象是举例,使用的是若依的用户数据。  二、批量新增或修改         在Mybatis-plus的ServiceImpl 类中有一个saveOrUpdateBatch 方法用于批量新增或修改,通过CollectionUtils.isEmpty(sqlSession.selectList(getSqlStatement(SqlMethod.SELECT_BY_ID), entity))根据id查询数据是否已存在,不存在新增,存在则修改,源码如下:      @Transactional(rollbackFor = Exception.class)     @Override     public boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize) {         TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClass);         Assert.notNull(tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!");         String keyProperty = tableInfo.getKeyProperty();         Assert.notEmpty(keyProperty, "error: can not execute. because can not find column for id from entity!");         return SqlHelper.saveOrUpdateBatch(this.entityClass, this.mapperClass, this.log, entityList, batchSize, (sqlSession, entity) -> {             Object idVal = tableInfo.getPropertyValue(entity, keyProperty);             return StringUtils.checkValNull(idVal)                 || CollectionUtils.isEmpty(sqlSession.selectList(getSqlStatement(SqlMethod.SELECT_BY_ID), entity));         }, (sqlSession, entity) -> {             MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();             param.put(Constants.ENTITY, entity);             sqlSession.update(getSqlStatement(SqlMethod.UPDATE_BY_ID), param);         });     } 最终调用的是SqlHelper.saveOrUpdateBatch方法,该方法第六个参数是BiPredicate,这就是用于判断数据库中数据是否存在的关键,所以我们需要修改这个函数式接口,修改为根据其他条件查询数据库。该方法的第七个参数是BiConsumer,该函数式接口用于修改数据,如果不想根据id修改数据,可以参考第一部门进行修改。      @Transactional(rollbackFor = Exception.class)     public boolean saveOrUpdateBatchByColumn(Collection<?> entityList, String idCard) {           return SqlHelper.saveOrUpdateBatch(this.entityClass, this.mapperClass, super.log, entityList, DEFAULT_BATCH_SIZE, (sqlSession, entity) -> {             LambdaQueryWrapper<SysUser> queryWrapper = Wrappers.<SysUser>lambdaQuery()                     .eq(SysUser::getIdCard, idCard);             Map<String, Object> map = CollectionUtils.newHashMapWithExpectedSize(1);             map.put(Constants.WRAPPER, queryWrapper);             return CollectionUtils.isEmpty(sqlSession.selectList(getSqlStatement(SqlMethod.SELECT_LIST), map));         }, (sqlSession, entity) -> {             Map<String, Object> param = CollectionUtils.newHashMapWithExpectedSize(2);             param.put(Constants.ENTITY, entity);             sqlSession.update(getSqlStatement(SqlMethod.UPDATE_BY_ID), param);         });     } 三、不使用Mybatis-plus进行批量操作         有时候项目里没有引用Mybatis-plus,但是也想进行批量操作,数据量大了后foreach循环会影响性能。所以可以参考Mybatis-plus的批量操作,编写在mybatis环境下的批量操作,代码如下:  @Component public class MybatisBatchUtils {       private static final int BATCH_SIZE = 1000;       @Autowired     private SqlSessionFactory sqlSessionFactory;       public <T,U,R> boolean batchUpdateOrInsert(List<T> data, Class<U> mapperClass, BiFunction<T,U,R> function) {         int i = 1;         SqlSession batchSqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);         try {             U mapper = batchSqlSession.getMapper(mapperClass);             int size = data.size();             for (T element : data) {                 function.apply(element, mapper);                 if ((i % BATCH_SIZE == 0) || i == size) {                     batchSqlSession.flushStatements();                 }                 i++;             }             // 非事务环境下强制commit,事务情况下该commit相当于无效             batchSqlSession.commit(!TransactionSynchronizationManager.isSynchronizationActive());             return true;         } catch (Exception e) {             batchSqlSession.rollback();             throw new RuntimeException(e);         } finally {             batchSqlSession.close();         }     } } 写在最后的话         Mybatis-plus真好用,少写好多代码。有些不太适用的方法,也可以很简单在官方的基础上进行扩展。 ————————————————                      原文链接:https://blog.csdn.net/WayneLee0809/article/details/126424482 
总条数:764 到第
上滑加载中