1 Star 6 Fork 2

岱鐘 / mvc-adapter-springcloud

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
Apache-2.0

项目介绍

  前几年web系统技术选型大多SpringMVC+Spring或Struts2+Spring的单体架构,在实际项目迭代开发过程中,任务紧开发周期短,开发人员能力参差不齐,系统运营几年后会遇到这样一些问题:
   1. 业务交织在一起,多个人维护后,可读性较差。
   2. 迭代过程中,原有代码与新业务有牵连导致很多僵尸代码无法清除。
   3. 历史遗留问题、架构问题,在底层设计不变情况下,一直堆积代码,导致系统越来越臃肿。
   4. 系统功能较多,启动时初始化加载资源缓慢,不利于敏捷开发。
   5. 调度任务、计算业务、采集业务、数据业务,某个业务出现异常会导致整个系统崩溃。

  近年来云原生,微服务Dubbo、SpringCloud技术架构盛行,传统业务想要从单体系统迁移到微服务架构存在如下问题:
   1. 迁移的开发成本问题:单体系统迁移微服务架构需要梳理业务、分解业务、划分服务,再进行技术改造迁移,无法一步到位完成迁移工作。
   2. 迁移过程保持平台的稳定运营问题:迁移工作无法短时间完成,存在单体架构与微服务架构同时运行的过度状态,如何保证单体架构上迁移微服务过程中的稳定性,即用户无感知。
   3. 单体架构在迁移微服务架构的过程中还需要进行新需求的功能开发,如何保持迭代任务与迁移计划同步进行却又不增加迁移的工作量。
   4. 单体架构(SpringMVC)与微服务架构(SpringCloud)并行运行,如何让两个架构间交互无障碍,让开发人员无感知的在两种架构间进行业务交互。

   基于上述问题,开发了本项目,让单体系统(非SpringBoot、非Dubbo、非SpringCloud)能够快速稳定的接入到云原生微服务架构体系中,并且将会在后续的架构中一步步说明单体系统的服务注册、服务发现、服务调用的技术原理以及模拟SpringCloud过程中的设计与理念。

适用项目

  本项目并非系统平台,属于中间件范畴,现在市面上已经有很多SpringCloud架构的开源项目,本项目适用于运营多年的单体系统(非SpringBoot、非Dubbo、非SpringCloud),帮助老项目接入云原生,并且平稳过度到微服务架构,期间会讲解开发项目时借鉴SpringCloud源码的想法理念以及个人的一些集成经验,学习微服务,研究SpringCloud或Dubbo源代码比较抽象,可以借助本项目从简化SpringCloud实现的角度去了解并应用到实际项目中能够更好的去掌握SpringCloud核心机制。

  1. 传统MVC项目将拥有什么能力:
    • 组件自动依赖的能力
    • 自动加载配置文件到Bean中的能力
    • 条件注入的能力
    • MVC自动服务注册的能力
    • MVC自动服务发现的能力
    • MVC实现Service层服务调用的能力(MVC与MVC间服务调用,MVC与Cloud间服务调用)
  2. 您将了解到什么技术:
    • Spring整体流程说明
    • SpringBoot核心技术,利用SPI实现动态加载组件
    • SpringBoot条件注入底层实现
    • SpringBoot配置文件前缀注入底层实现
    • Spring包扫描注入容器的实现
    • SpringMVC实现MVC输入输出加解码处理实现
    • SpringCloud服务注册实现
    • SpringCloud服务发现实现
    • Spring集成Feign实现RESTful服务调用

软件架构

软件架构说明:大道至简,以下为至简集成传统MVC具备SpringCloud能力的架构图,越是简单内部所需要实现的技术细节越是复杂,后续将从技术结构图角度说明。 输入图片说明

模块说明

