我眼中的服務提供和服務消費

?

服務提供和消費腦圖

服務提供和消費腦圖

?

參見: 服務提供者, 服務消費者, 服務注冊中心

服務提供者

1.服務提供者啟動,解析xml文件中配置的服務,這里使用Dom4j解析。

2.將服務的一些相關信息注冊到 服務注冊中心。

注:服務相關信息:服務中心接口url,接口名稱,方法名稱,參數信息。

3.提供一個接口,服務消費者通過調用這個接口url來調用相應的服務。

參見: 服務提供和消費腦圖, 服務注冊中心 (1.注冊服務), 服務消費者 (3.調用服務)

服務消費者

1.服務消費者啟動,使用dom4j解析xml獲取要消費的服務相關接口。

2.根據接口信息去服務注冊中心判斷是否有對應的注冊信息,如果有則通過jdk動態代理生成相應的代理類并注冊到spring中(代理方法中會根據服務中心返回的信息(服務提供者的url)去調用服務提供者對應的服務)。

參見: 服務提供和消費腦圖, 服務注冊中心 (2.消費服務), 服務提供者 (3.調用服務)

服務注冊中心

1.將來自服務提供者信息存儲到redis。

2.將服務信息提供給服務消費者。

參見: 服務提供者 (1.注冊服務), 服務消費者 (2.消費服務), 服務提供和消費腦圖

工程示例

  注:示例中為了簡單,采用rest請求方式來代替socket連接

  注冊中心

@RestController
@RequestMapping("index")
public class IndexController {@Autowiredprivate RedisCacheTemplate redisCacheTemplate;//注冊服務提供者信息,將信息放到redis中@RequestMapping(value = "register", method = RequestMethod.POST)public SimpleResponse register(@RequestBody RegisterMessage registerMessage) {try {Map<String, Object> map = new HashMap<>();for (InterfaceMessage interfaceMessage : registerMessage.getInterfaceMessageList()) {interfaceMessage.setProviderUrl(registerMessage.getProviderUrl());map.put(ToStringBuilder.reflectionToString(interfaceMessage, ToStringStyle.SHORT_PREFIX_STYLE), true);}redisCacheTemplate.batchPut(map);return SimpleResponse.success(map.size());} catch (Exception e) {e.printStackTrace();return SimpleResponse.error(e.getMessage());}}//消費者拿到配置的服務信息到注冊中心來匹配,驗證是否存在這個服務@RequestMapping(value = "contains", method = RequestMethod.POST)public SimpleResponse contains(@RequestBody InterfaceMessage interfaceMessage) {try {if(redisCacheTemplate.exist(ToStringBuilder.reflectionToString(interfaceMessage, ToStringStyle.SHORT_PREFIX_STYLE))) {return SimpleResponse.success(true);} else {return SimpleResponse.error(null);}} catch (Exception e) {e.printStackTrace();return SimpleResponse.error(e.getMessage());}}@RequestMapping(value = "test", method = {RequestMethod.GET, RequestMethod.POST})public SimpleResponse test(@RequestParam String providerUrl){return SimpleResponse.success(providerUrl);}
}

  服務提供者

<?xml version="1.0" encoding="UTF-8"?>
<services-provider><service id="testService" interface="com.hjzgg.simulation.api.ITestService"/>
</services-provider>

  自定義xml,配置將要注冊的服務id及對應的接口類。

