Spring解惑1

相信用过spring的人肯定写过这样一段代码:

    Resource resource = new FileSystemResource("res/springconf.xml");
     BeanFactory factory = new XmlBeanFactory(resource);

     HelloBean helloBean = (HelloBean)factory.getBean("helloBean");
  
     System.out.println(helloBean.getSpringInfo());
     
     当然Bean和XML有可能不同,下面贴出我写的一个简单的HelloBean和一个简单的spring配置文件springconf.xml:
HelloBean.java:

package com.spring.entrance;

public class HelloBean
{
 private String springInfo;
 
 /**
  * 显示的public构造函数
  */
 public HelloBean()
 {
  System.out.println("构造函数初始化");
 }
 
 /**
  *
  * setter
  *
  * @param info
  */
 public void setSpringInfo(String info)
 {
  this.springInfo = info;
 }
 
 /**
  *
  * getter
  *
  * @return
  */
 public String getSpringInfo()
 {
  return springInfo;
 }
}
springconf.xml:

<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
 
<beans>   
 <bean name="helloBean" class="com.spring.entrance.HelloBean">
  <property name="springInfo" value="hello,spring" />
 </bean>
</beans>

    大家仔细看一下,我特意在构造函数这里添加了一句打印信息,因为我想看看spring容器是如何初始化对象的。输出结果显而易见,首先执行执行了构造函数的打印语句,然后打印出了配置文件注入的属性值。不过马上我要和大家讨论的是,如果我将HelloBean的构造函数改成私有的构造函数 结果会怎么样呢?
    于是我将HelloBean的构造函数的修饰符public改成了private ,然后运行,看结果,令我很诧异,构造函数中的打印语句仍然执行 。让我诧异的原因是spring容器竟然可以调用我的私有构造函数 。起初,我想到这个问题是因为在做项目的时候,我想做一个单例的管理器,即构造函数私有,然后利用懒汉式或者饿汉式的方式来创建单例对象。不过因为项目的原因,一些类的实例化工作是利用spring做的,即你只需要在xml中配置一个bean即可。
    这让我对spring容器有了莫大的兴趣,发现之前对spring的理解有许多的不足,下面我就解惑spring到底是如何创建对象的:
    首先可以肯定的是,spring是利用反射机制 来创建对象的,可到底是怎么样利用反射创建的?下面我再写一个demo:

package com.spring.entrance;

import java.lang.reflect.Constructor;

/**
 *
 * 利用反射构造私有类对象
 *
 * @author landon
 *
 */
public class ReflectConstructDemo
{
 public static void main(String...args)
 {
  try
  {
   Constructor [] constructors = Class.forName("com.spring.entrance.HelloBean").getDeclaredConstructors();
   
   for(int i = 0;i < constructors.length;i++)
   {
    constructors[i].newInstance();
   }
  }
  catch(SecurityException e)
  {
   System.out.println(e.getMessage());
  }
  catch(ClassNotFoundException e)
  {
   System.out.println(e.getMessage());
  }
  catch(Exception e)
  {
   e.printStackTrace();
  }
 }
}

     注意这里,HelloBean的构造函数依然是私有的。然后我们看运行结果:

java.lang.IllegalAccessException: Class com.spring.entrance.ReflectConstructDemo can not access a member of class com.spring.entrance.HelloBean with modifiers "private"
 at sun.reflect.Reflection. ensureMemberAccess( Unknown Source)
 at java.lang.reflect.Constructor.newInstance(Unknown Source)
 at com.spring.entrance.ReflectConstructDemo.main(ReflectConstructDemo.java:22)

    运行结果出现异常,提示是不能访问私有的构造函数,那下面该怎么办呢?我在newInstance之前加上一句代码:constructors[i].setAccessible(true); 然后运行程序,我们发现顺利的打印出了私有构造函数的语句
    看来确实可以利用反射机制来运行时构造一个类,而不管其是private有的还是public的。如果大家有兴趣的话,还可以反编译一下Constructor 的源码:

public transient Object newInstance(Object aobj[])
        throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
    {
        if(!override && !Reflection.quickCheckMemberAccess(clazz, modifiers))
        {
            Class class1 = Reflection.getCallerClass(2);
            if(securityCheckCache != class1)
            {
                Reflection.ensureMemberAccess (class1, clazz, null, modifiers);
                securityCheckCache = class1;
            }
        }
        if((clazz.getModifiers() & 16384) != 0)
            throw new IllegalArgumentException("Cannot reflectively create enum objects");
        if(constructorAccessor == null)
            acquireConstructorAccessor();
        return constructorAccessor.newInstance(aobj);
    }

     我们可以看到第一次没有加上constructors[i].setAccessible(true) 这句代码之前的运行结果抛出的异常就是由ensureMemberAccess这个方法调用所抛出来的IllegalAccessException。

你可能感兴趣的:(spring,bean,xml,配置管理,项目管理)