mvc-adapter-cloud
├── adapter-common -- MVC适配Cloud公共模块(服务注册与发现定义、自动化依赖、自动化配置、条件注入)
├── adapter-consul -- MVC服务注册与发现(Consul注册中心)
├── adapter-nacos -- MVC服务注册与发现(Nacos注册中心)
├── adapter-openfeign -- MVC服务间调用(MVC间或MVC与Cloud间的大单体服务调用)
└── Example -- 测试事例
   └── SpringCloud -- SpringCloud案例
      ├── CloudCommon -- SpringCloud公共模块
      └── CloudDemo -- SpringCloud案例1(CloudDemo)
         ├── DemoApi -- CloudDemo调用接口
         └── DemoService -- CloudDemo服务
      └── CloudExample -- SpringCloud案例2(CloudExample)
         ├── ExampleApi -- CloudExample调用接口
         └── ExampleService -- CloudExample服务
   └── SpringMVC -- MVC案例
      ├── MvcCommon -- MVC公共模块
      └── MvcDemo -- MVC案例1(MvcDemo)
         ├── MvcDemoApi -- MvcDemo调用接口
         └── MvcDemoService -- MvcDemo服务
      └── MvcExample -- MVC案例2(MvcExample)
         ├── MvcExampleApi -- MvcExample调用接口
         └── MvcExampleService -- MvcExample服务

安装教程

  1. MVC项目pom.xml中依赖adapter-common组件,依赖该组件后,项目就拥有了动态装载第三方组件,动态根据配置文件前缀注入配置信息以及条件注入Spring容器的能力
<dependency>
    <groupId>com.opages.mvc.adapter</groupId>
    <artifactId>adapter-common</artifactId>
    <version>1.0.0</version>
</dependency>
  1. MVC项目applicationContext.xml中配置依赖入口类MVCAutoConfiguration.java,依赖了adapter-common组件后,还需要将入口类交由Spring托管才能实现本组件的所有能力(即:@SpringBootApplication能力)。
<bean class="com.opages.mvc.adapter.common.autoconfigure.MVCAutoConfiguration" />
  1. MVC项目pom.xml依赖adapter-nacos组件(注册中心以nacos为例),实现服务注册与发现
<dependency>
    <groupId>com.opages.mvc.adapter</groupId>
    <artifactId>adapter-nacos</artifactId>
    <version>1.0.0</version>
</dependency>
  1. MVC项目配置文件中增加服务注册与发现的注册中心配置
#激活nacos注册中心
spring.mvc.nacos.enable=true
#MVC服务定义为一个服务,设置服务名
spring.mvc.nacos.discovery.serviceName=mvc-example
#nacos注册中心地址
spring.mvc.nacos.discovery.serverAddr=http://127.0.0.1:8848
#nacos服务版本号
spring.mvc.nacos.discovery.metadata[version]=1.0
#nacos服务权重
spring.mvc.nacos.discovery.metadata[weight]=10
  1. MVC项目实现服务间调用,依赖adapter-openfeign
<dependency>
	<groupId>com.opages.mvc.adapter</groupId>
	<artifactId>adapter-openfeign</artifactId>
	<version>1.0.0</version>
</dependency>
  1. MVC项目配置文件中增加openfeign激活
#激活feign功能,默认false,不开启则不能使用openfeign
spring.mvc.openfeign.enable=true
#SpringMVC中启动FeignClient包的扫描路径地址
spring.mvc.openfeign.scan=com.opages.mvc.**.api

开发说明

  1. 建立API服务工程,定义服务API接口(一般来说可以独立API工程,这样如果其它服务调用服务时可以依赖本API工程,就能实现其它服务调用本服务的能力,如果已经存在的服务则只需将原来的url对应API的uri则可实现调用,注意参数一致性,建议使用DTO)
@FeignClient(name="mvc-example")
@RequestMapping("/mvc/example")
public interface MvcExampleApi {
    @PostMapping("/get")
    @ResponseBody
    public MvcExampleDto getExample(@RequestParam("id") Integer id);

    @PostMapping(value="/save")
    public void save(MvcExampleDto exampleDto);
}
  1. 建立服务提供者项目,实现API接口实现业务逻辑
@RestController
public class MvcExampleController implements MvcExampleApi {
	@Override
	public MvcExampleDto getExample(@RequestParam("id") Integer id) {
		MvcExampleDto dto = new MvcExampleDto();
		dto.setId(id);
		dto.setUsername("xiaomin");
		dto.setPassword("123456");
		return dto;
	}

	@Override
	public void save(@RequestBody MvcExampleDto demoDto) {
		System.err.println("MVC Example保存-->"+demoDto.toString());
	}
}

技术说明

  1. Spring整体流程说明

    学Spring时常提到的是IOC、AOP,去理解什么是IOC、AOP,然后会讲到Bean交由Spring托管;在讨论到Spring是怎么做到的,很多人还是归结到使用了反射机制,使用了JAVA动态代理或cglib,那如果换种方式理解:IOC和AOP是怎么设计出来的,你会怎么说呢?

