Spring中一些工具类

一、ClassUtils

1、forName方法

创建Class对象,但不是直接创建。如果是一些常见的类,如float、int、short就直接返回对应的class,如果是某些数组,也会返回对应的类型。如果都不是,就会Class.forName创建该类,第一次创建失败后,会把类名转成内部类创建,如果还失败,就抛出异常

public static Class<?> forName(String name, @Nullable ClassLoader classLoader)
			throws ClassNotFoundException, LinkageError {

		Assert.notNull(name, "Name must not be null");

		Class<?> clazz = resolvePrimitiveClassName(name);
		if (clazz == null) {
			clazz = commonClassCache.get(name);
		}
		if (clazz != null) {
			return clazz;
		}

		// "java.lang.String[]" style arrays
		if (name.endsWith(ARRAY_SUFFIX)) {
			String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length());
			Class<?> elementClass = forName(elementClassName, classLoader);
			return Array.newInstance(elementClass, 0).getClass();
		}

		// "[Ljava.lang.String;" style arrays
		if (name.startsWith(NON_PRIMITIVE_ARRAY_PREFIX) && name.endsWith(";")) {
			String elementName = name.substring(NON_PRIMITIVE_ARRAY_PREFIX.length(), name.length() - 1);
			Class<?> elementClass = forName(elementName, classLoader);
			return Array.newInstance(elementClass, 0).getClass();
		}

		// "[[I" or "[[Ljava.lang.String;" style arrays
		if (name.startsWith(INTERNAL_ARRAY_PREFIX)) {
			String elementName = name.substring(INTERNAL_ARRAY_PREFIX.length());
			Class<?> elementClass = forName(elementName, classLoader);
			return Array.newInstance(elementClass, 0).getClass();
		}

		ClassLoader clToUse = classLoader;
		if (clToUse == null) {
			clToUse = getDefaultClassLoader();
		}
		try {
            //name是org.springframework.web.reactive.DispatcherHandler,直接创建
			return Class.forName(name, false, clToUse);
		}
		catch (ClassNotFoundException ex) {
            //创建失败后,创建内部类org.springframework.web.reactive$DispatcherHandler
			int lastDotIndex = name.lastIndexOf(PACKAGE_SEPARATOR);
			if (lastDotIndex != -1) {
				String innerClassName =
						name.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR + name.substring(lastDotIndex + 1);
				try {
					return Class.forName(innerClassName, false, clToUse);
				}
				catch (ClassNotFoundException ex2) {
				}
			}
			throw ex;
		}
	}

2、isPresent方法

就是调用上面的forName方法,如果创建Class成功,就返回true

public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
		try {
			forName(className, classLoader);
			return true;
		}
		catch (IllegalAccessError err) {
			throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" +
					className + "]: " + err.getMessage(), err);
		}
		catch (Throwable ex) {
			// Typically ClassNotFoundException or NoClassDefFoundError...
			return false;
		}
	}

3、getDefaultClassLoader方法

按照获取当前线程上下文类加载器-->获取当前类类加载器-->获取系统启动类加载器的顺序来获取。通过Thread.getContextClassLoader()方法获取到的是线程绑定的类加载器,这个classloader是父线程在创建子线程的时候,通过Thread.setContextClassLoader()方法设置进去,用于该线程加载类和资源的,如果没有调用这个方法,那么直接使用父线程的classLoader;如果这个方法返回null,代表该线程直接使用的系统class loader或者bootstrap class loader;

public static ClassLoader getDefaultClassLoader() {
		ClassLoader cl = null;
		try {
			cl = Thread.currentThread().getContextClassLoader();
		}
		catch (Throwable ex) {
			// Cannot access thread context ClassLoader - falling back...
		}
		if (cl == null) {
			// No thread context class loader -> use class loader of this class.
			cl = ClassUtils.class.getClassLoader();
			if (cl == null) {
				// getClassLoader() returning null indicates the bootstrap ClassLoader
				try {
					cl = ClassLoader.getSystemClassLoader();
				}
				catch (Throwable ex) {
					// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
				}
			}
		}
		return cl;
	}

4、isCacheSafe

