精华内容
下载资源
问答
  • ClassUtils常用方法总结

    2021-07-26 08:36:04
    获取包名 getPackageName(Class<?> cls) @Test public void testGetPackageName(){ //获取包名 String packName = ClassUtils.getPackageName(String.class);... String packName2 = ClassUtils.getPackage

    获取包名

    getPackageName(Class<?> cls)

    @Test
    public void testGetPackageName(){
        //获取包名
        String packName = ClassUtils.getPackageName(String.class);
        System.out.println(packName); //  = java.lang
        
        String packName2 = ClassUtils.getPackageName(Beans.class);
        System.out.println(packName2); //  = java.beans
        
        //为空,默认返回第二个参数的值
        String packName3 = ClassUtils.getPackageName(null,"the object is null");
        System.out.println(packName3); //  = the object is null
        
        //传入参数为字符串
        String packName4 = ClassUtils.getPackageName("java.lang.String");
        System.out.println(packName4); //  = java.lang
        
        //获取规范名
        String packName5 = ClassUtils.getPackageCanonicalName(String.class);
        System.out.println(packName5); //  = java.lang
        
    }
    

    获取类名

    getShortCanonicalName(Class<?> cls)

    getShortClassName(Class<?> cls)

    getSimpleName(Class<?> cls)

    @Test
    public void testGetClassName(){
        //获取simpleName
        String simpleName1 = ClassUtils.getSimpleName(String.class);
        System.out.println(simpleName1); //  = String
        
        //获取simpleName
        String simpleName2 = ClassUtils.getSimpleName(null,"simpleName is null");
        System.out.println(simpleName2); //  = simpleName is null
        
        //获取canonicalName
        String canonicalName = ClassUtils.getShortCanonicalName(String.class);
        System.out.println(canonicalName); //  = String
        
        //获取className
        String className = ClassUtils.getShortClassName(String.class);
        System.out.println(className); //  = String
        
        //3者的区别:对于内部类,如内部类,区别就显示出来类
        //其他情况区别不大
        
        //获取simpleName
        String simpleName3 = ClassUtils.getSimpleName(Map.Entry.class);
        System.out.println(simpleName3); //  = Entry
        
        //获取canonicalName
        String canonicalName2 = ClassUtils.getShortCanonicalName(Map.Entry.class);
        System.out.println(canonicalName2); //  = Map.Entry
        
        //获取className
        String className2 = ClassUtils.getShortClassName(Map.Entry.class);
        System.out.println(className2); //  = Map.Entry
    }
    

    判断是否为转型

    isAssignable(Class<?> cls, Class<?> toClass)等等

    @Test
    public void testIsAssignable(){
        System.out.println("判断是否可以转型.");  
        System.out.println(ClassUtils.isAssignable(Date.class, Object.class)); //= true  
        System.out.println(ClassUtils.isAssignable(Object.class, Date.class)); //=false
        
    }
    

    判断是否为内部类

    isInnerClass(Class<?> cls)
    @Test
    public void testIsInnerClass(){
    System.out.println(ClassUtils.isInnerClass(Entry.class)); // = ture
    System.out.println(ClassUtils.isInnerClass(Object.class)); //= false
    }

    判读是否为基础类型或者包装类型

    isPrimitiveOrWrapper(Class<?> type)

    isPrimitiveWrapper(Class<?> type)

    @Test
    public void testIsPrimitiveWrapper(){
        // wrapper类有(Boolean, Byte, Character, Short, Integer, Long, Double, Float).
        System.out.println(ClassUtils.isPrimitiveWrapper(Integer.class)); // = ture
        System.out.println(ClassUtils.isPrimitiveWrapper(Object.class)); // = false
        System.out.println(ClassUtils.isPrimitiveWrapper(String.class)); // = false
        System.out.println(ClassUtils.isPrimitiveWrapper(int.class)); // = false
        
        System.out.println(ClassUtils.isPrimitiveOrWrapper(Integer.class)); // = ture
        System.out.println(ClassUtils.isPrimitiveOrWrapper(Object.class)); // = false
        System.out.println(ClassUtils.isPrimitiveOrWrapper(String.class)); // = false
        System.out.println(ClassUtils.isPrimitiveOrWrapper(int.class)); // = true
        
    }
    

    基础类型和包装类型转换

    wrapperToPrimitive(Class<?> cls)

     @Test
    public void testWrapperToPrimitive(){
        System.out.println(ClassUtils.wrapperToPrimitive(Integer.class));// = int
    }
    

    获取类名和class类互转

    convertClassesToClassNames(List<Class<?>> classes)

    convertClassNamesToClasses(List<String> classNames)

    对象转Class对象

    toClass(Object... array)

    @Test
    public void testToClass(){
        //类转化为class对象
        Class[] classAry =  ClassUtils.toClass(new String(),new Object());
        System.out.println(ArrayUtils.toString(classAry));
        // = {class java.lang.String,class java.lang.Object}
    }
    

    获取缩略名

    getAbbreviatedName(Class<?> cls,int len)

    getAbbreviatedName(String className,int len)

    @Test
    public void testGetAbbreviatedName(){
        //获取类的缩略名称
        String abbreviatedName1 = ClassUtils.getAbbreviatedName(String.class, 5);
        String abbreviatedName2 = ClassUtils.getAbbreviatedName(String.class, 10);
        String abbreviatedName3 = ClassUtils.getAbbreviatedName(String.class, 3);
        String abbreviatedName4 = ClassUtils.getAbbreviatedName(String.class, 20);
        System.out.println(abbreviatedName1);
        System.out.println(abbreviatedName2);
        System.out.println(abbreviatedName3);
        System.out.println(abbreviatedName4);
    }
    

    获得所有实现的接口

    getAllInterfaces(Class<?> cls)

    @Test
    public void testGetAllInterfaces(){
        //获得所有实现的接口
        List<Class<?>> classAry =  ClassUtils.getAllInterfaces(String.class);
        System.out.println(ArrayUtils.toString(classAry));
    }
    

    获取所有父类

    getAllSuperclasses(Class<?> cls)

    @Test
    public void testGetAllSuperclasses(){
        //获得所有的父类
        List<Class<?>> classArray =  ClassUtils.getAllSuperclasses(String.class);
        System.out.println(ArrayUtils.toString(classArray));
        //[class java.lang.Object]
    }  
    

    获取父类层级

    hierarchy(Class<?> type)

    @Test
    public void testHierarchy(){
        
        Iterator<Class<?>> myIterator =  ClassUtils.hierarchy(ArrayList.class).iterator();
        while(myIterator.hasNext()){
            System.out.println(myIterator.next());
        }
    }   
    
    结果:
    
    class java.util.ArrayList
    class java.util.AbstractList
    class java.util.AbstractCollection
    class java.lang.Object
    展开全文
  • ClassUtils 介绍 一、官方API地址     官网地址:ClassUtils.html 二、ClassUtils 常用API 1.导入POM文件   这里使用的是:3.9的版本,还是比较新的 <dependency> <...

    ClassUtils 介绍

    一、官方API地址

        官网地址:ClassUtils.html



    二、ClassUtils 常用API

    1.导入POM文件

        这里使用的是:3.9 的版本,还是比较新的

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
         <version>3.9</version>
    </dependency>
    

    2.常用API介绍
    方法名方法介绍
    getName获取类名(包含包路径)
    getShortClassName获取类名(不包含包路径)
    getSimpleName获取类名(不包含包路径)
    getShortCanonicalName获取类名(不包含包路径)
    getPackageName获取包名
    isAssignable判断是否可以转型
    isInnerClass判断是否为内部类
    isPrimitiveWrapper判断是否为包装类型
    isPrimitiveOrWrapper判断是否为基础类型
    wrapperToPrimitive基础类型和包装类型转换
    wrapperToPrimitives基础类型和包装类型转换
    convertClassesToClassNames类名字符串转 class类
    convertClassNamesToClassesclass类转类名字符串
    toClass对象转Class对象获取缩略名
    getAbbreviatedName获取缩略名
    getAllInterfaces获得所有实现的接口
    getAllSuperclasses获取所有父类
    hierarchy获取父类层级

    3.API的使用

           getName 获取类名(包含包路径)

    // getName(final Class<?> cls)
    System.out.println(ClassUtils.getName(String.class)); // java.lang.String
    // getName(final Class<?> cls, final String valueIfNull)
    System.out.println(ClassUtils.getName(null, "java.lang.Boolean")); // java.lang.Boolean
    
    // getName(final Object object)
    System.out.println(ClassUtils.getName(str)); // java.lang.String
    // getName(final Class<?> cls, final String valueIfNull)
    System.out.println(ClassUtils.getName(null, "java.lang.Boolean")); // java.lang.Boolean
    

           getShortClassName          获取类名(不包含包路径)
           getSimpleName                获取类名(不包含包路径)
           getShortCanonicalName  获取类名(不包含包路径)

    //获取simpleName
    String simpleName1 = ClassUtils.getSimpleName(String.class);
    System.out.println(simpleName1); // = String
    
    //获取simpleName
    String simpleName2 = ClassUtils.getSimpleName(null,"simpleName is null");
    System.out.println(simpleName2); // = simpleName is null
    
    //获取canonicalName
    String canonicalName = ClassUtils.getShortCanonicalName(String.class);
    System.out.println(canonicalName); // = String
    
    //获取className
    String className = ClassUtils.getShortClassName(String.class);
    System.out.println(className); // = String
    

           它们的区别:对于内部类区别就显示出来了,其他情况区别不大

    //获取simpleName
    String simpleName3 = ClassUtils.getSimpleName(Map.Entry.class);
    System.out.println(simpleName3); // = Entry
    
    //获取canonicalName
    String canonicalName2 = ClassUtils.getShortCanonicalName(Map.Entry.class);
    System.out.println(canonicalName2); // = Map.Entry
    
    //获取className
    String className2 = ClassUtils.getShortClassName(Map.Entry.class);
    System.out.println(className2); // = Map.Entry
    

           getPackageName(Class<?> cls) 获取包名

    //获取包名
    String packName = ClassUtils.getPackageName(String.class);
    System.out.println(packName); // = java.lang
    
    String packName2 = ClassUtils.getPackageName(Beans.class);
    System.out.println(packName2); // = java.beans
    
    //为空,默认返回第二个参数的值
    String packName3 = ClassUtils.getPackageName(null,"the object is null");
    System.out.println(packName3); // = the object is null
    
    //传入参数为字符串
    String packName4 = ClassUtils.getPackageName("java.lang.String");
    System.out.println(packName4); // = java.lang
    
    //获取规范名
    String packName5 = ClassUtils.getPackageCanonicalName(String.class);
    System.out.println(packName5); // = java.lang
    

          isAssignable(Class<?> cls, Class<?> toClass) 判断是否为转型
          isAssignable(Class<?> cls, Class<?> toClass, final boolean autoboxing)
          判断是否为转型,autoboxing是否在原语和包装器之间使用隐式自动装箱/拆箱

    System.out.println("判断是否可以转型.");
    System.out.println(ClassUtils.isAssignable(Date.class, Object.class)); //= true
    System.out.println(ClassUtils.isAssignable(Object.class, Date.class)); //=false
    

           isInnerClass(Class<?> cls) 判断是否为内部类

    System.out.println(ClassUtils.isInnerClass(Map.Entry.class)); // = ture
    System.out.println(ClassUtils.isInnerClass(Object.class)); //= false
    

           isPrimitiveWrapper(Class<?> type)     判断是否为包装类型
           isPrimitiveOrWrapper(Class<?> type) 判断是否为基础类型

    // wrapper类有(Boolean, Byte, Character, Short, Integer, Long, Double, Float).
    System.out.println(ClassUtils.isPrimitiveWrapper(Integer.class)); // = ture
    System.out.println(ClassUtils.isPrimitiveWrapper(Object.class)); // = false
    System.out.println(ClassUtils.isPrimitiveWrapper(String.class)); // = false
    System.out.println(ClassUtils.isPrimitiveWrapper(int.class)); // = false
    
    System.out.println(ClassUtils.isPrimitiveOrWrapper(Integer.class)); // = ture
    System.out.println(ClassUtils.isPrimitiveOrWrapper(Object.class)); // = false
    System.out.println(ClassUtils.isPrimitiveOrWrapper(String.class)); // = false
    System.out.println(ClassUtils.isPrimitiveOrWrapper(int.class)); // = true
    

           wrapperToPrimitive(Class<?> cls)      基础类型和包装类型转换
           wrapperToPrimitives(Class<?>… cls) 基础类型和包装类型转换

    System.out.println(ClassUtils.wrapperToPrimitive(Integer.class)); // = int
    

           convertClassesToClassNames(List<Class<?>> classes)    类名字符串转 class类
           convertClassNamesToClasses(List classNames)                class类转类名字符串

           toClass(Object… array) 对象转Class对象获取缩略名

    //类转化为class对象
    Class[] classAry = ClassUtils.toClass(new String(),new Object());
    System.out.println(ArrayUtils.toString(classAry));
    // = {class java.lang.String,class java.lang.Object}
    

          getAbbreviatedName(Class<?> cls,int len)          获取缩略名
          getAbbreviatedName(String className,int len) 获取缩略名

    //获取类的缩略名称
    String abbreviatedName1 = ClassUtils.getAbbreviatedName(String.class, 5);
    String abbreviatedName2 = ClassUtils.getAbbreviatedName(String.class, 10);
    String abbreviatedName3 = ClassUtils.getAbbreviatedName(String.class, 3);
    String abbreviatedName4 = ClassUtils.getAbbreviatedName(String.class, 20);
    System.out.println(abbreviatedName1); // j.l.String
    System.out.println(abbreviatedName2); // j.l.String
    System.out.println(abbreviatedName3); // j.l.String
    System.out.println(abbreviatedName4); // java.lang.String
    

          getAllInterfaces(Class<?> cls) 获得所有实现的接口

    //获得所有实现的接口
    List<Class<?>> classAry = ClassUtils.getAllInterfaces(String.class);
    System.out.println(ArrayUtils.toString(classAry));
    // [interface java.io.Serializable, interface java.lang.Comparable, interface java.lang.CharSequence]
    

          getAllSuperclasses(Class<?> cls) 获取所有父类

    //获得所有的父类
    List<Class<?>> classArray = ClassUtils.getAllSuperclasses(String.class);
    System.out.println(ArrayUtils.toString(classArray));
    //[class java.lang.Object]
    

          hierarchy(Class<?> type) 获取父类层级

    Iterator<Class<?>> myIterator = ClassUtils.hierarchy(ArrayList.class).iterator();
    while(myIterator.hasNext()){
    	System.out.println(myIterator.next());
    }
    // class java.util.ArrayList
    // class java.util.AbstractList
    // class java.util.AbstractCollection
    // class java.lang.Object
    
    展开全文
  • Spring中的ClassUtils详解

    2019-06-27 13:10:18
    Spring中的ClassUtils详解 转载 [Spring中的各种Utils(四):ClassUtils详解](https://blog.csdn.net/wolfcode_cn/article/details/80660552)本节中主要介绍ClassU...

    本节中主要介绍ClassUtils,这是关于类级别相关的工具类,虽然只是提供给Spring框架内部使用,但是很多方法还是有一定使用价值,并且理解这些方法的实现,也是有一定价值的。

    首先ClassUtils是一个非常大的工具类,提供了很多缓存数据和初始化内容,在涉及到相关方法的时候,再介绍。

    ClassLoader getDefaultClassLoader()
    该方法用于获取默认的类加载器;方法功能很明确,平时也用的比较多,可能平时我们要获取类加载器,就一个class.getClassLoader()就完了,我们来看看spring是如何考虑的:

    public static ClassLoader getDefaultClassLoader() {
        ClassLoader cl = null;
        try {
            //获取当前线程的context class loader
            cl = Thread.currentThread().getContextClassLoader();
        }catch (Throwable ex) {
        }
        if (cl == null) {
            // 如果没有context loader,使用当前类的类加载器;
            cl = ClassUtils.class.getClassLoader();
            if (cl == null) {
                // 如果当前类加载器无法获取,获得bootstrap ClassLoader
                try {
                    cl = ClassLoader.getSystemClassLoader();
                } catch (Throwable ex) {
                }
            }
        }
        return cl;
    }
    

    代码很简单,按照获取当前线程上下文类加载器—>获取当前类类加载器—>获取系统启动类加载器的顺序来获取;

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

    2,几种类加载器的层级关系简单说明:
    在这里插入图片描述
    级别从低到高,分别是:

    bootstrap class loader:主要负责main方法启动的时候,加载JAVA_HOME/lib下的jar包;
    extension class loader:主要负责加载JAVA_HOME/ext/lib下的jar包;
    system class loader:主要负责加载classpath下的jar包或者类;
    这里只需要明白,system class loader是比bootstrap class loader离我们应用更近;而除了bootstrap class loader,其他几个class loader都继承了Classloader类;可以获取:

      @Test
      public void testClassLoader(){
          System.out.println(Thread.currentThread().getContextClassLoader());
          System.out.println(this.getClass().getClassLoader());
          System.out.println(ClassLoader.getSystemClassLoader());
          System.out.println(ClassLoader.getSystemClassLoader().getParent());
      }
    

    打印效果类似:

    sun.misc.Launcher$AppClassLoader@36c51089
    sun.misc.Launcher$AppClassLoader@36c51089
    sun.misc.Launcher$AppClassLoader@36c51089
    sun.misc.Launcher$ExtClassLoader@43c0ae76
    

    可以看到,在一个正常应用中,类获取到的classloader就是system class loader;而extention class loader是不一样的;
    类加载器是一个很大的话题,后面还需要再额外介绍;对于我们正常的应用,我们一般得到的system class loader就可以了,除非一些特殊的应用服务器,还会有自定义的user class loader,这些就是后话了。

    ClassLoader overrideThreadContextClassLoader(ClassLoader classLoaderToUse):这个方法较为简单,使用传入的classloader替换线程的classloader;使用场景,比如一个线程的classloader和spring的classloader不一致的时候,就可以使用这个方法替换;

    Class<?> forName(String name, ClassLoader classLoader) throws ClassNotFoundException, LinkageError:看名字就知道,是Class.forName的一个增强版本;通过指定的classloader加载对应的类;除了能正常加载普通的类型,还能加载简单类型,数组,或者内部类,测试代码:

    @Test
    public void testClassforName() throws Exception{
        System.out.println(ClassUtils.forName("int",ClassUtils.getDefaultClassLoader()));
        System.out.println(ClassUtils.forName("java.lang.String[]",ClassUtils.getDefaultClassLoader()));
        System.out.println(ClassUtils.forName("java.lang.Thread.State",ClassUtils.getDefaultClassLoader()));
    }
    

    打印结果:

    int
    class [Ljava.lang.String;
    class java.lang.Thread$State
    

    可以看到,简单类型int,数组String[]和内部类(内部类通过父类.子类的方式即可获取);

    Class<?> resolveClassName(String className, ClassLoader classLoader) throws IllegalArgumentException
    和forName方法相同,内部就是直接调用的forName方法,只是抛出的异常不一样而已;
    Class<?> resolvePrimitiveClassName(String name):获取简单类型的类;这个方法是用于处理forName方法中简单类型的调用方法;

    System.out.println(ClassUtils.resolvePrimitiveClassName(“int”));
    
    打印:`int`
    

    简单看一下这个方法的实现原理:

    public static Class<?> resolvePrimitiveClassName(String name) {
        Class<?> result = null;
        // 简单类型,最长值不要超过8,如果超过8,反而可以忽略了
        if (name != null && name.length() <= 8) {
            // Could be a primitive - likely.
            result = primitiveTypeNameMap.get(name);
        }
        return result;
    }
    

    可以看到,实现方式很简单,就是把名字扔给primitiveTypeNameMap去获取,那么primitiveTypeNameMap又是什么呢?继续看代码:

    private static final Map<String, Class<?>> primitiveTypeNameMap =
          new HashMap<String, Class<?>>(32);
    //在ClassUtils类中,声明了这样一个静态的map;而他的初始化方法:
    
    static {
        primitiveWrapperTypeMap.put(Boolean.class, boolean.class);
        primitiveWrapperTypeMap.put(Byte.class, byte.class);
        primitiveWrapperTypeMap.put(Character.class, char.class);
        primitiveWrapperTypeMap.put(Double.class, double.class);
        primitiveWrapperTypeMap.put(Float.class, float.class);
        primitiveWrapperTypeMap.put(Integer.class, int.class);
        primitiveWrapperTypeMap.put(Long.class, long.class);
        primitiveWrapperTypeMap.put(Short.class, short.class);
        Set<Class<?>> primitiveTypes = new HashSet<Class<?>>(32);
        primitiveTypes.addAll(primitiveWrapperTypeMap.values());
        primitiveTypes.addAll(Arrays.asList(new Class<?>[] {
                boolean[].class, byte[].class, char[].class, double[].class,
                float[].class, int[].class, long[].class, short[].class}));
        primitiveTypes.add(void.class);
        for (Class<?> primitiveType : primitiveTypes) {
            primitiveTypeNameMap.put(primitiveType.getName(), primitiveType);
        }
    }
    

    可以看到,其实就是把简单类型对应的名字和类型直接放到primitiveTypeNameMap中即可;

    boolean isPresent(String className, ClassLoader classLoader):判断当前class loader中是否存在对应的类型了;

    Class<?> getUserClass(Class<?> clazz):获取用户定义的本来的类型,大部分情况下就是类型本身,主要针对cglib做了额外的判断,获取cglib代理之后的父类;

    boolean isCacheSafe(Class<?> clazz, ClassLoader classLoader):判断类是否是可以缓存的,原理很简单,就是判断该类型是否在指定classloader或者其parent classloader中;

    String getShortName(String className):这个方法也较为简单,得到一个全限定类名的简写,可以处理简单类型和内部类的情况:

    System.out.println(ClassUtils.getShortName(getClass()));
    System.out.println(ClassUtils.getShortName("java.lang.Thread$State"));
    

    打印结果:

    ClassUtilsTest
    Thread.State
    

    命名相关方法
    String getShortName(Class<?> clazz):得到一个类的简写;
    String getShortNameAsProperty(Class<?> clazz):得到一个类的简写,并按照属性的方式来命名;
    针对这两个方法,写一个测试就明白了:

    System.out.println(ClassUtils.getShortName(getClass()));
    System.out.println(ClassUtils.getShortNameAsProperty(getClass()));
    

    打印输出:

    ClassUtilsTest
    classUtilsTest
    

    可以看到,第二个打印首字母小写,其实并不是首字母小写,而是按照property的命名方式;这里我们可以看看spring是怎么实现的:

    public static String getShortNameAsProperty(Class<?> clazz) {
    
      String shortName = ClassUtils.getShortName(clazz);
      int dotIndex = shortName.lastIndexOf(PACKAGE_SEPARATOR);
      shortName = (dotIndex != -1 ? shortName.substring(dotIndex + 1) : shortName);
      return Introspector.decapitalize(shortName);
    }
    

    前面都很简单,去掉包名,得到最后的类名,然后最后一句代码,使用了Introspector的decapitalize,就可以按照正确的属性命名的方式来格式化类名;这是一个比较有意思的内省的工具方法,大家可以了解一下;

    String getClassFileName(Class<?> clazz):获取类对应的类的字节码文件名称,比如:

    //打印:ClassUtilsTest.class
    System.out.println(ClassUtils.getClassFileName(getClass()));
    String getPackageName(String fqClassName):根据类名得到报名;
    

    String getPackageName(Class<?> clazz):根据类得到包名,这个方法是交给getPackageName(String fqClassName)方法完成的;

    String getQualifiedName(Class<?> clazz):根据类得到类的权限定名;这个方法主要特点在于可以正确处理数组的类名;

    //输出:java.lang.String[]
    System.out.println(ClassUtils.getQualifiedName(String[].class));
    //输出:[Ljava.lang.String;
    System.out.println(String[].class.getName());
    

    String getQualifiedNameForArray(Class<?> clazz):专门用于处理数组的类名,上面getQualifiedName方法,判断如果是数组,就交给该方法处理;

    String getQualifiedMethodName(Method method):获取方法的全名,包括类的权限定名.方法名;

    String getDescriptiveType(Object value):获取一个对象的描述类型;一般来说,就是类名,能够正确处理数组,如果是JDK代理对象,能够正确输出其接口类型:

    //java.lang.Class
    System.out.println(ClassUtils.getDescriptiveType(getClass()));
    //java.lang.String[]
    System.out.println(ClassUtils.getDescriptiveType(new String[]{}));
    
    //com.sun.proxy.$Proxy20 implementing cn.wolfcode.springboot.utilstest.IEmployeeService,
    //cn.wolfcode.springboot.utilstest.IAddition,
    //org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised
    System.out.println(ClassUtils.getDescriptiveType(service));
    

    重点在最后一行,在AopUtils中我们的IEmployeeService代理对象,可以清楚的看到实现了4个接口;具体这四个接口是什么意思,可以去看看Spring中的各种Utils(三)文章;

    Constructor getConstructorIfAvailable(Class clazz, Class<?>… paramTypes)
    得到一个类的构造器方法;该方法其实就是使用了clazz.getConstructor(paramTypes)方法;只是对异常进行了拦截,在没有找到指定构造器的时候,返回null;

    boolean hasConstructor(Class<?> clazz, Class<?>… paramTypes)
    判定是否存在指定构造器;

    Method getMethod(Class<?> clazz, String methodName, Class<?>… paramTypes)
    得到类上指定的public方法;其实现也是一个标准的反射使用:

    public static Method getMethod(Class<?> clazz, String methodName, Class<?>... paramTypes) {
        Assert.notNull(clazz, "Class must not be null");
        Assert.notNull(methodName, "Method name must not be null");
        if (paramTypes != null) {
            try {
                //如果指定了参数类型,直接使用getMethod方法;
                return clazz.getMethod(methodName, paramTypes);
            }catch (NoSuchMethodException ex) {
                throw new IllegalStateException("Expected method not found: " + ex);
            }
        }else {
            Set<Method> candidates = new HashSet<Method>(1);
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                if (methodName.equals(method.getName())) {
                    candidates.add(method);
                }
            }
            if (candidates.size() == 1) {
                return candidates.iterator().next();
            }else if (candidates.isEmpty()) {
                throw new IllegalStateException("Expected method not found: " + clazz + "." + methodName);
            }else {
                throw new IllegalStateException("No unique method found: " + clazz + "." + methodName);
            }
        }
    }
    

    为什么把这段代码贴出来,其实技术含量不高,但是我们在看这段代码的时候,会比较奇怪,为什么不能使用clazz.getMethod(methodName)来获取没有参数列表的方法呢?而要选择通过clazz.getMethods()先得到所有方法,然后再去对比方法名的方式来获取没有参数列表的方法呢?其实就是为了处理重载的异常而已;这些细节我觉得都展示出了在写一个框架或者只是一个工具类的时候的严谨思路;

    boolean hasMethod(Class<?> clazz, String methodName, Class<?>… paramTypes)
    判断类是否有指定的public方法;

    Method getMethodIfAvailable(Class<?> clazz, String methodName, Class<?>… paramTypes)
    该方法类似Method getMethod(Class<?> clazz, String methodName, Class<?>… paramTypes) 方法,只是当出现重载的情况,不会抛出异常,返回找到的第一个方法返回;

    int getMethodCountForName(Class<?> clazz, String methodName)
    获取指定类中匹配该方法名称的方法个数,包括非public方法;

    boolean hasAtLeastOneMethodWithName(Class<?> clazz, String methodName)
    判定指定的类及其父类中是否包含指定方法名称的方法,包括非public方法;我们又来看看这个方法的实现:

    public static boolean hasAtLeastOneMethodWithName(Class<?> clazz, String methodName) {
        Assert.notNull(clazz, "Class must not be null");
        Assert.notNull(methodName, "Method name must not be null");
        Method[] declaredMethods = clazz.getDeclaredMethods();
        //判断当前类是否包含指定方法;
        for (Method method : declaredMethods) {
            if (method.getName().equals(methodName)) {
                return true;
            }
        }
        Class<?>[] ifcs = clazz.getInterfaces();
        //递归判断当前类实现的接口上是否有该方法;
        for (Class<?> ifc : ifcs) {
            if (hasAtLeastOneMethodWithName(ifc, methodName)) {
                return true;
            }
        }
        //递归判定当前类的父类上是否有该方法;
        return (clazz.getSuperclass() != null 
            && hasAtLeastOneMethodWithName(clazz.getSuperclass(), methodName));
    }
    

    同样,代码非常简单,但是也看出了细节的完整性;

    Method getMostSpecificMethod(Method method, Class<?> targetClass)
    获得最匹配的一个可以执行的方法;比如传入IEmployeeService.someLogic方法,在EmployeeServiceImpl类上找到匹配的EmployeeServiceImpl.someLogic方法,这个方法是一个可以执行的方法;

    Method interfaceMethod=ClassUtils.getMethod(IEmployeeService.class, "someLogic");
    System.out.println(ClassUtils.getQualifiedMethodName(interfaceMethod));
    Method targetMethod=ClassUtils.getMostSpecificMethod(interfaceMethod, EmployeeServiceImpl.class);
    System.out.println(ClassUtils.getQualifiedMethodName(targetMethod));
    

    打印效果:

    cn.wolfcode.springboot.utilstest.IEmployeeService.someLogic
    cn.wolfcode.springboot.utilstest.EmployeeServiceImpl.someLogic
    

    boolean isUserLevelMethod(Method method)
    该方法用于判定一个方法是否是用户可用的方法,我们先简单来看看这个方法的实现:

    public static boolean isUserLevelMethod(Method method) {
            Assert.notNull(method, "Method must not be null");
            return (method.isBridge() || (!method.isSynthetic() && !isGroovyObjectMethod(method)));
        }
    

    方法实现很简单,就是三个判定,但这三个判定是什么意思?
    1,method.isBridge:判定一个方法是否是桥接方法。什么是bridge方法?这个是JDK1.5引入了泛型之后的概念。为了让1.5泛型方法和1.5之前的字节码保持兼容,编译器会自动的生成桥接方法;
    比如,我们有这样一个接口:

    public interface IGenericInterface<T> {
    
        T get(T param);
    }
    
    //加入一个实现:
    public class ConcreateClass implements IGenericInterface<String> {
    
        @Override
        public String get(String param) {
            return "hello";
        }
    }
    

    那实际上,在字节码级别,是怎么处理这个方法调用的呢?我们这里就不去展示具体的字节码,其实,JVM在ConcreateClass中生成了一个额外的方法:

    public Object get(Object param){
        return this.get((String)param);
    }
    

    那么这个get方法就可以称为桥接(bridge method)方法;

    2,method. isSynthetic方法:判定一个方法是否是虚构方法(synthetic method);什么是synthetic方法?由编译器创建的,非默认构造方法(我们知道,类都有默认构造方法,当然重载了默认构造方法的除外,编译器都会生成一个默认构造方法的实现)在源码中没有对应的方法实现的方法都是虚构方法。比如上面介绍的bridge方法就是一个典型的synthetic方法;

    3,isGroovyObjectMethod:判定一个方法是否是Groovy的方法,因为Spring支持Groovy,而Groovy的类都实现了groovy.lang.GroovyObject类;关于Groovy可以自己去了解一下;

    Method getStaticMethod(Class<?> clazz, String methodName, Class<?>… args)
    针对给定的类和方法名字,参数类型列表,得到一个对应的static方法,这个方法很简单,但是我又忍不住给大家看一下这个方法的实现:

    public static Method getStaticMethod(Class<?> clazz, String methodName, Class<?>... args) {
        try {
            Method method = clazz.getMethod(methodName, args);
            return Modifier.isStatic(method.getModifiers()) ? method : null;
        }
        catch (NoSuchMethodException ex) {
            return null;
        }
    }
    

    其中,Modifier是一个我们很少接触的类;这个类可以用来判定修饰符:

    这些方法都只需要传入通过Method.getModifiers或者Class.getModifiers等获取的修饰符,即可进行各种判定;

     boolean isPrimitiveWrapper(Class<?> clazz)
    判定一个类是否是简单类型的包装类;

     boolean isPrimitiveOrWrapper(Class<?> clazz)
    判定一个类是否是简单类型,或者是简单类型的包装类型;

     boolean isPrimitiveArray(Class<?> clazz)
    判定一个类是否是简单类型的数组;

     boolean isPrimitiveWrapperArray(Class<?> clazz)
    判定一个类是否是简单类型的包装类型数组;

     Class<?> resolvePrimitiveIfNecessary(Class<?> clazz)
    如果传入的类型是一个简单类型,返回这个简单类型的包装类型;

     String convertResourcePathToClassName(String resourcePath)
    把文件路径形式变成包路径形式;

     String convertClassNameToResourcePath(String className)
    把类路径形式变成文件路径形式;

     String classPackageAsResourcePath(Class<?> clazz)
    把指定类的包从包路径形式变为文件路径形式;

     String addResourcePathToPackagePath(Class<?> clazz, String resourceName)
    在指定类的所属包下面,寻找一个资源文件,并返回该资源文件的文件路径;

    //打印:cn/wolfcode/springboot/utilstest/someResource.xml
    System.out.println(ClassUtils.addResourcePathToPackagePath(IEmployeeService.class,
    “someResource.xml”));

    String classNamesToString(Collection<Class<?>> classes)
    把一组指定的类类名连成一个字符串;主要用于日志相关处理;

     Class<?>[] toClassArray(Collection

     boolean isVisible(Class<?> clazz, ClassLoader classLoader)
    判断一个类型是否在指定类加载器中可见。

      Class<?>[] getAllInterfaces(Object instance)
    获取一个对象的所有接口;
     Class<?>[] getAllInterfacesForClass(Class<?> clazz)
    获取一个类型的所有接口;
     Class<?>[] getAllInterfacesForClass(Class<?> clazz, ClassLoader classLoader)
    获取指定类加载器下的指定类型的所有接口;
     Set<Class<?>> getAllInterfacesAsSet(Object instance)
    获取一个对象的所有接口,返回Set;
     Set<Class<?>> getAllInterfacesForClassAsSet(Class<?> clazz)
    获取一个类的所有接口,返回Set;
     Set<Class<?>> getAllInterfacesForClassAsSet(Class<?> clazz, ClassLoader classLoader)
    获取指定类加载器下的指定类型的所有接口;返回Set,这个方法才是上面5个方法的调用方法;
    简单来看一下代码实现:

    public static Set<Class<?>> getAllInterfacesForClassAsSet(Class<?> clazz, ClassLoader classLoader) {
        Assert.notNull(clazz, "Class must not be null");
        //如果本身就是接口,返回自己
        if (clazz.isInterface() && isVisible(clazz, classLoader)) {
            return Collections.<Class<?>>singleton(clazz);
        }
        Set<Class<?>> interfaces = new LinkedHashSet<Class<?>>();
        //使用递归获取类继承体系上所有接口;
        while (clazz != null) {
            Class<?>[] ifcs = clazz.getInterfaces();
            for (Class<?> ifc : ifcs) {
                interfaces.addAll(getAllInterfacesForClassAsSet(ifc, classLoader));
            }
            clazz = clazz.getSuperclass();
        }
        return interfaces;
    }
    

    Class<?> createCompositeInterface(Class<?>[] interfaces, ClassLoader classLoader)
    返回一个类型,该类型实现了所有给定的接口;这个方法可能乍一看很难理解,我们先来看看他的实现:

    public static Class<?> createCompositeInterface(Class<?>[] interfaces, ClassLoader classLoader) {
        Assert.notEmpty(interfaces, "Interfaces must not be empty");
        Assert.notNull(classLoader, "ClassLoader must not be null");
        return Proxy.getProxyClass(classLoader, interfaces);
    }
    

    可以看到,实际上,该方法只是调用了Proxy.getProxyClass方法。那么getProxyClass方法又是干什么的呢?

    Class<?> getProxyClass(ClassLoader loader,
    Class<?>… interfaces)
    该方法返回一个代理类型,并且该类型实现了指定的接口。注意,如果这组接口在对应的classloader中已经被代理过,返回的是相同的代理类型。意思就是如果A,B,C三个接口已经通过Proxy A 实 现 了 动 态 代 理 , 以 后 通 过 g e t P r o x y C l a s s ( A , B , C ) 获 取 到 的 是 相 同 的 P r o x y A实现了动态代理,以后通过getProxyClass(A,B,C)获取到的是相同的Proxy AgetProxyClass(A,B,C)ProxyA这个类型;
    那这个方法有什么用呢?我们先不说Spring里面使用他来干什么,我们知道,JDK的动态代理就是使用Proxy来完成的,以前我们使用最多的方法就是Proxy.newInstance(ClassLoader classloader,Class[] interfaces,InvocationHandler ih)来创建一个动态代理对象,而所有的动态代理对象都继承了Proxy类:

    public class Proxy implements java.io.Serializable {
        private static final Class<?>[] constructorParams =
            { InvocationHandler.class };
        protected InvocationHandler h;
        private Proxy() {
        }
    
        /**
         * Constructs a new {@code Proxy} instance from a subclass
         * (typically, a dynamic proxy class) with the specified value
         * for its invocation handler.
         *
         * @param   h the invocation handler for this proxy instance
         */
        protected Proxy(InvocationHandler h) {
            doNewInstanceCheck();
            this.h = h;
        }
        //...
    }
    

    所以,其实我们还可以这样来写:

    @Test
    public void testProxys() throws Exception {
        Class<?> clzz = ClassUtils.createCompositeInterface(
                new Class[] { IEmployeeService.class, IAddition.class },
                ClassUtils.getDefaultClassLoader());
        Object proxyObj = clzz
                .getConstructor(new Class[] { InvocationHandler.class })
                .newInstance(new MyInvocationHandler());
    }
    

    这段代码等价于:

    Object proxyObj=Proxy.newInstance(
        ClassUtils.getDefaultClassLoader(),
        new Class[]{IEmployeeService.class,IAddition.class},
        new MyInvocationHandler());
    

    在Spring中,该方法用于返回代理类的固定类型;比如,ProxyFactoryBean中,因为实现了FactoryBean接口,那么其getObjectType方法,就应该使用createCompositeInterface方法,固定返回代理的类型;

    原文:Spring中的各种Utils(四):ClassUtils详解

    展开全文
  • 原创文章,转载请注明出处本节中主要介绍ClassUtils,这是关于类级别相关的工具类,虽然只是提供给Spring框架内部使用,但是很多方法还是有一定使用价值,并且理解这些方法的实现,也是有一定价值的。首先ClassUtils...

    原创文章,转载请注明出处

    本节中主要介绍ClassUtils,这是关于类级别相关的工具类,虽然只是提供给Spring框架内部使用,但是很多方法还是有一定使用价值,并且理解这些方法的实现,也是有一定价值的。

    首先ClassUtils是一个非常大的工具类,提供了很多缓存数据和初始化内容,在涉及到相关方法的时候,再介绍。

    • ClassLoader getDefaultClassLoader()

    该方法用于获取默认的类加载器;方法功能很明确,平时也用的比较多,可能平时我们要获取类加载器,就一个class.getClassLoader()就完了,我们来看看spring是如何考虑的:

    public static ClassLoader getDefaultClassLoader() {
        ClassLoader cl = null;
        try {
            //获取当前线程的context class loader
            cl = Thread.currentThread().getContextClassLoader();
        }catch (Throwable ex) {
        }
        if (cl == null) {
            // 如果没有context loader,使用当前类的类加载器;
            cl = ClassUtils.class.getClassLoader();
            if (cl == null) {
                // 如果当前类加载器无法获取,获得bootstrap ClassLoader
                try {
                    cl = ClassLoader.getSystemClassLoader();
                } catch (Throwable ex) {
                }
            }
        }
        return cl;
    }
    

    代码很简单,按照获取当前线程上下文类加载器—>获取当前类类加载器—>获取系统启动类加载器的顺序来获取;

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

    2,几种类加载器的层级关系简单说明:
    0_1319366276S7Uf.gif
    级别从低到高,分别是:

    • bootstrap class loader:主要负责main方法启动的时候,加载JAVA_HOME/lib下的jar包;
    • extension class loader:主要负责加载JAVA_HOME/ext/lib下的jar包;
    • system class loader:主要负责加载classpath下的jar包或者类;
      这里只需要明白,system class loader是比bootstrap class loader离我们应用更近;而除了bootstrap class loader,其他几个class loader都继承了Classloader类;可以获取:
        @Test
        public void testClassLoader(){
            System.out.println(Thread.currentThread().getContextClassLoader());
            System.out.println(this.getClass().getClassLoader());
            System.out.println(ClassLoader.getSystemClassLoader());
            System.out.println(ClassLoader.getSystemClassLoader().getParent());
        }
      
      打印效果类似:
      sun.misc.Launcher$AppClassLoader@36c51089
      sun.misc.Launcher$AppClassLoader@36c51089
      sun.misc.Launcher$AppClassLoader@36c51089
      sun.misc.Launcher$ExtClassLoader@43c0ae76
      
      可以看到,在一个正常应用中,类获取到的classloader就是system class loader;而extention class loader是不一样的;

    类加载器是一个很大的话题,后面还需要再额外介绍;对于我们正常的应用,我们一般得到的system class loader就可以了,除非一些特殊的应用服务器,还会有自定义的user class loader,这些就是后话了。


    • ClassLoader overrideThreadContextClassLoader(ClassLoader classLoaderToUse):这个方法较为简单,使用传入的classloader替换线程的classloader;使用场景,比如一个线程的classloader和spring的classloader不一致的时候,就可以使用这个方法替换;

    • Class<?> forName(String name, ClassLoader classLoader) throws ClassNotFoundException, LinkageError:看名字就知道,是Class.forName的一个增强版本;通过指定的classloader加载对应的类;除了能正常加载普通的类型,还能加载简单类型,数组,或者内部类,测试代码:

    @Test
    public void testClassforName() throws Exception{
        System.out.println(ClassUtils.forName("int",ClassUtils.getDefaultClassLoader()));
        System.out.println(ClassUtils.forName("java.lang.String[]",ClassUtils.getDefaultClassLoader()));
        System.out.println(ClassUtils.forName("java.lang.Thread.State",ClassUtils.getDefaultClassLoader()));
    }
    

    打印结果:

    int
    class [Ljava.lang.String;
    class java.lang.Thread$State
    

    可以看到,简单类型int,数组String[]和内部类(内部类通过父类.子类的方式即可获取);

    • Class<?> resolveClassName(String className, ClassLoader classLoader) throws IllegalArgumentException
      和forName方法相同,内部就是直接调用的forName方法,只是抛出的异常不一样而已;

    Class<?> resolvePrimitiveClassName(String name):获取简单类型的类;这个方法是用于处理forName方法中简单类型的调用方法;
    System.out.println(ClassUtils.resolvePrimitiveClassName(“int”));
    打印:int

    简单看一下这个方法的实现原理:

    public static Class<?> resolvePrimitiveClassName(String name) {
        Class<?> result = null;
        // 简单类型,最长值不要超过8,如果超过8,反而可以忽略了
        if (name != null && name.length() <= 8) {
            // Could be a primitive - likely.
            result = primitiveTypeNameMap.get(name);
        }
        return result;
    }
    

    可以看到,实现方式很简单,就是把名字扔给primitiveTypeNameMap去获取,那么primitiveTypeNameMap又是什么呢?继续看代码:

    private static final Map<String, Class<?>> primitiveTypeNameMap =
          new HashMap<String, Class<?>>(32);
    

    在ClassUtils类中,声明了这样一个静态的map;而他的初始化方法:

    static {
        primitiveWrapperTypeMap.put(Boolean.class, boolean.class);
        primitiveWrapperTypeMap.put(Byte.class, byte.class);
        primitiveWrapperTypeMap.put(Character.class, char.class);
        primitiveWrapperTypeMap.put(Double.class, double.class);
        primitiveWrapperTypeMap.put(Float.class, float.class);
        primitiveWrapperTypeMap.put(Integer.class, int.class);
        primitiveWrapperTypeMap.put(Long.class, long.class);
        primitiveWrapperTypeMap.put(Short.class, short.class);
        Set<Class<?>> primitiveTypes = new HashSet<Class<?>>(32);
        primitiveTypes.addAll(primitiveWrapperTypeMap.values());
        primitiveTypes.addAll(Arrays.asList(new Class<?>[] {
                boolean[].class, byte[].class, char[].class, double[].class,
                float[].class, int[].class, long[].class, short[].class}));
        primitiveTypes.add(void.class);
        for (Class<?> primitiveType : primitiveTypes) {
            primitiveTypeNameMap.put(primitiveType.getName(), primitiveType);
        }
    

    }

    可以看到,其实就是把简单类型对应的名字和类型直接放到primitiveTypeNameMap中即可;


    • boolean isPresent(String className, ClassLoader classLoader):判断当前class loader中是否存在对应的类型了;

    • Class<?> getUserClass(Class<?> clazz):获取用户定义的本来的类型,大部分情况下就是类型本身,主要针对cglib做了额外的判断,获取cglib代理之后的父类;

    • boolean isCacheSafe(Class<?> clazz, ClassLoader classLoader):判断类是否是可以缓存的,原理很简单,就是判断该类型是否在指定classloader或者其parent classloader中;

    • String getShortName(String className):这个方法也较为简单,得到一个全限定类名的简写,可以处理简单类型和内部类的情况:

    System.out.println(ClassUtils.getShortName(getClass()));
    System.out.println(ClassUtils.getShortName("java.lang.Thread$State"));
    

    打印结果:

    ClassUtilsTest
    Thread.State
    

    命名相关方法

    • String getShortName(Class<?> clazz):得到一个类的简写;
    • String getShortNameAsProperty(Class<?> clazz):得到一个类的简写,并按照属性的方式来命名;
      针对这两个方法,写一个测试就明白了:

      System.out.println(ClassUtils.getShortName(getClass()));
      System.out.println(ClassUtils.getShortNameAsProperty(getClass()));
      

      打印输出:

      ClassUtilsTest
      classUtilsTest
      可以看到,第二个打印首字母小写,其实并不是首字母小写,而是按照property的命名方式;这里我们可以看看spring是怎么实现的:

      public static String getShortNameAsProperty(Class<?> clazz) {

        String shortName = ClassUtils.getShortName(clazz);
        int dotIndex = shortName.lastIndexOf(PACKAGE_SEPARATOR);
        shortName = (dotIndex != -1 ? shortName.substring(dotIndex + 1) : shortName);
        return Introspector.decapitalize(shortName);
      

      }
      前面都很简单,去掉包名,得到最后的类名,然后最后一句代码,使用了Introspector的decapitalize,就可以按照正确的属性命名的方式来格式化类名;这是一个比较有意思的内省的工具方法,大家可以了解一下;

    • String getClassFileName(Class<?> clazz):获取类对应的类的字节码文件名称,比如:

    //打印:ClassUtilsTest.class
    System.out.println(ClassUtils.getClassFileName(getClass()));
    
    • String getPackageName(String fqClassName):根据类名得到报名;

    • String getPackageName(Class<?> clazz):根据类得到包名,这个方法是交给getPackageName(String fqClassName)方法完成的;

    • String getQualifiedName(Class<?> clazz):根据类得到类的权限定名;这个方法主要特点在于可以正确处理数组的类名;

    //输出:java.lang.String[]
    System.out.println(ClassUtils.getQualifiedName(String[].class));
    //输出:[Ljava.lang.String;
    System.out.println(String[].class.getName());
    
    • String getQualifiedNameForArray(Class<?> clazz):专门用于处理数组的类名,上面getQualifiedName方法,判断如果是数组,就交给该方法处理;

    • String getQualifiedMethodName(Method method):获取方法的全名,包括类的权限定名.方法名;


    String getDescriptiveType(Object value):获取一个对象的描述类型;一般来说,就是类名,能够正确处理数组,如果是JDK代理对象,能够正确输出其接口类型:

    //java.lang.Class
    System.out.println(ClassUtils.getDescriptiveType(getClass()));
    //java.lang.String[]
    System.out.println(ClassUtils.getDescriptiveType(new String[]{}));
    
    //com.sun.proxy.$Proxy20 implementing cn.wolfcode.springboot.utilstest.IEmployeeService,
    //cn.wolfcode.springboot.utilstest.IAddition,
    //org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised
    System.out.println(ClassUtils.getDescriptiveType(service));
    

    重点在最后一行,在AopUtils中我们的IEmployeeService代理对象,可以清楚的看到实现了4个接口;具体这四个接口是什么意思,可以去看看Spring中的各种Utils(三)文章;


    Constructor<T> getConstructorIfAvailable(Class<T> clazz, Class<?>… paramTypes)
    得到一个类的构造器方法;该方法其实就是使用了clazz.getConstructor(paramTypes)方法;只是对异常进行了拦截,在没有找到指定构造器的时候,返回null;

    boolean hasConstructor(Class<?> clazz, Class<?>… paramTypes)
    判定是否存在指定构造器;

    Method getMethod(Class<?> clazz, String methodName, Class<?>… paramTypes)
    得到类上指定的public方法;其实现也是一个标准的反射使用:

    public static Method getMethod(Class<?> clazz, String methodName, Class<?>... paramTypes) {
        Assert.notNull(clazz, "Class must not be null");
        Assert.notNull(methodName, "Method name must not be null");
        if (paramTypes != null) {
            try {
                //如果指定了参数类型,直接使用getMethod方法;
                return clazz.getMethod(methodName, paramTypes);
            }catch (NoSuchMethodException ex) {
                throw new IllegalStateException("Expected method not found: " + ex);
            }
        }else {
            Set<Method> candidates = new HashSet<Method>(1);
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                if (methodName.equals(method.getName())) {
                    candidates.add(method);
                }
            }
            if (candidates.size() == 1) {
                return candidates.iterator().next();
            }else if (candidates.isEmpty()) {
                throw new IllegalStateException("Expected method not found: " + clazz + "." + methodName);
            }else {
                throw new IllegalStateException("No unique method found: " + clazz + "." + methodName);
            }
        }
    }
    

    为什么把这段代码贴出来,其实技术含量不高,但是我们在看这段代码的时候,会比较奇怪,为什么不能使用clazz.getMethod(methodName)来获取没有参数列表的方法呢?而要选择通过clazz.getMethods()先得到所有方法,然后再去对比方法名的方式来获取没有参数列表的方法呢?其实就是为了处理重载的异常而已;这些细节我觉得都展示出了在写一个框架或者只是一个工具类的时候的严谨思路;

    boolean hasMethod(Class<?> clazz, String methodName, Class<?>… paramTypes)
    判断类是否有指定的public方法;

    Method getMethodIfAvailable(Class<?> clazz, String methodName, Class<?>… paramTypes)
    该方法类似Method getMethod(Class<?> clazz, String methodName, Class<?>… paramTypes) 方法,只是当出现重载的情况,不会抛出异常,返回找到的第一个方法返回;

    int getMethodCountForName(Class<?> clazz, String methodName)
    获取指定类中匹配该方法名称的方法个数,包括非public方法;

    boolean hasAtLeastOneMethodWithName(Class<?> clazz, String methodName)
    判定指定的类及其父类中是否包含指定方法名称的方法,包括非public方法;我们又来看看这个方法的实现:

    public static boolean hasAtLeastOneMethodWithName(Class<?> clazz, String methodName) {
        Assert.notNull(clazz, "Class must not be null");
        Assert.notNull(methodName, "Method name must not be null");
        Method[] declaredMethods = clazz.getDeclaredMethods();
        //判断当前类是否包含指定方法;
        for (Method method : declaredMethods) {
            if (method.getName().equals(methodName)) {
                return true;
            }
        }
        Class<?>[] ifcs = clazz.getInterfaces();
        //递归判断当前类实现的接口上是否有该方法;
        for (Class<?> ifc : ifcs) {
            if (hasAtLeastOneMethodWithName(ifc, methodName)) {
                return true;
            }
        }
        //递归判定当前类的父类上是否有该方法;
        return (clazz.getSuperclass() != null 
            && hasAtLeastOneMethodWithName(clazz.getSuperclass(), methodName));
    }
    

    同样,代码非常简单,但是也看出了细节的完整性;


    Method getMostSpecificMethod(Method method, Class<?> targetClass)
    获得最匹配的一个可以执行的方法;比如传入IEmployeeService.someLogic方法,在EmployeeServiceImpl类上找到匹配的EmployeeServiceImpl.someLogic方法,这个方法是一个可以执行的方法;

    Method interfaceMethod=ClassUtils.getMethod(IEmployeeService.class, "someLogic");
    System.out.println(ClassUtils.getQualifiedMethodName(interfaceMethod));
    Method targetMethod=ClassUtils.getMostSpecificMethod(interfaceMethod, EmployeeServiceImpl.class);
    System.out.println(ClassUtils.getQualifiedMethodName(targetMethod));
    

    打印效果:

    cn.wolfcode.springboot.utilstest.IEmployeeService.someLogic
    cn.wolfcode.springboot.utilstest.EmployeeServiceImpl.someLogic
    

    boolean isUserLevelMethod(Method method)
    该方法用于判定一个方法是否是用户可用的方法,我们先简单来看看这个方法的实现:

    public static boolean isUserLevelMethod(Method method) {
            Assert.notNull(method, "Method must not be null");
            return (method.isBridge() || (!method.isSynthetic() && !isGroovyObjectMethod(method)));
        }
    

    方法实现很简单,就是三个判定,但这三个判定是什么意思?
    1,method.isBridge:判定一个方法是否是桥接方法。什么是bridge方法?这个是JDK1.5引入了泛型之后的概念。为了让1.5泛型方法和1.5之前的字节码保持兼容,编译器会自动的生成桥接方法;
    比如,我们有这样一个接口:

    public interface IGenericInterface<T> {
    
        T get(T param);
    }
    
    //加入一个实现:
    public class ConcreateClass implements IGenericInterface<String> {
    
        @Override
        public String get(String param) {
            return "hello";
        }
    }
    

    那实际上,在字节码级别,是怎么处理这个方法调用的呢?我们这里就不去展示具体的字节码,其实,JVM在ConcreateClass中生成了一个额外的方法:

    public Object get(Object param){
        return this.get((String)param);
    }
    

    那么这个get方法就可以称为桥接(bridge method)方法;

    2,method. isSynthetic方法:判定一个方法是否是虚构方法(synthetic method);什么是synthetic方法?由编译器创建的,非默认构造方法(我们知道,类都有默认构造方法,当然重载了默认构造方法的除外,编译器都会生成一个默认构造方法的实现)在源码中没有对应的方法实现的方法都是虚构方法。比如上面介绍的bridge方法就是一个典型的synthetic方法;

    3,isGroovyObjectMethod:判定一个方法是否是Groovy的方法,因为Spring支持Groovy,而Groovy的类都实现了groovy.lang.GroovyObject类;关于Groovy可以自己去了解一下;


    Method getStaticMethod(Class<?> clazz, String methodName, Class<?>… args)
    针对给定的类和方法名字,参数类型列表,得到一个对应的static方法,这个方法很简单,但是我又忍不住给大家看一下这个方法的实现:

    public static Method getStaticMethod(Class<?> clazz, String methodName, Class<?>... args) {
        try {
            Method method = clazz.getMethod(methodName, args);
            return Modifier.isStatic(method.getModifiers()) ? method : null;
        }
        catch (NoSuchMethodException ex) {
            return null;
        }
    }
    

    其中,Modifier是一个我们很少接触的类;这个类可以用来判定修饰符:
    image.png
    这些方法都只需要传入通过Method.getModifiers或者Class.getModifiers等获取的修饰符,即可进行各种判定;


    boolean isPrimitiveWrapper(Class<?> clazz)
    判定一个类是否是简单类型的包装类;

    boolean isPrimitiveOrWrapper(Class<?> clazz)
    判定一个类是否是简单类型,或者是简单类型的包装类型;

    boolean isPrimitiveArray(Class<?> clazz)
    判定一个类是否是简单类型的数组;

    boolean isPrimitiveWrapperArray(Class<?> clazz)
    判定一个类是否是简单类型的包装类型数组;

    Class<?> resolvePrimitiveIfNecessary(Class<?> clazz)
    如果传入的类型是一个简单类型,返回这个简单类型的包装类型;

    String convertResourcePathToClassName(String resourcePath)
    把文件路径形式变成包路径形式;

    String convertClassNameToResourcePath(String className)
    把类路径形式变成文件路径形式;

    String classPackageAsResourcePath(Class<?> clazz)
    把指定类的包从包路径形式变为文件路径形式;

    String addResourcePathToPackagePath(Class<?> clazz, String resourceName)
    在指定类的所属包下面,寻找一个资源文件,并返回该资源文件的文件路径;

    //打印:cn/wolfcode/springboot/utilstest/someResource.xml
    System.out.println(ClassUtils.addResourcePathToPackagePath(IEmployeeService.class,
        "someResource.xml"));
    

    String classNamesToString(Collection<Class<?>> classes)
    把一组指定的类类名连成一个字符串;主要用于日志相关处理;

    Class<?>[] toClassArray(Collection<Class<?>> collection)
    将类集合变成类型数组;

    boolean isVisible(Class<?> clazz, ClassLoader classLoader)
    判断一个类型是否在指定类加载器中可见。

    Class<?>[] getAllInterfaces(Object instance)
    获取一个对象的所有接口;
    Class<?>[] getAllInterfacesForClass(Class<?> clazz)
    获取一个类型的所有接口;
    Class<?>[] getAllInterfacesForClass(Class<?> clazz, ClassLoader classLoader)
    获取指定类加载器下的指定类型的所有接口;
    Set<Class<?>> getAllInterfacesAsSet(Object instance)
    获取一个对象的所有接口,返回Set;
    Set<Class<?>> getAllInterfacesForClassAsSet(Class<?> clazz)
    获取一个类的所有接口,返回Set;
    Set<Class<?>> getAllInterfacesForClassAsSet(Class<?> clazz, ClassLoader classLoader)
    获取指定类加载器下的指定类型的所有接口;返回Set,这个方法才是上面5个方法的调用方法;简单来看一下代码实现:

    public static Set<Class<?>> getAllInterfacesForClassAsSet(Class<?> clazz, ClassLoader classLoader) {
        Assert.notNull(clazz, "Class must not be null");
        //如果本身就是接口,返回自己
        if (clazz.isInterface() && isVisible(clazz, classLoader)) {
            return Collections.<Class<?>>singleton(clazz);
        }
        Set<Class<?>> interfaces = new LinkedHashSet<Class<?>>();
        //使用递归获取类继承体系上所有接口;
        while (clazz != null) {
            Class<?>[] ifcs = clazz.getInterfaces();
            for (Class<?> ifc : ifcs) {
                interfaces.addAll(getAllInterfacesForClassAsSet(ifc, classLoader));
            }
            clazz = clazz.getSuperclass();
        }
        return interfaces;
    }
    

    Class<?> createCompositeInterface(Class<?>[] interfaces, ClassLoader classLoader) 
    返回一个类型,该类型实现了所有给定的接口;这个方法可能乍一看很难理解,我们先来看看他的实现:

    public static Class<?> createCompositeInterface(Class<?>[] interfaces, ClassLoader classLoader) {
        Assert.notEmpty(interfaces, "Interfaces must not be empty");
        Assert.notNull(classLoader, "ClassLoader must not be null");
        return Proxy.getProxyClass(classLoader, interfaces);
    }
    

    可以看到,实际上,该方法只是调用了Proxy.getProxyClass方法。那么getProxyClass方法又是干什么的呢?

    Class<?> getProxyClass(ClassLoader loader,
                                         Class<?>... interfaces)
    

    该方法返回一个代理类型,并且该类型实现了指定的接口。注意,如果这组接口在对应的classloader中已经被代理过,返回的是相同的代理类型。意思就是如果A,B,C三个接口已经通过Proxy$A实现了动态代理,以后通过getProxyClass(A,B,C)获取到的是相同的Proxy$A这个类型;
    那这个方法有什么用呢?我们先不说Spring里面使用他来干什么,我们知道,JDK的动态代理就是使用Proxy来完成的,以前我们使用最多的方法就是Proxy.newInstance(ClassLoader classloader,Class[] interfaces,InvocationHandler ih)来创建一个动态代理对象,而所有的动态代理对象都继承了Proxy类:

    public class Proxy implements java.io.Serializable {
        private static final Class<?>[] constructorParams =
            { InvocationHandler.class };
        protected InvocationHandler h;
        private Proxy() {
        }
    
        /**
         * Constructs a new {@code Proxy} instance from a subclass
         * (typically, a dynamic proxy class) with the specified value
         * for its invocation handler.
         *
         * @param   h the invocation handler for this proxy instance
         */
        protected Proxy(InvocationHandler h) {
            doNewInstanceCheck();
            this.h = h;
        }
        //...
    }
    

    所以,其实我们还可以这样来写:

    @Test
    public void testProxys() throws Exception {
        Class<?> clzz = ClassUtils.createCompositeInterface(
                new Class[] { IEmployeeService.class, IAddition.class },
                ClassUtils.getDefaultClassLoader());
        Object proxyObj = clzz
                .getConstructor(new Class[] { InvocationHandler.class })
                .newInstance(new MyInvocationHandler());
    }
    

    这段代码等价于:

    Object proxyObj=Proxy.newInstance(
        ClassUtils.getDefaultClassLoader(),
        new Class[]{IEmployeeService.class,IAddition.class},
        new MyInvocationHandler());
    

    在Spring中,该方法用于返回代理类的固定类型;比如,ProxyFactoryBean中,因为实现了FactoryBean接口,那么其getObjectType方法,就应该使用createCompositeInterface方法,固定返回代理的类型;


    小结

    ClassUtils中包含了大量的方法,虽然很多方法在平时的开发中使用不到,但是其中涉及到的类加载器,代理等内容,当去看每一个方法的实现的时候,又会收获很多;


    展开全文
  • org.apache.commons.lang3.ClassUtils 源码中文注释,之后会加上其他的工具类注释,方便以后复习阅读
  • Spring-框架-ClassUtils类isAssignable方法

    千次阅读 2019-10-18 11:44:27
    isAssignable方法位于spring的org.springframework.util工具包的ClassUtils类中下 源码: /** * Check if the right-hand side type may be assigned to the left-hand side * type, assuming se...
  • deduceFromClasspath方法由枚举WebApplicationType提供,具体实现如下: static WebApplicationType deduceFromClasspath() { if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.is...
  • 接口,成员变量,方法等都抽象成对象的机制,而commons-lang3包下提供的工具类,更简单,容易的操作这些对象,我们就用几个代码示例,来了解下ClassUtils类。 ClassUtils 是取得类信息的工具类,例如,包名,类名,...
  • java组件开发(9)ClassUtils
  • 接上一篇,本篇针对ClassUtils.forName()方法进行研究。源码是这样事儿的: public static Class<?> forName(String name, ClassLoader classLoader) throws ClassNotFoundException, LinkageError { ...
  • 开心一笑【向美术教师交作业时,一位学生之交了一张白纸. 老师问:”画呢?” 学生答:”这呀!”他指着白纸说....”】提出问题Lang3中的ClassUtils类如何使用???解决问题获取包名getPackageName(Class获取类名
  • public class ClassUtils { //给一个接口,返回这个接口的所有实现类 public static List&lt;Class&gt; getAllClassByInterface(Class c){ List&lt;Class&gt; returnClassList = new ArrayList&...
  • 在static加个文件文件就ok,不信你看看~
  • Caused by: java.lang.NoSuchMethodError: org.springframework.util.ClassUtils.isJavaLanguageInterface(Ljava/lang/Class;)Z at org.springframework.beans.CachedIntrospectionResults.(CachedIntrospecti...
  • org.springframework.util.ClassUtils.getXXX错误解决方案 目前网上资料太杂乱 说一下解决思路,因为spring版本不同,所以得先找到错误的代码处 看是哪个jar包 一般这个报错的方法只要是在springjar包内的都是...
  • 问题出现<!...<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> <!-- 配置事务通
  • java web程序放linux上报错java.lang.NoSuchMethodError: org.springframework.util.ClassUtils.forName(Ljava/lang/String;)Ljava/lang/Class; at org.springframework.web.context.ContextLoader....
  • 1.引入了两个core包,remove一个 2.applicationcontext中xsd的版本和引入的不同,将3.1改成3.0
  • 搭建SSJ项目结构时遇到了一个异常,记录一下(排了好久) java.lang.NoClassDefFoundError: org/springframework/util/ClassUtils at org.springframework.test.context.junit4.Spr...
  • java.lang.NoClassDefFoundError: org/springframework/util/ClassUtils at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.&lt;clinit&gt;(SpringJUnit4ClassRunner.java:101) at sun...
  • java.lang.NoClassDefFoundError: Could not initialize class utils.MyBatisUtil at test.UserMapperTest.test(UserMapperTest.java:30) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.r...
  • maven+springmvc 中tomcat启动报java.lang.NoSuchMethodError: org.springframework.util.ClassUtils.getMethod  严重: StandardWrapper.Throwable java.lang.NoSuchMethodError: org.springframework.util....
  • 1、Spring版本冲突; 2、找到使用冲突的地方,把低版本去除  由于今天用到了spring和cxf,3.0的包与其他版本包冲突导致,附件是相应的架包,花了两个豆,何必呢。
  • 包冲突,classUtils在spring-core包中,检查spring-core包引用是不是重复了
  • 问题描述:添加如下即发生java.lang.NoSuchMethodError: org.springframework.util.ClassUtils.isUserLevelMethod 错误原因:未添加asm.jar,
  • spring工具类ClassUtils使用

    千次阅读 2017-05-08 09:55:57
    获取到的class或interface为如下的形式: class org.apache.commons.lang3.ClassUtils 需要获得的形式:org.apache.commons.lang3.ClassUtils或者ClassUtilsClassUtils提供的这样的方法: ClassUtils.getShortName()...
  • 由一个接口找他实现类(ClassUtils)

    千次阅读 2010-11-01 14:42:00
    import java.io.File; import java.io.IOException; import java.net.URL; import java.util.ArrayList;...public class ClassUtils { public static List<Class> getAllClassByInterface(Class c) {
  • ]public class TravelModel { @Required @Column(desc="卡类型",allowedValues={"0","1"},defaultValue="0") private String cardType; @Required @...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 264,784
精华内容 105,913
关键字:

ClassUtils