Bean的实例化

(与service层无关,换句话 与DI无关,主要讲解IoC创建bean的原理)

Spring创建bean时调用的是无参构造方法,而私有方法能被调用 因为底层用了反射。用构造方法来实例化对象。

对于Spring的报错,从下往上看

1 Bean是如何创建的【理解】

bean本质上就是对象,创建bean使用构造方法完成

2 实例化Bean的三种方式

2.1 构造方法方式【重点】
  • BookDaoImpl实现类
public class BookDaoImpl implements BookDao {
    public BookDaoImpl() {
        System.out.println("book dao constructor is running ....");
    }
    public void save() {
        System.out.println("book dao save ...");
    }
}
  • applicationContext.xml配置
<!--方式一:构造方法实例化bean-->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
  • AppForInstanceBook测试类
public class AppForInstanceBook {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        BookDao bookDao = (BookDao) ctx.getBean("bookDao");

        bookDao.save();
    }
}
  • 运行结果

注意:**无参构造方法如果不存在,将抛出异常BeanCreationException**

2.2 静态工厂方式实例化bean【了解】

我们需要的是工厂里面的OrderDao对象===>配置工厂类名和方法名

  • OrderDao接口和OrderDaoImpl实现类
public interface OrderDao {
    public void save();
}
public class OrderDaoImpl implements OrderDao {
    public void save() {
        System.out.println("order dao save ...");
    }
}
  • OrderDaoFatory工厂类
//静态工厂创建对象
public class OrderDaoFactory {
    public static OrderDao getOrderDao(){
        System.out.println("static factory setup....");
        return new OrderDaoImpl();
    }
}
  • applicationContext.xml配置
<!--方式二:使用静态工厂实例化bean-->
<bean id="orderDao" class="com.itheima.factory.OrderDaoFactory" factory-method="getOrderDao"/>

springboot有参构造函数注入 spring构造方法注入bean_java

  • AppForInstanceOrder测试类
public class AppForInstanceOrder {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        OrderDao orderDao = (OrderDao) ctx.getBean("orderDao");

        orderDao.save();
    }
}
  • 运行结果
2.3 实例工厂方式【了解】

和静态工厂类似,多一个配置步骤 factory-bean="userFactory

  • UserDao接口和UserDaoImpl实现类
public interface UserDao {
    public void save();
}
public class UserDaoImpl implements UserDao {
    public void save() {
        System.out.println("user dao save ...");
    }
}
  • UserDaoFactory工厂类
//实例工厂创建对象
public class UserDaoFactory {
    public UserDao getUserDao(){
        return new UserDaoImpl();
    }
}
  • applicationContext.xml配置
<!--方式三:使用实例工厂实例化bean-->
<bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/>

<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>

springboot有参构造函数注入 spring构造方法注入bean_java_02

  • AppForInstanceUser测试类
public class AppForInstanceUser {
    public static void main(String[] args) {
        //        //创建实例工厂对象
        //        UserDaoFactory userDaoFactory = new UserDaoFactory();
        //        //通过实例工厂对象创建对象
        //        UserDao userDao = userDaoFactory.getUserDao();
        //        userDao.save();
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao = (UserDao) ctx.getBean("userDao");
        userDao.save();
    }
}
  • 运行结果
2.4 实现FactoryBean<T>方式【扩展,了解】
  • 定义UserDaoFactoryBean实现FactoryBean<UserDao>

UserDaoFactoryBean中实例化什么类型的对象泛型就是该类型。

//FactoryBean创建对象
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
    //代替原始实例工厂中创建对象的方法
    public UserDao getObject() throws Exception {
        return new UserDaoImpl();
    }

    public Class<?> getObjectType() {
        return UserDao.class;
    }
}
  • applicationContext.xml配置
<!--方式四:使用FactoryBean实例化bean-->
<bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean"/>

使用之前的AppForInstanceUser测试类去运行看结果就行了。注意配置文件中id="userDao"是否重复。

代码
package com.itheima;

