天天看點

SSM架構-Spring中的bean初識

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的别名

  1. 基本配置
SSM架構-Spring中的bean初識
  1. 定義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
           
  1. 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

  1. 異常

如果名字不對,異常名字是NoSuchBeanDefinitionException

SSM架構-Spring中的bean初識

3.2 bean的作用範圍

  1. 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"

  1. 非單例配置
  • 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
           
  1. 為什麼bean預設為單例
  • 如果非單例,每一次bean幫忙造對象的時候,就會産生一個對象,久而久之,會有無數個,進而造成容器壓力
  • 如果是單例,一直用一個對象問題不大,隻不過方法跟着變化就可以了,并不影響使用
SSM架構-Spring中的bean初識
  • 不适合給bean的對象一般是包含狀态的,裡面有記錄成員變量的屬性值

3.3 bean執行個體化

3.3.1 概述

  • bean本質就是個對象,是以建立bean使用構造方法完成

3.3.2 使用無參構造器建立bean對象

  1. 代碼

如果有參就不能調到

  • 無參構造【更改位置】
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
           
  1. 結論

無論私有或者public的無參構造器,bean都能調到,私有的構造器可以通路,應該是用的反射機制

不寫或者寫無參構造都ok,但是如果沒有無參構造方法,将抛出異常:BeanCreationException

如果寫的有參,則抛出異常NoSuchMethodException

  1. 小技巧

spring的報錯一般看最下面,下面問題解決了,上面也就沒有了,但是上面的報錯細緻

SSM架構-Spring中的bean初識

3.3.3 通過靜态工廠建立對象

  1. 代碼
  • 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,實作一定程度的解耦
  1. 利用靜态工廠建立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 建立執行個體工廠建立對象

  1. 代碼
  • 接口
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
           
  1. 使用建立執行個體工廠建立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 優化執行個體工廠[重要]

  1. 代碼
  • 寫一個接口實作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"/>
           
  1. 驗證是否單例
  • 是單例
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 概念

SSM架構-Spring中的bean初識

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的方法,原因是因為虛拟機退出了

  • 解決方法
SSM架構-Spring中的bean初識

在虛拟機退出前,把容器給關閉了,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的生命周期

  1. 方法
  • 繼承InitializingBean和DisposableBean,然後重寫destroy()和afterPropertiesSet()
  1. 代碼
  • 接口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...
           
  1. 總結
  • 生命周期流程
    • 建立對象,配置設定記憶體
    • 執行構造方法
    • 執行屬性注入(set操作)
    • 執行bean的初始化操作
    • 使用bean執行業務操作
    • 執行bean銷毀方法