public static boolean isCacheSafe(Class<?> clazz, @Nullable ClassLoader classLoader) {
		Assert.notNull(clazz, "Class must not be null");
		try {
			ClassLoader target = clazz.getClassLoader();
			// Common cases
			if (target == classLoader || target == null) {
				return true;
			}
			if (classLoader == null) {
				return false;
			}
			// Check for match in ancestors -> positive
			ClassLoader current = classLoader;
			while (current != null) {
				current = current.getParent();
				if (current == target) {
					return true;
				}
			}
			// Check for match in children -> negative
			while (target != null) {
				target = target.getParent();
				if (target == classLoader) {
					return false;
				}
			}
		}
		catch (SecurityException ex) {
			// Fall through to loadable check below
		}

		// Fallback for ClassLoaders without parent/child relationship:
		// safe if same Class can be loaded from given ClassLoader
		return (classLoader != null && isLoadable(clazz, classLoader));
	}

二、PropertiesLoaderUtils

1、loadProperties方法

Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			result = new LinkedMultiValueMap<>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryClassName = ((String) entry.getKey()).trim();
					for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryClassName, factoryName.trim());
					}
				}
			}

三、Assert

1、isAssignable方法

Class1.isAssignableFrom(Class2) 是用来判断类Class1是否是类Class2的父类或接口,obj instanceof A是用来判断一个对象是否是子类或实现类。比如:

Father.class.isAssignableFrom(son.getClass())    结果 true
Son.class.isAssignableFrom(father.getClass())    结果 false
son instanceof Father                            结果 true
father instanceof Son                            结果 false

Assert的isAssignable方法就是判断superType是否是subType的父类,如果不是就报错

public static void isAssignable(Class<?> superType, @Nullable Class<?> subType, String message) {
		notNull(superType, "Super type to check against must not be null");
		if (subType == null || !superType.isAssignableFrom(subType)) {
			assignableCheckFailed(superType, subType, message);
		}
	}

四、BeanUtils

1、instantiateClass

有对Kotlin的支持

public static <T> T instantiateClass(Class<T> clazz) throws BeanInstantiationException {
		Assert.notNull(clazz, "Class must not be null");
		if (clazz.isInterface()) {
			throw new BeanInstantiationException(clazz, "Specified class is an interface");
		}
		try {
			return instantiateClass(clazz.getDeclaredConstructor());
		}
		catch (NoSuchMethodException ex) {
			Constructor<T> ctor = findPrimaryConstructor(clazz);
			if (ctor != null) {
				return instantiateClass(ctor);
			}
			throw new BeanInstantiationException(clazz, "No default constructor found", ex);
		}
		catch (LinkageError err) {
			throw new BeanInstantiationException(clazz, "Unresolvable class definition", err);
		}
	}

五、ApplicationContext

1、获取Spring上下文中指定类的实现类

 String[] beanDefinitionNames = applicationContext.getBeanNamesForType(XXX.class)

六、其他一些好用的方法

1、System.setProperty(key, value)

把参数设置成为系统的全局变量,可以在项目的任何一个地方通过System.getProperty(key)来获得

2、new RuntimeException().getStackTrace()

可以按照调用顺序,获取调用该方法的所有类名和方法名,感觉可以用来自己写报警日志

StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
		for (StackTraceElement stackTraceElement : stackTrace) {
			String methodName = stackTraceElement.getMethodName();
			String className = stackTraceElement.getClassName();
		}

3、获取类中添加指定注解的方法(下面是xxl-job中的源码,获取类中所有添加了@XxlJob注解的方法)

        Object bean = applicationContext.getBean(beanDefinitionName);

        Map<Method, XxlJob> annotatedMethods = null;
        annotatedMethods = MethodIntrospector.selectMethods(bean.getClass(),
                new MethodIntrospector.MetadataLookup<XxlJob>() {
                    @Override
                    public XxlJob inspect(Method method) {
                        return AnnotatedElementUtils.findMergedAnnotation(method, XxlJob.class);
                    }
                });

4、@AutoConfigureAfter  

在加载配置的类之后再加载当前类

5、@Value   @RefreshScope

可以通过重写源码刷新Environment中的变量

6、ConfigurationClassPostProcessor

7、Introspector内省

// 获取class类中的所有成员变量对象,这些对象包括成员变量名、类型、get和set方法
BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();

for (PropertyDescriptor pd : pds) {
    // 成员变量名
    pd.getName();
    // 成员变量类型
    pd.getPropertyType();
    // 成员变量set方法
    pd.getWriteMethod()
    // 成员变量get方法
    pd.getReadMethod()
}