- 回答上面问题前先看下ApplicationContext的流程,由于本项目应用传统SpringMVC,因此先看下加载xml的上下文ClassPathXmlApplicationContext(Spring版本:4.3.29) Spring整体流程图
- Spring流程分析每次都想把spring流程简洁化,以整体来俯瞰全貌,但里面的设计环环相扣,单从某块为出发点来讲都会有所缺失,所以先整体以业务图的方式说明,再标注重点流程(其它并不是不重点,只是关注点不同),spring源码越分析越觉得自己是弱鸡,如有不对的希望大家留言反馈。下面是关注的重点流程:
Spring简化流程图
- 从上面的流程可以看出,单纯从IOC、AOP讲技术原理并不能说清怎么实现的问题,实现问题需要从设计与整体架构上考虑,也就是常说的整体解决方案; 从Bean的定义(抽象BeanDefinition),Bean的构建(反射构建和FactoryBean构建),Bean的创建过程(实例化与初始化),而核心设计扩展点:后置处理器贯穿始终。IOC与AOP就与后置处理器有关。 想让spring做事,需要具备两个条件:①. bean受spring托管,②. 注入的属性(值或对象)受spring管理,而达到这个目的的核心操作是后置处理器。 从上面的简化流程图中可以看出spring大量使用后置处理器,后置处理器分两大类:
  (1). BeanFactoryPostProcessor:干预BeanFactory的创建过程,可以在容器实例化任何其它bean之前读取配置元数据,根据需要进行修改bean的定义属性,需要关注子接口BeanDefinitionRegistryPostProcessor,该接口定义的postProcessBeanDefinitionRegistry方法比父类优先调用,主要用于动态注册符合spring规范的注解(@Component、@Service、@Controller等)生成DebanDefinition到容器中
  (2). BeanPostProcessor:干预Bean的创建过程,在Bean实例化进行属性赋值,如:Aware注入、@Autowired注解为属性性赋值、通用属性校验、初始化与销毁方法(@PostConstruct、@PreDestroy)调用,实现底层动态代理。

- 事情总要在共同的意识形态下才可能讨论,经过上面的简略说明,现在可以讲下IOC与AOP的设计了,首先是准备阶段,Spring前期准备了环境变量、解析器、转换器等等,在读取文件、解析文件时应用,将需要的信息比如配置文件或xml文件等解析、同时回调BeanFactoryPostProcessor,先执行子接口BeanDefinitionRegistryPostProcessor将使用注解Bean解析出来,最终解析成BeanDefinition,再执行BeanFactoryPostProcessor接口,将有关Bean属性值做一些修改,默认内部实现接口:

- ConfigurationClassPostProcessor:配置类后置处理器,解析加了@Configuration的配置类,解析@ComponentScan、@ComponentScans注解扫描的包,解析@Import、@Bean注解。 - PropertyPlaceholderConfigurer:在XML配置文件中加入外部属性文件,${key}替换指定的properties文件中的值。
- PropertyOverrideConfigurer: 允许对Spring容器中配置的任何我们想处理的bean定义的property信息(beanName.propertyName=value)进行覆盖替换。
- CustomEditorConfigurer:类型转换器,可以根据对象类型取得与其相对应的PropertyEditor来做具体的类型转换。

- 处理完Bean属性后,根据BeanPostProcessor(子接口InstantiationAwareBeanPostProcessor)后置处理器,在实例化前后,初始化前后做动态扩展(注入属性、创建代理、执行初始化方法、注入销毁方法),主要后置处理器有:

- CommonAnnotationBeanPostProcessor:重写postProcessPropertyValues,注入@Resource
- AutowiredAnnotationBeanPostProcessor: 重写postProcessPropertyValues,注入@Autowire
- PersistenceAnnotationBeanPostProcessor:用于注入相应的JPA资源
- AbstractAutoProxyCreator:初始化前(postProcessBeforeInstantiation)返回指定代理对象的动态代理(不常用),初始化后(postProcessAfterInitialization)返回常规动态代理(Spring AOP)
- CommonAnnotationBeanPostProcessor:收集@PostConstruct,@PreDestroy(初始化方法与销毁方法),注入@Resource注解的类
- ImportAwareBeanPostProcessor:注入ImportAware子类的AnnotationMetadata