import com.itheima.dao.OrderDao;
import com.itheima.dao.UserDao;
import com.itheima.factory.OrderDaoFactory;
import com.itheima.factory.UserDaoFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AppForInstanceOrder {
    //使用工厂创建对象
    public static void main(String[] args) {
        //方法二  静态工厂
                //造对象不要直接new 用工厂的方式做适当的解耦
/*        OrderDao orderDao = OrderDaoFactory.getOrderDao();
        orderDao.save();*/
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        OrderDao orderDao =(OrderDao) ctx.getBean("orderDao");
        orderDao.save();

/*        //方法三    实例工厂  先找到工厂的对象,再用对象调用方法
        UserDaoFactory userDaoFactory = new UserDaoFactory();
        UserDao userDao = userDaoFactory.getUserDao();

        userDao.save();*/


        //Spring 的形式来运行
/*        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        UserDao userDao =(UserDao) ctx.getBean("userDao");

        userDao.save();*/
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<!--1.导入Spring坐标spring-context,对应的版本号是5.2.10.RELEASE    -->
<!--2.配置bean      id随便起-->
<!--方式一   构造方法实例化bean    构造方法默认时无参构造,写不写均可-->
                <!--使用IoC   和   DI    -->
<!--    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
    <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
        <!–7.配置server与dao的关系    把dao放到server中–>
        <!–property代表当前bean的属性    name中 代表BookServiceImpl  private BookDao bookDao–>
        <!–关系绑定–>
        <property name="bookDao" ref="bookDao" />
    </bean>-->

    <!--方式二:静态工厂实例化bean-->
            <!--这样造出来的对象时factory对象而不是到对象   需要进一步进缩小范围(factory中的dao对象)-getOrderDao() -->
    <bean id="orderDao" class="com.itheima.factory.OrderDaoFactory" factory-method="getOrderDao"/>


    <!--方式三:实例工厂实例化bean  -->
        <!--工厂的bean造出来    -->
   <!-- <bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/>
        <!–静态工厂直接写class类名,实例化工厂不写class   –>
    <bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>-->
        <!--方式三的变种   去掉了 <bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/>   的臃肿-->
    <!--方式四:使用FactoryBean实例bean    -->
        <!--id给个名称,class写定义新的UserFactoryBean       造非单例对象时,接口方法isSingleton  false -->
    <!--<bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean"/>-->



</beans>

依赖注入(DI配置)

1 依赖注入方式【重点】

1.1 依赖注入的两种方式
  • setter注入
  • 简单类型
  • 引用类型(很常用)
  • 构造方法注入
1.2 setter方式注入
问题导入

setter方式注入使用什么子标签?

引用类型

springboot有参构造函数注入 spring构造方法注入bean_java_03

  • 标签中:使用ref
  • 完整代码:
public class BookServiceImpl implements BookService{
    private BookDao bookDao;

    //setter注入需要提供要注入对象的set方法
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }

    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
}
<!--注入引用类型-->
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
    <!--property标签:设置注入属性-->
    <!--name属性:设置注入的属性名,实际是set方法对应的名称-->
    <!--ref属性:设置注入引用类型bean的id或name-->
    <property name="bookDao" ref="bookDao"/>
</bean>
  • 测试
public class AppForDISet {
    public static void main( String[] args ) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookService bookService = (BookService) ctx.getBean("bookService");
        bookService.save();
    }
}
简单类型
  • 简单类型包含:基本类型(int, char, long…)+ 特殊类型String
  • 标签中:使用value
  • 完整代码
public class BookDaoImpl implements BookDao {

    private int connectionNum;
    //setter注入需要提供要注入对象的set方法
    public void setConnectionNum(int connectionNum) {
        this.connectionNum = connectionNum;
    }

    public void save() {
        System.out.println("book dao save ..."+connectionNum);
    }
}
<!--注入简单类型-->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
    <!--property标签:设置注入属性-->
    <!--name属性:设置注入的属性名,实际是set方法对应的名称-->
    <!--value属性:设置注入简单类型数据值-->
    <property name="connectionNum" value="100"/>
</bean>
总结
#简单类型使用value,引用类型使用ref
1.3 构造方式注入
问题导入

构造方式注入使用什么子标签?

引用类型

springboot有参构造函数注入 spring构造方法注入bean_实例化_04

  • 完整代码
public class BookServiceImpl implements BookService{
    private BookDao bookDao;

