今天讨论的是一点注意事项,大家都知道spring 一个很大的有点就是ioc,DI,今天想着接口注入能不能在属性配置中配置接口,然后看下回报出什么错误?相关测试配置如下:
<bean id="documentManager" class="com.itheima12.spring.iocdi.document.DocumentManager">
<!--
该属性是一个接口
-->
<property name="document">
<ref bean="document"/>
</property>
</bean>
<bean id="document" class="com.itheima12.spring.iocdi.document.Document"></bean>
<bean id="wordDocument" class="com.itheima12.spring.iocdi.document.WordDocument"></bean>
----------------------------------------------------------------------------
public interface Document {
public void readDocument();
public void writeDocument();
}
----------------------------------------------------------
public class DocumentManager {
private Document document;
public Document getDocument() {
return document;
}
public void setDocument(Document document) {
this.document = document;
}
public void readDocument(){
this.document.readDocument();
}
public void writeDocument(){
this.document.writeDocument();
}
}
------------------------------------------------------------------
这里只说明下关键信息点,我们spring 读取xml配置属性创建beandefination对象,在popule 注入中,创建bean注入引用类型对象,AbstractAutowireCapableBeanFactory类中createBean,
protected Object createBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
if (logger.isDebugEnabled()) {
logger.debug("Creating instance of bean '" + beanName + "'");
}
// Make sure bean class is actually resolved at this point.
resolveBeanClass(mbd, beanName);
// Prepare method overrides.
try {
mbd.prepareMethodOverrides();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(mbd.getResourceDescription(),
beanName, "Validation of method overrides failed", ex);
}
try {
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
Object bean = resolveBeforeInstantiation(beanName, mbd);
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
}
Object beanInstance = doCreateBean(beanName, mbd, args);
if (logger.isDebugEnabled()) {
logger.debug("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
创建我们实体的bean,调用resolveBeforeInstantiation方法,首先判断是否是class类型,如果是则进行进一步bean解析创建,代码如下:
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
// Make sure bean class is actually resolved at this point.
if (mbd.hasBeanClass() && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
bean = applyBeanPostProcessorsBeforeInstantiation(mbd.getBeanClass(), beanName);
if (bean != null) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
mbd.beforeInstantiationResolved = (bean != null);
}
return bean;
}
所以我们上述配置并不会创建bean,因为在创建bean时首先判断该类是否是class类型,所以执行Object beanInstance = doCreateBean(beanName, mbd, args);方法,创建bena对象调用createBeanInstance方法,
public Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) {
// Don't override the class with CGLIB if no overrides.
if (beanDefinition.getMethodOverrides().isEmpty()) {
Constructor<?> constructorToUse;
synchronized (beanDefinition.constructorArgumentLock) {
constructorToUse = (Constructor<?>) beanDefinition.resolvedConstructorOrFactoryMethod;
if (constructorToUse == null) {
final Class clazz = beanDefinition.getBeanClass();
if (clazz.isInterface()) {
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
try {
if (System.getSecurityManager() != null) {
constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor>() {
public Constructor run() throws Exception {
return clazz.getDeclaredConstructor((Class[]) null);
}
});
}
else {
constructorToUse = clazz.getDeclaredConstructor((Class[]) null);
}
beanDefinition.resolvedConstructorOrFactoryMethod = constructorToUse;
}
catch (Exception ex) {
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
}
}
}
return BeanUtils.instantiateClass(constructorToUse);
}
else {
// Must generate CGLIB subclass.
return instantiateWithMethodInjection(beanDefinition, beanName, owner);
}
}
在此方法中final Class clazz = beanDefinition.getBeanClass();,调试可以知道clazz对象interface com.itheima12.spring.iocdi.document.Document,然后判断是否为借口,那么此时判断为true,抛出BeanInstantiationException(clazz, "Specified class is an interface");异常,所以根据探究源码可以得出,我们在注入接口时要指向它的实现类,这点大家注意,如果配置指向实现类则ok,
<bean id="documentManager" class="com.itheima12.spring.iocdi.document.DocumentManager">
<!--
该属性是一个接口
-->
<property name="document">
<ref bean="document"/>
</property>
</bean>
<bean id="document" class="com.itheima12.spring.iocdi.document.wordDocument"></bean>
调试出真知,有不正确的地方请大家指出,希望大家交流。