相信用过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();
}
}
}
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。