在Spring MVC中,您能够创立REST应用程序。

您创立一个应用@RestController正文的控制器类。

而后定义应用@RequestMapping正文的处理程序办法。

您还能够应用@PathVariable正文的URI模板参数。 此处显示的代码示例公开了REST端点/ users / {user}。 {user}的代码是用于自定义REST申请的门路变量。 Spring Boot提供了一个Jackson ObjectMapper的主动配置,用于JSON无效负载和POJO对象之间的主动编组。

Spring在构建REST资源时,通常应用@RestController和@RequestMapping来定义。而JavaEE定义的时候,通常应用JAX-RS实现@Path。当然,在Spring中也能够应用JAX-RS。

作为开发的另一种抉择,您能够应用JAX-RS。 默认的Spring Boot JAX-RS实现是Jersey,它是Glassfish Jax-RS实现。 您能够应用spring-boot-starter-jersey Spring Boot启动器。 Spring Boot提供Jersey servlet和Jackson数据绑定的主动配置。 您能够应用规范的JAX-RS正文构建REST端点,例如@ javax.ws.rs.Path,@ javax.ws.rs.Produces和javax.ws.rs.GET。此代码示例演示如何应用JAX-RS在Spring中构建REST资源。

REST资源类须要在Jersey上下文中注册。这里显示了一个例子。

Spring Boot包含对嵌入式Tomcat,Jetty和Undertow服务器的反对。 默认状况下,Spring Boot启动程序(特地是spring-boot-starter-web)应用Tomcat作为嵌入式容器。要应用备用服务器,请排除Tomcat依赖项并蕴含所需的依赖项。 这里的代码片段显示了如何排除Tomcat并蕴含Undertow服务器所需的依赖关系。

  • Spring Framework为应用SQL数据库提供了宽泛的反对。 您能够应用JdbcTemplate间接进行JDBC拜访。 您还能够将JPA对象关系映射与Hibernate一起应用。 Spring Data为反对SQL,NoSQL,MapReduce框架和基于云的数据服务的数据拜访提供了基于Spring的编程模型。 Spring Boot提供以下启动器:spring-boot-starter-jdbc和spring-boot-starter-data-jpa。 Spring Boot的一个很好的性能是内存数据库的主动配置,非常适合测试。 此外,Spring Boot还提供具备内部配置属性的数据源的主动配置。

Spring Framework还提供实体类扫描,以及Spring Data Repository类的主动注册。 Spring JPA存储库是封装数据拜访的接口。 JPA查问是从办法名称主动创立的。这里显示了一个例子。

Spring Boot配置能够内部化,以便雷同的利用程序代码能够在不同的环境中工作。 您能够应用属性文件,YAML文件,环境变量和命令行参数来内部化配置。 能够应用@Value正文将属性值间接注入bean,通过Spring的Environment形象拜访,或通过@ConfigurationProperties绑定到结构化对象。

Spring Boot应用一个有序的序列来指定属性,以便容许正当地笼罩值。程序如下:

@TestPropertySource

命令行参数

Java零碎属性

OS环境变量

打包JAR之外的特定于配置文件的应用程序属性

打包的JAR中的特定于配置文件的应用程序属性

默认属性

应用@Value(“$ {property}”)正文来注入配置属性可能很麻烦。 Spring Boot提供了另一种抉择。您能够定义强类型bean来治理和验证应用程序的配置。

上面的代码示例定义了以下属性:

foo.enabled,默认为false

foo.remote-address,具备能够从String强制转换的类型

foo.security.username,具备嵌套安全性

foo.security.roles,带有String汇合

您能够像应用任何其余bean一样注入此配置。

Spring Profiles提供了一种隔离应用程序配置局部的办法,并使每个局部仅在某些环境中可用 - 例如,dev,QA或production。

任何@Component或@Configuration都能够用@Profile标记,以限度何时加载。

特定于配置文件的属性文件名为application- {profile} .properties。

