zoukankan      html  css  js  c++  java
  • SpringBoot启动中,BeanDefinition的加载(一)AnnotatedBeanDefinitionReader 注解BeanDefinition的注册


    在SpringApplication的run方法中,调用了prepareContext方法,prepareContext方法用来准备上下文,即加载上下文时需要的资源。在方法的最后,加载了资源路径和加载器的load()方法,使用加载器从资源文件中注册BeanDefinition。
    此篇我们看一下注解类型的BeanDefinition加载过程

    SpringApplication.load()

    public class SpringApplication {
    	//向上下文加载bean
    	protected void load(ApplicationContext context, Object[] sources) {
    		if (logger.isDebugEnabled()) {
    			logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
    		}
    		//创建BeanDefinitionLoader加载器
    		BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
    		if (this.beanNameGenerator != null) {
    			loader.setBeanNameGenerator(this.beanNameGenerator);
    		}
    		if (this.resourceLoader != null) {
    			loader.setResourceLoader(this.resourceLoader);
    		}
    		if (this.environment != null) {
    			loader.setEnvironment(this.environment);
    		}
    		loader.load();
    	}
    	protected BeanDefinitionLoader createBeanDefinitionLoader(BeanDefinitionRegistry registry, Object[] sources) {
    		return new BeanDefinitionLoader(registry, sources);
    	}
    }
    

    BeanDefinitionLoader

    BeanDefinitionLoader是一个门面类:在类构造函数中,构造了AnnotatedBeanDefinitionReader、XmlBeanDefinitionReader和ClassPathBeanDefinitionScanner三个类型的读取类。并在ClassPathBeanDefinitionScanner中添加了一个类排除过滤器。

    /**
    *从给定的资源文件加载Bean Definition,包括XML和java config.是AnnotatedBeanDefinitionReader, XmlBeanDefinitionReader 
    *and ClassPathBeanDefinitionScanner的门面类。
    */
    class BeanDefinitionLoader {
    	BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
    		Assert.notNull(registry, "Registry must not be null");
    		Assert.notEmpty(sources, "Sources must not be empty");
    		this.sources = sources;
    		this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
    		this.xmlReader = (XML_ENABLED ? new XmlBeanDefinitionReader(registry) : null);
    		this.groovyReader = (isGroovyPresent() ? new GroovyBeanDefinitionReader(registry) : null);
    		this.scanner = new ClassPathBeanDefinitionScanner(registry);
    		this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
    	}
    
     	//加载所有的资源到读取器中
     	void load() {
    		for (Object source : this.sources) {
    			load(source);
    		}
    	}
     	//此处使用重载的方式,加载不同类型的资源文件,此处的资源类型为Class
     	private void load(Object source) {
    		Assert.notNull(source, "Source must not be null");
    		if (source instanceof Class<?>) {
    			load((Class<?>) source);
    			return;
    		}
    		if (source instanceof Resource) {
    			load((Resource) source);
    			return;
    		}
    		if (source instanceof Package) {
    			load((Package) source);
    			return;
    		}
    		if (source instanceof CharSequence) {
    			load((CharSequence) source);
    			return;
    		}
    		throw new IllegalArgumentException("Invalid source type " + source.getClass());
    	}
    
    	private void load(Class<?> source) {
    		if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
    			// Any GroovyLoaders added in beans{} DSL can contribute beans here
    			GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
    			((GroovyBeanDefinitionReader) this.groovyReader).beans(loader.getBeans());
    		}
    		//判断类是否符合条件
    		if (isEligible(source)) {
    			//使用注解读取器注册资源
    			this.annotatedReader.register(source);
    		}
    	}
    	//不是匿名类、Groovy闭包和有构造函数
    	private boolean isEligible(Class<?> type) {
    		return !(type.isAnonymousClass() || isGroovyClosure(type) || hasNoConstructors(type));
    	}
    }
    

    AnnotatedBeanDefinitionReader annotatedReader

    执行完doRegisterBean方法,就将一个完成的BeanDefinition注册了进去

    public class AnnotatedBeanDefinitionReader {
    
    	//beanName生成器
    	private BeanNameGenerator beanNameGenerator = AnnotationBeanNameGenerator.INSTANCE;
    
    	public void register(Class<?>... componentClasses) {
    		for (Class<?> componentClass : componentClasses) {
    			registerBean(componentClass);
    		}
    	}
    	//从给定的Bean中注册bean,推导元数据信息从声明的类注解上
    	public void registerBean(Class<?> beanClass) {
    		doRegisterBean(beanClass, null, null, null, null);
    	}
    
    	/*
    	*beanClass:bean class;     name:bean执行的显式名称
    	*qualifiers:注解限定符         supplier:创建bean实例之后的回调功能
    	*customizers:一个或多个回调函数用于定制工厂的BeanDefinition,例如设置lazy-init或primary标志
    	*/
    	private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
    			@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
    			@Nullable BeanDefinitionCustomizer[] customizers) {
    		//首先根据传入的首要类构建一个AnnotatedGenericBeanDefinition:一般注解型BeanDefinition
    		AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
    		if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
    			return;
    		}
    		//设置实例化回调器
    		abd.setInstanceSupplier(supplier);
    		//解析范围元数据
    		ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
    		//scopeMetadata.getScopeName() = singleton
    		abd.setScope(scopeMetadata.getScopeName());
    		String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
    		//处理普通BeanDefinition
    		AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
    		//设置限定符属性
    		if (qualifiers != null) {
    			for (Class<? extends Annotation> qualifier : qualifiers) {
    				if (Primary.class == qualifier) {
    					abd.setPrimary(true);
    				}
    				else if (Lazy.class == qualifier) {
    					abd.setLazyInit(true);
    				}
    				else {
    					abd.addQualifier(new AutowireCandidateQualifier(qualifier));
    				}
    			}
    		}
    		if (customizers != null) {
    			for (BeanDefinitionCustomizer customizer : customizers) {
    				customizer.customize(abd);
    			}
    		}
    		//根据abd构建一个BeanDefinitionHolder
    		BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
    		//根据scope注解,判断是否生成范围域代理类
    		definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    		//调用工具类将definitionHolder注册到工厂容器
    		BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
    	}
    }
    

    qualifier的属性应用

    AnnotatedGenericBeanDefinition

    public class AnnotatedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition {
    
    	public AnnotatedGenericBeanDefinition(Class<?> beanClass) {
    		setBeanClass(beanClass);
    		this.metadata = AnnotationMetadata.introspect(beanClass);
    	}
    }
    
    • GenericBeanDefinition
    public class GenericBeanDefinition extends AbstractBeanDefinition {
    
    }
    
    • AbstractBeanDefinition
    public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
    		implements BeanDefinition, Cloneable {
    	//织入Bean属性自动装配策略  名称、类型、构造函数、自省
    	public static final int AUTOWIRE_NO = AutowireCapableBeanFactory.AUTOWIRE_NO;
    	public static final int AUTOWIRE_BY_NAME = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;
    	public static final int AUTOWIRE_BY_TYPE = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE;
    	public static final int AUTOWIRE_CONSTRUCTOR = AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR;
    	public static final int AUTOWIRE_AUTODETECT = AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT;
    
    	private volatile Object beanClass;
    
    	public void setBeanClass(@Nullable Class<?> beanClass) {
    		this.beanClass = beanClass;
    	}
    }
    
    AnnotationMetadata
    public interface AnnotationMetadata extends ClassMetadata, AnnotatedTypeMetadata {
    	//工厂方法使用标准反射为给定类创建一个新的AnnotationMetadata实例
    	static AnnotationMetadata introspect(Class<?> type) {
    		return StandardAnnotationMetadata.from(type);
    	}
    }
    
    
    • StandardAnnotationMetadata
    public class StandardAnnotationMetadata extends StandardClassMetadata implements AnnotationMetadata {
    	static AnnotationMetadata from(Class<?> introspectedClass) {
    		return new StandardAnnotationMetadata(introspectedClass, true);
    	}
    
    	public StandardAnnotationMetadata(Class<?> introspectedClass, boolean nestedAnnotationsAsMap) {
    		super(introspectedClass);
    		this.mergedAnnotations = MergedAnnotations.from(introspectedClass,
    				SearchStrategy.INHERITED_ANNOTATIONS, RepeatableContainers.none());
    		this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
    	}
    
    
    }
    
    • StandardClassMetadata
    public class StandardClassMetadata implements ClassMetadata {
    	//自检类
    	private final Class<?> introspectedClass;
    	public StandardClassMetadata(Class<?> introspectedClass) {
    		Assert.notNull(introspectedClass, "Class must not be null");
    		//设置自检类为首要类
    		this.introspectedClass = introspectedClass;
    	}
    }
    
    

    AnnotationBeanNameGenerator

    public class AnnotationBeanNameGenerator implements BeanNameGenerator {
    
    	public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
    		if (definition instanceof AnnotatedBeanDefinition) {
    			//如果beanName为空,则继续向下执行,生成bean默认名称
    			String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
    			if (StringUtils.hasText(beanName)) {
    				// Explicit bean name found.
    				return beanName;
    			}
    		}
    		// Fallback: generate a unique default bean name.
    		return buildDefaultBeanName(definition, registry);
    	}
    
    	//获取注解,根据注解类型判断注解是否是模板注解(Component),并判断注解中的value是否有值,有值的话即要返回的beanName;
    	protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) {
    		AnnotationMetadata amd = annotatedDef.getMetadata();
    		Set<String> types = amd.getAnnotationTypes();
    		String beanName = null;
    		for (String type : types) {
    			AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(amd, type);
    			if (attributes != null) {
    				Set<String> metaTypes = this.metaAnnotationTypesCache.computeIfAbsent(type, key -> {
    					Set<String> result = amd.getMetaAnnotationTypes(key);
    					return (result.isEmpty() ? Collections.emptySet() : result);
    				});
    				if (isStereotypeWithNameValue(type, metaTypes, attributes)) {
    					Object value = attributes.get("value");
    					if (value instanceof String) {
    						String strVal = (String) value;
    						if (StringUtils.hasLength(strVal)) {
    							if (beanName != null && !strVal.equals(beanName)) {
    								throw new IllegalStateException("Stereotype annotations suggest inconsistent " +
    										"component names: '" + beanName + "' versus '" + strVal + "'");
    							}
    							beanName = strVal;
    						}
    					}
    				}
    			}
    		}
    		return beanName;
    	}
    
    	protected String buildDefaultBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
    		return buildDefaultBeanName(definition);
    	}
    
    	protected String buildDefaultBeanName(BeanDefinition definition) {
    		String beanClassName = definition.getBeanClassName();
    		Assert.state(beanClassName != null, "No bean class name set");
    		String shortClassName = ClassUtils.getShortName(beanClassName);
    		//调用内省类,处理beanName
    		return Introspector.decapitalize(shortClassName);
    	}
    
    }
    
    

    AnnotationConfigUtils

    /**
    *实用程序类,它允许方便地注册通用的org.springframework.beans.factory.config.BeanPostProcessor
    *和org.springframework.beans.factory.config.BeanFactoryPostProcessor定义,用于基于注释的配置。
    *还注册了一个通用的org.springframework.beans.factory.support. autowireccandidateresolver。
    */
    public abstract class AnnotationConfigUtils {
    	public static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) {
    		processCommonDefinitionAnnotations(abd, abd.getMetadata());
    	}
    
    	static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
    		AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);
    		//lazy默认为空
    		if (lazy != null) {
    			abd.setLazyInit(lazy.getBoolean("value"));
    		}
    		else if (abd.getMetadata() != metadata) {
    			lazy = attributesFor(abd.getMetadata(), Lazy.class);
    			if (lazy != null) {
    				abd.setLazyInit(lazy.getBoolean("value"));
    			}
    		}
    		//Primary无标注
    		if (metadata.isAnnotated(Primary.class.getName())) {
    			abd.setPrimary(true);
    		}
    		//dependsOn 为空
    		AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);
    		if (dependsOn != null) {
    			abd.setDependsOn(dependsOn.getStringArray("value"));
    		}
    		//role为空
    		AnnotationAttributes role = attributesFor(metadata, Role.class);
    		if (role != null) {
    			abd.setRole(role.getNumber("value").intValue());
    		}
    		//description 为空
    		AnnotationAttributes description = attributesFor(metadata, Description.class);
    		if (description != null) {
    			abd.setDescription(description.getString("value"));
    		}
    	}
    	//判断是否为范围域生成代理类,
    	static BeanDefinitionHolder applyScopedProxyMode(
    			ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {
    
    		ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();
    		if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
    			return definition;
    		}
    		boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
    		return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);
    	}
    }
    
    
    ScopedProxyCreator

    调用ScopedProxyCreator的createScopedProxy方法,方法内部调用ScopedProxyUtils工具类生成代理类BeanDefinitionHolder,详情可参考博客Scope注解的proxyMode的作用以及如何影响IoC容器的依赖查找

    final class ScopedProxyCreator {
    
    	private ScopedProxyCreator() {
    	}
    
    
    	public static BeanDefinitionHolder createScopedProxy(
    			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry, boolean proxyTargetClass) {
    
    		return ScopedProxyUtils.createScopedProxy(definitionHolder, registry, proxyTargetClass);
    	}
    
    	public static String getTargetBeanName(String originalBeanName) {
    		return ScopedProxyUtils.getTargetBeanName(originalBeanName);
    	}
    
    }
    

    BeanDefinitionHolder

    public class BeanDefinitionHolder implements BeanMetadataElement {
    
    	private final BeanDefinition beanDefinition;
    
    	private final String beanName;
    
    	@Nullable
    	private final String[] aliases;
    
    	public BeanDefinitionHolder(BeanDefinition beanDefinition, String beanName) {
    		this(beanDefinition, beanName, null);
    	}
    
    	/**
    	 * Create a new BeanDefinitionHolder.
    	 * @param beanDefinition 被装饰的BeanDefinition
    	 * @param beanName the name of the bean, as specified for the bean definition
    	 * @param aliases alias names for the bean, or {@code null} if none
    	 */
    	public BeanDefinitionHolder(BeanDefinition beanDefinition, String beanName, @Nullable String[] aliases) {
    		Assert.notNull(beanDefinition, "BeanDefinition must not be null");
    		Assert.notNull(beanName, "Bean name must not be null");
    		this.beanDefinition = beanDefinition;
    		this.beanName = beanName;
    		this.aliases = aliases;
    	}
    
    }
    

    BeanDefinitionReaderUtils

    调用BeanDefinitionReaderUtils 的registerBeanDefinition,注册BeanDefinition,内部调用ApplicationContext的registerBeanDefinition方法,判断别名是否为空,如果不为空,则进行别名注册

    public abstract class BeanDefinitionReaderUtils {
    	public static void registerBeanDefinition(
    			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
    			throws BeanDefinitionStoreException {
    
    		// Register bean definition under primary name.
    		String beanName = definitionHolder.getBeanName();
    		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    
    		// Register aliases for bean name, if any.
    		String[] aliases = definitionHolder.getAliases();
    		if (aliases != null) {
    			for (String alias : aliases) {
    				registry.registerAlias(beanName, alias);
    			}
    		}
    	}
    
    }
    
    • GenericApplicationContext
      GenericApplicationContext内部调用了DefaultListableBeanFactory 的registerBeanDefinition方法
    public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
    	private final DefaultListableBeanFactory beanFactory;
    	@Override
    	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
    			throws BeanDefinitionStoreException {
    
    		this.beanFactory.registerBeanDefinition(beanName, beanDefinition);
    	}
    	//别名注册
    	@Override
    	public void registerAlias(String beanName, String alias) {
    		this.beanFactory.registerAlias(beanName, alias);
    	}
    }
    
    • SimpleAliasRegistry
      使用map缓存别名和beanName之间的关系,在存储之前会检查别名是否循环引用
    public class SimpleAliasRegistry implements AliasRegistry {
    	public void registerAlias(String name, String alias) {
    	
    		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;
    					}
    				    ......
    				}
    				checkForAliasCircle(name, alias);
    				this.aliasMap.put(alias, name);
    				if (logger.isTraceEnabled()) {
    					logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
    				}
    			}
    		}
    	}
    }
    
    • DefaultListableBeanFactory
    public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
    		implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
    
    	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
    			throws BeanDefinitionStoreException {
    
    		Assert.hasText(beanName, "Bean name must not be empty");
    		Assert.notNull(beanDefinition, "BeanDefinition must not be null");
    		//如果beanDefinition 是AbstractBeanDefinition类型
    		if (beanDefinition instanceof AbstractBeanDefinition) {
    			try {
    				//对beanDefinition进行验证
    				((AbstractBeanDefinition) beanDefinition).validate();
    			}
    			catch (BeanDefinitionValidationException ex) {
    				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
    						"Validation of bean definition failed", ex);
    			}
    		}
    		//从容器中获取BeanDefinition 
    		BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
    		if (existingDefinition != null) {
    			if (!isAllowBeanDefinitionOverriding()) {
    				throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
    			}
    			else if (existingDefinition.getRole() < beanDefinition.getRole()) {
    				// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
    				if (logger.isInfoEnabled()) {
    					logger.info("Overriding user-defined bean definition for bean '" + beanName +
    							"' with a framework-generated bean definition: replacing [" +
    							existingDefinition + "] with [" + beanDefinition + "]");
    				}
    			}
    			else if (!beanDefinition.equals(existingDefinition)) {
    				if (logger.isDebugEnabled()) {
    					logger.debug("Overriding bean definition for bean '" + beanName +
    							"' with a different definition: replacing [" + existingDefinition +
    							"] with [" + beanDefinition + "]");
    				}
    			}
    			else {
    				if (logger.isTraceEnabled()) {
    					logger.trace("Overriding bean definition for bean '" + beanName +
    							"' with an equivalent definition: replacing [" + existingDefinition +
    							"] with [" + beanDefinition + "]");
    				}
    			}
    			this.beanDefinitionMap.put(beanName, beanDefinition);
    		}
    		//如果BeanDefinition 为空,并且BeanDefinition 不再创建过程中,进入else,将beanDefinition放入容器
    		else {
    			if (hasBeanCreationStarted()) {
    				// Cannot modify startup-time collection elements anymore (for stable iteration)
    				synchronized (this.beanDefinitionMap) {
    					this.beanDefinitionMap.put(beanName, beanDefinition);
    					List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
    					updatedDefinitions.addAll(this.beanDefinitionNames);
    					updatedDefinitions.add(beanName);
    					this.beanDefinitionNames = updatedDefinitions;
    					removeManualSingletonName(beanName);
    				}
    			}
    			else {
    				// Still in startup registration phase
    				this.beanDefinitionMap.put(beanName, beanDefinition);
    				this.beanDefinitionNames.add(beanName);
    				removeManualSingletonName(beanName);
    			}
    			this.frozenBeanDefinitionNames = null;
    		}
    
    		if (existingDefinition != null || containsSingleton(beanName)) {
    			resetBeanDefinition(beanName);
    		}
    		else if (isConfigurationFrozen()) {
    			clearByTypeCache();
    		}
    	}
    	//调用lambda表达式,对处在手动创建的beanName进行处理
    	private void removeManualSingletonName(String beanName) {
    		updateManualSingletonNames(set -> set.remove(beanName), set -> set.contains(beanName));
    	}
    
    	private void updateManualSingletonNames(Consumer<Set<String>> action, Predicate<Set<String>> condition) {
    		if (hasBeanCreationStarted()) {
    			// Cannot modify startup-time collection elements anymore (for stable iteration)
    			synchronized (this.beanDefinitionMap) {
    				if (condition.test(this.manualSingletonNames)) {
    					Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
    					action.accept(updatedSingletons);
    					this.manualSingletonNames = updatedSingletons;
    				}
    			}
    		}
    		else {
    			// Still in startup registration phase
    			if (condition.test(this.manualSingletonNames)) {
    				action.accept(this.manualSingletonNames);
    			}
    		}
    	}
    }
    
    

    AbstractBeanDefinitionReader xmlReader

    ClassPathBeanDefinitionScanner scanner

    工具类

    Introspector 自省类

    public class Introspector {
        
        /*
        *工具方法获取一个字符串并将其转换为普通Java变量名大写。这通常意味着将第一个字符从大写字母
        *转换为小写字母,但在(不寻常的)特殊情况下,当有多个字符且第一个和第二个字符都是大写字母时,
        *我们不去管它
        */
        public static String decapitalize(String name) {
            if (name == null || name.length() == 0) {
                return name;
            }
            if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
                            Character.isUpperCase(name.charAt(0))){
                return name;
            }
            char chars[] = name.toCharArray();
            chars[0] = Character.toLowerCase(chars[0]);
            return new String(chars);
        }
    }
    
  • 相关阅读:
    kernel-devel-3.10.0-957.el7.x86_64.rpm kernel-headers-3.10.0-957.el7.x86_64.rpm
    解决MySQL安装:找不到msvcr120.dll和msvcp120.dll
    Node.js安装
    大数据电商数据仓库
    spark
    redis 脑裂等极端情况分析
    Redis解决并发超卖问题
    解决OutOfMemoryError: unable to create new native thread问题
    好用的java工具
    java初始化和实例化
  • 原文地址:https://www.cnblogs.com/nangonghui/p/15673471.html
Copyright © 2011-2022 走看看