经过上述处理后,Bean创建成功再放入缓存中,下次直接从缓存中获取。

- AOP则是在上面BeanPostProcessor的子类实现中动态扩展进来的
  (1). 前置条件:配置类中注解@EnableAspectJAutoProxy,该注解属性@Import(AspectJAutoProxyRegistrar.class)导入了AspectJAutoProxyRegistrar类,该类继承ImportBeanDefinitionRegistrar,因此能自定义给容器注入BeanDefinetion -> AnnotationAwareAspectJAutoProxyCreator,而注入的类正是上面后置处理器AbstractAutoProxyCreator的子类,该类又继承了BeanFactoryAware,因为在设置BeanFactory时会创建advisor工厂类(ReflectiveAspectJAdvisorFactory)和构造类(BeanFactoryAspectJAdvisorsBuilderAdapter)用于构建Aspect对象
  (2). 在Bean实例化后,初始化前(postProcessBeforeInstantiation)回调时判断是否能代理(是否@Aspect,是否实现Advice、Pointcut、Advisor、AopInfrastructureBean,有则不被代理),判断时就查找@Aspect注解类,解析生成Advisor对象(由ReflectiveAspectJAdvisorFactory.getAdvisors()方法根据是否存在pointCut构造),如果存在pointCut则将切点表达式expressionPointcut、ReflectiveAspectJAdvisorFactory、方法名包装成advisor对象(InstantiationModelAwarePointcutAdvisorImpl),InstantiationModelAwarePointcutAdvisorImpl构造方法会触发构造通知对象(对应的注解生成Before.class,Around.class,After.class,AfterReturning.class, AfterThrowing.class,Pointcut.class),通过通知的构造方法,将通知增强方法,切面表达式传入到通知当中,然后Advisor存入advisedBeans(如果没有自定义目标类则就止结束)
  创建advisor的逻辑发生在扩展接口中的postProcessBeforeInstantiation,实例化之前执行,如果有自定义的TargetSource指定类,则则直接生成代理类,并直接执行初始化之后的方法postProcessAfterInitialization。这种情况使用不多,常规代理类还是在postProcessAfterInitialization中创建,也就是IOC最后一个扩展方法。
  (3). 在Bean初始化后回调AnnotationAwareAspectJAutoProxyCreator.postProcessAfterInitialization方法,深入会调用getAdvicesAndAdvisorsForBean()获取到合适的advisor,该方法最终会调用findEligibleAdvisors(),然后根据AopUtils类筛选规则(遍历被代理类的所有的方法,跟切面表达式进行匹配,如果有方法匹配到,也就意味着该类会被代理,如果代理目标是接口或者Proxy类型,则走jdk类型,否则使用cglib类型,springboot1.x与2.x有所不同),最终返回代理类。

用一句话来总结:激活注解AnnotationAwareAspectJAutoProxyCreator时创建AnnotationAwareAspectJAutoProxyCreator,该类继承BeanPostProcessor和实现BeanFactoryAware,所以又创建Advisor工厂,在Bean实例化后使用Advisor工厂创建Advisor对象时也构造增强通知类,在Bean初始化后根据AopUtils规则匹配pointCup表达式来创建目标类的JDK或Cglib动态代理来返回给容器。

  1. SpringBoot核心技术,利用SPI实现动态加载组件 (待叙...)
  2. SpringBoot条件注入底层实现 (待叙...)
  3. SpringBoot配置文件前缀注入底层实现 (待叙...)
  4. Spring包扫描注入容器的实现 (待叙...)
  5. SpringMVC实现MVC输入输出加解码处理实现 (待叙...)
  6. SpringCloud服务注册实现 (待叙...)
  7. SpringCloud服务发现实现 (待叙...)
  8. Spring集成Feign实现RESTful服务调用 (待叙...)
Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

简介

传统springMVC项目运营一段时间后,由于原有业务与业务的不断迭代开发,迁移SpringCloud时无法一次性迁移,本组件主要是适配单体系统扩展为单体微服务接入到微服务平台,在不影响业务的情况下一步步迁移直到全部迁移到微服务平台,期间不影响当前业务的解决方案。 展开 收起
Java
Apache-2.0
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
Java
1
https://gitee.com/yoin/mvc-adapter-cloud.git
git@gitee.com:yoin/mvc-adapter-cloud.git
yoin
mvc-adapter-cloud
mvc-adapter-springcloud
master

搜索帮助