3 bean配置
前情代码
- BookDaoImpl实现接口BookDao
public class BookDaoImpl implements BookDao{
public void save(){
System.out.println("book dao save");
}
}
- 接口BookDao
public interface BookDao {
void save();
}
- BookServiceImpl实现接口BookService
public class BookServiceImpl implements BookService {
private BookDao bookDao = new BookDaoImpl();
public void save(){
System.out.println("book service save ...");
bookDao.save();
}
- BookService接口
public interface BookService {
void save();
}
- App入口
public class App2 {
public static void main(String[] args) {
//3.获取IoC容器,ApplicationContext和配置一样,new一个接口实现类
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//4.获取bean,BookDao就是id
// BookDao bookDao = (BookDao) ctx.getBean("bookDao");
// bookDao.save();
//5.获取bean,bookService
BookService bookService = (BookService) ctx.getBean("bookService");
bookService.save();
}
}
- 配置文件
<!--1. 导入spring的坐标spring-context,对应版本是5.2.10.RELEASE-->
<!--2.配置bean-->
<!-- 使用bean标签配置bean,id表示给bean取名字 class属性表示给bean定义类型-->
<bean id ="bookDao" class = "com.itheima.dao.impl.BookDaoImpl"/>
<bean id = "bookService" class = "com.itheima.service.impl.BookServiceImpl">
<!--7.配置server与dao的关系-->
<!--property表示配置当前bean的属性-->
<!--name属性表示哪一个具体的属性,哪一个-->
<!--ref属性表示参照一个bean-->
<property name="bookDao" ref="bookDao"/>
</bean>
3.1 bean的别名
- 基本配置
- 定义bean的名字
- 在配置文件中使用name属性命名
用空格隔开可以整多个
<!--2.配置bean-->
<!-- 使用bean标签配置bean,id表示给bean取名字 class属性表示给bean定义类型-->
<bean id = "bookService" name = "service bookEbi" class = "com.itheima.service.impl.BookServiceImpl">
- 更名service试试看
public static void main(String[] args) {
//3.获取IoC容器,ApplicationContext和配置一样,new一个接口实现类
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//5.获取bean,bookService
BookService bookService = (BookService) ctx.getBean("service");
bookService.save();
- 看下结果一样
book service save ...
book dao save
Process finished with exit code 0
- DI的配置文件ref的参照名称
<!--1. 导入spring的坐标spring-context,对应版本是5.2.10.RELEASE-->
<!--2.配置bean-->
<!-- 使用bean标签配置bean,id表示给bean取名字 class属性表示给bean定义类型-->
<bean id ="bookDao" name = "dao" class = "com.itheima.dao.impl.BookDaoImpl"/>
<bean id = "bookService" name = "service bookEbi" class = "com.itheima.service.impl.BookServiceImpl">
<!--7.配置server与dao的关系-->
<!--property表示配置当前bean的属性-->
<!--name属性表示哪一个具体的属性,哪一个-->
<!--ref属性表示参照一个bean-->
<property name="bookDao" ref="dao"/>
</bean>
跑程序也是ok的
不过还是建议用id
- 异常
如果名字不对,异常名字是NoSuchBeanDefinitionException
3.2 bean的作用范围
- bean是个单例
- BookDaoImpl实现类
public class BookDaoImpl implements BookDao{
public void save(){
System.out.println("book dao save");
}
}
- BookServiceImpl实现类
public class BookServiceImpl implements BookService {
//删除业务层中使用new的方式创建的dao对象
private BookDao bookDao;
// private BookDao bookDao = new BookDaoImpl();
public void save(){
System.out.println("book service save ...");
bookDao.save();
}
//生成set方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}
- 配置文件
<!--1. 导入spring的坐标spring-context,对应版本是5.2.10.RELEASE-->
<!--2.配置bean-->
<!-- 使用bean标签配置bean,id表示给bean取名字 class属性表示给bean定义类型-->
<bean id ="bookDao" name = "dao" class = "com.itheima.dao.impl.BookDaoImpl"/>
<bean id = "bookService" name = "service bookEbi"
class = "com.itheima.service.impl.BookServiceImpl">
<!--7.配置server与dao的关系-->
<!--property表示配置当前bean的属性-->
<!--name属性表示哪一个具体的属性,哪一个-->
<!--ref属性表示参照一个bean-->
<property name="bookDao" ref="dao"/>
</bean>
- 测试范围【更改位置】
public class App2 {
public static void main(String[] args) {
//3.获取IoC容器,ApplicationContext和配置一样,new一个接口实现类
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//6.测试bean的作用范围
BookDao bookDao1 = (BookDao) ctx.getBean("bookDao");
BookDao bookDao2 = (BookDao) ctx.getBean("bookDao");
System.out.println(bookDao1);
System.out.println(bookDao2);
}
}
- 结果
com.itheima.dao.impl.BookDaoImpl@71423665
com.itheima.dao.impl.BookDaoImpl@71423665
- 总结
是个单例,默认 scope="singleton"
- 非单例配置
- xml配置
<bean id ="bookDao" name = "dao" class = "com.itheima.dao.impl.BookDaoImpl" scope="prototype"/>
- 程序显示有区别
com.itheima.dao.impl.BookDaoImpl@71423665
com.itheima.dao.impl.BookDaoImpl@20398b7c
- 为什么bean默认为单例
- 如果非单例,每一次bean帮忙造对象的时候,就会产生一个对象,久而久之,会有无数个,从而造成容器压力
- 如果是单例,一直用一个对象问题不大,只不过方法跟着变化就可以了,并不影响使用
- 不适合给bean的对象一般是包含状态的,里面有记录成员变量的属性值
3.3 bean实例化
3.3.1 概述
- bean本质就是个对象,因此创建bean使用构造方法完成
3.3.2 使用无参构造器创建bean对象
- 代码
如果有参就不能调到
- 无参构造【更改位置】
public class BookDaoImpl implements BookDao{
private BookDaoImpl(){
System.out.println("book dao constructor is running...");
}
public void save(){
System.out.println("book dao save");
}
}
- App类
public class App2 {
public static void main(String[] args) {
//3.获取IoC容器,ApplicationContext和配置一样,new一个接口实现类
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//4.获取bean,BookDao就是id
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
}
}
- 结果
book dao constructor is running...
book dao save
Process finished with exit code 0
- 结论
无论私有或者public的无参构造器,bean都能调到,私有的构造器可以访问,应该是用的反射机制
不写或者写无参构造都ok,但是如果没有无参构造方法,将抛出异常:BeanCreationException
如果写的有参,则抛出异常NoSuchMethodException
- 小技巧
spring的报错一般看最下面,下面问题解决了,上面也就没有了,但是上面的报错细致
3.3.3 通过静态工厂创建对象
- 代码
- OrderDao接口
public interface OrderDao {
public void save();
}
- OrderDaoImpl实现类
public class OrderDaoImpl implements OrderDao{
public void save(){
System.out.println("order dao sava ...");
}
}
- 静态工厂类
一般工厂类里面还能干点别的事情
public class OrderDaoFactory {
public static OrderDao getOrderDao(){
System.out.println("factory setup ...");
return new OrderDaoImpl();
}
}
- App类
public class AppForInstanceOrder {
public static void main(String[] args) {
OrderDao orderDao = OrderDaoFactory.getOrderDao();
orderDao.save();
}
}
- 运行结果
order dao sava ...
Process finished with exit code 0
- 小结:静态工厂是造对象不要自己new,用工厂方式new,实现一定程度的解耦
- 利用静态工厂创建bean对象
- 在配置中增加
<!--方式二:使用静态工厂实例化bean-->
<!--告知工厂类还有方法-->
<bean id = "orderDao" class = "com.itheima.factory.OrderDaoFactory" factory-method="getOrderDao"/>
- App类改成bean方式
public class AppForInstanceOrder {
public static void main(String[] args) {
// OrderDao orderDao = OrderDaoFactory.getOrderDao();
// orderDao.save();
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
OrderDao orderDao = (OrderDao) ctx.getBean("orderDao");
orderDao.save();
}
}
3.3.4 创建实例工厂创建对象
- 代码
- 接口
public interface UserDao {
public void save();
}
- 实现类
public class UserDaoImpl implements UserDao{
public void save(){
System.out.println("user dao save...");
}
}
- 实例工厂
public class UserDaoFactory {
public UserDao getUserDao(){//非静态
return new UserDaoImpl();
}
}
- App类
public class AppForInstanceUser {
public static void main(String[] args) {
//创建实例工厂对象
UserDaoFactory userDaoFactory = new UserDaoFactory();
//通过实例工厂创建对象
UserDao userDao = userDaoFactory.getUserDao();
userDao.save();
}
}
- 运行结果
user dao save...
Process finished with exit code 0
- 使用创建实例工厂创建bean对象
- 在配置中增加
<!--方式二:使用实例工厂实例化bean-->
<!--先造一个工厂的bean出来,然后把工厂bean和方法给到bean-->
<bean id = "userFactory" class="com.itheima.factory.UserDaoFactory"/>
<bean id = "userDao" factory-method="getUserDao" factory-bean="userFactory"/>
- App类
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();
}
}
- 结果
user dao save...
Process finished with exit code 0
3.3.5 优化实例工厂[重要]
- 代码
- 写一个接口实现FactoryBean,泛型是你要的对象类型
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
//代替原来实例工厂中创造实例对象的方法,统一方法名
@Override
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
//对象要啥类型的
@Override
public Class<?> getObjectType() {
return UserDao.class;
}
}
- 配置优化了
<!--方法四:使用FactoryBean实例化Bean-->
<bean id = "userDao" class="com.itheima.factory.UserDaoFactoryBean"/>
- 验证是否单例
- 是单例
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// UserDao userDao = (UserDao) ctx.getBean("userDao");
// userDao.save();
UserDao userDao1 = (UserDao) ctx.getBean("userDao");
UserDao userDao2 = (UserDao) ctx.getBean("userDao");
System.out.println(userDao1);
System.out.println(userDao2);
结果
com.itheima.dao.impl.UserDaoImpl@cb644e
com.itheima.dao.impl.UserDaoImpl@cb644e
- 怎么改成非单例
UserDaoFactoryBean这个类中增加,false表示非单例
@Override
public boolean isSingleton() {
return false;
}
3.4 bean的生命周期
3.4.1 概念
3.4.2 具体控制
- 接口BookDao
public interface BookDao {
void save();
}
- 接口BookService
public interface BookService {
void save();
}
- 在BookDaoImpl实现类增加init和destroy的方法**[更改之处]**
public class BookDaoImpl implements BookDao{
public void save(){
System.out.println("book dao save");
}
//表示bean初始化对应的操作
public void init(){
System.out.println("init...");
}
//表示bean销毁前对应的操作
public void destory(){
System.out.println("destroy...");
}
}
- App类
public class App2 {
public static void main(String[] args) {
//3.获取IoC容器,ApplicationContext和配置一样,new一个接口实现类
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//4.获取bean,BookDao就是id
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
}
}
- 配置文件增加属性**[更改之处]**
<bean id ="bookDao" class = "com.itheima.dao.impl.BookDaoImpl"
init-method="init" destroy-method="destory" />
- 结果
init...
book dao save
Process finished with exit code 0
3.4.3关闭容器
没有出现destroy的方法,原因是因为虚拟机退出了
- 解决方法
在虚拟机退出前,把容器给关闭了,ClassPathXmlApplicationContext这个类的close方法进行关闭
public class App2 {
public static void main(String[] args) {
//3.获取IoC容器,ApplicationContext和配置一样,new一个接口实现类
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//4.获取bean,BookDao就是id
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
ctx.close();//ApplicationContext接口不具有这个方法,ctrl+h,使用ClassPathXmlApplicationContext
}
}
- 执行后看下结果
init...
book dao save
destroy...
3.4.4 另一种解决方法
通过设置关闭钩子registerShutdownHook
public class App2 {
public static void main(String[] args) {
//3.获取IoC容器,ApplicationContext和配置一样,new一个接口实现类
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
ctx.registerShutdownHook();//也是关掉虚拟机之前把他容器关闭
//4.获取bean,BookDao就是id
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
//ctx.close();//ApplicationContext接口不具有这个方法,ctrl+h
}
}
3.4.5 按照bean的接口控制bean的生命周期
- 方法
-
继承InitializingBean和DisposableBean,然后重写destroy()和afterPropertiesSet()
-
不需要配置文件再配置内容了
- 代码
- 接口BookDao
public interface BookDao {
void save();
}
- 接口BookService
public interface BookService {
void save();
}
- 接口实现类BookDaoImpl
public class BookDaoImpl implements BookDao{
public void save(){
System.out.println("book dao save");
}
//表示bean初始化对应的操作
public void init(){
System.out.println("init...");
}
//表示bean销毁前对应的操作
public void destory(){
System.out.println("destroy...");
}
}
- BookServiceImpl接口实现类**【更改之处】**
public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {
//删除业务层中使用new的方式创建的dao对象
private BookDao bookDao;
public void save(){
System.out.println("book service save ...");
bookDao.save();
}
//生成set方法
public void setBookDao(BookDao bookDao) {
System.out.println("set ...");
this.bookDao = bookDao;
}
@Override
public void destroy() throws Exception {
System.out.println("service destroy");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("service init");
}
}
- App2类
public class App2 {
public static void main(String[] args) {
//3.获取IoC容器,ApplicationContext和配置一样,new一个接口实现类
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//4.获取bean,BookDao就是id
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
ctx.close();//ApplicationContext接口不具有这个方法,ctrl+h
}
}
service因为在配置文件中也被加载到bean对象中
- 结果
init...
set ...
service init
book dao save
service destroy
destroy...
set在service init之前表示通过set进行属性设置之后,才会运行afterPropertiesSet()方法进行初始化
- 总结
- 生命周期流程
- 创建对象,分配内存
- 执行构造方法
- 执行属性注入(set操作)
- 执行bean的初始化操作
- 使用bean执行业务操作
- 执行bean销毁方法