    public BookServiceImpl(BookDao bookDao) {
        this.bookDao = bookDao;
    }

    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
}
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" />

<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
    <constructor-arg name="bookDao" ref="bookDao"/>
</bean>
  • 测试
public class AppForDIConstructor {
    public static void main( String[] args ) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookService bookService = (BookService) ctx.getBean("bookService");
        bookService.save();
    }
}
简单类型

springboot有参构造函数注入 spring构造方法注入bean_java_05

  • 完整代码
public class BookDaoImpl implements BookDao {

    private int connectionNum;
    private String databaseName;

    public BookDaoImpl(String databaseName, int connectionNum) {
        this.databaseName = databaseName;
        this.connectionNum = connectionNum;
    }

    public void save() {
        System.out.println("book dao save ..."+databaseName+","+connectionNum);
    }
}
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
    <!--根据构造方法参数名称注入-->
    <constructor-arg name="connectionNum" value="10"/>
    <constructor-arg name="databaseName" value="mysql"/>
</bean>

<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
    <constructor-arg name="bookDao" ref="bookDao"/>
</bean>
参数适配【了解】

springboot有参构造函数注入 spring构造方法注入bean_java_06

<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
    <!--根据构造方法参数名称注入-->
    <!--<constructor-arg name="connectionNum" value="10"/>-->
    <!--<constructor-arg name="databaseName" value="mysql"/>-->

    <!--根据构造方法参数类型注入-->
    <!--<constructor-arg type="int" value="10"/>-->
    <!--<constructor-arg type="java.lang.String" value="mysql"/>-->

    <!--根据构造方法参数位置注入-->
    <!--<constructor-arg index="0" value="mysql"/>-->
    <!--<constructor-arg index="1" value="100"/>-->
</bean>
1.4 依赖注入方式选择
  1. 强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现
  2. 可选依赖使用setter注入进行,灵活性强
  3. Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
  4. 如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入
  5. 实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
  6. 自己开发的模块推荐使用setter注入
代码
BookDaoImpl

package com.itheima.dao.impl;
import com.itheima.dao.BookDao;
public class BookDaoImpl implements BookDao {
    private int connectionNum;
    private String databaseName;


    //注入一:set注入
/*    public void setConnectionNum(int connectionNum) {
        this.connectionNum = connectionNum;
    }

    public void setDatabaseName(String databaseName) {
        this.databaseName = databaseName;
    }*/

    //注入二:构造器注入
    public BookDaoImpl(int connectionNum, String databaseName) {
        this.connectionNum = connectionNum;
        this.databaseName = databaseName;
    }

    public void save() {
        System.out.println("book dao..."+ databaseName+","+connectionNum);
    }
}
BookServiceImpl

package com.itheima.service.impl;
import com.itheima.dao.BookDao;
import com.itheima.dao.UserDao;
import com.itheima.service.BookService;

public class BookServiceImpl implements BookService {
    private BookDao bookDao;
    private UserDao userDao;
    
    //注入方法一:set注入
/*    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }*/
    //注入方法二:构造器注入
    public BookServiceImpl(BookDao bookDao, UserDao userDao) {
        this.bookDao = bookDao;
        this.userDao = userDao;
    }

    public void save() {
        System.out.println("book service...");
        bookDao.save();
        userDao.save();
    }
}
AppForDISet

package com.itheima;
import com.itheima.service.BookService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AppForDISet {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx= new ClassPathXmlApplicationContext("applicationContext.xml");

        BookService bookService = (BookService) ctx.getBean("bookService");
        bookService.save();

        ctx.close();
    }
}
applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

        <!--注入一:set注入   -->
    <!--     <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" >
          <property name="connectionNum" value="10"/>
          <property name="databaseName" value="mySql"/>
       </bean>
     <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
            <property name="bookDao" ref="bookDao" />
        </bean>-->

    <!--注入二:构造器注入   标准书写  推荐使用。其他方法会存在问题-->
