解析反射

  反射离不开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(www.gcyl159.com/);
  
  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(www.xinhuiyule1.cn);
  
  }
  
  public loadClass方法的作用为给定一个类名,加载一个类,返回代表这个类的Class实例,如果找不到类,则返回ClassNotFoundException异常。它调用了protected loadClass方法。源码如下(加了注释)
  
  protected Class<www.mren2yule.com> 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(www.yongshiyule178.com 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 www.dfgjpt.com 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(www.yongshi123.cn).addElapsedTimeFrom(www.dfgjyl.cn1);
  
  sun.misc.PerfCounter.getFindClasses(www.120xh.cn).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 !=www.078881.cn 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<www.jiahuayulpt.com> findBootstrapClassOrNull(String name)
  
  {
  
  if (!checkName(name)) return null;
  
  return findBootstrapClass(name);
  
  }
  
  // return null if not found
  
  private native Class<www.thd178.com/> 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[www.taincguoji.com] 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实例)中调用类本身的实例。

转载于:https://www.cnblogs.com/qwangxiao/p/10508481.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值