Spring Cloud系列之Commons - 1. 背景与基础知识准备(下)

简介: Spring Cloud系列之Commons - 1. 背景与基础知识准备(下)

bean-config-1.yaml

server:
  port: 8080
spring:
  application:
    name: child1


接下来分别是ChildContext2,ChildContext3的:

package com.test.spring.context.config.child2;
import com.hopegaming.scaffold.spring.context.bean.ChildBean;
import com.hopegaming.scaffold.spring.context.bean.RootBean;
import com.hopegaming.scaffold.spring.context.config.YamlPropertyLoaderFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
@SpringBootApplication(scanBasePackages = {"com.test.spring.context.controller"})
@PropertySource(value = "classpath:/bean-config-2.yaml", factory = YamlPropertyLoaderFactory.class)
public class ChildContext2 {
    @Bean
    public ChildBean getChildBean(@Value("${spring.application.name}") String name, RootBean fatherBean) {
        ChildBean childBean = new ChildBean();
        childBean.setFatherBean(fatherBean);
        childBean.setName(name);
        return childBean;
    }
}
server:
  port: 8081
spring:
  application:
    name: child2
management:
  endpoint:
    health:
      show-details: always
  endpoints:
    jmx:
      exposure:
        exclude: '*'
    web:
      exposure:
        include: '*'
package com.test.spring.context.config.child3;
import com.hopegaming.scaffold.spring.context.bean.ChildBean;
import com.hopegaming.scaffold.spring.context.bean.RootBean;
import com.hopegaming.scaffold.spring.context.config.YamlPropertyLoaderFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
@SpringBootApplication(scanBasePackages = {"com.test.spring.context.controller"})
@PropertySource(value = "classpath:/bean-config-3.yaml", factory = YamlPropertyLoaderFactory.class)
public class ChildContext3 {
    @Bean
    public ChildBean getChildBean(@Value("${spring.application.name}") String name, RootBean fatherBean) {
        ChildBean childBean = new ChildBean();
        childBean.setFatherBean(fatherBean);
        childBean.setName(name);
        return childBean;
    }
}
server:
  port: 8082
spring:
  application:
    name: child3
management:
  endpoint:
    health:
      show-details: always
  endpoints:
    jmx:
      exposure:
        exclude: '*'
    web:
      exposure:
        include: '*'


测试接口TestController

package com.test.spring.context.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Locale;
@RestController
public class TestController {
    @Autowired
    private ChildBean childBean;
    @RequestMapping("/test")
    public ChildBean getChildBean() {
        return childBean;
    }
}


启动类:

package com.test.spring.context;
import com.hopegaming.scaffold.spring.context.config.child1.ChildContext1;
import com.hopegaming.scaffold.spring.context.config.child2.ChildContext2;
import com.hopegaming.scaffold.spring.context.config.child3.ChildContext3;
import com.hopegaming.scaffold.spring.context.config.root.RootContext;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
public class ContextMain {
    public static void main(String[] args) {
        SpringApplicationBuilder appBuilder =
                new SpringApplicationBuilder()
                        .sources(RootContext.class)
                        //第一个子context用child,剩下的都用sibling
                        .child(ChildContext1.class)
                        .sibling(ChildContext2.class)
                        .sibling(ChildContext3.class);
        ConfigurableApplicationContext applicationContext = appBuilder.run();
    }
}


启动后,访问http://127.0.0.1:8080/test返回:

{"fatherBean":{"name":"root"},"name":"child1"}


访问http://127.0.0.1:8081/test返回:

{"fatherBean":{"name":"root"},"name":"child2"}


访问http://127.0.0.1:8082/test返回:

{"fatherBean":{"name":"root"},"name":"child3"}


访问http://127.0.0.1:8080/actuator/beans会有类似于下面的返回(省略了不关心的bean):