当配置文件处于活动状态时,您能够笼罩application.properties中的默认属性。

  • 您能够通过以下任何形式指定流动配置文件:应用-Dspring.profiles.active = dev作为命令行上的零碎属性 作为应用导出SPRING_PROFILES_ACTIVE = dev的环境变量 在应用spring.profiles.active = dev的应用程序属性中 在应用@ActiveProfiles(“test”)的测试用例中

Spring Cloud是一个用于开发云原生应用程序的框架。Spring Cloud提供了通用设计模式的实现,以反对云本机应用程序的开发。 Spring Cloud提供的解决方案:

  • 集中配置管理
  • 服务注册和发现
  • 负载平衡
  • 断路器
  • 异步通信
  • 分布式跟踪

Spring Cloud还提供第三方工具和库的集成和形象:

  • Netflix OSS Eureka服务注册表
  • HashiCorp服务注销
  • Netflix OSS Hystrix断路器和隔板
  • Netflix OSS功能区客户端负载均衡器
  • Apache Kafka和RabbitMQ音讯代理
  • Zipkin分布式追踪

Spring Cloud Kubernetes提供Spring Cloud与Kubernetes和OpenShift的集成。 它由Red Hat Fabric8.io团队发动,当初由Spring Cloud Incubator托管。

性能包含以下内容:

  • 带有ConfigMaps和secret的Spring Boot配置
  • 当在ConfigMap中检测到更改时,PropertySource从新加载以触发应用程序从新加载
  • pod运行状况指示器,用于将特定于pod的运行状况数据增加到Spring Actuator运行状况端点
  • 在Kubernetes上运行时,Kubernetes配置文件主动配置
  • Kubernetes的功能区发现
  • Archaius-a Netflix OSS配置管理库-ConfigMap属性源
  • 透明度 - 当应用程序在Kubernetes / OpenShift之外运行时,Spring Cloud Kubernetes不会中断

在Red Hat Fuse Integration Services 2.x版中,Spring Boot是在OpenShift上开发Camel应用程序的首选框架。 启动器模块是camel-spring-boot-starter。 CamelContext的主动配置在Spring应用程序上下文中注册。 应用@Component正文的Camel路由会自动检测并注入CamelContext。

二:试验展示:构建购物车

在本试验中,您增加了为Coolstore应用程序的购物车微服务公开REST API的性能。实验室从上一个实验室的解决方案代码开始,包含您应用的其余文件。

为购物车微服务实现和公开REST API

查看并运行REST API的单元测试

利用架构

购物车微服务由一个Maven我的项目组成,该我的项目外部由许多服务对象组成:

PriceCalculationService蕴含用于计算购物车的运费和总价值的逻辑。

CatalogService负责调用目录服务以获取产品数据。

ShoppingCartService负责管理购物车。

CartEndpoint蕴含用于拜访购物车微服务的REST API。

在本试验中,您将增加REST API的实现以拜访CartEndpoint类中的购物车微服务。

启动Red Hat Developer Studio。 抉择文件→导入。 在“导入”对话框中,抉择“Maven”→“现有Maven我的项目”,而后单击“下一步”。 单击“浏览”并导航到〜/ appmod_springboot_experienced / lab-02。 这是您解压缩本试验的代码的目录。 确保为我的项目选中了/pom.xml框,而后单击Finish。 导入后,验证您是否看到该我的项目。

查看购物车服务项目的pom.xml文件,并留神以下事项: 在dependencyManagement局部中,导入spring-boot-dependencies物料清单(BOM)POM。此POM蕴含特定Spring Boot版本反对的所有依赖项的策动列表。实际上,这意味着您不用手动跟踪在构建配置中增加的依赖项的版本,因为Spring Boot正在为您治理。降级Spring Boot自身时,依赖关系也会以统一的形式降级。 在Red Hat OpenShift Application Runtimes环境中认证的Spring Boot版本是1.5.10.RELEASE。 spring-boot-maven插件用于构建可执行的JAR文件(fat JAR)。插件的从新打包指标创立了一个可主动执行的JAR(或WAR)文件。

