• SSM-Spring中bean的生命周期


    3 bean配置

    前情代码

    • BookDaoImpl实现接口BookDao
    public class BookDaoImpl implements BookDao{
        public void save(){
            System.out.println("book dao save");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 接口BookDao
    public interface BookDao {
        void save();
    }
    
    • 1
    • 2
    • 3
    • BookServiceImpl实现接口BookService
    public class BookServiceImpl implements BookService {
        private BookDao bookDao = new BookDaoImpl();
        public void save(){
            System.out.println("book service save ...");
            bookDao.save();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • BookService接口
    public interface BookService {
        void save();
    }
    
    • 1
    • 2
    • 3
    • 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 配置文件
            
    
            
            
            <bean id ="bookDao" class = "com.itheima.dao.impl.BookDaoImpl"/>
            <bean id = "bookService" class = "com.itheima.service.impl.BookServiceImpl">
    
            
            
            
            
            <property name="bookDao" ref="bookDao"/>
            bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    3.1 bean的别名

    1. 基本配置

    image.png

    1. 定义bean的名字
    • 在配置文件中使用name属性命名

    用空格隔开可以整多个

            
            
            <bean id = "bookService" name = "service bookEbi" class = "com.itheima.service.impl.BookServiceImpl">
    
    • 1
    • 2
    • 3
    • 更名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();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 看下结果一样
    book service save ...
    book dao save
    
    Process finished with exit code 0
    
    • 1
    • 2
    • 3
    • 4
    1. DI的配置文件ref的参照名称
            
    
            
            
            <bean id ="bookDao" name = "dao" class = "com.itheima.dao.impl.BookDaoImpl"/>
    
            <bean id = "bookService" name = "service bookEbi" class = "com.itheima.service.impl.BookServiceImpl">
    
            
            
            
            
            <property name="bookDao" ref="dao"/>
            bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    跑程序也是ok的

    不过还是建议用id

    1. 异常

    如果名字不对,异常名字是NoSuchBeanDefinitionException

    image.png

    3.2 bean的作用范围

    1. bean是个单例
    • BookDaoImpl实现类
    public class BookDaoImpl implements BookDao{
        public void save(){
            System.out.println("book dao save");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 配置文件
            
    
            
            
            <bean id ="bookDao" name = "dao" class = "com.itheima.dao.impl.BookDaoImpl"/>
    
            <bean id = "bookService" name = "service bookEbi"
                  class = "com.itheima.service.impl.BookServiceImpl">
    
            
            
            
            
            <property name="bookDao" ref="dao"/>
            bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 测试范围【更改位置】
    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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 结果
    com.itheima.dao.impl.BookDaoImpl@71423665
    com.itheima.dao.impl.BookDaoImpl@71423665
    
    • 1
    • 2
    • 总结

    是个单例,默认 scope=“singleton”

    1. 非单例配置
    • xml配置
            <bean id ="bookDao" name = "dao" class = "com.itheima.dao.impl.BookDaoImpl"  scope="prototype"/>
    
    • 1
    • 程序显示有区别
    com.itheima.dao.impl.BookDaoImpl@71423665
    com.itheima.dao.impl.BookDaoImpl@20398b7c
    
    • 1
    • 2
    1. 为什么bean默认为单例
    • 如果非单例,每一次bean帮忙造对象的时候,就会产生一个对象,久而久之,会有无数个,从而造成容器压力
    • 如果是单例,一直用一个对象问题不大,只不过方法跟着变化就可以了,并不影响使用

    image.png

    • 不适合给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");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 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();
                }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 结果
    book dao constructor is running...
    book dao save
    
    Process finished with exit code 0
    
    • 1
    • 2
    • 3
    • 4
    1. 结论

    无论私有或者public的无参构造器,bean都能调到,私有的构造器可以访问,应该是用的反射机制

    不写或者写无参构造都ok,但是如果没有无参构造方法,将抛出异常:BeanCreationException

    如果写的有参,则抛出异常NoSuchMethodException

    1. 小技巧

    spring的报错一般看最下面,下面问题解决了,上面也就没有了,但是上面的报错细致

    image.png

    3.3.3 通过静态工厂创建对象

    1. 代码
    • OrderDao接口
    public interface OrderDao {
        public void save();
    }
    
    • 1
    • 2
    • 3
    • OrderDaoImpl实现类
    public class OrderDaoImpl implements OrderDao{
        public void save(){
            System.out.println("order dao sava ...");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 静态工厂类

    一般工厂类里面还能干点别的事情

    public class OrderDaoFactory {
        public static OrderDao getOrderDao(){
            System.out.println("factory setup ...");
            return new OrderDaoImpl();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • App类
    public class AppForInstanceOrder {
        public static void main(String[] args) {
            OrderDao orderDao = OrderDaoFactory.getOrderDao();
            orderDao.save();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 运行结果
    order dao sava ...
    
    Process finished with exit code 0
    
    • 1
    • 2
    • 3
    • 小结:静态工厂是造对象不要自己new,用工厂方式new,实现一定程度的解耦
    1. 利用静态工厂创建bean对象
    • 在配置中增加
            
            
            <bean id = "orderDao" class = "com.itheima.factory.OrderDaoFactory" factory-method="getOrderDao"/>
    
    • 1
    • 2
    • 3
    • 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();
        }
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3.3.4 创建实例工厂创建对象

    1. 代码
    • 接口
    public interface UserDao {
        public void save();
    }
    
    • 1
    • 2
    • 3
    • 实现类
    public class UserDaoImpl implements UserDao{
        public void save(){
            System.out.println("user dao save...");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 实例工厂
    public class UserDaoFactory {
        public UserDao getUserDao(){//非静态
            return new UserDaoImpl();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • App类
    public class AppForInstanceUser {
        public static void main(String[] args) {
            //创建实例工厂对象
            UserDaoFactory userDaoFactory = new UserDaoFactory();
            //通过实例工厂创建对象
            UserDao userDao = userDaoFactory.getUserDao();
            userDao.save();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 运行结果
    user dao save...
    
    Process finished with exit code 0
    
    • 1
    • 2
    • 3
    1. 使用创建实例工厂创建bean对象
    • 在配置中增加
            
            
            <bean id = "userFactory" class="com.itheima.factory.UserDaoFactory"/>
            <bean id = "userDao"  factory-method="getUserDao" factory-bean="userFactory"/>
    
    • 1
    • 2
    • 3
    • 4
    • 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();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 结果
    user dao save...
    
    Process finished with exit code 0
    
    • 1
    • 2
    • 3

    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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 配置优化了
            
            <bean id = "userDao" class="com.itheima.factory.UserDaoFactoryBean"/>
    
    • 1
    • 2
    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 怎么改成非单例
    UserDaoFactoryBean这个类中增加,false表示非单例     	
    @Override
        public boolean isSingleton() {
            return false;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3.4 bean的生命周期

    3.4.1 概念

    image.png

    3.4.2 具体控制

    • 接口BookDao
    public interface BookDao {
        void save();
    }
    
    • 1
    • 2
    • 3
    • 接口BookService
    public interface BookService {
        void save();
    }
    
    • 1
    • 2
    • 3
    • 在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...");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 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();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 配置文件增加属性**[更改之处]**
            <bean id ="bookDao" class = "com.itheima.dao.impl.BookDaoImpl"
                  init-method="init" destroy-method="destory" />
    
    • 1
    • 2
    • 结果
    init...
    book dao save
    
    Process finished with exit code 0
    
    • 1
    • 2
    • 3
    • 4

    3.4.3关闭容器

    没有出现destroy的方法,原因是因为虚拟机退出了

    • 解决方法

    image.png

    在虚拟机退出前,把容器给关闭了,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
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 执行后看下结果
    init...
    book dao save
    destroy...
    
    • 1
    • 2
    • 3

    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
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    3.4.5 按照bean的接口控制bean的生命周期

    1. 方法
    • 继承InitializingBean和DisposableBean,然后重写destroy()和afterPropertiesSet()

    • 不需要配置文件再配置内容了

    1. 代码
    • 接口BookDao
    public interface BookDao {
        void save();
    }
    
    • 1
    • 2
    • 3
    • 接口BookService
    public interface BookService {
        void save();
    }
    
    • 1
    • 2
    • 3
    • 接口实现类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...");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 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");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 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
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

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

    • 结果
    init...
    set ...
    service init
    book dao save
    service destroy
    destroy...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

    1. 总结
    • 生命周期流程
      • 创建对象,分配内存
      • 执行构造方法
      • 执行属性注入(set操作)
      • 执行bean的初始化操作
      • 使用bean执行业务操作
      • 执行bean销毁方法
  • 相关阅读:
    阿里P8大牛整理总结,影响全球250W工程师,两大阿里java开发手册
    艾美捷双链RNA定量试剂盒试验方案
    Linux知识点:系统目录结构的理解,环境变量的作用和添加,创建自己的共享库并使用,引用第三方库
    C语言笔记(六)
    DruidDataSource导致OOM问题处理
    【MySQL】常见错误汇总
    19.Chain of Responsibility职责链(行为型模式)
    Redis夺命十二问,你能扛到第几问?
    禁忌搜索算法TS求解TSP问题
    有关JWT的面试问题总结
  • 原文地址:https://blog.csdn.net/m0_46507516/article/details/127707077