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
如何将Spring Boot + RabbitMQ应用程序部署到Pivotal Cloud Foundry (PCF)
如何将Spring Boot + RabbitMQ应用程序部署到Pivotal Cloud Foundry (PCF)
38 6
|
1月前
|
Java 关系型数据库 MySQL
如何将Spring Boot + MySQL应用程序部署到Pivotal Cloud Foundry (PCF)
如何将Spring Boot + MySQL应用程序部署到Pivotal Cloud Foundry (PCF)
62 5
|
1月前
|
缓存 监控 Java
如何将Spring Boot应用程序部署到Pivotal Cloud Foundry (PCF)
如何将Spring Boot应用程序部署到Pivotal Cloud Foundry (PCF)
44 5
|
6月前
|
资源调度 Java 调度
Spring Cloud Alibaba 集成分布式定时任务调度功能
Spring Cloud Alibaba 发布了 Scheduling 任务调度模块 [#3732]提供了一套开源、轻量级、高可用的定时任务解决方案,帮助您快速开发微服务体系下的分布式定时任务。
15119 34
|
6月前
|
负载均衡 Java Spring
Spring cloud gateway 如何在路由时进行负载均衡
Spring cloud gateway 如何在路由时进行负载均衡
661 15
|
6月前
|
Java Spring
spring cloud gateway在使用 zookeeper 注册中心时,配置https 进行服务转发
spring cloud gateway在使用 zookeeper 注册中心时,配置https 进行服务转发
139 3
|
6月前
|
消息中间件 Java Nacos
通用快照方案问题之通过Spring Cloud实现配置的自动更新如何解决
通用快照方案问题之通过Spring Cloud实现配置的自动更新如何解决
87 0
|
6月前
|
缓存 监控 Java
通用快照方案问题之Spring Boot Admin的定义如何解决
通用快照方案问题之Spring Boot Admin的定义如何解决
76 0
|
6月前
|
监控 NoSQL Java
通用快照方案问题之Martin Flower提出的微服务之间的通信如何解决
通用快照方案问题之Martin Flower提出的微服务之间的通信如何解决
47 0