Spring占位符解析器
Posted 骆驼整理说
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring占位符解析器相关的知识,希望对你有一定的参考价值。
目录
Spring PropertyPlaceholderHelper
Spring AnnotationFormatterFactory
Spring5 新特性spring.components解析
Spring PropertyPlaceholderHelper
类全路径: org.springframework.util.PropertyPlaceholderHelper
parseStringValue
org.springframework.util.PropertyPlaceholderHelper#parseStringValue
这个是主要方法
protected String parseStringValue(
String value, PlaceholderResolver placeholderResolver, @Nullable Set<String> visitedPlaceholders)
// 占位符所在位置
int startIndex = value.indexOf(this.placeholderPrefix);
if (startIndex == -1)
return value;
// 返回值
StringBuilder result = new StringBuilder(value);
while (startIndex != -1)
// 寻找结尾占位符
int endIndex = findPlaceholderEndIndex(result, startIndex);
if (endIndex != -1)
// 返回值切分留下中间内容
String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
String originalPlaceholder = placeholder;
if (visitedPlaceholders == null)
visitedPlaceholders = new HashSet<>(4);
if (!visitedPlaceholders.add(originalPlaceholder))
throw new IllegalArgumentException(
"Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
// Recursive invocation, parsing placeholders contained in the placeholder key.
// 递归获取占位符内容
placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
// Now obtain the value for the fully resolved key...
// 解析占位符内容获得真正的属性值
String propVal = placeholderResolver.resolvePlaceholder(placeholder);
if (propVal == null && this.valueSeparator != null)
int separatorIndex = placeholder.indexOf(this.valueSeparator);
if (separatorIndex != -1)
String actualPlaceholder = placeholder.substring(0, separatorIndex);
String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
if (propVal == null)
propVal = defaultValue;
if (propVal != null)
// Recursive invocation, parsing placeholders contained in the
// previously resolved placeholder value.
propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
if (logger.isTraceEnabled())
logger.trace("Resolved placeholder '" + placeholder + "'");
startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
else if (this.ignoreUnresolvablePlaceholders)
// Proceed with unprocessed value.
startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
else
throw new IllegalArgumentException("Could not resolve placeholder '" +
placeholder + "'" + " in value \\"" + value + "\\"");
visitedPlaceholders.remove(originalPlaceholder);
else
startIndex = -1;
return result.toString();
占位符解析
@FunctionalInterface
public interface PlaceholderResolver
/**
* Resolve the supplied placeholder name to the replacement value.
* @param placeholderName the name of the placeholder to resolve
* @return the replacement value, or @code null if no replacement is to be made
*/
@Nullable
String resolvePlaceholder(String placeholderName);
findPlaceholderEndIndex
/**
* 寻找结尾占位符索引
*/
private int findPlaceholderEndIndex(CharSequence buf, int startIndex)
int index = startIndex + this.placeholderPrefix.length();
int withinNestedPlaceholder = 0;
while (index < buf.length())
if (StringUtils.substringMatch(buf, index, this.placeholderSuffix))
if (withinNestedPlaceholder > 0)
withinNestedPlaceholder--;
index = index + this.placeholderSuffix.length();
else
return index;
else if (StringUtils.substringMatch(buf, index, this.simplePrefix))
withinNestedPlaceholder++;
index = index + this.simplePrefix.length();
else
index++;
return -1;
Spring AnnotationFormatterFactory
类全路径: org.springframework.format.AnnotationFormatterFactory
public interface AnnotationFormatterFactory<A extends Annotation>
/**
* The types of fields that may be annotated with the <A> annotation.
* 字段类型
*/
Set<Class<?>> getFieldTypes();
/**
* Get the Printer to print the value of a field of @code fieldType annotated with
* @code annotation.
* <p>If the type T the printer accepts is not assignable to @code fieldType, a
* coercion from @code fieldType to T will be attempted before the Printer is invoked.
* 通过注解和字段类型获取输出接口
* @param annotation the annotation instance
* @param fieldType the type of field that was annotated
* @return the printer
*/
Printer<?> getPrinter(A annotation, Class<?> fieldType);
/**
* Get the Parser to parse a submitted value for a field of @code fieldType
* annotated with @code annotation.
* <p>If the object the parser returns is not assignable to @code fieldType,
* a coercion to @code fieldType will be attempted before the field is set.
* 通过注解和字段类型获取解析接口
* @param annotation the annotation instance
* @param fieldType the type of field that was annotated
* @return the parser
*/
Parser<?> getParser(A annotation, Class<?> fieldType);
Spring Printer
类全路径: org.springframework.format.Printer
类作用: 对象转换成字符串
@FunctionalInterface
public interface Printer<T>
/**
* Print the object of type T for display.
* 打印对象
* @param object the instance to print
* @param locale the current user locale
* @return the printed text string
*/
String print(T object, Locale locale);
Spring5 新特性spring.components解析
相关类: org.springframework.context.index.CandidateComponentsIndexLoader
测试用例: org.springframework.context.annotation.ClassPathScanningCandidateComponentProviderTests.defaultsWithIndex
,org.springframework.context.index.CandidateComponentsIndexLoaderTests
CandidateComponentsIndexLoader
全文搜索spring.components
使用介绍
下面是从resources/example/scannable/spring.components
测试用例中复制过来的,从中可以发现等号左侧放的是我们写的组件,等号右边是属于什么组件
example.scannable.AutowiredQualifierFooService=example.scannable.FooService
example.scannable.DefaultNamedComponent=org.springframework.stereotype.Component
example.scannable.NamedComponent=org.springframework.stereotype.Component
example.scannable.FooService=example.scannable.FooService
example.scannable.FooServiceImpl=org.springframework.stereotype.Component,example.scannable.FooService
example.scannable.ScopedProxyTestBean=example.scannable.FooService
example.scannable.StubFooDao=org.springframework.stereotype.Component
example.scannable.NamedStubDao=org.springframework.stereotype.Component
example.scannable.ServiceInvocationCounter=org.springframework.stereotype.Component
example.scannable.sub.BarComponent=org.springframework.stereotype.Component
org.springframework.context.index.CandidateComponentsIndexLoader.loadIndex
@Nullable
public static CandidateComponentsIndex loadIndex(@Nullable ClassLoader classLoader)
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null)
classLoaderToUse = CandidateComponentsIndexLoader.class.getClassLoader();
return cache.computeIfAbsent(classLoaderToUse, CandidateComponentsIndexLoader::doLoadIndex);
/**
* 解析 META-INF/spring.components 文件
* @param classLoader
* @return
*/
@Nullable
private static CandidateComponentsIndex doLoadIndex(ClassLoader classLoader)
if (shouldIgnoreIndex)
return null;
try
Enumeration<URL> urls = classLoader.getResources(COMPONENTS_RESOURCE_LOCATION);
if (!urls.hasMoreElements())
return null;
List<Properties> result = new ArrayList<>();
while (urls.hasMoreElements())
URL url = urls.nextElement();
// 读取META-INF/spring.components文件转换成map对象
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
result.add(properties);
if (logger.isDebugEnabled())
logger.debug("Loaded " + result.size() + "] index(es)");
int totalCount = result.stream().mapToInt(Properties::size).sum();
// 查看CandidateComponentsIndex方法
return (totalCount > 0 ? new CandidateComponentsIndex(result) : null);
catch (IOException ex)
throw new IllegalStateException("Unable to load indexes from location [" +
COMPONENTS_RESOURCE_LOCATION + "]", ex);
CandidateComponentsIndex(List<Properties> content)
this.index = parseIndex(content);
/**
* 解析 MATE-INF\\spring.components 转换成 map
*
* @param content
* @return
*/
private static MultiValueMap<String, Entry> parseIndex(List<Properties> content)
MultiValueMap<String, Entry> index = new LinkedMultiValueMap<>();
for (Properties entry : content)
entry.forEach((type, values) ->
String[] stereotypes = ((String) values).split(",");
for (String stereotype : stereotypes)
index.add(stereotype, new Entry((String) type));
);
return index;
该类给org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.findCandidateComponents
提供了帮助
public Set<BeanDefinition> findCandidateComponents(String basePackage)
// 扫描
/**
* if 测试用例: @link org.springframework.context.annotation.ClassPathScanningCandidateComponentProviderTests#defaultsWithIndex()
* 解析 spring.components文件
*/
if (this.componentsIndex != null && indexSupportsIncludeFilters())
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
else
return scanCandidateComponents(basePackage);
Spring RMI
服务提供方
服务提供方需要准备接口、接口实现
public interface IDemoRmiService
int add(int a, int b);
public class IDemoRmiServiceImpl implements IDemoRmiService
@Override
public int add(int a, int b)
return a + b;
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="demoRmiService" class="com.huifer.source.spring.rmi.impl.IDemoRmiServiceImpl"/>
<bean id="demoRmi" class="org.springframework.remoting.rmi.RmiServiceExporter">
<!-- 服务-->
<property name="service" ref="demoRmiService"/>
<!-- 服务名称-->
<property name="serviceName" value="springRmi"/>
<!-- 服务接口-->
<property name="serviceInterface" value="com.huifer.source.spring.rmi.IDemoRmiService"/>
<!-- 注册端口-->
<property name="registryPort" value="9999"/>
</bean>
</beans>
public class RMIServerSourceCode
public static void main(String[] args) throws Exception
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("rmi/RMISourceCode.xml");
服务调用方
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="rmiClient" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
<property name="serviceUrl" value="rmi://localhost:9999/springRmi"/>
<property name="serviceInterface" value="com.huifer.source.spring.rmi.IDemoRmiService"/>
</bean>
</beans>
rmi 使用小写 大写报错信息
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'rmiClient' defined in class path resource [rmi/RMIClientSourceCode.xml]: Invocation of init method failed; nested exception is org.springframework.remoting.RemoteLookupFailureException: Service URL [RMI://localhost:9999/springRmi] is invalid; nested exception is java.net.MalformedURLException: invalid URL scheme: RMI://localhost:9999/springRmi at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1795) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:559) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:478) at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:309) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:272) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:306) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:176) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:877) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:953) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:579) at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:163) at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:90) at com.huifer.source.spring.rmi.RMIClientSourceCode.main(RMIClientSourceCode.java:7) Caused by: org.springframework.remoting.RemoteLookupFailureException: Service URL [RMI://localhost:9999/springRmi] is invalid; nested exception is java.net.MalformedURLException: invalid URL scheme: RMI://localhost:9999/springRmi at org.springframework.remoting.rmi.RmiClientInterceptor.lookupStub(RmiClientInterceptor.java:209) at org.springframework.remoting.rmi.RmiClientInterceptor.prepare(RmiClientInterceptor.java:147) at org.springframework.remoting.rmi.RmiClientInterceptor.afterPropertiesSet(RmiClientInterceptor.java:134) at org.springframework.remoting.rmi.RmiProxyFactoryBean.afterPropertiesSet(RmiProxyFactoryBean.java:68) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1868) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1791) ... 12 more Caused by: java.net.MalformedURLException: invalid URL scheme: RMI://localhost:9999/springRmi at java.rmi.Naming.intParseURL(Naming.java:290) at java.rmi.Naming.parseURL(Naming.java:237) at java.rmi.Naming.lookup(Naming.java:96) at org.springframework.remoting.rmi.RmiClientInterceptor.lookupStub(RmiClientInterceptor.java:201) ... 17 more
运行
public class RMIClientSourceCode
public static void main(String[] args)
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("rmi/RMIClientSourceCode.xml");
IDemoRmiService rmiClient = context.getBean("rmiClient", IDemoRmiService.class);
int add = rmiClient.add(1, 2);
System.out.println(add);
服务端初始化
org.springframework.remoting.rmi.RmiServiceExporter
,该类定义了 spring 的远程服务信息
RmiServiceExporter
基础属性如下
public class RmiServiceExporter extends RmiBasedExporter implements InitializingBean, DisposableBean
/**
* 服务名称,rmi的服务名,在Spring中使用ref指向impl实现类
*/
private String serviceName;
/**
*
*/
private int servicePort = 0; // anonymous port
private RMIClientSocketFactory clientSocketFactory;
private RMIServerSocketFactory serverSocketFactory;
private Registry registry;
/**
* 注册的IP地址
*/
private String registryHost;
/**
* 注册的端口
*/
private int registryPort = Registry.REGISTRY_PORT;
/**
* 进行远程对象调用的Socket工厂
*/
private RMIClientSocketFactory clientSocketFactory;
/**
* 接收远程调用服务端的Socket工厂
*/
private RMIServerSocketFactory serverSocketFactory;
/**
* true: 该参数在获取@link Registry时测试是否建立连接,如果建立连接则直接使用,否则创建新连接
* 是否总是创建@link Registry
*/
private boolean alwaysCreateRegistry = false;
/**
* 设置替换RMI注册表中的绑定关系
*/
private boolean replaceExistingBinding = true;
private Remote exportedObject;
/**
* 创建@link Registry
*/
private boolean createdRegistry = false;
初始化bean调用afterPropertiesSet
方法DisposableBean: 摧毁bean调用destroy
方法
@Override
public void afterPropertiesSet() throws RemoteException
prepare();
public void prepare() throws RemoteException
// 校验service
checkService();
// 服务名称校验
if (this.serviceName == null)
throw new IllegalArgumentException("Property 'serviceName' is required");
// Check socket factories for exported object.
if (this.clientSocketFactory instanceof RMIServerSocketFactory)
this.serverSocketFactory = (RMIServerSocketFactory) this.clientSocketFactory;
if ((this.clientSocketFactory != null && this.serverSocketFactory == null) ||
(this.clientSocketFactory == null && this.serverSocketFactory != null))
throw new IllegalArgumentException(
"Both RMIClientSocketFactory and RMIServerSocketFactory or none required");
// Check socket factories for RMI registry.
if (this.registryClientSocketFactory instanceof RMIServerSocketFactory)
this.registryServerSocketFactory = (RMIServerSocketFactory) this.registryClientSocketFactory;
if (this.registryClientSocketFactory == null && this.registryServerSocketFactory != null)
throw new IllegalArgumentException(
"RMIServerSocketFactory without RMIClientSocketFactory for registry not supported");
this.createdRegistry = false;
// Determine RMI registry to use.
if (this.registry == null)
// registry 获取
this.registry = getRegistry(this.registryHost, this.registryPort,
this.registryClientSocketFactory, this.registryServerSocketFactory);
this.createdRegistry = true;
// Initialize and cache exported object.
// 初始化并且获取缓存的object
this.exportedObject = getObjectToExport();
if (logger.isDebugEnabled())
logger.debug("Binding service '" + this.serviceName + "' to RMI registry: " + this.registry);
// Export RMI object.
// 导出RMI object
if (this.clientSocketFactory != null)
UnicastRemoteObject.exportObject(
this.exportedObject, this.servicePort, this.clientSocketFactory, this.serverSocketFactory);
else
UnicastRemoteObject.exportObject(this.exportedObject, this.servicePort);
// Bind RMI object to registry.
// 对象绑定,把serviceName 和 到处的RMI对象进行绑定,JDK实现
try
if (this.replaceExistingBinding)
this.registry.rebind(this.serviceName, this.exportedObject);
else
this.registry.bind(this.serviceName, this.exportedObject);
catch (AlreadyBoundException ex)
// Already an RMI object bound for the specified service name...
unexportObjectSilently();
throw new IllegalStateException(
"Already an RMI object bound for name '" + this.serviceName + "': " + ex.toString());
catch (RemoteException ex)
// Registry binding failed: let's unexport the RMI object as well.
unexportObjectSilently();
throw ex;
checkService,校验service是否为空
/**
* Check whether the service reference has been set.
*
* 校验service
* @see #setService
*/
protected void checkService() throws IllegalArgumentException
Assert.notNull(getService(), "Property 'service' is required");
getRegistry获取Registry实例简单说就是通过host和port创建socket
protected Registry getRegistry(String registryHost, int registryPort,
@Nullable RMIClientSocketFactory clientSocketFactory, @Nullable RMIServerSocketFactory serverSocketFactory)
throws RemoteException
if (registryHost != null)
// Host explicitly specified: only lookup possible.
if (logger.isDebugEnabled())
logger.debug("Looking for RMI registry at port '" + registryPort + "' of host [" + registryHost + "]");
// 通过 host port socket创建
Registry reg = LocateRegistry.getRegistry(registryHost, registryPort, clientSocketFactory);
testRegistry(reg);
return reg;
else
return getRegistry(registryPort, clientSocketFactory, serverSocketFactory);
getObjectToExport
初始化并且获取缓存的 object
protected Remote getObjectToExport()
// determine remote object
if (getService() instanceof Remote &&
(getServiceInterface() == null || Remote.class.isAssignableFrom(getServiceInterface())))
// conventional RMI service
// 获取service
return (Remote) getService();
else
// RMI invoker
if (logger.isDebugEnabled())
logger.debug("RMI service [" + getService() + "] is an RMI invoker");
// RMI 包装类
/**
* 1. getProxyForService() 获取代理的接口
* 2. 创建RMI包装对象 RmiInvocationWrapper
*/
return new RmiInvocationWrapper(getProxyForService(), this);
getProxyForService
获取代理服务
protected Object getProxyForService()
// service 校验
checkService();
// 校验接口
checkServiceInterface();
// 代理工厂
ProxyFactory proxyFactory = new ProxyFactory();
// 添加代理接口
proxyFactory.addInterface(getServiceInterface());
if (this.registerTraceInterceptor != null ? this.registerTraceInterceptor : this.interceptors == null)
// 添加切面
proxyFactory.addAdvice(new RemoteInvocationTraceInterceptor(getExporterName()));
if (this.interceptors != null)
AdvisorAdapterRegistry adapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();
for (Object interceptor : this.interceptors)
proxyFactory.addAdvisor(adapterRegistry.wrap(interceptor));
// 设置代理类
proxyFactory.setTarget(getService());
proxyFactory.setOpaque(true);
// 获取代理对象
return proxyFactory.getProxy(getBeanClassLoader());
RmiInvocationWrapper
的invoke
方法会在调用方调用接口时候触发
rebind和bind,绑定和重新绑定,这部分源码在:sun.rmi.registry.RegistryImpl
public void rebind(String var1, Remote var2) throws RemoteException, AccessException
checkAccess("Registry.rebind");
this.bindings.put(var1, var2);
public void bind(String var1, Remote var2) throws RemoteException, AlreadyBoundException, AccessException
checkAccess("Registry.bind");
synchronized(this.bindings)
Remote var4 = (Remote)this.bindings.get(var1);
if (var4 != null)
throw new AlreadyBoundException(var1);
else
this.bindings.put(var1, var2);
unexportObjectSilently
出现异常时候调用方法
private void unexportObjectSilently()
try
UnicastRemoteObject.unexportObject(this.exportedObject, true);
catch (NoSuchObjectException ex)
if (logger.isInfoEnabled())
logger.info("RMI object for service '" + this.serviceName + "' is not exported anymore", ex);
客户端初始化
RmiProxyFactoryBean
该类实现了InitializingBean
接口直接看afterPropertiesSet
方法
org.springframework.remoting.rmi.RmiProxyFactoryBean
@Override
public void afterPropertiesSet()
super.afterPropertiesSet();
Class<?> ifc = getServiceInterface();
Assert.notNull(ifc, "Property 'serviceInterface' is required");
this.serviceProxy = new ProxyFactory(ifc, this).getProxy(getBeanClassLoader());
org.springframework.remoting.rmi.RmiClientInterceptor#afterPropertiesSet
@Override
public void afterPropertiesSet()
super.afterPropertiesSet();
prepare();
org.springframework.remoting.support.UrlBasedRemoteAccessor#afterPropertiesSet
@Override
public void afterPropertiesSet()
if (getServiceUrl() == null)
throw new IllegalArgumentException("Property 'serviceUrl' is required");
org.springframework.remoting.support.UrlBasedRemoteAccessor#afterPropertiesSet
该方法对serviceUrl
进行空判断,如果是空的抛出异常
/**
* 判断服务地址是否为空
*/
@Override
public void afterPropertiesSet()
if (getServiceUrl() == null)
throw new IllegalArgumentException("Property 'serviceUrl' is required");
org.springframework.remoting.rmi.RmiClientInterceptor#afterPropertiesSet
@Override
public void afterPropertiesSet()
super.afterPropertiesSet();
prepare();
调用父类的afterPropertiesSet
方法判断serviceUrl
是否为空
执行prepare()
方法
public void prepare() throws RemoteLookupFailureException
// Cache RMI stub on initialization?
if (this.lookupStubOnStartup)
// 获取remote对象
Remote remoteObj = lookupStub();
if (logger.isDebugEnabled())
if (remoteObj instanceof RmiInvocationHandler)
logger.debug("RMI stub [" + getServiceUrl() + "] is an RMI invoker");
else if (getServiceInterface() != null)
// 是否接口
boolean isImpl = getServiceInterface().isInstance(remoteObj);
logger.debug("Using service interface [" + getServiceInterface().getName() +
"] for RMI stub [" + getServiceUrl() + "] - " +
(!isImpl ? "not " : "") + "directly implemented");
if (this.cacheStub)
this.cachedStub = remoteObj;
org.springframework.remoting.rmi.RmiClientInterceptor#lookupStub
protected Remote lookupStub() throws RemoteLookupFailureException
try
Remote stub = null;
if (this.registryClientSocketFactory != null)
// RMIClientSocketFactory specified for registry access.
// Unfortunately, due to RMI API limitations, this means
// that we need to parse the RMI URL ourselves and perform
// straight LocateRegistry.getRegistry/Registry.lookup calls.
// 通过 serviceUrl 创建 URL
URL url = new URL(null, getServiceUrl(), new DummyURLStreamHandler());
// url 的协议
String protocol = url.getProtocol();
if (protocol != null && !"rmi".equals(protocol))
throw new MalformedURLException("Invalid URL scheme '" + protocol + "'");
// 获取host
String host = url.getHost();
// 获取port
int port = url.getPort();
// 获取serviceName
String name = url.getPath();
if (name != null && name.startsWith("/"))
name = name.substring(1);
// 创建 Registry
Registry registry = LocateRegistry.getRegistry(host, port, this.registryClientSocketFactory);
// 获取Remote
stub = registry.lookup(name);
else
// Can proceed with standard RMI lookup API...
stub = Naming.lookup(getServiceUrl());
if (logger.isDebugEnabled())
logger.debug("Located RMI stub with URL [" + getServiceUrl() + "]");
return stub;
catch (MalformedURLException ex)
throw new RemoteLookupFailureException("Service URL [" + getServiceUrl() + "] is invalid", ex);
catch (NotBoundException ex)
throw new RemoteLookupFailureException(
"Could not find RMI service [" + getServiceUrl() + "] in RMI registry", ex);
catch (RemoteException ex)
throw new RemoteLookupFailureException("Lookup of RMI stub failed", ex);
org.springframework.remoting.rmi.RmiProxyFactoryBean#afterPropertiesSet
@Override
public void afterPropertiesSet()
super.afterPropertiesSet();
// 获取 服务提供的接口
Class<?> ifc = getServiceInterface();
// 如果服务接口不为空
Assert.notNull(ifc, "Property 'serviceInterface' is required");
// 创建服务代理
this.serviceProxy = new ProxyFactory(ifc, this).getProxy(getBeanClassLoader());
RmiProxyFactoryBean
实现了MethodInterceptor
,具体实现方法在org.springframework.remoting.rmi.RmiClientInterceptor#invoke
@Override
public Object invoke(MethodInvocation invocation) throws Throwable
// 获取remote
Remote stub = getStub();
try
// 真正的invoke调用
return doInvoke(invocation, stub);
catch (RemoteConnectFailureException ex)
return handleRemoteConnectFailure(invocation, ex);
catch (RemoteException ex)
if (isConnectFailure(ex))
return handleRemoteConnectFailure(invocation, ex);
else
throw ex;
protected Remote getStub() throws RemoteLookupFailureException
if (!this.cacheStub || (this.lookupStubOnStartup && !this.refreshStubOnConnectFailure))
// 如果缓存stub存在直接获取,否则创建
return (this.cachedStub != null ? this.cachedStub : lookupStub());
else
synchronized (this.stubMonitor)
if (this.cachedStub == null)
this.cachedStub = lookupStub();
return this.cachedStub;
org.springframework.remoting.rmi.RmiClientInterceptor#doInvoke(org.aopalliance.intercept.MethodInvocation, org.springframework.remoting.rmi.RmiInvocationHandler)
@Nullable
protected Object doInvoke(MethodInvocation methodInvocation, RmiInvocationHandler invocationHandler)
throws RemoteException, NoSuchMethodException, IllegalAccessException, InvocationTargetException
if (AopUtils.isToStringMethod(methodInvocation.getMethod()))
return "RMI invoker proxy for service URL [" + getServiceUrl() + "]";
/**
* 1. 参数组装成对象@link RemoteInvocationBasedAccessor#createRemoteInvocation(org.aopalliance.intercept.MethodInvocation)
* 2. invoke 执行 简单来说就是调用@link RmiInvocationHandler#invoke(RemoteInvocation)方法
*/
return invocationHandler.invoke(createRemoteInvocation(methodInvocation));
最后的invoke
方法
org.springframework.remoting.rmi.RmiInvocationWrapper#invoke
/**
* Delegates the actual invocation handling to the RMI exporter.
*
*
* 远程调用的时候会执行
* @see RmiBasedExporter#invoke(org.springframework.remoting.support.RemoteInvocation, Object)
*/
@Override
@Nullable
public Object invoke(RemoteInvocation invocation)
throws RemoteException, NoSuchMethodException, IllegalAccessException, InvocationTargetException
return this.rmiExporter.invoke(invocation, this.wrappedObject);
继续跟踪org.springframework.remoting.rmi.RmiBasedExporter#invoke
@Override
protected Object invoke(RemoteInvocation invocation, Object targetObject)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
return super.invoke(invocation, targetObject);
继续跟踪org.springframework.remoting.support.RemoteInvocationBasedExporter#invoke
protected Object invoke(RemoteInvocation invocation, Object targetObject)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
if (logger.isTraceEnabled())
logger.trace("Executing " + invocation);
try
return getRemoteInvocationExecutor().invoke(invocation, targetObject);
catch (NoSuchMethodException ex)
if (logger.isDebugEnabled())
logger.debug("Could not find target method for " + invocation, ex);
throw ex;
catch (IllegalAccessException ex)
if (logger.isDebugEnabled())
logger.debug("Could not access target method for " + invocation, ex);
throw ex;
catch (InvocationTargetException ex)
if (logger.isDebugEnabled())
logger.debug("Target method failed for " + invocation, ex.getTargetException());
throw ex;
以上是关于Spring占位符解析器的主要内容,如果未能解决你的问题,请参考以下文章
Spring PropertyResolver 占位符解析源码分析
Spring PropertyResolver 占位符解析API 介绍
无法使用 Spring Boot 解析 JavaFX 应用程序的占位符
Spring 4 PropertySourcesPlaceholderConfigurer 不能跨模块解析 $.. 占位符