# 內置tomcat服務器配置
server.port=8088
server.context-path=/provider-server#打印彩色日志
spring.output.ansi.enabled=always# 日志打印級別
logging.level.root=debug# service
service.xml.path=classpath:service-provider.xml 自定義服務提供者配置文件 位置
service.provider.path=http://localhost:8088/provider-server/index/provider  服務提供者執行相應服務接口
service.register.path=http://localhost:8090/register-server/index/register  調用注冊中心 接口
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hjzgg.simulation.common.node.InterfaceMessage;
import com.hjzgg.simulation.common.node.RegisterMessage;
import com.hjzgg.simulation.common.parsexml.BeanNode;
import com.hjzgg.simulation.common.parsexml.ParseServiceXML;
import com.hjzgg.simulation.common.response.ReturnCode;
import com.hjzgg.simulation.common.utils.RestTemplateUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.http.MediaType;
import org.springframework.util.CollectionUtils;import java.util.ArrayList;
import java.util.List;public class Registrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {private static Logger logger = LoggerFactory.getLogger(Registrar.class);private String servicesXmlPath;private String serviceProviderPath;private String serviceRegisterPath;@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {List<BeanNode> beanNodes = ParseServiceXML.getProviderServices(servicesXmlPath); 解析自定義服務提供配置文件List<InterfaceMessage> list = new ArrayList<>();for(BeanNode beanNode : beanNodes) { 根據服務對應id去 尋找實現的 beanif(!registry.containsBeanDefinition(beanNode.getBeanName())) {logger.error("接口" + beanNode.getBeanName() + " " + beanNode.getInterfaceCls().getTypeName() + " 沒有對應的實現類");} else {InterfaceMessage interfaceMessage = new InterfaceMessage();interfaceMessage.setBeanName(beanNode.getBeanName());interfaceMessage.setInterfacType(beanNode.getInterfaceCls().getTypeName());list.add(interfaceMessage);}}if(!CollectionUtils.isEmpty(list)) { 將配置的服務信息發送的注冊中心RegisterMessage registerMessage = new RegisterMessage();registerMessage.setProviderUrl(this.serviceProviderPath);registerMessage.setInterfaceMessageList(list);try {String result = RestTemplateUtils.post(this.serviceRegisterPath, (JSONObject) JSON.toJSON(registerMessage), MediaType.APPLICATION_JSON_UTF8);JSONObject retJson = JSONObject.parseObject(result);if(retJson.getInteger("code") == ReturnCode.SUCCESS.getValue()) {logger.debug("服務注冊成功...");} else {logger.error("服務注冊失敗..." + retJson.getString("msg"));}} catch (Exception e) {e.printStackTrace();logger.error("服務注冊失敗..." + e.getMessage());}}}@Overridepublic void setEnvironment(Environment environment) { 獲取環境變量this.servicesXmlPath = environment.getProperty("service.xml.path");this.serviceProviderPath = environment.getProperty("service.provider.path");this.serviceRegisterPath = environment.getProperty("service.register.path");assert(StringUtils.isNotEmpty(this.serviceProviderPath) && StringUtils.isNotEmpty(serviceRegisterPath) &&StringUtils.isNotEmpty(this.servicesXmlPath));}
}
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;/*** Created by hujunzheng on 2017/7/7.*/@Configuration
@Import(Registrar.class)
public class Config { 注冊服務配置啟動
}
import com.hjzgg.simulation.common.node.ServiceMessage;
import com.hjzgg.simulation.common.response.SimpleResponse;
import com.hjzgg.simulation.common.utils.ContextUtils;
import com.hjzgg.simulation.common.utils.SerializeUtil;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;/*** Created by hujunzheng on 2017/7/8.*/
@RestController
@RequestMapping("index")
public class IndexController {

   服務提供者執行相應服務接口@RequestMapping(value
= "invoke", method = RequestMethod.POST)public Object invoke(@RequestParam String serviceMessageBody) {try {
       根據消費者傳遞的服務信息 找到對應的服務bean以及方法,并利用反射執行方法,最后返回結果ServiceMessage serviceMessage
= (ServiceMessage) SerializeUtil.unserialize(Hex.decodeHex(serviceMessageBody.toCharArray()));Object bean = null;if((bean = ContextUtils.getBean(serviceMessage.getBeanName(), serviceMessage.getRequireType())) != null) {List<Class<?>> classList = new ArrayList<>();if(serviceMessage.getArgs() != null) {for (Object obj : serviceMessage.getArgs()) {classList.add(obj.getClass());}}Method method = ReflectionUtils.findMethod(bean.getClass(), serviceMessage.getMethodName(), classList.toArray(new Class<?>[0]));if(method != null) {return method.invoke(bean, serviceMessage.getArgs());} else {return SimpleResponse.error("服務" + serviceMessage.getRequireType().getTypeName() + "中沒有對應參數"+ ToStringBuilder.reflectionToString(classList) + "的" + serviceMessage.getMethodName() + "方法");}} else {return SimpleResponse.error("沒有名稱為" + serviceMessage.getBeanName() + "且類型為"+ serviceMessage.getRequireType().getTypeName() + "對應的bean");}} catch (Exception e) {e.printStackTrace();return SimpleResponse.error(e.getMessage());}}}

  服務消費者

