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
}
}
- 結果
init...
set ...
service init
book dao save
service destroy
destroy...
- 總結
- 生命周期流程
- 建立對象,配置設定記憶體
- 執行構造方法
- 執行屬性注入(set操作)
- 執行bean的初始化操作
- 使用bean執行業務操作
- 執行bean銷毀方法