这段是dble的ParameterMapping类中的方法,利用Introspector对类中的属性赋值

    public static void mapping(Object object, Properties parameter, ProblemReporter problemReporter) throws IllegalAccessException,
            InvocationTargetException {
        PropertyDescriptor[] pds = getDescriptors(object.getClass());
        for (PropertyDescriptor pd : pds) {
            Object obj = parameter.get(pd.getName());
            Object value = obj;
            Class<?> cls = pd.getPropertyType();
            if (cls == null) {
                if (problemReporter != null) {
                    problemReporter.warn("unknown property [ " + pd.getName() + " ]");
                } else {
                    LOGGER.warn("unknown property [ " + pd.getName() + " ]");
                }
                continue;
            }

            if (obj instanceof String) {
                String valStr = ((String) obj).trim();
                if (!StringUtil.isEmpty(valStr)) {
                    valStr = ConfigUtil.filter(valStr);
                }
                if (isPrimitiveType(cls)) {
                    try {
                        value = convert(cls, valStr);
                    } catch (NumberFormatException nfe) {
                        if (problemReporter != null) {
                            problemReporter.warn("property [ " + pd.getName() + " ] '" + valStr + "' data type should be " + cls.toString() + "");
                        } else {
                            LOGGER.warn("property [ " + pd.getName() + " ] '" + valStr + "' data type should be " + cls.toString() + "");
                        }
                        parameter.remove(pd.getName());
                        continue;
                    }
                }
            }
            if (value != null) {
                Method method = pd.getWriteMethod();
                if (method != null) {
                    method.invoke(object, value);
                    parameter.remove(pd.getName());
                }
            }
        }
    }

内省相对反射来说,更加规范。反射如果需要赋值,一般直接通过字段赋值,如果通过set方法还要生成set方法名。而内省的话可以直接获得set方法。

Introspector类的getBeanInfo还提供了stopClass和flags属性,可以控制获取属性的层次。

Spring的BeanUtils类中的copyProperties等就是通过内省来实现的

    public static BeanInfo getBeanInfo(Class<?> beanClass, Class<?> stopClass,
                                        int flags) throws IntrospectionException {
        BeanInfo bi;
        if (stopClass == null && flags == USE_ALL_BEANINFO) {
            bi = getBeanInfo(beanClass);
        } else {
            bi = (new Introspector(beanClass, stopClass, flags)).getBeanInfo();
        }
        return bi;

        //return new GenericBeanInfo(bi);
    }

8、Orika实体映射

实现原理?与BeanUtils的区别?

9、CommandLine使用

    public static void main(String[] args) throws Exception {
		
    	String[] arguments = { "-h=ABC", "-c=abc"};
    	
    	testOptions(arguments);
	}
    
    private static void testOptions(String[] args) throws Exception {
    	
    	Options options = new Options();
    	
        Option opt = new Option("h", "help", true, "Print help");
        opt.setRequired(false);
        options.addOption(opt);
 
        opt = new Option("c", "configFile", true, "Name server config properties file");
        opt.setRequired(false);
        options.addOption(opt);
 
        opt = new Option("p", "printConfigItem", false, "Print all config item");
        opt.setRequired(false);
        options.addOption(opt);
 
        CommandLine commandLine = new PosixParser().parse(options, args);
 
        Option[] opts = commandLine.getOptions();
        if (opts != null) {
            for (Option opt1 : opts) {
                String name = opt1.getLongOpt();
                String value = commandLine.getOptionValue(name);
                System.out.println(name + "=>" + value);
            }
        }
    }

运行结果

help=>=ABC
configFile=>=abc

 

七、一些面试题

1、ThreadLocal和InheritableThreadLocal

  • ThreadLocal和InheritableThreadLocal本质上只是为了方便编码给的工具类,具体存数据是ThreadLocalMap 对象
  • ThreadLocalMap 存的key对象是ThreadLocal,value就是真正需要存的业务对象
  • Thread里通过两个变量持用ThreadLocalMap 对象,分别为:threadLocals和inheritableThreadLocals
  • InheritableThreadLocal之所以能够完成线程间变量的传递,是在new Thread()的时候将父线程inheritableThreadLocals对像里的值复制到了子线程中
  • 子线程通过继承得到的InheritableThreadLocal里的值与父线程里的InheritableThreadLocal的值具有相同的引用,如果父子线程想实现不影响各自的对象,可以重写InheritableThreadLocal的childValue方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值