<?xml version="1.0" encoding="UTF-8"?>
<services-consumer><service ref="testService" interface="com.hjzgg.simulation.api.ITestService" url="http://localhost:8088/provider-server/index/provider"/>
</services-consumer>

  自定義服務消費者配置,服務引用名稱,接口類型,調用服務提供者URL

# 內置tomcat服務器配置
server.port=8089
server.context-path=/consumer-server#打印彩色日志
spring.output.ansi.enabled=always# 日志打印級別
logging.level.root=debug# service xml
service.xml.path=classpath:service-consumer.xml 自定義服務消費配置文件位置
service.contains.url=http://localhost:8090/register-server/index/contains 注冊中心服務查詢接口
service.invoke.url=http://localhost:8088/provider-server/index/invoke 服務提供者執行相應服務接口
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hjzgg.simulation.common.dynamic.JdkDynamicProxy;
import com.hjzgg.simulation.common.node.InterfaceMessage;
import com.hjzgg.simulation.common.parsexml.BeanNode;
import com.hjzgg.simulation.common.parsexml.ParseServiceXML;
import com.hjzgg.simulation.common.register.SpringBeanRegister;
import com.hjzgg.simulation.common.response.ReturnCode;
import com.hjzgg.simulation.common.utils.RestTemplateUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.http.MediaType;import java.util.Iterator;
import java.util.List;public class Registrar implements ImportBeanDefinitionRegistrar, EnvironmentAware{private Logger logger = LoggerFactory.getLogger(Registrar.class);private String servicesXmlPath;private String serviceContainsPath;@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
     解析自定義服務消費配置文件List
<BeanNode> beanNodes = ParseServiceXML.getConsumerServices(servicesXmlPath); 判斷注冊中心 是否注冊了這個服務了for(Iterator<BeanNode> it = beanNodes.iterator(); it.hasNext(); ) {BeanNode beanNode = it.next();InterfaceMessage interfaceMessage = new InterfaceMessage();interfaceMessage.setProviderUrl(beanNode.getUrl());interfaceMessage.setInterfacType(beanNode.getInterfaceCls().getTypeName());interfaceMessage.setBeanName(beanNode.getBeanName());try {String result = RestTemplateUtils.post(this.serviceContainsPath, (JSONObject) JSON.toJSON(interfaceMessage), MediaType.APPLICATION_JSON_UTF8);JSONObject retJson = JSON.parseObject(result);if (retJson.getInteger("code") == ReturnCode.FAILURE.getValue()) {it.remove();logger.error(interfaceMessage.getBeanName() + "對應類型" + interfaceMessage.getInterfacType() + "的服務在" +interfaceMessage.getProviderUrl() + "上沒有注冊");}} catch (Exception e) {e.printStackTrace();logger.error("服務" + interfaceMessage.getBeanName() + "對應類型" + interfaceMessage.getInterfacType() + "查找失敗..." + e.getMessage());}}
     將與注冊中心一直的服務 以 動態代理的方式 注冊到spring中SpringBeanRegister.registerBean(importingClassMetadata, registry, beanNodes);}@Override
public void setEnvironment(Environment environment) { 設置環境變量this.servicesXmlPath = environment.getProperty("service.xml.path");this.serviceContainsPath = environment.getProperty("service.contains.url");String serviceInvokePath = environment.getProperty("service.invoke.url");assert(StringUtils.isNotEmpty(serviceContainsPath) && StringUtils.isNotEmpty(this.servicesXmlPath)&& StringUtils.isNotEmpty(serviceInvokePath));JdkDynamicProxy.setServerProviderInvokeUrl(serviceInvokePath);} }
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;/*** Created by hujunzheng on 2017/7/7.*/@Configuration
@Import(Registrar.class)
public class Config {
}

?  測試一下

  api接口

import com.alibaba.fastjson.JSONObject;/*** Created by hujunzheng on 2017/7/7.*/
public interface ITestService {JSONObject testService();
}

  服務提供者對應的實例

import com.alibaba.fastjson.JSONObject;
import com.hjzgg.simulation.api.ITestService;
import org.springframework.stereotype.Service;/*** Created by hujunzheng on 2017/7/8.*/
@Service("testService")
public class TestServiceImpl implements ITestService {@Overridepublic JSONObject testService() {JSONObject result = new JSONObject();result.put("name", "hujunzheng");result.put("age", 25);return result;}
}

  消費者對應的測試

