反射离不开Class.forName(),我们先从Class.forName说起。
上一篇我们说要得到一个类的实例有4个方法:new,反射,克隆,反序列化。
反射可以跟new一个对象有相同的效果。例如
public class Company { private String a; private String b; @Override public String toString() { return "Company{" + "a='" + a + '\'' + ", b='" + b + '\'' + '}'; } public Company() { this.a = "A"; this.b = "B"; } }
public class CompanyInstance { private static Company company = new Company(); public static void main(String[] args) { System.out.println(company); } }
运行结果
Company{a='A', b='B'}
又可以写成如下
public class CompanyInstance { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException { System.out.println(Class.forName("com.guanjian.Company").newInstance()); } }
运行结果
Company{a='A', b='B'}
虽然效果一样,但他们的过程并不一样。 首先,newInstance( )是一个方法,而new是一个关键字;其次,Class下的newInstance()的使用有局限,因为它生成对象只能调用无参的构造函数,而使用 new关键字生成对象没有这个限制。
newInstance()的时候是使用的上篇说的类装载机制的,它会走完全部过程。具体可以看 浅析类装载 ,而new一个实例的时候,走的流程不太一样,它会先在JVM内部先去寻找该类的Class实例,然后依照该Class实例的定义,依葫芦画瓢,把该类的实例给生成出来。但如果找不到该类的Class实例,则会走上篇说的装载流程。 其中JDK的Class实例一般是在jvm启动时用启动类加载器完成加载,用户的Class实例则是在用到的时候再加载。
Class.forName()被重载为有一个参数和三个参数的,我们来看一下其源码
@CallerSensitive public static Class<?> forName(String className) throws ClassNotFoundException { Class<?> caller = Reflection.getCallerClass(); return forName0(className, true, ClassLoader.getClassLoader(caller), caller); }
@CallerSensitive public static Class<?> forName(String name, boolean initialize, ClassLoader loader) throws ClassNotFoundException { Class<?> caller = null; SecurityManager sm = System.getSecurityManager(); if (sm != null) { // Reflective call to get caller class is only needed if a security manager // is present. Avoid the overhead of making this call otherwise. caller = Reflection.getCallerClass(); if (sun.misc.VM.isSystemDomainLoader(loader)) { ClassLoader ccl = ClassLoader.getClassLoader(caller); if (!sun.misc.VM.isSystemDomainLoader(ccl)) { sm.checkPermission( SecurityConstants.GET_CLASSLOADER_PERMISSION); } } } return forName0(name, initialize, loader, caller); }
其中Reflection.getCallerClass()源码如下
@CallerSensitive public static native Class<?> getCallerClass();
这是一个跟C语言交互的,用户无权限调用的方法,只能被 bootstrap class loader 和 extension class loader 调用的,这两个加载类后面再说。意思是返回调用者的Class实例。
private static native Class<?> forName0(String name, boolean initialize, ClassLoader loader, Class<?> caller) throws ClassNotFoundException;
它的第二个参数boolean initialize表示是否要初始化该类,单参Class.forName()默认true是要初始化的,三参的Class.forName()由你自己选择。一旦初始化,就会触发目标对象的 static块代码执行,static参数也也会被再次初始化。当然如果你使用了三个参数的Class.forName(),并调用了newInstance()以后,是肯定会初始化的。
public class CompanyInstance { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException { System.out.println(Class.forName("com.guanjian.Company",false,Thread.currentThread().getContextClassLoader()).newInstance()); } }
运行结果
Company{a='A', b='B'}
现在我们重点要说的是它的ClassLoader,这个才是真正装载类的核心组件。所有的Class都是由ClassLoader进行加载的,ClassLoader负责通过各种方式将Class信息的二进制字节码数据流读入系统,然后交给JVM虚拟机进行连接、初始化等操作。ClassLoader是一个抽象类,我们来看一下它的部分源码。
public abstract class ClassLoader { private static native void registerNatives(); static { registerNatives(); } // The parent class loader for delegation // Note: VM hardcoded the offset of this field, thus all new fields // must be added *after* it. private final ClassLoader parent;
我们来看一下它部分对外公开的public方法。
public Class<?> loadClass(String name) throws ClassNotFoundException { return loadClass(name, false); }
static ClassLoader getClassLoader(Class<?> caller) { // This can be null if the VM is requesting it if (caller == null) { return null; } // Circumvent security check since this is package-private return caller.getClassLoader0(); }
public loadClass方法的作用为给定一个类名,加载一个类,返回代表这个类的Class实例,如果找不到类,则返回ClassNotFoundException异常。它调用了protected loadClass方法。源码如下(加了注释)
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { //对这个名称产生一个锁对象,并进行加锁处理 synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded //findLoadedClass的底层也是C语言交互实现的,应该是在JVM内存中查找该类的Class实例 Class<?> c = findLoadedClass(name); //如果在JVM内存中找不到该类的Class实例 if (c == null) { long t0 = System.nanoTime(); try {//parent为该加载器的双亲,具体会在后面介绍,如果双亲对象不为null,使用双亲加载 if (parent != null) { c = parent.loadClass(name, false); } else { //如果找不到双亲,启用最高权限的BootstrapClassLoader加载,BootstrapClassLoader在Java中没有对象,是用C语言实现的 c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } //如果找不到最高权限的加载器 if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); //直接抛出异常 c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } //如果在JVM内存中找到该类的Class实例,当前加载器自己处理 if (resolve) { resolveClass(c); } return c; } }
加锁处理片段代码
private final ConcurrentHashMap<String, Object> parallelLockMap;
protected Object getClassLoadingLock(String className) { //加载器对象赋给一个锁对象 Object lock = this; //如果该hashmap不为空 if (parallelLockMap != null) { //产生一把新锁 Object newLock = new Object(); //如果该hashmap中存在className的key,则返回key的value,如果不存在,则将className和newLock作为key,value放入hashmap中,返回null lock = parallelLockMap.putIfAbsent(className, newLock); if (lock == null) { lock = newLock; } } return lock; }
查找最高权限加载器源码
private Class<?> findBootstrapClassOrNull(String name) { if (!checkName(name)) return null; return findBootstrapClass(name); } // return null if not found private native Class<?> findBootstrapClass(String name);
findClass源码
protected Class<?> findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); }
resolveClass源码
protected final void resolveClass(Class<?> c) { resolveClass0(c); } private native void resolveClass0(Class<?> c);
ClassLoader的分类
在标准的Java程序中,Java虚拟机会创建3类ClassLoader为整个应用程序服务。它们分别是:BootStrap ClassLoader(启动类加载器),Extension ClassLoader(扩展类加载器),App ClassLoader(应用类加载器,也称为系统类加载器)。此外,每一个应用程序还可以拥有自定义的ClassLoader,扩展Java虚拟机获取Class数据的能力。其中,应用类加载器的双亲为扩展类加载器,扩展类加载器的双亲为启动类加载器。当系统需要使用一个类时,在判断类是否已经被加载时,会先从当前底层类加载器进行判断。当系统需要加载一个类时,会从顶层类开始加载,依次向下尝试,直到成功。
这里根类加载器即为启动类加载器。通过代码验证
public class PrintClassLoaderTree { public static void main(String[] args) { ClassLoader cl = PrintClassLoaderTree.class.getClassLoader(); while (cl != null) { System.out.println(cl); cl = cl.getParent(); } System.out.println(String.class.getClassLoader()); } }
运行结果
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@4554617c
null
由此可知,PrintClassLoaderTree用户类加载于AppClassLoader中,而AppClassLoader的双亲为ExtClassLoader.而从ExtClassLoader无法再取得启动类加载器,因为这是一个纯C实现。因此,任何加载在启动类加载器中的类时无法获得其ClassLoader实例的,比如String属于Java核心类,因此会被启动类加载器加载,所以最后一条打印为null.
因为后面还有一些双亲委托的东西,这个不是我的重点,就不重点写了。重点是结合我之前的一篇文章,做一个解析,见 @Compenent,@Autowired,@PostConstruct自实现
程序主入口
public class Test { public static void main(String[] args) { Manager.scanAndImp("com.guanjian.test"); Test2 test2 = (Test2)Manager.getBean(Test2.class); test2.show(); } }
Manager.scanAndImp("com.guanjian.test")代码如下
public static void scanAndImp(String basePackage) { ClassHelper.setClassSet(basePackage); BeanHelper.setBeanMap(); IocHelper.ioc(); }
/** * 定义类集合(用于存放所加载的类) * @param basePackage */ private static Set<Class<?>> CLASS_SET; /** * 扫描所有的包,获取类集合放入CLASS_SET * @param basePackage */ public static void setClassSet(String basePackage) { CLASS_SET = ClassUtil.getClassSet(basePackage); }
很明显,第一步是扫描包,获取所有的Class实例,我们根据前面的介绍知道,要装载类,就必须要有一个类装载器,而这个类装载器就是
/** * 获取类加载器 * @return */ public static ClassLoader getClassLoader() { return Thread.currentThread().getContextClassLoader(); }
因为要装载的类都是我们自己写的类,而不是系统类,所以此时在JVM内存中是肯定没有它们的Class实例的,而装载它们的肯定也就是应用类装载器。
/** * 获取制定包名下的所有类 * @param packageName * @return */ public static Set<Class<?>> getClassSet(String packageName) { ...
代码就不详细分析了,只要知道这里使用了加载器装载了这些类,并产生了Class实例,但并未初始化。其中调用了方法
private static void doAddClass(Set<Class<?>> classSet,String className) { Class<?> cls = loadClass(className,false); classSet.add(cls); }
/** * 加载类 * @param className * @param isInitialized * @return */ public static Class<?> loadClass(String className,boolean isInitialized) { Class<?> cls; try { cls = Class.forName(className,isInitialized,getClassLoader()); } catch (ClassNotFoundException e) { LOGGER.error("load class failure",e); throw new RuntimeException(e); } return cls; }
即为我们之前说的Class.forName()的三参形式。这些所有的Class实例被放入了一个HashSet集合中,即为private static Set<Class<?>> CLASS_SET;
这样第一条语句ClassHelper.setClassSet(basePackage);就分析完了。
--------------------------------------------------------------------------------------------------
然后是第二条语句BeanHelper.setBeanMap();
/** * 获取所有Class实例跟类本身实例的映射关系 */ public static void setBeanMap() { Set<Class<?>> beanClassSet = ClassHelper.getBeanClassSet(); for (Class<?> beanClass:beanClassSet) { Object obj = ReflectionUtil.newInstance(beanClass); BEAN_MAP.put(beanClass,obj); } }
/** * 定义Bean映射(用于存放Bean类与Bean实例的映射关系) */ private static final Map<Class<?>,Object> BEAN_MAP = new HashMap<Class<?>, Object>();
/** * 获取应用包名下所有Bean类 * @return */ public static Set<Class<?>> getBeanClassSet() { Set<Class<?>> beanClassSet = new HashSet<Class<?>>(); beanClassSet.addAll(getComponentClassSet()); return beanClassSet; }
/** * 获取应用包名下所有Comonent类 * @return */ public static Set<Class<?>> getComponentClassSet() { Set<Class<?>> classSet = new HashSet<Class<?>>(); for (Class<?> cls:CLASS_SET) { //这个Class实例是否带有@Component标签 if (cls.isAnnotationPresent(Component.class)) { classSet.add(cls); } } return classSet; }
/** * 创建实例 * @param cls * @return */ public static Object newInstance(Class<?> cls) { Object instance; try { instance = cls.newInstance(); } catch (Exception e) { LOGGER.error("new instance failure",e); throw new RuntimeException(e); } return instance; }
这里是被@Component标签识别加载的称为bean,我们之前的确获取了所有的类,并且加载了,但并没有初始化。但有一些可能并没有打上@Component标签的就不能称为bean.我们需要对有@Component标签的进行初始化。把bean类跟带有@Component的类分离,是为了方便扩展,以后有其他的标签的可以方便修改。
BeanHelper.setBeanMap();的意思就是把所有的bean都给初始化了,并建立了一个Class实例跟bean类本身的实例的映射关系的HashMap.其中cls.isAnnotationPresent(Component.class)就是检测Class实例是否被我们自定义的标签@Component标记,这是一个比较重点的地方吧。
--------------------------------------------------------------------------------------------------------
然后是第三条语句IocHelper.ioc();
public static void ioc(){ //获取所有的Bean类与Bean实例之间的映射关系 Map<Class<?>,Object> beanMap = BeanHelper.getBeanMap(); if (CollectionUtil.isNotEmpty(beanMap)) { //遍历Bean Map for (Map.Entry<Class<?>,Object> beanEntry:beanMap.entrySet()) { //从BeanMap中获取Bean类与Bean实例 Class<?> beanClass = beanEntry.getKey(); Object beanInstance = beanEntry.getValue(); //获取Bean类定义的所有成员变量 Field[] beanFields = beanClass.getDeclaredFields(); if (ArrayUtil.isNotEmpty(beanFields)) { //遍历Bean Field for (Field beanField:beanFields) { //判断当前Bean Field是否带有Autowired注解 if (beanField.isAnnotationPresent(Autowired.class)) { //获取当前Bean Field的Class实例 Class<?> beanFieldClass = beanField.getType(); //通过该Class实例在HashMap中获取对应的Bean Field类本身的实例,此时并没有设置到Field中 //且注意beanFieldInstance是beanInstance某个属性的实例,他们不是同一个实例 Object beanFieldInstance = beanMap.get(beanFieldClass); if (beanFieldInstance != null) { //通过反射初始化BeanField的值,把在HashMap中找到的Bean类实例设置给beanInstance //的beanField ReflectionUtil.setField(beanInstance,beanField,beanFieldInstance); } } } } Method[] beanMethods = beanClass.getMethods(); if (ArrayUtil.isNotEmpty(beanMethods)) { //遍历method for (Method method:beanMethods) { //判断当前注解是否有PostConstruct注解 if (method.isAnnotationPresent(PostConstruct.class)) { try { //执行该方法 method.invoke(beanInstance,null); } catch (Exception e) { e.printStackTrace(); } } } } } } }
/** * 获取Class实例和Bean类本身实例的映射 * @return */ public static Map<Class<?>,Object> getBeanMap() { return BEAN_MAP; }
/** * 设置成员变量值 * @param obj * @param field * @param value */ public static void setField(Object obj, Field field,Object value) { try { //如果field在类中为private,则必须setAccessible(true)才可以在反射中正常访问 field.setAccessible(true); //obj为要注入的Bean类的实例,value为该Bean类的field字段的要注入的值 field.set(obj,value); } catch (IllegalAccessException e) { LOGGER.error("set field failure",e); throw new RuntimeException(e); } }
这里主要是为了实现IOC依赖注入(@Autowired标签)以及@PostConstruct标签方法的自动运行。其中Class<?> beanFieldClass = beanField.getType();用来获取类的属性的Class实例,比较重要,再去HashMap中查找该Class实例对应的Bean类本身的实例,这里Class实例跟类本身的实例一定要分清楚。然后ReflectionUtil.setField(beanInstance,beanField,beanFieldInstance);把找到的实例设置给要设置的Bean类的field属性,完成初始化。同样method.invoke(beanInstance,null);也是调用beanInstance自身的方法。
总体思路就是在Class实例中查找各种属性,方法以及类自定义标签、属性自定义标签,方法自定义标签,再结合类本身的实例,通过Class实例的Field,method进行属性赋值,方法运行,这些又都必须在反射(Class实例)中调用类本身的实例。
具体谈一下反射在抽象工厂模式下的应用。
抽象工厂模式下有3个概念,抽象工厂,抽象零件,抽象产品。它是一个以抽象对抽象的处理,无需具体的实现类参与。
具体例子可以参考 设计模式整理