SSM框架-Spring中的bean初识

时间:2022-11-06 07:19:10

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
    }
}

service因为在配置文件中也被加载到bean对象中

  • 结果
init...
set ...
service init
book dao save
service destroy
destroy...

set在service init之前表示通过set进行属性设置之后,才会运行afterPropertiesSet()方法进行初始化

  1. 总结
  • 生命周期流程
    • 创建对象,分配内存
    • 执行构造方法
    • 执行属性注入(set操作)
    • 执行bean的初始化操作
    • 使用bean执行业务操作
    • 执行bean销毁方法