创建和实例化bean总是分不清?装配和注入总是弄混?

522 阅读4分钟

一、实例化Bean的三种方式

1、通过构造函数实例化

通过构造方法实例化bean通常使用的是默认的无参构造函数,如果使用自定义的有参构造函数则需要自行配置bean元素的子元素类型。

public class UserService {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public String say() {
        return name;
    }
}
<bean id="userService" class="com...UserService">
 <property name="name" value="Spring"></property>
</bean>

2、通过静态工厂方法实例化

通过静态工厂方法实例化bean,bean标签与上没什么太大区别,只多了一个factory-method属性,属性值为工厂方法名称。(这里的代码将构造方法设为了private,目的是不允许外部类直接通过new来创建对象)

public class UserService {
    private String name;

    private Integer age;

    private UserService(String name) {
        this.name = name;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public static UserService createInstance(String name) {
        return new UserService(name);
    }
}
<bean id="userService" class="com...UserService" factory-method="createInstance">
 <constructor-arg value="Spring"></constructor-arg>
 <property name="age" value="1"></property>
</bean>

3、通过实例工厂方法实例化

通过实例工厂方法实例化,会从容器中调用工厂bean,从而调用它的工厂方法来创建新的bean,factory-bean属性值为工厂bean,factory-method属性值为工厂方法。

public class UserService {
    private String name;

    private Integer age;

    public UserService(String name) {
        this.name = name;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

public class Servicefactory {

    public UserService createServiceInstance(String name) {
        return new UserService(name);
    }
}
<bean id="userService" factory-bean="servicefactory" factory-method="createServiceInstance">
 <constructor-arg value="Spring"></constructor-arg>
 <property name="age" value="1"></property>
</bean>

<bean id="servicefactory" class="bean.Servicefactory"></bean>

二、创建和实例化bean的关系

看了上面的代码和配置有同学可能就很疑问?这不就是创建bean么?这就是创建bean,实例化bean是创建bean的一个步骤,上述代码和配置是创建bean,同时也指定了bean在实例化时是选择工厂方法实例化还是构造方法实例化。(为方便查看,下面代码省略了一些检查之类的)

    protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
        //配置的回调方法,通过回调方法创建bean,感兴趣的搜索:Spring 自动创建bean或者registerBean,建议谷歌
        Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
        if (instanceSupplier != null) {
            return this.obtainFromSupplier(instanceSupplier, beanName);
        } else if (mbd.getFactoryMethodName() != null) {
            //通过工厂方法创建
            return this.instantiateUsingFactoryMethod(beanName, mbd, args);
        } else {
            //类可能有多个构造函数,根据配置的参数选择对应的构造函数来实例化
            boolean resolved = false;
            boolean autowireNecessary = false;
            if (args == null) {
                synchronized (mbd.constructorArgumentLock) {
                    if (mbd.resolvedConstructorOrFactoryMethod != null) {
                        resolved = true;
                        autowireNecessary = mbd.constructorArgumentsResolved;
                    }
                }
            }

            if (resolved) {
                //根据autowireNecessary判断是选择构造函数自动注入还是默认构造函数
                return autowireNecessary ? this.autowireConstructor(beanName, mbd, (Constructor[]) null, (Object[]) null) : this.instantiateBean(beanName, mbd);
            } else {
                //参数解析后,和上面差不多的判断
                Constructor<?>[] ctors = this.determineConstructorsFromBeanPostProcessors(beanClass, beanName);
                if (ctors == null && mbd.getResolvedAutowireMode() != 3 && !mbd.hasConstructorArgumentValues() && ObjectUtils.isEmpty(args)) {
                    ctors = mbd.getPreferredConstructors();
                    return ctors != null ? this.autowireConstructor(beanName, mbd, ctors, (Object[]) null) : this.instantiateBean(beanName, mbd);
                } else {
                    return this.autowireConstructor(beanName, mbd, ctors, args);
                }
            }
        }
    }

三、两种依赖注入方式

1、基于构造函数的依赖注入

基于构造函数的依赖注入是通过调用具有参数的构造函数来进行依赖注入。

public class Food {
    private String name;

    public Food(String name) {
        this.name = name;
    }
}

public class Eat {
    private Food food;

    public Eat(Food food) {
        this.food = food;
    }
}
<bean id="food" class="com...Food">
 <constructor-arg value="apple"></constructor-arg>
</bean>
<bean id="eat" class="com...Eat">
 <constructor-arg ref="food"></constructor-arg>
</bean>

2、基于Setter的依赖注入

基于Setter的依赖注入使用过创建bean后调用对应的Setter方法来进行依赖注入。

public class Food {
    private String name;

    public void setName(String name) {
        this.name = name;
    }
}

public class Eat {
    private Food food;

    public void setFood(Food food) {
        this.food = food;
    }
}
<bean id="food" class="bean.Food">
 <property name="name" value="apple"></property>
</bean>
<bean id="eat" class="bean.Eat">
 <property name="food" ref="food"></property>
</bean>

四、装配和注入的关系

创建应用对象之间协作关系的行为通常称为装配,是依赖注入DI的本质——《Spring 实战》,可这样理解为装配是注入的具体行为,也即是说Spring中的装配方式实现必须靠构造器和Setter依赖注入来实现,Spring中提供了三种方式装配:自动装配、Java配置类装配、XML装配;五种装配策略:

public interface AutowireCapableBeanFactory{
 
 //无需自动装配
 int AUTOWIRE_NO = 0;
 
 //按名称自动装配bean属性
 int AUTOWIRE_BY_NAME = 1;
 
 //按类型自动装配bean属性
 int AUTOWIRE_BY_TYPE = 2;
 
 //按构造器自动装配
 int AUTOWIRE_CONSTRUCTOR = 3;
 
 //过时方法,Spring3.0之后不再支持
 @Deprecated
 int AUTOWIRE_AUTODETECT = 4;
}

五、结语

下一篇文章会写Spring创建bean的各种方式和详细写一下装配方式与装配策略(篇幅太长会分为两篇)

若是哪里有理解错误的或写错的地方,望各位读者评论指正,不胜感激。

有兴趣的小伙伴可关注我的公众号:JavaCodeWking,文章也会同步到掘金,努力持续更新