前言
上一篇分享了:【小家Spring】一文读懂Spring中的BeanFactory和FactoryBean的区别。为了缓解疲劳,本文来个小插曲~~不用费太多脑力的
Spring是一个非常优秀且流行的框架,里面不乏有很多优秀的设计模式、设计思想。
本文主要针对其中一个非常小巧的类:SimpleAliasRegistry做一个源码解读。顺便也分享给大家,若有分析得不到位的地方,非常欢迎指正,毕竟我也是第一次看。
分析此类的源码是因为此类很具有代表性,可以部分代表Spring的代码功底,优雅~~~因为群里有好几次提到过说此类虽然很小巧,但是代码设计得很优雅
初识
首先看到,这个类实现了接口AliasRegistry,而这个接口顾名思义:它就是别名管理器。Spring提供了一个默认实现:SimpleAliasRegistry。内部会缓存这些别名和真实名称的对应关系
在Spring环境下,我们很容易的为一个Bean定义一个或者多个别名:
<bean id="app:dataSource" class="..."> <alias name="app:dataSoure" alias="user:dataSoure"/> <alias name="app:dataSoure" alias="device:dataSoure"/> </bean> 或者: 直接使用bean标签的name属性,就是别名 <bean id="aaa",name="bbb,ccc,ddd"/>
到这个时候,可能很多人就会问了,当前都SpringBoot环境了,哪还会用到xml了啊,所以别名这个在Boot这就不会再用了。
其实不然,SpringBoot中我们配置一个Bean一般会这么来写:
@Configuration public class ApplicationConfig { @Bean public Object object() { return new Object(); } }
这个时候我打断点,发现还真的没有注册别名。而@Bean注解里也并没有alias等相关属性,是不是Boot就真的不支持了呢?
其实,只支持的。@Bean虽然没有alias属性,但是它的名称可以是数组,可以写多个名称,而经过我实现发现。当只写一个值的时候,只有名称没有别名。但是当你写多个值的时候,除了第一个是名称,后面的全都是别名。
@Bean(value = {"aaa", "bbb", "ccc"}) public Object object() { return new Object(); }
断点启动:
证实了我上面的结论。虽然别名我们用得挺少,特别是在当下的Boot环境了。但是强大的Spring还是支持的。
当然这并不是本文讨论的重点,重点还是看“优雅的”代码:
public interface AliasRegistry { //增 给name新增一个别名alias void registerAlias(String name, String alias); //删 删除一个别名 void removeAlias(String alias); //此name是否含有别名 boolean isAlias(String name); //获取此name对应的所有的别名 String[] getAliases(String name); }
它的实现类有不少,此处我们只看SimpleAliasRegistry :
public class SimpleAliasRegistry implements AliasRegistry { /** Logger available to subclasses. */ protected final Log logger = LogFactory.getLog(getClass()); /** Map from alias to canonical name. */ private final Map<String, String> aliasMap = new ConcurrentHashMap<>(16); @Override public void registerAlias(String name, String alias) { Assert.hasText(name, "'name' must not be empty"); Assert.hasText(alias, "'alias' must not be empty"); synchronized (this.aliasMap) { if (alias.equals(name)) { this.aliasMap.remove(alias); if (logger.isDebugEnabled()) { logger.debug("Alias definition '" + alias + "' ignored since it points to same name"); } } else { String registeredName = this.aliasMap.get(alias); if (registeredName != null) { if (registeredName.equals(name)) { // An existing alias - no need to re-register return; } if (!allowAliasOverriding()) { throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" + name + "': It is already registered for name '" + registeredName + "'."); } if (logger.isDebugEnabled()) { logger.debug("Overriding alias '" + alias + "' definition for registered name '" + registeredName + "' with new target name '" + name + "'"); } } checkForAliasCircle(name, alias); this.aliasMap.put(alias, name); if (logger.isTraceEnabled()) { logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'"); } } } } /** * Return whether alias overriding is allowed. * Default is {@code true}. */ protected boolean allowAliasOverriding() { return true; } /** * Determine whether the given name has the given alias registered. * @param name the name to check * @param alias the alias to look for * @since 4.2.1 */ public boolean hasAlias(String name, String alias) { for (Map.Entry<String, String> entry : this.aliasMap.entrySet()) { String registeredName = entry.getValue(); if (registeredName.equals(name)) { String registeredAlias = entry.getKey(); if (registeredAlias.equals(alias) || hasAlias(registeredAlias, alias)) { return true; } } } return false; } @Override public void removeAlias(String alias) { synchronized (this.aliasMap) { String name = this.aliasMap.remove(alias); if (name == null) { throw new IllegalStateException("No alias '" + alias + "' registered"); } } } @Override public boolean isAlias(String name) { return this.aliasMap.containsKey(name); } @Override public String[] getAliases(String name) { List<String> result = new ArrayList<>(); synchronized (this.aliasMap) { retrieveAliases(name, result); } return StringUtils.toStringArray(result); } /** * Transitively retrieve all aliases for the given name. * @param name the target name to find aliases for * @param result the resulting aliases list */ private void retrieveAliases(String name, List<String> result) { this.aliasMap.forEach((alias, registeredName) -> { if (registeredName.equals(name)) { result.add(alias); retrieveAliases(alias, result); } }); } /** * Resolve all alias target names and aliases registered in this * factory, applying the given StringValueResolver to them. * <p>The value resolver may for example resolve placeholders * in target bean names and even in alias names. * @param valueResolver the StringValueResolver to apply */ public void resolveAliases(StringValueResolver valueResolver) { Assert.notNull(valueResolver, "StringValueResolver must not be null"); synchronized (this.aliasMap) { Map<String, String> aliasCopy = new HashMap<>(this.aliasMap); aliasCopy.forEach((alias, registeredName) -> { String resolvedAlias = valueResolver.resolveStringValue(alias); String resolvedName = valueResolver.resolveStringValue(registeredName); if (resolvedAlias == null || resolvedName == null || resolvedAlias.equals(resolvedName)) { this.aliasMap.remove(alias); } else if (!resolvedAlias.equals(alias)) { String existingName = this.aliasMap.get(resolvedAlias); if (existingName != null) { if (existingName.equals(resolvedName)) { // Pointing to existing alias - just remove placeholder this.aliasMap.remove(alias); return; } throw new IllegalStateException( "Cannot register resolved alias '" + resolvedAlias + "' (original: '" + alias + "') for name '" + resolvedName + "': It is already registered for name '" + registeredName + "'."); } checkForAliasCircle(resolvedName, resolvedAlias); this.aliasMap.remove(alias); this.aliasMap.put(resolvedAlias, resolvedName); } else if (!registeredName.equals(resolvedName)) { this.aliasMap.put(alias, resolvedName); } }); } } /** * Check whether the given name points back to the given alias as an alias * in the other direction already, catching a circular reference upfront * and throwing a corresponding IllegalStateException. * @param name the candidate name * @param alias the candidate alias * @see #registerAlias * @see #hasAlias */ protected void checkForAliasCircle(String name, String alias) { if (hasAlias(alias, name)) { throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" + name + "': Circular reference - '" + name + "' is a direct or indirect alias for '" + alias + "' already"); } } /** * Determine the raw name, resolving aliases to canonical names. * @param name the user-specified name * @return the transformed name */ public String canonicalName(String name) { String canonicalName = name; // Handle aliasing... String resolvedName; do { resolvedName = this.aliasMap.get(canonicalName); if (resolvedName != null) { canonicalName = resolvedName; } } while (resolvedName != null); return canonicalName; } }
本文就是重点分析此类,代码行数若除去注释,100行左右,但是里面的写法确实是有不少可圈可点的地方。