dubbo的consumer代理的生成方式原始碼
dubbo的consumer只需要在配置檔案中配置一個介面的地址,並不需要這個介面有具體的實現類,就可以生成這個介面的代理,通過代理遠端呼叫provider中的方法。
和JDK還有CGlib不同,dubbo的consumer生成代理是通過dubbo中的一個com.alibaba.dubbo.common.bytecode.Proxy來生成的,使用了javassist工具來生成代理類的位元組碼。
如果在程式碼中使用
Object obj=ApplicationContextUtil.getBean("orderService");
可以發現obj的class並不是com.alibaba.dubbo.common.bytecode.Proxy,而是類似com.alibaba.dubbo.common.bytecode.Proxy26這樣的(後面的數字不一定是26),這個Proxy26就是最終生成的代理類,他的父類就是Object,但是有對應介面中的方法。
如果在程式碼中使用
Map<String,BeanDefinition> map=ApplicationContextUtil.getBeanDefinitions();
BeanDefinition definition=map.get(service);
String className=definition.getBeanClassName();
可以看到className是com.alibaba.dubbo.config.spring.ReferenceBean。
dubbo在spring配置檔案中的配置可以是這樣的:
<dubbo:reference id="orderService" interface="test.orderService" protocol="dubbo" check="false"/>
<dubbo >標籤是由com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler來解析的。
DubboNamespaceHandler類是這樣的
所以<dubbo:reference>對應的就是com.alibaba.dubbo.config.spring.ReferenceBeanpackagecom.alibaba.dubbo.config.spring.schema; importcom.alibaba.dubbo.common.Version; importcom.alibaba.dubbo.config.ApplicationConfig; importcom.alibaba.dubbo.config.ConsumerConfig; importcom.alibaba.dubbo.config.ModuleConfig; importcom.alibaba.dubbo.config.MonitorConfig; importcom.alibaba.dubbo.config.ProtocolConfig; importcom.alibaba.dubbo.config.ProviderConfig; importcom.alibaba.dubbo.config.RegistryConfig; importcom.alibaba.dubbo.config.spring.AnnotationBean; importcom.alibaba.dubbo.config.spring.ReferenceBean; importcom.alibaba.dubbo.config.spring.ServiceBean; importorg.springframework.beans.factory.xml.NamespaceHandlerSupport; /** * DubboNamespaceHandler * * @author william.liangf * @export */ public class DubboNamespaceHandler extends NamespaceHandlerSupport { static { Version.checkDuplicate(DubboNamespaceHandler.class); } public void init() { registerBeanDefinitionParser("application", newDubboBeanDefinitionParser(ApplicationConfig.class, true)); registerBeanDefinitionParser("module", newDubboBeanDefinitionParser(ModuleConfig.class, true)); registerBeanDefinitionParser("registry", newDubboBeanDefinitionParser(RegistryConfig.class, true)); registerBeanDefinitionParser("monitor", newDubboBeanDefinitionParser(MonitorConfig.class, true)); registerBeanDefinitionParser("provider",new DubboBeanDefinitionParser(ProviderConfig.class, true)); registerBeanDefinitionParser("consumer", newDubboBeanDefinitionParser(ConsumerConfig.class, true)); registerBeanDefinitionParser("protocol", newDubboBeanDefinitionParser(ProtocolConfig.class, true)); registerBeanDefinitionParser("service", newDubboBeanDefinitionParser(ServiceBean.class, true)); registerBeanDefinitionParser("reference", newDubboBeanDefinitionParser(ReferenceBean.class, false)); registerBeanDefinitionParser("annotation", newDubboBeanDefinitionParser(AnnotationBean.class, true)); } }
ReferenceBean的定義是這樣的:
public class ReferenceBean<T> extends ReferenceConfig<T>
implements FactoryBean,ApplicationContextAware, InitializingBean, DisposableBean
ReferenceBean實現了org.springframework.beans.factory.FactoryBean介面,因此ReferenceBean是一個FactoryBean,spring對FactoryBean不會直接構造他的實現類來作為代理類,而是通過FactoryBean的getObject()方法來獲得代理類。
ReferenceBean實現了org.springframework.beans.factory.InitializingBean介面,實現了這個介面的類需要實現afterPropertiesSet()方法,並且在初始化結束後呼叫該方法。dubbo的consumer的代理類也是在這個方法中生成。
ReferenceBean的afterPropertiesSet()方法是這樣的:
@SuppressWarnings({"unchecked"})
public void afterPropertiesSet() throwsException {
if (getConsumer() == null) {
Map<String, ConsumerConfig>consumerConfigMap = applicationContext == null ? null :BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext,ConsumerConfig.class, false, false);
if (consumerConfigMap != null&& consumerConfigMap.size() > 0) {
ConsumerConfig consumerConfig =null;
for (ConsumerConfig config :consumerConfigMap.values()) {
if (config.isDefault() ==null || config.isDefault().booleanValue()) {
if (consumerConfig !=null) {
throw newIllegalStateException("Duplicate consumer configs: " + consumerConfig+ " and " + config);
}
consumerConfig =config;
}
}
if (consumerConfig != null) {
setConsumer(consumerConfig);
}
}
}
if (getApplication() == null
&& (getConsumer() ==null || getConsumer().getApplication() == null)) {
Map<String,ApplicationConfig> applicationConfigMap = applicationContext == null ? null: BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext,ApplicationConfig.class, false, false);
if (applicationConfigMap != null&& applicationConfigMap.size() > 0) {
ApplicationConfigapplicationConfig = null;
for (ApplicationConfig config :applicationConfigMap.values()) {
if (config.isDefault() == null ||config.isDefault().booleanValue()) {
if (applicationConfig!= null) {
throw newIllegalStateException("Duplicate application configs: " +applicationConfig + " and " + config);
}
applicationConfig =config;
}
}
if (applicationConfig != null){
setApplication(applicationConfig);
}
}
}
if (getModule() == null
&& (getConsumer() ==null || getConsumer().getModule() == null)) {
Map<String, ModuleConfig>moduleConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext,ModuleConfig.class, false, false);
if (moduleConfigMap != null&& moduleConfigMap.size() > 0) {
ModuleConfig moduleConfig =null;
for (ModuleConfig config :moduleConfigMap.values()) {
if (config.isDefault() ==null || config.isDefault().booleanValue()) {
if (moduleConfig !=null) {
throw newIllegalStateException("Duplicate module configs: " + moduleConfig +" and " + config);
}
moduleConfig = config;
}
}
if (moduleConfig != null) {
setModule(moduleConfig);
}
}
}
if ((getRegistries() == null ||getRegistries().size() == 0)
&& (getConsumer() ==null || getConsumer().getRegistries() == null ||getConsumer().getRegistries().size() == 0)
&& (getApplication() ==null || getApplication().getRegistries() == null ||getApplication().getRegistries().size() == 0)) {
Map<String, RegistryConfig>registryConfigMap = applicationContext == null ? null :BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext,RegistryConfig.class, false, false);
if (registryConfigMap != null&& registryConfigMap.size() > 0) {
List<RegistryConfig>registryConfigs = new ArrayList<RegistryConfig>();
for (RegistryConfig config :registryConfigMap.values()) {
if (config.isDefault() ==null || config.isDefault().booleanValue()) {
registryConfigs.add(config);
}
}
if (registryConfigs != null&& registryConfigs.size() > 0) {
super.setRegistries(registryConfigs);
}
}
}
if (getMonitor() == null
&& (getConsumer() ==null || getConsumer().getMonitor() == null)
&& (getApplication() ==null || getApplication().getMonitor() == null)) {
Map<String, MonitorConfig>monitorConfigMap = applicationContext == null ? null :BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext,MonitorConfig.class, false, false);
if (monitorConfigMap != null&& monitorConfigMap.size() > 0) {
MonitorConfig monitorConfig =null;
for (MonitorConfig config :monitorConfigMap.values()) {
if (config.isDefault() ==null || config.isDefault().booleanValue()) {
if (monitorConfig !=null) {
throw newIllegalStateException("Duplicate monitor configs: " + monitorConfig +" and " + config);
}
monitorConfig = config;
}
}
if (monitorConfig != null) {
setMonitor(monitorConfig);
}
}
}
Boolean b = isInit();
if (b == null && getConsumer()!= null) {
b = getConsumer().isInit();
}
if (b != null &&b.booleanValue()) {
getObject();
}
}
主要是按照預設的配置初始化了一些屬性,最後呼叫了getObject()方法,得到代理類
getObject()方法:
public Object getObject() throws Exception {
return get();
}
get()方法在ReferenceBean的父類com.alibaba.dubbo.config.ReferenceConfig中:
public synchronized T get() {
if (this.destroyed) {
throw new IllegalStateException("Alreadydestroyed!");
}
if (this.ref == null) {
init();
}
return this.ref;
}
最後呼叫了init()方法:
private void init() {
if (initialized) {
return;
}
initialized = true;
if (interfaceName == null ||interfaceName.length() == 0) {
throw newIllegalStateException("<dubbo:reference interface=\"\" />interface not allow null!");
}
// 獲取消費者全域性配置
checkDefault();
appendProperties(this);
if (getGeneric() == null &&getConsumer() != null) {
setGeneric(getConsumer().getGeneric());
}
if (ProtocolUtils.isGeneric(getGeneric())){
interfaceClass =GenericService.class;
} else {
try {
interfaceClass =Class.forName(interfaceName, true, Thread.currentThread()
.getContextClassLoader());
} catch (ClassNotFoundException e){
throw newIllegalStateException(e.getMessage(), e);
}
checkInterfaceAndMethods(interfaceClass, methods);
}
String resolve =System.getProperty(interfaceName);
String resolveFile = null;
if (resolve == null || resolve.length()== 0) {
resolveFile =System.getProperty("dubbo.resolve.file");
if (resolveFile == null ||resolveFile.length() == 0) {
File userResolveFile = newFile(new File(System.getProperty("user.home")),"dubbo-resolve.properties");
if (userResolveFile.exists()) {
resolveFile =userResolveFile.getAbsolutePath();
}
}
if (resolveFile != null &&resolveFile.length() > 0) {
Properties properties = newProperties();
FileInputStream fis = null;
try {
fis = newFileInputStream(new File(resolveFile));
properties.load(fis);
} catch (IOException e) {
throw newIllegalStateException("Unload " + resolveFile + ", cause: "+ e.getMessage(), e);
} finally {
try {
if (null != fis)fis.close();
} catch (IOException e) {
logger.warn(e.getMessage(), e);
}
}
resolve =properties.getProperty(interfaceName);
}
}
if (resolve != null &&resolve.length() > 0) {
url = resolve;
if (logger.isWarnEnabled()) {
if (resolveFile != null&& resolveFile.length() > 0) {
logger.warn("Usingdefault dubbo resolve file " + resolveFile + " replace " +interfaceName + "" + resolve + " to p2p invoke remoteservice.");
} else {
logger.warn("Using -D"+ interfaceName + "=" + resolve + " to p2p invoke remoteservice.");
}
}
}
if (consumer != null) {
if (application == null) {
application =consumer.getApplication();
}
if (module == null) {
module = consumer.getModule();
}
if (registries == null) {
registries =consumer.getRegistries();
}
if (monitor == null) {
monitor =consumer.getMonitor();
}
}
if (module != null) {
if (registries == null) {
registries =module.getRegistries();
}
if (monitor == null) {
monitor = module.getMonitor();
}
}
if (application != null) {
if (registries == null) {
registries =application.getRegistries();
}
if (monitor == null) {
monitor = application.getMonitor();
}
}
checkApplication();
checkStubAndMock(interfaceClass);
Map<String, String> map = newHashMap<String, String>();
Map<Object, Object> attributes =new HashMap<Object, Object>();
map.put(Constants.SIDE_KEY,Constants.CONSUMER_SIDE);
map.put(Constants.DUBBO_VERSION_KEY,Version.getVersion());
map.put(Constants.TIMESTAMP_KEY,String.valueOf(System.currentTimeMillis()));
if (ConfigUtils.getPid() > 0) {
map.put(Constants.PID_KEY,String.valueOf(ConfigUtils.getPid()));
}
if (!isGeneric()) {
String revision =Version.getVersion(interfaceClass, version);
if (revision != null &&revision.length() > 0) {
map.put("revision",revision);
}
String[] methods =Wrapper.getWrapper(interfaceClass).getMethodNames();
if (methods.length == 0) {
logger.warn("NO methodfound in service interface " + interfaceClass.getName());
map.put("methods",Constants.ANY_VALUE);
} else {
map.put("methods",StringUtils.join(new HashSet<String>(Arrays.asList(methods)),","));
}
}
map.put(Constants.INTERFACE_KEY,interfaceName);
appendParameters(map, application);
appendParameters(map, module);
appendParameters(map, consumer,Constants.DEFAULT_KEY);
appendParameters(map, this);
String prifix =StringUtils.getServiceKey(map);
if (methods != null &&methods.size() > 0) {
for (MethodConfig method : methods){
appendParameters(map, method,method.getName());
String retryKey =method.getName() + ".retry";
if (map.containsKey(retryKey)){
String retryValue =map.remove(retryKey);
if("false".equals(retryValue)) {
map.put(method.getName()+ ".retries", "0");
}
}
appendAttributes(attributes,method, prifix + "." + method.getName());
checkAndConvertImplicitConfig(method, map, attributes);
}
}
//attributes通過系統context進行儲存.
StaticContext.getSystemContext().putAll(attributes);
ref = createProxy(map);
}
init()方法把配置檔案中的配置項經過判斷和拼裝,最後全放到了一個map中,最後呼叫createProxy()方法來生成代理類:
@SuppressWarnings({"unchecked","rawtypes", "deprecation"})
private T createProxy(Map<String,String> map) {
URL tmpUrl = new URL("temp","localhost", 0, map);
final boolean isJvmRefer;
if (isInjvm() == null) {
if (url != null &&url.length() > 0) { //指定URL的情況下,不做本地引用
isJvmRefer = false;
} else if(InjvmProtocol.getInjvmProtocol().isInjvmRefer(tmpUrl)) {
//預設情況下如果本地有服務暴露,則引用本地服務.
isJvmRefer = true;
} else {
isJvmRefer = false;
}
} else {
isJvmRefer =isInjvm().booleanValue();
}
if (isJvmRefer) {
URL url = newURL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0,interfaceClass.getName()).addParameters(map);
invoker = refprotocol.refer(interfaceClass,url);
if (logger.isInfoEnabled()) {
logger.info("Using injvmservice " + interfaceClass.getName());
}
} else {
if (url != null &&url.length() > 0) { // 使用者指定URL,指定的URL可能是對點對直連地址,也可能是註冊中心URL
String[] us =Constants.SEMICOLON_SPLIT_PATTERN.split(url);
if (us != null &&us.length > 0) {
for (String u : us) {
URL url =URL.valueOf(u);
if (url.getPath() ==null || url.getPath().length() == 0) {
url =url.setPath(interfaceName);
}
if(Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
urls.add(url.addParameterAndEncoded(Constants.REFER_KEY,StringUtils.toQueryString(map)));
} else {
urls.add(ClusterUtils.mergeUrl(url, map));
}
}
}
} else { // 通過註冊中心配置拼裝URL
List<URL> us =loadRegistries(false);
if (us != null &&us.size() > 0) {
for (URL u : us) {
URL monitorUrl =loadMonitor(u);
if (monitorUrl != null){
map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
}
urls.add(u.addParameterAndEncoded(Constants.REFER_KEY,StringUtils.toQueryString(map)));
}
}
if (urls == null || urls.size()== 0) {
throw newIllegalStateException("No such any registry to reference " +interfaceName + " on the consumer " + NetUtils.getLocalHost() +" use dubbo version " + Version.getVersion() + ", please config<dubbo:registry address=\"...\" /> to your springconfig.");
}
}
if (urls.size() == 1) {
invoker =refprotocol.refer(interfaceClass, urls.get(0));
} else {
List<Invoker<?>>invokers = new ArrayList<Invoker<?>>();
URL registryURL = null;
for (URL url : urls) {
invokers.add(refprotocol.refer(interfaceClass,url));
if(Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
registryURL = url; // 用了最後一個registry url
}
}
if (registryURL != null) { // 有 註冊中心協議的URL
// 對有註冊中心的Cluster 只用AvailableCluster
URL u =registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
invoker = cluster.join(newStaticDirectory(u, invokers));
} else { // 不是 註冊中心的URL
invoker = cluster.join(newStaticDirectory(invokers));
}
}
}
Boolean c = check;
if (c == null && consumer != null){
c = consumer.isCheck();
}
if (c == null) {
c = true; // default true
}
if (c &&!invoker.isAvailable()) {
throw newIllegalStateException("Failed to check the status of the service " +interfaceName + ". No provider available for the service " + (group== null ? "" : group + "/") + interfaceName + (version ==null ? "" : ":" + version) + " from the url " +invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() +" use dubbo version " + Version.getVersion());
}
if (logger.isInfoEnabled()) {
logger.info("Refer dubboservice " + interfaceClass.getName() + " from url " +invoker.getUrl());
}
// 建立服務代理
return (T)proxyFactory.getProxy(invoker);
}
createProxy()方法載入了和註冊中心有關的配置,其中還區分了是否是本地jvm的服務,如果本地有服務,那就直接用本地的。
最後呼叫proxyFactory.getProxy(this.invoker)方法來生成代理類
proxyFactory是com.alibaba.dubbo.rpc.ProxyFactory介面的實現類,預設由com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory類來實現,他的getProxy()方法來自他的父類com.alibaba.dubbo.rpc.proxy.AbstractProxyFactory:
public <T> TgetProxy(Invoker<T> invoker) throws RpcException {
Class<?>[] interfaces = null;
String config = invoker.getUrl().getParameter("interfaces");
if (config != null &&config.length() > 0) {
String[] types =Constants.COMMA_SPLIT_PATTERN.split(config);
if (types != null &&types.length > 0) {
interfaces = newClass<?>[types.length + 2];
interfaces[0] =invoker.getInterface();
interfaces[1] =EchoService.class;
for (int i = 0; i <types.length; i++) {
interfaces[i + 1] =ReflectUtils.forName(types[i]);
}
}
}
if (interfaces == null) {
interfaces = newClass<?>[]{invoker.getInterface(), EchoService.class};
}
return getProxy(invoker, interfaces);
}
最後的getProxy()方法在AbstractProxyFactory中是個抽象方法,實現的方法在子類JavassistProxyFactory中:
@SuppressWarnings("unchecked")
public <T> TgetProxy(Invoker<T> invoker, Class<?>[] interfaces) {
return (T)Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}
這裡生成了代理類並建立了代理物件,可以在程式碼中引用,比如這樣:
public class OrderController {
@Autowired
private OrderService orderService;
public void testDubboService() throws Exception{
orderService.selectByOrderId();
}
}
相關推薦
dubbo的consumer代理的生成方式原始碼
dubbo的consumer只需要在配置檔案中配置一個介面的地址,並不需要這個介面有具體的實現類,就可以生成這個介面的代理,通過代理遠端呼叫provider中的方法。 和JDK還有CGlib不同,dubbo的consumer生成代理是通過dubbo中的一個com.aliba
dubbo原始碼分析-consumer端1-consumer代理生成
dubbo(官網地址)是一個分散式服務框架,致力於提供高效能和透明化的RPC遠端服務呼叫方案,是阿里巴巴SOA服務化治理方案的核心框架。目前,阿里巴巴內部已經不再使用dubbo,但對很對未到一定量級的公司來說,dubbo依然是一個很好的選擇。
AOP代理物件生成方式
1 AbstractAutoProxyCreator 類分析 /** * Create a proxy with the
數據庫主鍵生成方式 轉http://blog.csdn.net/w183705952/article/details/7102920
digg .com 表示 加鎖 -m 之間 sequence 權力 ont 1) assigned主鍵由外部程序負責生成,無需Hibernate參與。2) hilo通過hi/lo 算法實現的主鍵生成機制,需要額外的數據庫表保存主鍵生成歷史狀態。3) seqhilo與hilo
hibernate 主鍵生成方式
程序 操作 外部 解鎖 數據庫表 body 要求 讀寫 字符串表 1)assigned主鍵由外部程序負責生成,無需Hibernate參與。2)hilo通過hi/lo 算法實現的主鍵生成機制,需要額外的數據庫表保存主鍵生成歷史狀態。3)seqhilo與hilo 類似,通過hi
python時間生成方式
%x ear 程序 sta 指定 utc時間 一個 utc 大小 程序 第一版 #coding=utf-8 import time def get_year_mon_day(): time_array = time.localtime() result= u
獲取動態代理生成的.class文件
clas htm 插入 www. syntax 父類 系統 實現類 error 生成代理類,並寫入硬盤:配置系統屬性sun.misc.ProxyGenerator.saveGeneratedFile為true,代理類生成時將自動將生成的代理類寫入硬盤 ? 1 2
webservice客戶端生成方式
IE ide lib 自動 返回 client idea pro 選項 1.idea中File-new-Project 在彈出框中選擇java選項,選擇WebServices Client, 2.選擇Use library: 在本地中找到相關的lib包使用 (1)axi
Maven基礎(3):代理設定方式
在很多場景下,maven不能直接訪問到外網時,使用代理是其中常見的一種方式。這篇文章整理一下常見的maven中設定代理的方法。 代理伺服器 代理伺服器的搭建,可以使用nginx,或者更為專業一點的使用squid,輕鬆實現http和https方式。具體方式可以參看如下文章:
嵌入式裝置LCD模組漢字型檔檔案生成方式
近期有專案需要用的LCD顯示沐足顯示一些中文,對比了下帶字型檔的模組要比不帶字型檔的模組要貴得多,想想那就自己建立字型檔吧,能剩下不少費用,再說裝置內部的FLASH大把的容量,不利用也有點浪費了。 下文轉載自:http://www.rationmcu.com/elecjc/356.html
keras實現DCGAN生成mnist原始碼
演算法原理 具體演算法原理,在這裡不再闡述,可以參考: https://blog.csdn.net/stdcoutzyx/article/details/53872121 https://ask.julyedu.com/question/7681 基於keras的原始碼 fr
子集生成方式
1.增量構造法 void print_subset(int n, int* A, int cur) { for (int i = 0; i < cur; i++)printf("%d ", A[i]);//列印當前集合 printf("\n"); int s = cur ? A[cu
C#——後臺管理端多級選單的生成方式
現在大多的應用程式(後端)都有選單,不管是在左側還是上面,介面基本都是基於現有的UI框架或者網上找的單獨的選單的外掛。而實現的方式,大部分都是使用的AJAX形式來完成。前段時間,在總結一些過往的專案時,發現了一些不同的實現的方式,這裡想總結一下。所以也就有了這篇文章~~ 我建立的測試Demo
以太坊HD錢包地址生成方式一
使用nodeJs生成以太坊HD錢包地址 程式碼 var bip39 = require('bip39') var hdkey = require('ethereumjs-wallet/hdkey') var util = require('ethereumjs-util') v
【轉】支付寶 二維碼/轉賬碼/生成方式,突破二維碼生成數量的限制
支付寶收款的幾種方式: 通過xposed 設定金額/備註.然後可以得到一張二維碼.這是傳統的方式. 通過支付寶的介面,自己拼接字串.然後根據字串生成一個二維碼對於 對於傳統方式.支付寶限制了一天二維碼的生成數量.在這之間簡直是好用得不要不要得.但是突然間支付寶爸爸說.你搞那麼多二維碼幹啥.還
CGLIB動態代理示例與原始碼解析
1. 原理,代理模式 代理模式的本質:呼叫方--->代理方--->實現方。 1.1 動態代理模式步驟 生成代理類二進位制位元組碼,可配置引數生成檔案。 classloader load二進位制位元組碼,生成Class物件( 可使
同學們自行調研Linux下軟體安裝的幾種方式(原始碼安裝, rpm安裝, yum安裝). 重點要理解rpm安裝和yum安裝的區別.
一、原始碼安裝 原始碼安裝的步驟就是:編譯原始碼,然後安裝 1.1 安裝準備 a. 首先用yum安裝gcc,用於編譯原始碼 b. 官網下載原始碼包 c. winSCP windows與linux傳輸檔案(如果直接用要安裝軟體的linux電腦下載就不用傳輸了) 1
Mybatis 學習筆記(三)——使用Mapper代理的方式實現資料增刪改查
一、介紹 Mapper代理的方式只需要程式設計師編寫 Mapper.xml 檔案及 Mapper介面。 本文是基於上一篇文章:Mybatis 學習筆記——原生DAO實現資料增刪改查,所以接下來的內容是以其為基礎的,如果有什麼不懂的請留言或檢視上一篇。所
支付寶免籤不可改備註(支付寶 二維碼/轉賬碼/生成方式,微信收款維碼)
支付寶 二維碼/轉賬碼/生成方式,微信收款維碼 先介紹下支付寶收款的幾種方式: 通過xposed 設定金額/備註.然後生成二維碼.(受支付寶限制一天二維碼的生成數量20個) 通過支付寶的介面,自己拼接字串.然後根據字串生成一個二維碼對於(可改備註,使用者改備註則會掉單) 下面就是我們的改備註
支付寶免籤、解決最新版支付寶10.1.50拉起問題!(支付寶 二維碼/轉賬碼/生成方式,微信收款維碼)
已解決最新版支付寶(10.1.50)部分機型拉不起支付寶問題。 微信,支付寶,收款二維碼實時生成,訂單監控,免籤支,付支付系統,個人收款,收款二維碼 微信和支付寶個人支付二維碼生成與監控!有JAVA介面回撥,個人收款好助手! 實現收款即時到個人微信或支付寶賬戶!方便安全。 這