import com.hjzgg.simulation.api.ITestService;
import com.hjzgg.simulation.common.response.SimpleResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** Created by hujunzheng on 2017/7/9.*/
@RestController
@RequestMapping("index")
public class ConsumerController {@Autowiredprivate ITestService testService;@RequestMapping("test")public SimpleResponse test() {try {return SimpleResponse.success(testService.testService());} catch (Exception e) {e.printStackTrace();return SimpleResponse.error(e.getMessage());}}
}

  這就是我的實現方式,就說到這里了。最后我想說,思路很重要,掌握的知識很重要,多積累,多思考,任重而道遠。最后附上我從中積累的知識和經驗。

知識和經驗

  執行順序及ProxyFactoryBean實現

  Public class ProxyFactoryBean implements FactoryBean, InitializingBean;
  方法執行順序 getObjectType->afterPropertiesSet->getObject
bean 的屬性設置的 先于 getObjectType

  Springboot 工程自定義jar包中獲取上下文工具類

要加上 @Component注解

  實體類型(例如下面)網絡傳輸方法,避免字符串編碼格式問題

發送請求
ServiceMessage serviceMessage = new ServiceMessage();
。。。。。

JSONObject params = new JSONObject();
params.put("serviceMessageBody", Hex.encodeHexString(SerializeUtil.serialize(serviceMessage)));