<!--    <bean name="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
        <constructor-arg name="connectionNum" value="10"/>
        <constructor-arg name="databaseName" value="MySql"/>
    </bean>
    <bean name="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
    <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
        <constructor-arg name="bookDao" ref="bookDao"/>
        <constructor-arg name="userDao" ref="userDao"/>
    </bean>-->
    <!--注入二-1:进一步解耦,消除参数捆绑  与形参名不耦合了-->
<!--    <bean name="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
        <constructor-arg type="int" value="10"/>
        <constructor-arg type="java.lang.String" value="MySql"/>
    </bean>
    <bean name="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
    <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
        <constructor-arg name="bookDao" ref="bookDao"/>
        <constructor-arg name="userDao" ref="userDao"/>
    </bean>-->
    <!--注入二-2:参数位置注入,消除二-1出现两个同类型的数据-->
    <bean name="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
        <constructor-arg index="0" value="10"/>
        <constructor-arg index="1" value="MySql"/>
    </bean>
    <bean name="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
    <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
        <constructor-arg name="bookDao" ref="bookDao"/>
        <constructor-arg name="userDao" ref="userDao"/>
    </bean>
</beans>

2 依赖自动装配【理解】

问题导入

如何配置按照类型自动装配?

2.1 自动装配概念
  • IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配
  • 自动装配方式
    按类型(常用)
    按名称:
    按构造方法
    不启用自动装配
2.2 自动装配类型
依赖自动装配

配置中使用bean标签autowire属性设置自动装配的类型

<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byType"/>
依赖自动装配特征
  1. 自动装配用于引用类型依赖注入,不能对简单类型进行操作
  2. 使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用
  3. 使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推荐使用
  4. 自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--手动注入-->
<!--    <bean name="bookDao" class="com.itheima.dao.impl.BookDaoImpl" />
    <bean name="bookService" class="com.itheima.service.impl.BookServiceImpl">
        <property name="bookDao" ref="bookDao"/>
    </bean>-->
    <!--自动注入    -->
    <bean name="bookDao" class="com.itheima.dao.impl.BookDaoImpl" />
    <!--那边无论写了多少,set方法给够,这边一个autowire 结束-->
    <bean name="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byType"/>
</beans>

3 集合注入-07’32

准备工作

在类中添加集合类型属性:添加get,set方法

public class BookDaoImpl implements BookDao {

    private int[] array;

    private List<String> list;

    private Set<String> set;

    private Map<String,String> map;

    private Properties properties;

    public void setArray(int[] array) {
        this.array = array;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public void setSet(Set<String> set) {
        this.set = set;
    }

    public void setMap(Map<String, String> map) {
        this.map = map;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public void save() {
        System.out.println("book dao save ...");

        System.out.println("遍历数组:" + Arrays.toString(array));

        System.out.println("遍历List" + list);

        System.out.println("遍历Set" + set);

        System.out.println("遍历Map" + map);

        System.out.println("遍历Properties" + properties);
    }
}
3.1 注入数组类型数据
<property name="array">
    <array>
        <value>100</value>
        <value>200</value>
        <value>300</value>
    </array>
</property>
3.2 注入List类型数据
<property name="list">
    <list>
        <value>itcast</value>
        <value>itheima</value>
        <value>boxuegu</value>
        <value>chuanzhihui</value>
    </list>
</property>
3.3 注入Set类型数据
<property name="set">
    <set>
        <value>itcast</value>
        <value>itheima</value>
        <value>boxuegu</value>
        <value>boxuegu</value>
    </set>
</property>
3.4 注入Map类型数据
<property name="map">
    <map>
        <entry key="country" value="china"/>
        <entry key="province" value="henan"/>
        <entry key="city" value="kaifeng"/>
    </map>
</property>
3.5 注入Properties类型数据
<property name="properties">
    <props>
        <prop key="country">china</prop>
        <prop key="province">henan</prop>
        <prop key="city">kaifeng</prop>
    </props>
</property>

说明:property标签表示setter方式注入,构造方式注入constructor-arg标签内部也可以写<array>、<list>、<set>、<map>、<props>标签

3.6 验证结果
public class AppForDICollection {
    public static void main( String[] args ) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        BookDao bookDao = (BookDao) ctx.getBean("bookDao");

        bookDao.save();
    }
}

springboot有参构造函数注入 spring构造方法注入bean_springboot有参构造函数注入_07