应用Spring Boot开发一个购物车微服务应用Spring Boot,能够应用不同的技术来构建REST API。您能够应用Spring MVC框架或实现JAX-RS标准的框架。 对于购物车服务,须要以下REST端点: GET / cart / {cartId}按ID获取购物车。 POST / cart / {cartId} / {itemId} / {quantity}将商品增加到购物车。 DELETE / cart / {cartId} / {itemId} / {quantity}从购物车中删除商品。 POST / cart / checkout / {cartId}查看购物车。

咱们查看源码,进行剖析:

package com.redhat.coolstore.cart.rest;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Autowired;

import com.redhat.coolstore.cart.service.ShoppingCartService;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.PathVariable;

import com.redhat.coolstore.cart.model.ShoppingCart;

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.DeleteMapping;

@RestController

@RequestMapping("/cart")

//@RequestMapping批注标识资源类或类办法为其申请的URI门路。购物车服务的URI门路是/ cart。@RestController正文将此类标识为REST资源//

public class CartEndpoint {

private static final Logger LOG = LoggerFactory.getLogger(CartEndpoint.class);

//应用SLF4J API配置一个logger//

    @Autowired

    private ShoppingCartService shoppingCartService;

//应用Spring @Autowired正文注入ShoppingCartService//

    @GetMapping("/{cartId}")

    public ShoppingCart getCart(@PathVariable String cartId) {

        return shoppingCartService.getShoppingCart(cartId);

    }

//@GetMapping示意带正文的办法响应HTTP GET申请。URI局部附加到类级别定义的根本门路。 {cartId}示意模板参数名称。@PathVariable将URI模板参数的值绑定到办法变量。此代码将调用委托给ShoppingCartService.getShoppingCart办法。//

    @PostMapping("/{cartId}/{itemId}/{quantity}")

    public ShoppingCart add(@PathVariable String cartId,

                            @PathVariable String itemId,

                            @PathVariable int quantity) {

            return shoppingCartService.addToCart(cartId, itemId, quantity);

    }

//此办法实现REST POST / cart / {cartId} / {itemId} / {quantity}端点。//

    @DeleteMapping("/{cartId}/{itemId}/{quantity}")

    public ShoppingCart delete(@PathVariable String cartId,

                                    @PathVariable String itemId,

                                    @PathVariable int quantity) {

        return shoppingCartService.removeFromCart(cartId, itemId, quantity);

    }

//此办法实现DELETE / cart / {cartId} / {itemId} / {quantity}端点。//

    @PostMapping("/checkout/{cartId}")

    public ShoppingCart checkout(@PathVariable String cartId) {

        ShoppingCart cart = shoppingCartService.checkoutShoppingCart(cartId);

        LOG.info("ShoppingCart " + cart + " checked out");

        return cart;

    }

}

//此办法实现REST POST / cart / checkout / {cartId}端点。目前,只需记录购物车已签出的事实就足够了。//

查看并运行端到端集成测试此时,您已筹备好所有局部。在本节中,您将查看并运行购物车服务的端到端测试(或集成测试)。 Spring Boot容许集成测试,而无需理论部署应用程序或连贯到其余基础架构。 Spring Boot应用程序作为测试自身的一部分进行自举。所须要的只是依赖于spring-boot-starter-test启动器。 您能够应用不同的技术在集成测试中测试REST端点。 REST Assured是一种晦涩而优雅的Java DSL,用于简化基于REST的服务的测试,可用于验证和验证这些服务的响应。 Rest Assured使得验证JSON或XML无效负载变得特地容易。 要模仿近程目录服务,您能够应用WireMock框架,就像在CatalogService服务的测试中一样。 Spring Boot应用程序 - 更具体地说,是CatalogService实现 - 冀望将catalog.service.url零碎属性设置为近程目录服务的URL。将此属性注入测试的一种办法是利用Spring配置文件和Spring Boot对特定于配置文件的属性的反对。 在我的项目的src / test / resources文件夹中,查看名为application-test.properties的文件:

无需为此属性设置值,因为理论的URL(特地是WireMock服务器绑定的端口)在执行测试自身之前是未知的。您将理论URL注入测试代码。 Spring Boot从类门路根目录中的application.properties和application- {profile} .properties文件加载属性,并将它们增加到Spring环境中。在这种状况下,仅在测试配置文件处于活动状态时才加载application-t

est.properties。

抉择我的项目的src / test / java文件夹。 查看com.redhat.coolstore.cart.rest包中的CartEndpointTest类。

此批注激活测试配置文件,因而application-test.properties文件将加载到Spring上下文中。

咱们查看测试的源码:

package com.redhat.coolstore.cart.rest;

import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;

import static com.github.tomakehurst.wiremock.client.WireMock.get;

import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;

import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;

import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;

import static io.restassured.RestAssured.given;

import static org.hamcrest.Matchers.equalTo;

import static org.hamcrest.Matchers.hasItems;

import static org.hamcrest.Matchers.hasSize;

import java.io.InputStream;

import java.nio.charset.Charset;

import org.apache.commons.io.IOUtils;

import org.junit.Before;

import org.junit.Rule;

import org.junit.Test;

import org.junit.runner.RunWith;

//SpringRunner类提供了JUnit测试框架和Spring框架之间的集成。当应用SpringRunner,Spring应用程序上下文时 - 在Spring Boot的状况下,这是Spring Boot应用程序自身 - 在测试和启用Spring组件的依赖注入之前的bootstraps。//

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.context.embedded.LocalServerPort;

import org.springframework.boot.test.context.SpringBootTest;

import org.springframework.test.annotation.DirtiesContext;

import org.springframework.test.context.ActiveProfiles;

import org.springframework.test.context.junit4.SpringRunner;

import org.springframework.test.util.ReflectionTestUtils;

import com.github.tomakehurst.wiremock.junit.WireMockRule;

import com.redhat.coolstore.cart.service.CatalogService;

import io.restassured.RestAssured;

import io.restassured.http.ContentType;

@ActiveProfiles("test")

@RunWith(SpringRunner.class)

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

//@SpringBootTest正文在惯例Spring测试框架上提供了一些特定于Spring Boot的加强性能。特地是,SpringBootTest.WebEnvironment.RANDOM_PORT环境加载了一个嵌入式WebApplicationContext并提供了一个真正的servlet环境。

嵌入式servlet容器(在本例中为Tomcat)在随机端口上启动和监听。

//

public class CartEndpointTest {

    @LocalServerPort

    private int port;

    @Rule

    public WireMockRule wireMockRule = new WireMockRule(wireMockConfig().dynamicPort());

    @Autowired

    private CatalogService catalogService;

    @Before

    public void beforeTest() throws Exception {

        RestAssured.baseURI = String.format("http://localhost:%d/cart", port);

        ReflectionTestUtils.setField(catalogService, null, "catalogServiceUrl", "http://localhost:" + wireMockRule.port(), null);

        initWireMockServer();

    }

    @Test

    public void retrieveCartById() throws Exception {

        given().get("/{cartId}", "123456")

            .then()

            .assertThat()

            .statusCode(200)

            .contentType(ContentType.JSON)

            .body("id", equalTo("123456"))

            .body("cartItemTotal", equalTo(0.0f))

            .body("shoppingCartItemList", hasSize(0));

    }

    @Test

    @DirtiesContext

//为了可能测试调用近程目录服务的REST端点,能够应用WireMock模仿目录服务。因为WireMock服务器绑定到随机端口(对于每个测试方法可能都是不同的端口),因而必须应用ReflectionTestUtils将理论的WireMock URL注入CatalogService实例。

应用WireMock时,会为每个测试方法实例化WireMock服务器的新实例,并绑定到不同的端口。因而,还必须为应用WireMock服务器的测试方法从新创立Spring上下文。这能够通过应用Spring @DirtiesContext正文来正文这些测试方法来实现。

//