Class<?> returnType = method.getReturnType();
return RestTemplateUtils.post(SERVER_PROVIDER_INVOKE_URL, params, MediaType.APPLICATION_FORM_URLENCODED, returnType);
接收請求
@RequestMapping(value = "invoke", method = RequestMethod.POST)
public Object invoke(@RequestParam String serviceMessageBody) {
try {
ServiceMessage serviceMessage = (ServiceMessage) SerializeUtil.unserialize(Hex.decodeHex(serviceMessageBody.toCharArray()));
。。。。。?
}
參考工具類
<dependency>
??? <groupId>commons-codec</groupId>
??? <artifactId>commons-codec</artifactId>
</dependency>
Hex實現十六進制字符串和byte[]之間的轉換,另附RestTemplateUtils工具鏈接
  完整項目下載,請點這里!

轉載于:https://www.cnblogs.com/hujunzheng/p/7131212.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/531249.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/531249.shtml
英文地址,請注明出處:http://en.pswp.cn/news/531249.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

shiro整合oauth

前言 如果oauth原理還不清楚的地方&#xff0c;其參考這里。 一、基本思路腦圖 二、客戶端shiro配置 shiro配置文件 <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans"xmlns:util&q…

springmvc+swagger2

一、swagger2依賴 <!--swagger--> <dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><exclusions><exclusion><artifactId>spring-aop</artifactId><groupId>org.s…

獲取資源文件工具類

如果沒有依賴spring&#xff0c;可以將分割線下的方法去掉 import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframe…

無狀態shiro認證組件(禁用默認session)

準備內容 簡單的shiro無狀態認證 無狀態認證攔截器 import com.hjzgg.stateless.shiroSimpleWeb.Constants; import com.hjzgg.stateless.shiroSimpleWeb.realm.StatelessToken; import org.apache.shiro.web.filter.AccessControlFilter;import javax.servlet.ServletRequest;…

Spring根據包名獲取包路徑下的所有類

參考mybatis MapperScannerConfigurer.java 最終找到 Spring的一個類 ClassPathBeanDefinitionScanner.java 參考ClassPathBeanDefinitionScanner 和它的父類 ClassPathScanningCandidateComponentProvider&#xff0c;將一些代碼進行抽取&#xff0c;得到如下工具類。 import…

java8 Optional正確使用姿勢

Java 8 如何正確使用 Optional import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; import org.apache.commons.lang3.StringUtils;import java.util.Optional;Data EqualsAndHashCode(exclude{"self"}) ToString(callSupertrue, exclud…

idea springboot熱部署無效問題

Intellij IDEA 使用Spring-boot-devTools無效解決辦法 springboot項目中遇到的bug <dependencies><!--spring boot 熱加載--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId&g…

lintcode 單詞接龍II

題意 給出兩個單詞&#xff08;start和end&#xff09;和一個字典&#xff0c;找出所有從start到end的最短轉換序列 比如&#xff1a; 1、每次只能改變一個字母。 2、變換過程中的中間單詞必須在字典中出現。 注意事項 所有單詞具有相同的長度。所有單詞都只包含小寫字母。樣例…

lintcode 最大子數組III

題目描述 給定一個整數數組和一個整數 k&#xff0c;找出 k 個不重疊子數組使得它們的和最大。每個子數組的數字在數組中的位置應該是連續的。 返回最大的和。 注意事項 子數組最少包含一個數 樣例 給出數組 [-1,4,-2,3,-2,3] 以及 k 2&#xff0c;返回 8 思路 dp[i][j] max(…

idea模板注釋

類文件頭部的注釋 #if (${PACKAGE_NAME} && ${PACKAGE_NAME} ! "")package ${PACKAGE_NAME};#end #parse("File Header.java") /** * ${DESCRIPTION} * author ${USER} hujunzheng * create ${YEAR}-${MONTH}-${DAY} ${TIME} **/ public class ${N…

redis分布式鎖小試

一、場景 項目A監聽mq中的其他項目的部署消息&#xff08;包括push_seq, status, environment&#xff0c;timestamp等&#xff09;&#xff0c;然后將部署消息同步到數據庫中&#xff08;項目X在對應環境[environment]上部署的push_seq[項目X的版本]&#xff09;。那么問題來了…

Jackson ObjectMapper readValue過程

1.整體調用棧 2.看一下調用棧的兩個方法 resolve 方法中通過 Iterator i$ this._beanProperties.iterator() 遍歷屬性的所有子屬性&#xff0c;緩存對應的 deserializer。觀察調用棧的方法&#xff0c;可以發現是循環調用的。 3.比如尋找自定義的 LocalDateTime類的序列化實現…

java如何尋找main函數對應的類

參考springboot Class<?> deduceMainApplicationClass() {try {StackTraceElement[] stackTrace new RuntimeException().getStackTrace();for (StackTraceElement stackTraceElement : stackTrace) {if ("main".equals(stackTraceElement.getMethodName())…

jooq實踐

用法 sql語句 SELECT AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME, COUNT(*)FROM AUTHORJOIN BOOK ON AUTHOR.ID BOOK.AUTHOR_IDWHERE BOOK.LANGUAGE DEAND BOOK.PUBLISHED > DATE 2008-01-01 GROUP BY AUTHOR.FIRST_NAME, AUTHOR.LAST_NAMEHAVING COUNT(*) > 5 ORDER BY AUT…

不同包下,相同數據結構的兩個類進行轉換

import com.alibaba.fastjson.JSON; JSON.parseObject(JSON.toJSONString(obj1), obj2.class) import com.fasterxml.jackson.databind.ObjectMapper; objectMapper.convertValue(obj1, obj2.class); 兩個工具類 JsonUtil JacksonHelper 轉載于:https://www.cnblogs.com/hujunz…

git根據用戶過濾提交記錄

使用SourceTree 使用gitk 轉載于:https://www.cnblogs.com/hujunzheng/p/8398203.html

springboot Autowired BeanNotOfRequiredTypeException

現象 org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named xxxxImpl is expected to be of type com.xxx.xxxImpl but was actually of type com.sun.proxy.$Proxy62 直接Autowired一個實現類&#xff0c;而不是接口 Autowired private XxxServiceI…

cglib動態代理導致注解丟失問題及如何修改注解允許被繼承

現象 SOAService這個bean先后經過兩個BeanPostProcessor&#xff0c;會發現代理之后注解就丟失了。 開啟了cglib代理 SpringBootApplication EnableAspectJAutoProxy(proxyTargetClass true) public class Application {public static void main(String[] args) {SpringApplic…

spring AbstractBeanDefinition創建bean類型是動態代理類的方式

1.接口 Class<?> resourceClass 2.獲取builder BeanDefinitionBuilder builder BeanDefinitionBuilder.genericBeanDefinition(resourceClass); 3.獲取接口對應的動態代理class Class<?> targetProxyClass Proxy.getProxyClass(XXX.class.getClassLoader(), ne…

TypeReference -- 讓Jackson Json在List/Map中識別自己的Object

private Map<String, Object> buildHeaders(Object params) {ObjectMapper objectMapper JacksonHelper.getMapper();return objectMapper.convertValue(params, new TypeReference<Map<String, Object>>(){}); } 參考How to use Jackson to deserialis…