只有当执行到IAccountDao accountDao = applicationContext.getBean("accountDao", IAccountDao.class);和IAccountDao accountDao1 = (IAccountDao) applicationContext.getBean("accountDao");时,才会输出“dao创建了”。
并且accountDao == accountDao1的结果是false。
3.bean对象的生命周期单例对象:生命周期和容器相同,容器创建对象就创建,容器销毁对象就销毁
多例对象:当需要使用对象时(根据id获取对象时),对象被创建;当没有引用指向对象且对象长时间不用时,由Java的垃圾回收机制回收
为了演示,这里需要介绍bean标签的两个属性:init-method属性指定初始化方法,destroy-method属性指定销毁方法
<bean scope="singleton" init-method="init" destroy-method="destroy"></bean> <bean scope="prototype" init-method="init" destroy-method="destroy"></bean>同时,还有在AccountDaoImpl类和AccountService类中添加如下代码:
//AccountDaoImpl: public void init() { System.out.println("dao初始化了"); } public void destroy() { System.out.println("dao销毁了"); } //AccountServiceImpl: public void init() { System.out.println("service初始化了"); } public void destroy() { System.out.println("service销毁了"); }为了手动关闭容器需要在Client类中的main方法中最后加入:
//容器需要手动关闭,因为applicationContext是接口类型,所以没有close方法,需要强制转换为实现类对象 ((ClassPathXmlApplicationContext) applicationContext).close();这个时候,我们再去使用断点调试,可以发现:
当执行到ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");时,就会输出“service创建了”和“service初始化了”。
只有当执行到IAccountDao accountDao = applicationContext.getBean("accountDao", IAccountDao.class);和IAccountDao accountDao1 = (IAccountDao) applicationContext.getBean("accountDao");时,才会输出“dao创建了”和“dao初始化了”。
执行到((ClassPathXmlApplicationContext) applicationContext).close();时,会输出“service销毁了”,不会输出“dao销毁了”。这是因为创建AccountDaoImpl类的对象时,使用的是多例模式。多例模式下的对象回收由JVM决定,关闭Ioc容器并不能使得JVM回收对象。
四、IOC的依赖注入 1.之前代码中的问题在之前的代码中,我们一直没有使用AccountServiceImpl对象中的saveAccount方法,这是因为我们还没有实例化该类中的accountDao对象。我们先看看AccountServiceImpl的源代码:
public class AccountServiceImpl implements IAccountService { //持久层接口对象的引用,为了降低耦合,这里不应该是new AccountDaoImpl private IAccountDao accountDao; public AccountServiceImpl() { System.out.println("service创建了"); } /** 模拟保存账户操作 */ public void saveAccounts() { System.out.println("执行保存账户操作"); //调用持久层接口函数 accountDao.saveAccounts(); } }在之前的三层架构中,对于accoutDao对象,我们是private IAccountDao accountDao = new AccountDaoImpl(); 实际上,为了降低耦合,我们不应该在此处对accountDao对象进行实例化操作,应该直接是private IAccountDao accountDao; 。为了将该对象实例化,我们就需要用到依赖注入。
2.依赖注入介绍依赖注入(Dependency Injection, DI):它是spring框架核心IoC的具体实现(IoC是一种思想,而DI是一种设计模式)。 在编写程序时,通过控制反转,把对象的创建交给了 spring,但是代码中不可能出现没有依赖的情况。IoC 解耦只是降低他们的依赖关系,但不会消除。例如:我们的业务层仍会调用持久层的方法,这种业务层和持久层的依赖关系,在使用 spring 之后,就让 spring 来维护了。简单的说,就是让框架把持久层对象传入业务层,而不用我们自己去获取。
3.依赖注入的数据类型和方式在依赖注入中,能够注入的数据类型有三类:
基本类型和String类型
其他Bean类型:在注解或配置文件中配置过的Bean,也就是Spring容器中的Bean
复杂类型(集合类型):例如List、Array、Map等
为了演示依赖注入,我们在src/main/java目录下,新建一个包entity,在该包下新建实体类People:
代码中的字段如下,注意构造方法一定要加上无参构造方法。
public class People { //如果是经常变化的数据,并不适用于依赖注入 private String name; private Integer age; //Date类型不是基本类型,属于Bean类型 private Date birthDay; //以下都是集合类型 private String[] myString; private List<String> myList; private Set<String> mySet; private Map<String, String> myMap; private Properties myProps; //为了节省空间,这里省略了所有的set方法和toString方法,在实际代码中要补上 public People() { } //提供默认构造方法 public People(String name, Integer age, Date birthDay) { this.name = name; this.age = age; this.birthDay = birthDay; } }注入的方式有三种:
使用构造方法注入
这种方式使用的标签为constructor-arg,在bean标签的内部使用,该标签的属性有五种,其中的1-3种用于指定给构造方法中的哪个参数注入数据:
type:用于要注入的数据的数据类型,该数据类型也是构造方法中某个或某些参数的类型
index:用于给构造方法中指定索引位置的参数注入数据,索引从0开始
name:用于给构造方法中指定名称的参数注入数据(最常用)
value:要注入的数据的值(只能是基本类型或者String类型)