    public void addItemToCart() throws Exception {

        given().post("/{cartId}/{itemId}/{quantity}", "234567", "111111", new Integer(1))

            .then()

            .assertThat()

            .statusCode(200)

            .contentType(ContentType.JSON)

            .body("id", equalTo("234567"))

            .body("cartItemTotal", equalTo(new Float(100.0)))

            .body("shoppingCartItemList", hasSize(1))

            .body("shoppingCartItemList.product.itemId", hasItems("111111"))

            .body("shoppingCartItemList.price", hasItems(new Float(100.0)))

            .body("shoppingCartItemList.quantity", hasItems(new Integer(1)));

    }

    @Test

    @DirtiesContext

    public void addExistingItemToCart() throws Exception {

        given().post("/{cartId}/{itemId}/{quantity}", "345678", "111111", new Integer(1));

        given().post("/{cartId}/{itemId}/{quantity}", "345678", "111111", new Integer(1))

            .then()

            .assertThat()

            .statusCode(200)

            .contentType(ContentType.JSON)

            .body("id", equalTo("345678"))

            .body("cartItemTotal", equalTo(new Float(200.0)))

            .body("shoppingCartItemList", hasSize(1))

            .body("shoppingCartItemList.product.itemId", hasItems("111111"))

            .body("shoppingCartItemList.price", hasItems(new Float(100.0)))

            .body("shoppingCartItemList.quantity", hasItems(new Integer(2)));

    }

    @Test

    @DirtiesContext

    public void addItemToCartWhenCatalogServiceThrowsError() throws Exception {

        given().post("/{cartId}/{itemId}/{quantity}", "234567", "error", new Integer(1))

            .then()

            .assertThat()

            .statusCode(500);

    }

    @Test

    @DirtiesContext

    public void removeAllInstancesOfItemFromCart() throws Exception {

        given().post("/{cartId}/{itemId}/{quantity}", "456789", "111111", new Integer(2));

        given().delete("/{cartId}/{itemId}/{quantity}", "456789", "111111", new Integer(2))

            .then()

            .assertThat()

            .statusCode(200)

            .contentType(ContentType.JSON)

            .body("id", equalTo("456789"))

            .body("cartItemTotal", equalTo(new Float(0.0)))

            .body("shoppingCartItemList", hasSize(0));

    }

    @Test

    @DirtiesContext

    public void removeSomeInstancesOfItemFromCart() throws Exception {

        given().post("/{cartId}/{itemId}/{quantity}", "567890", "111111", new Integer(3));

        given().delete("/{cartId}/{itemId}/{quantity}", "567890", "111111", new Integer(1))

            .then()

            .assertThat()

            .statusCode(200)

            .contentType(ContentType.JSON)

            .body("id", equalTo("567890"))

            .body("cartItemTotal", equalTo(new Float(200.0)))

            .body("shoppingCartItemList", hasSize(1))

            .body("shoppingCartItemList.quantity", hasItems(new Integer(2)));

    }

    @Test

    @DirtiesContext

    public void checkoutCart() throws Exception {

        given().post("/{cartId}/{itemId}/{quantity}", "678901", "111111", new Integer(3));

        given().post("/checkout/{cartId}", "678901")

            .then()

            .assertThat()

            .statusCode(200)

            .contentType(ContentType.JSON)

            .body("id", equalTo("678901"))

            .body("cartItemTotal", equalTo(new Float(0.0)))

            .body("shoppingCartItemList", hasSize(0));

    }

    private void initWireMockServer() throws Exception {

        InputStream isresp = Thread.currentThread().getContextClassLoader().getResourceAsStream("catalog-response.json");

        stubFor(get(urlEqualTo("/product/111111")).willReturn(

                aResponse().withStatus(200).withHeader("Content-type", "application/json").withBody(IOUtils.toString(isresp, Charset.defaultCharset()))));

        stubFor(get(urlEqualTo("/product/error")).willReturn(

                aResponse().withStatus(500)));

    }

}

应用Red Hat Developer Studio中的JUnit测试运行器运行测试。

或者,在命令行中应用Maven:

开始测试:

测试过程中的打印:

14项测试胜利:

https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
https://github.com/aolu308189...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...
http://www.lukou.com/userfeed...