Spring占位符解析器

Posted 骆驼整理说

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring占位符解析器相关的知识,希望对你有一定的参考价值。

目录

Spring PropertyPlaceholderHelper

Spring AnnotationFormatterFactory

Spring Printer

Spring5 新特性spring.components解析

Spring RMI


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 &lt;A&gt; 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());
    

 RmiInvocationWrapperinvoke方法会在调用方调用接口时候触发

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 中以编程方式解析属性占位符

无法使用 Spring Boot 解析 JavaFX 应用程序的占位符

Spring 4 PropertySourcesPlaceholderConfigurer 不能跨模块解析 $.. 占位符

Spring boot 无法解析占位符 application.yml