{
  "contexts": {
    "application-1": {
      "beans": {
        "getChildBean": {
          "aliases": [],
          "scope": "singleton",
          "type": "com.hopegaming.scaffold.spring.context.bean.ChildBean",
          "resource": "com.hopegaming.scaffold.spring.context.config.child2.ChildContext2",
          "dependencies": [
            "getFatherBean"
          ]
        },
        "childContext2": {
          "aliases": [],
          "scope": "singleton",
          "type": "com.hopegaming.scaffold.spring.context.config.child2.ChildContext2$$EnhancerBySpringCGLIB$$26f80b15",
          "resource": null,
          "dependencies": []
        }
        .......
      },
      "parentId": "application"
    },
    "application": {
      "beans": {
        "getFatherBean": {
          "aliases": [],
          "scope": "singleton",
          "type": "com.hopegaming.scaffold.spring.context.bean.RootBean",
          "resource": "com.hopegaming.scaffold.spring.context.config.root.RootContext",
          "dependencies": []
        },
        "rootContext": {
          "aliases": [],
          "scope": "singleton",
          "type": "com.hopegaming.scaffold.spring.context.config.root.RootContext$$EnhancerBySpringCGLIB$$18d9c26f",
          "resource": null,
          "dependencies": []
        }
        .......
      },
      "parentId": null
    }
  }
}

通过这个例子,想必大家对于 ApplicationContext 层级有了一定的理解


Bean 加载条件


我们会经常看到@Conditional相关的注解,例如@ConditionalOnBean还有@ConditionalOnClass等等,这些注解提供了自动装载时候根据某些条件加载不同类的灵活性。@Conditional注解是 spring-context 提供的特性,Spring Boot 在这个注解的基础上,提供了更多具体的条件配置注解,包括:

  • @ConditionalOnBean,如果当前 ApplicationContext 的 BeanFactory 已经包含这些 Bean,则满足条件。与之相反的是 @ConditionalOnMissingBean,如果当前 ApplicationContext 的 BeanFactory 不包含这些 Bean,则满足条件。
  • @ConditionalOnClass,如果当前 classpath 中有这些类,则满足条件。与之相反的是@ConditionalOnMissingClass,如果当前 classpath 中没有这些类,则满足条件
  • @ConditionalOnProperty,指定属性是否存在,并且值满足havingValue指定的值(没设置就是不为false就行),matchIfMissing代表如果属性不存在代表条件满足还是不满足。

以上几个注解是比较常用的,剩下的例如ConditionalOnCloudPlatform这些不太常用,这里先不提了。

如果有多个类似的@Conditional注解作用于同一个方法或者类,这些加载条件是“And”的关系


Configuration 加载顺序


由于 Bean 加载条件的复杂性,有时候我们想某些 Configuration 类先加载,某些在特定的 Configuration 加载完之后再加载。例如:

@Configuration
public class FirstConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public Service service1() {
        ......
    }
}
@Configuration
public class SecondConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public Service service1() {
        ......
    }
}

假设这两个类在不同 jar 包,我们没有办法确定最后创建的是哪一个类的 Service,这时候我们就需要用到一些决定 Configuration 加载顺序的注解。注意这里的 Configuration 加载顺序仅仅是 Bean 定义加载顺序,主要是为了限制上面提到的 Bean 加载条件的判断顺序,而不是创建 Bean 的顺序。Bean 创建的顺序主要由 Bean 依赖决定以及@DependsOn注解限制。

相关的注解如下:

  • @AutoConfigureAfter 指定当前 Configuration 在 某个 Configuration 之后加载。
  • @AutoConfigureBefore 指定当前 Configuration 在 某个 Configuration 之前加载。
  • @AutoConfigureOrder 类似于@Order注解,指定当前 Configuration 的加载序号,默认是 0 ,越小越先加载。


Bean 排序


对于同一类型的 Bean(实现了同一接口的 Bean),我们可以用一个 List 进行自动装载,例如:

public interface Service {
    void test();
}
@Componenet
public class ServiceA implements Service {
    @Override
    public void test() {
        System.out.println("ServiceA");
    }
}
@Componenet
public class ServiceB implements Service {
    @Override
    public void test() {
        System.out.println("ServiceB");
    }
}
@Componenet
public class Test {
    @Autowired
    private List<Service> services;
}


private List<Service> services 中就会有 serviceAserviceB 这两个 Bean,但是谁在前谁在后呢?可以通过@Order注解指定。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Documented
public @interface Order {
  /**
   * The order value.
   * <p>Default is {@link Ordered#LOWEST_PRECEDENCE}.
   * @see Ordered#getOrder()
   */
  int value() default Ordered.LOWEST_PRECEDENCE;
}


值越小,越靠前。

相关文章
|
1月前
|
负载均衡 Java API
Spring Cloud 面试题及答案整理,最新面试题
Spring Cloud 面试题及答案整理,最新面试题
133 1
|
1月前
|
Java Nacos Sentinel
Spring Cloud Alibaba 面试题及答案整理,最新面试题
Spring Cloud Alibaba 面试题及答案整理,最新面试题
196 0
|
1月前
|
SpringCloudAlibaba Java 持续交付
【构建一套Spring Cloud项目的大概步骤】&【Springcloud Alibaba微服务分布式架构学习资料】
【构建一套Spring Cloud项目的大概步骤】&【Springcloud Alibaba微服务分布式架构学习资料】
140 0
|
6天前
|
负载均衡 Java 开发者
细解微服务架构实践:如何使用Spring Cloud进行Java微服务治理
【4月更文挑战第17天】Spring Cloud是Java微服务治理的首选框架,整合了Eureka(服务发现)、Ribbon(客户端负载均衡)、Hystrix(熔断器)、Zuul(API网关)和Config Server(配置中心)。通过Eureka实现服务注册与发现,Ribbon提供负载均衡,Hystrix实现熔断保护,Zuul作为API网关,Config Server集中管理配置。理解并运用Spring Cloud进行微服务治理是现代Java开发者的关键技能。
|
6天前
|
Java API 对象存储
对象存储OSS产品常见问题之使用Spring Cloud Alibaba情况下文档添加水印如何解决
对象存储OSS是基于互联网的数据存储服务模式,让用户可以安全、可靠地存储大量非结构化数据,如图片、音频、视频、文档等任意类型文件,并通过简单的基于HTTP/HTTPS协议的RESTful API接口进行访问和管理。本帖梳理了用户在实际使用中可能遇到的各种常见问题,涵盖了基础操作、性能优化、安全设置、费用管理、数据备份与恢复、跨区域同步、API接口调用等多个方面。
23 2
|
21天前
|
负载均衡 网络协议 Java
构建高效可扩展的微服务架构:利用Spring Cloud实现服务发现与负载均衡
本文将探讨如何利用Spring Cloud技术实现微服务架构中的服务发现与负载均衡,通过注册中心来管理服务的注册与发现,并通过负载均衡策略实现请求的分发,从而构建高效可扩展的微服务系统。
|
21天前
|
开发框架 负载均衡 Java
Spring boot与Spring cloud之间的关系
总之,Spring Boot和Spring Cloud之间的关系是一种构建和扩展的关系,Spring Boot提供了基础,而Spring Cloud在此基础上提供了分布式系统和微服务架构所需的扩展和工具。
17 4
Spring boot与Spring cloud之间的关系
|
1月前
|
SpringCloudAlibaba 负载均衡 Java
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(目录大纲)
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(目录大纲)
63 1
|
1月前
|
Java Nacos Sentinel
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(九)Nacos+Sentinel+Seata
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(九)Nacos+Sentinel+Seata
194 0
|
1月前
|
消息中间件 SpringCloudAlibaba Java
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(八)Config服务配置+bus消息总线+stream消息驱动+Sleuth链路追踪
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(八)Config服务配置+bus消息总线+stream消息驱动+Sleuth链路追踪
782 0

热门文章

最新文章