@[TOC]
引入
在前文的IOC初次使用中我们发现:在BookServiceImpl
的类中依然存在BookDaoImpl
对象的new操作,它们之间的耦合度还是比较高:
这块该如何解决,就需要用到下面的DI:依赖注入
。
DI初次使用
步骤①:去除代码中的new,为属性提供setter方法
public class BookServiceImpl implements BookService {
//删除业务层中使用new的方式创建的dao对象
private BookDao bookDao;
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
//提供对应的set方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}
注意:这个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标签标示配置bean
id属性标示给bean起名字
class属性表示给bean定义类型
-->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<!--配置server与dao的关系-->
<!--property标签表示配置当前bean的属性
name属性表示配置哪一个具体的属性
ref属性表示参照哪一个bean
-->
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
==注意:配置中的两个bookDao的含义是不一样的==
- name="bookDao"中
bookDao
的作用是让Spring的IOC容器在获取到名称后,将首字母大写,前面加set找对应的setBookDao()
方法进行对象注入 - ref="bookDao"中
bookDao
的作用是让Spring能在IOC容器中找到id为bookDao
的Bean对象给bookService
进行注入 - 综上所述,对应关系如下:
bean基础配置
bean基础配置
对于bean的基础配置,在前面的案例中已经使用过:
<bean id="" class=""/>
其中,bean标签的功能、使用方式以及id和class属性的作用,我们通过一张图来描述下:
bean的别名配置(name属性)
别名的配置说明:
配置完bean的别名之后,我们在property的ref配置项中(不过这个我们还是推荐使用id),以及getBean方法获取bean的时候都可以采用他的别名
例如:
我们先配置name,给他起名name="bookBookDao"
:
<?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 id="bookDao" name="bookBookDao" class="impl.BookDaoImpl"/>
<bean id="bookService" class="impl.BookServiceImpl">
<property name="bookDao" ref="bookBookDao"></property>
</bean>
</beans>
在上面代码中我的ref替换成为别名了,包括接下来的bean获取都可以使用别名:
public class App1 {
public static void main(String[] args) {
//获取容器
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookBookDao");
bookDao.save();
BookService bookService = (BookService) ctx.getBean("bookService");
bookService.save();
}
}
获取bean无论是通过id还是name获取,如果无法获取到,将抛出异常==NoSuchBeanDefinitionException==
bean作用范围scope配置
bean作用范围的配置属性
:
我们可以来试验一下:
我们先配置为单例(也就是默认配置):
public class App1 {
public static void main(String[] args) {
//获取容器
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookService bookService1 = (BookService) ctx.getBean("bookService");
BookService bookService2 = (BookService) ctx.getBean("bookService");
System.out.println(bookService1);
System.out.println(bookService2);
}
}
通过打印他们的地址值发现,bean默认为单例模式建造:
我们修改scope配置项之后:
最后的结果:
bean默认为单例引发的思考
问题①:为什么bean默认为单例?
bean为单例的意思是在Spring的IOC容器中只会有该类的一个对象。bean对象只有一个就避免了对象的频繁创建与销毁,达到了bean对象的复用,性能高。
问题②:bean在容器中是单例的,会不会产生线程安全问题?
- 如果对象是有状态对象,即该对象有成员变量可以用来存储数据的,因为所有请求线程共用一个bean对象,所以会存在线程安全问题。
- 如果对象是无状态对象,即该对象没有成员变量没有进行数据存储的,因方法中的局部变量在方法调用完成后会被销毁,所以不会存在线程安全问题。
问题③:哪些bean对象适合交给容器进行管理?哪些不适合?
- 表现层对象、业务层对象、数据层对象、工具对象适合交给容器管理
- 封装实例的域对象,因为会引发线程安全问题,所以不适合。
bean基础配置总结
关于bean的基础配置中,需要大家掌握以下属性: