一、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方法