Spring Cloud还允许您通过使用@RibbonClient声明其他配置(在RibbonClientConfiguration之上)来完全控制客户端,如以下示例所示:
@Configuration
@RibbonClient(name = "custom", configuration = CustomConfiguration.class)
public class TestConfiguration {
}
在这种情况下,客户端由RibbonClientConfiguration中已有的组件以及CustomConfiguration中的任何组件组成(其中后者通常会覆盖前者)。
CustomConfiguration类必须是@Configuration类,但请注意,对于主应用程序上下文,它不在@ComponentScan中。否则,它由所有@RibbonClients共享。如果您使用@ComponentScan(或@SpringBootApplication),则需要采取措施避免将其包括在内(例如,可以将其放在单独的,不重叠的程序包中,或指定要在@ComponentScan)。
以上官方的解释说,这样说可能有点绕,说白了,如果我们自定义一个CustomConfiguration来指定对应RibbonClient的负载均衡规则的时候,CustomConfiguration不能被SpringBoot的ComponentScan扫描到,如果被扫描扫,这样会造成全局配置,也是就所有的client可能会使用同一个IRule规则。
Spring Cloud官方说明
简单来说:SpringBootApplication默认扫描的component是位于当前启动类所在的包以及所在包以下的所有component,包括:@Service、@Controller``@Bean等等,@SpringBootApplication扫描的叫主上下文,而Ribbon也有一个上下文,可以称为子上下文,如果父子上下文扫描重叠,可能会带来预想不到的问题,类似如果开发过Spring、SpringMVC项目的知道,通过都会定义Spring配置文件applicationContext.xml以及SpringMVC配置文件spring-mvc.xml,比如:
applicationContext.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:aop="http://www.springframework.org/schema/aop"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
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/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 在applicationContext.xml中只加载除表现层之外的所有bean,因此下面一行中不需要加载@Controller -->
<!-- 扫描注解Bean 不包括@Controller(表现层) 保证@Service @Repository的属性被注入-->
<context:component-scan base-package="com.zml.oa">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 引入配置文件 -->
<context:property-placeholder ignore-unresolvable="true" local-override="true" location="classpath:application.properties"/>
<util:properties id="APP_PROPERTIES" location="classpath:application.properties" local-override="true"/>
<!-- 数据源 -->
<bean id="dataSource" class="org.logicalcobwebs.proxool.ProxoolDataSource" >
<!-- 数据源别名 -->
<property name="alias" value="proxoolDataSource"/>
<property name="driver" value="${connection.driverClassName}" />
<property name="driverUrl" value="${connection.url}" />
<property name="user" value="${connection.username}" />
<property name="password" value="${connection.password}" />
<!--最大连接数(默认5个),超过了这个连接数,再有请求时,就排在队列中等候,最大的等待请求数由maximum-new-connections决定 -->
<property name="maximumConnectionCount" value="${proxool.maximum.connection.count}"/>
<!--最小连接数(默认2个)-->
<property name="minimumConnectionCount" value="${proxool.minimum.connection.count}" />
<property name="statistics" value="${proxool.statistics}" />
<property name="simultaneousBuildThrottle" value="${proxool.simultaneous.build.throttle}"/>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.zml.oa.entity"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<!-- jdbc模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg ref="dataSource" />
</bean>
<!-- jdbc命名参数模板 -->
<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg ref="dataSource" />
</bean>
<bean id="applicationContextHelper" class="com.zml.oa.util.ApplicationContextHelper"/>
<!-- 开启AOP监听 只对当前配置文件有效 -->
<aop:aspectj-autoproxy expose-proxy="true"/>
<!-- 开启注解事务 只对当前配置文件有效 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="doSave*" propagation="REQUIRED" />
<tx:method name="doAdd*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="set*" propagation="REQUIRED" />
<tx:method name="doCreate*" propagation="REQUIRED" />
<tx:method name="doInsert*" propagation="REQUIRED" />
<tx:method name="doUpdate*" propagation="REQUIRED" />
<tx:method name="doMerge*" propagation="REQUIRED" />
<tx:method name="doDelete*" propagation="REQUIRED" />
<tx:method name="doRemove*" propagation="REQUIRED" />
<tx:method name="delete" propagation="REQUIRED" />
<tx:method name="doPut*" propagation="REQUIRED" />
<!-- workflowService.bankTransfer 需要回滚,但是传播行为设置为REQUIRES_NEW 相应的 act_ru_identitylink表中又不能保存task_id_ -->
<!-- 先记下来,待解决 -->
<tx:method name="bankTransfer*" propagation="REQUIRES_NEW" />
<!--hibernate4必须配置为开启事务 否则 getCurrentSession()获取不到-->
<tx:method name="get*" propagation="REQUIRED" read-only="true"/>
<tx:method name="count*" propagation="REQUIRED" read-only="true"/>
<tx:method name="find*" propagation="REQUIRED" read-only="true"/>
<tx:method name="list*" propagation="REQUIRED" read-only="true"/>
<tx:method name="toList*" propagation="REQUIRED" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config expose-proxy="true" proxy-target-class="true">
<!-- 只对业务逻辑层实施事务 -->
<aop:pointcut id="txPointcut" expression="execution(* com.zml.oa..service..*.*(..))"/>
<aop:advisor id="txAdvisor" advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
<!-- Activiti的bean -->
<!-- 流程引擎的配置bean -->
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
<property name="dataSource" ref="dataSource" />
<property name="databaseSchemaUpdate" value="true"/>
<property name="transactionManager" ref="transactionManager" />
<!-- 生成流程图的字体 -->
<property name="activityFontName" value="${diagram.activityFontName}"/>
<property name="labelFontName" value="${diagram.labelFontName}"/>
<!-- <property name="deploymentResources" value="classpath*:/deploy/*" /> -->
</bean>
<!-- 流程引擎的bean -->
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>
<!-- 服务组件的bean -->
<bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService" />
<bean id="runtimeService" factory-bean="processEngine" factory-method="getRuntimeService" />
<bean id="taskService" factory-bean="processEngine" factory-method="getTaskService" />
<bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService" />
<bean id="managementService" factory-bean="processEngine" factory-method="getManagementService" />
<bean id="identityService" factory-bean="processEngine" factory-method="getIdentityService" />
<!-- 启用缓存注解功能(请将其配置在Spring主配置文件中) -->
<cache:annotation-driven cache-manager="springCacheManager" mode="proxy" />
<import resource="classpath:spring-config-cache.xml"/>
<import resource="classpath:spring-config-shiro.xml"/>
</beans>
在Spring配置文件applicationContext.xml会看到:
<!-- 扫描注解Bean 不包括@Controller(表现层) 保证@Service @Repository的属性被注入-->
<context:component-scan base-package="com.zml.oa">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
目的是为了不让Spring排除扫描到com.zml.oa下的controller,防止与springMvc重复扫描。
spring-mvc.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" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<description>Spring MVC Configuration</description>
<!-- 加载配置属性文件 -->
<context:property-placeholder ignore-unresolvable="true" location="classpath:config.properties" />
<!-- 使用Annotation自动注册Bean,只扫描@Controller -->
<context:component-scan base-package="com.itstyle" use-default-filters="false"><!-- base-package 如果多个,用“,”分隔 -->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 默认的注解映射的支持,org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping -->
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager">
<mvc:message-converters register-defaults="true">
<!-- 将StringHttpMessageConverter的默认编码设为UTF-8 -->
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8" />
</bean>
<!-- 将Jackson2HttpMessageConverter的默认格式化输出为false -->
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list><value>application/json;charset=UTF-8</value></list>
</property>
<property name="prettyPrint" value="false"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!--启动Spring MVC的注解功能,设置编码方式,防止乱码-->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<bean class = "org.springframework.http.converter.StringHttpMessageConverter">
<property name = "supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
</list>
</property>
</bean>
</list>
</property>
</bean>
<!-- REST中根据URL后缀自动判定Content-Type及相应的View -->
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="mediaTypes" >
<map>
<entry key="xml" value="application/xml"/>
<entry key="json" value="application/json"/>
</map>
</property>
<property name="ignoreAcceptHeader" value="true"/>
<property name="favorPathExtension" value="true"/>
</bean>
<!-- Thymeleaf模版 -->
<bean id="templateResolver" class="org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver">
<property name="prefix" value="/WEB-INF/modules/" />
<property name="suffix" value=".html" />
<property name="templateMode" value="HTML" />
</bean>
<bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine">
<property name="templateResolvers">
<set>
<ref bean="templateResolver" />
</set>
</property>
</bean>
<bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
<property name="templateEngine" ref="templateEngine" />
<property name="characterEncoding" value="UTF-8"/>
</bean>
<!-- 对静态资源文件的访问, 将无法mapping到Controller的path交给default servlet handler处理 -->
<mvc:default-servlet-handler />
<!-- 静态资源映射 SpringMVC会自动给静态资源Response添加缓存头Cache-Control和Expires值 cache-period="31536000"-->
<mvc:resources mapping="/static/**" location="/static/" />
<!-- 定义无Controller的path<->view直接映射 -->
<mvc:view-controller path="/" view-name="redirect:${web.view.index}"/>
<!-- 自定义拦截器之类的 -->
</beans>
在spring-mvc.xml配置文件会看到:
<!-- 使用Annotation自动注册Bean,只扫描@Controller -->
<context:component-scan base-package="com.itstyle" use-default-filters="false">
<!-- base-package 如果多个,用“,”分隔 -->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
Spring、SpringMVC这样的配置其实就是为了防止父子上下文重叠的问题,这样解释应该就明了了~
解决办法:
将Ribbon配置定义到启动类所在包的同级文件夹中,防止被SpringBoot重复扫描,类似:
这样就可以防止父子上下文扫描重叠问题了~