Spring Bean相关内容

1 Bean的基础配置

//bean标签抽象理解就是将某个类添加到Ioc容器中,然后实例化为类对象(bean)。
//以下均为<bean>标签的属性。

(1) id和class属性

<1> id:使用容器可以通过id值获取bean,在一个容器中id值唯一。
<2> class:bean的类型,即配置的bean的全路径类名。

(2) 设置别名:name

//与id的使用上几乎没有区别。
<1> 定义bean的别名,可以定义多个,使用逗号,分号;空格 分隔。
<bean id="bookService" name="service service4 bookEbi" class="com.itheima.service.impl.BookServiceImpl"/>
//通过Ioc容器获取时,既可以使用bookService获取,也可以使用service、service4、bookEbi获取。
//说明:Ebi全称Enterprise Business Interface,翻译为企业业务接口。
<2> 获取bean无论是通过id还是name获取,如果无法获取到,将抛出异常NoSuchBeanDefinitionException

(3) 控制范围:scope

<1> 定义bean的作用范围,可选范围如下:singleton(默认单例)、prototype(非单例)。
<bean id="bookDao" name="dao" class="com.itheima.dao.impl.BookDaoImpl" scope="prototype"/>
//单例:被多次使用的该bean,对象地址相同,是同一个对象,即在Spring的IOC容器中只会有该类的一个对象。
//非单例:每次使用的该bean,对象地址不同,每次都实例化一个对象,即在Spring的IOC容器中可能有该类的多个对象
<2> bean默认为单例避免了对象的频繁创建与销毁,达到了bean对象的复用,性能高。
<3> 表现层对象、业务层对象、数据层对象、工具对象适合交给容器进行管理;封装实例的域对象不适合交给容器进行管理。

(4) 小结

<bean id="bean的唯一标识" class="bean的路径" scope="bean的作用范围,有singleton(默认)和prototype两种" name="bean的别名"/>

 

2 Bean的实例化

//当Ioc容器被获取时类开始实例化,实例化完成后成为bean,bean本质是类对象。
//Ioc容器默认立即加载,会在初始化容器时实例化所有bean(按Spring配置文件中的bean标签顺序)。
//当Ioc容器被手动销毁时,其中的所有bean也会被销毁,与Spring配置文件中的bean标签顺序相反。

(1) 构造方法实例化

<1> 准备需要被创建的类

public interface BookDao {
    public void save();
}

public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("BookDaoImpl_save");
    }
}

<2> 类中提供构造函数测试
//在BookDaoImpl类中添加一个无参构造函数,并打印一句话,方便观察结果。

public class BookDaoImpl implements BookDao {
    //构造方法
    public BookDaoImpl() {
        //将构造函数改成private测试,运行程序,能执行成功。内部走的依然是构造函数,能访问到类中的私有构造方法,显而易见Spring底层用的是反射。
        //构造函数中添加一个参数测试,运行程序,程序会报错,说明Spring底层使用的是类的无参构造方法。
        //因为每一个类默认都会提供一个无参构造函数,所以其实真正在使用这种方式的时候,我们什么也不需要做。这也是我们以后比较常用的一种方式。
        System.out.println("BookDaoImpl的构造函数正在运行...");
    }
    public void save() {
        System.out.println("BookDaoImpl_save");
    }                                                                              
}

<3> 将类配置到Spring容器
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<4> 编写运行类

public class App {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext cpxac = new ClassPathXmlApplicationContext("applicationContext.xml");
        //该语句运行后,构造函数就会被执行。
        //当容器被获取时,其中所有的bean都会实例化。
        BookDao bookDao = (BookDao) cpxac.getBean("bookDao");
        bookDao.save();
    }
}
//运行结果:BookDaoImpl的构造函数正在运行...BookDaoImpl_save

(2) 静态工厂实例化

//介绍完静态工厂实例化后,这种方式一般是用来兼容早期的一些老系统,所以了解为主。
//通过scope属性实现非单例。
<1> 准备需要被创建的类

public interface BookDao {
    public void save();
}

public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("BookDaoImpl_save");
    }
}

<2> 创建一个工厂类并提供一个静态方法
//静态工厂创建对象

public class BookDaoFactory {
    public static BookDao getBookDao(){
        //静态方法可直接通过类名访问,然后执行方法体,返回一个对象。
        return new BookDaoImpl();
    }
}

<3> 将工厂类配置到Spring容器
<bean id="bookDao" class="com.itheima.factory.BookDaoFactory" factory-method="getBookDao"/>
//class:工厂类的路径 factory-mehod:具体工厂类中创建对象的方法名
//代表实例化工厂类BookDaoFactory,并执行其中的getBookDao方法来创建bean。
<4> 编写运行类,在类中通过工厂获取对象

public class App {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext cpxac = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookDao bookDao = (BookDao) cpxac.getBean("BookDaoImpl");
        bookDao.save();
    }
}
//运行结果:BookDaoImpl_save

//工厂类存在的理由:在工厂的静态方法中,我们除了new对象还可以做其他的一些业务操作,这些操作必不可少。之前new对象的方式就无法添加其他的业务内容。
public class BookDaoFactory {
    public static BookDao getBookDao(){
        System.out.println("工厂启动...");
        //模拟必要的业务操作
        return new BookDaoImpl();
    }
}

(3) 实例工厂实例化

//通过scope属性实现非单例。
<1> 准备需要被创建的类

public interface BookDao {
    public void save();
}

public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("BookDaoImpl_save");
    }
} 

<2> 创建一个工厂类并提供一个普通方法
//注意此处和静态工厂的工厂类不一样地方是方法不是静态方法。

public class BookDaoFactory {
    public BookDao getBookDao(){
        return new BookDaoImpl();
    }
}

<3> 将工厂类配置到Spring容器

<bean id="bookFactory" class="com.itheima.factory.BookDaoFactory"/>
<!--代表实例化工厂类bookFactory。-->
<!--因为工厂类中的方法不是静态方法,所以必须先示例化工厂类,再来通过另一条XML语句调用工厂类对象中的方法创建bean。-->
<!--注意:实例化类的XML语句结束后才代表类实例化完成。因此不能将调用方法属性写在XML语句中,因为此时类对象还为实例化完成。除非为静态方法。-->
<bean id="bookDao" factory-bean="bookFactory" factory-method="getBookDao"/>
<!--代表调用bean(工厂类对象bookFactory)中的方法来创建bean。-->

<4> 编写运行类,在类中通过工厂获取对象

public class App {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext cpxac = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookDao bookDao = (BookDao) cpxac.getBean("bookDao");
        bookDao.save();
    }
}
//运行结果:BookDaoImpl_save

(4) FactoryBean实例化

//简化实例工厂实例化的开发
<1> 准备需要被创建的类

public interface BookDao {
    public void save();
}

public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("BookDaoImpl_save");
    }
}

<2> 创建一个工厂类,实现FactoryBean接口,重写接口的方法

public class BookDaoFactoryBean implements FactoryBean<BookDao> {
    //代替原始实例工厂中创建对象的方法
    public BookDao getObject() throws Exception {
        return new BookDaoImpl();
    }

    //返回所创建类的Class对象
    public Class<?> getObjectType() {
        return BookDao.class;
    }
}

<3> 将工厂类配置到Spring容器

<bean id="bookDao" class="com.itheima.factory.BookDaoFactoryBean"/>
//因为工厂类实现了FactoryBean<?>接口,所以不必再先用一条XML语句实例化工厂类,再用另一条XML语句执行对应方法。
//getObject()方法代替原始实例工厂中创建对象的方法,该方式任何情况下,所需执行的方法都是getObject(),所以不必再写factory-method属性。

<4> 编写运行类,在类中通过工厂获取对象

public class App {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext cpxac = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookDao bookDao = (BookDao) cpxac.getBean("bookDao");
        bookDao.save();
    }
}
//运行结果:BookDaoImpl_save
// FactoryBean接口其实会有三个方法
// 方法一:getObject(),被重写后,在方法中进行对象的创建并返回。
// 方法二:getObjectType(),被重写后,主要返回的是被创建类的Class对象。
// 方法三:isSingleton(),设置对象是否为单例,没有被重写,因为它已经给了默认值,默认为true。
// 那如果想改成单例,只需要将isSingleton()方法进行重写,修改返回为false,即可
public boolean isSingleton() {
    return false;
}


3 bean的生命周期

(1) 生命周期设置

<1> 准备需要被创建的类,添加初始化和销毁方法
//在BookDaoImpl类中分别添加两个方法,方法名任意

public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("BookDaoImpl_save");
    }
    //表示bean初始化对应的操作
    public void init(){
        System.out.println("BookDaoImpl_init");
    }
    //表示bean销毁前对应的操作
    public void destory(){
        System.out.println("BookDaoImpl_destory");
    }
}

<2> 配置生命周期
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" init-method="init" destroy-method="destory"/>
//init-method属性对应初始化方法的方法名。
//destroy-method属性对应销毁方法的方法名。
<3> 运行程序

public class App {
    public static void main( String[] args ) {
        ClassPathXmlApplicationContext cpxac = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookDao bookDao = (BookDao) cpxac.getBean("bookDao");
        bookDao.save();
    }
}
//运行结果:BookDaoImpl_initBookDaoImpl_save

//从结果中可以看出,init方法执行了,但是destroy方法却未执行。
//pring的IOC容器是运行在JVM中。运行main方法后,JVM启动,Spring加载配置文件生成IOC容器,从容器获取bean对象,然后调方法执行。
//main方法执行完后,JVM退出,这个时候IOC容器中的bean还没有来得及销毁就已经结束了,所以没有调用对应的destroy方法。

(2) Spring简化生命周期配置

//Spring提供了两个接口来完成生命周期的控制,好处是可以不用再进行配置 init-method和destroy-method。
<1> 准备需要被创建的类,添加初始化和销毁方法
//添加两个接口InitializingBean、DisposableBean并实现接口中的两个方法afterPropertiesSetdestroy

public class BookServiceImpl implements BookService,InitializingBean,DisposableBean {
//实现InitializingBean,DisposableBean两个接口,其中分别有destroy()和afterPropertiesSet()方法。
    private BookDao bookDao;
    //setBookDao方法是Spring的IOC容器为其注入属性的方法。
    
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
    public void save() {
        System.out.println("BookDaoImpl_save");
        bookDao.save();
    }
    public void destroy() throws Exception {
        System.out.println("BookDaoImpl_destroy");
    }
    public void afterPropertiesSet() throws Exception {
    //对于InitializingBean接口中的afterPropertiesSet方法,翻译过来为属性设置之后。
        System.out.println("BookDaoImpl_init");
    }
}

<2> 配置生命周期
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
//不用再进行配置 init-method和destroy-method。

(3) close()关闭容器

ClassPathXmlApplicationContext cpxac = new ClassPathXmlApplicationContext("applicationContext.xml");
cpxac.close();
//运行上述程序,就能执行destroy方法的内容。

(4) 注册钩子关闭容器

<1> 在容器未关闭之前,提前设置好回调函数,让JVM在退出之前回调此函数来关闭容器。

ClassPathXmlApplicationContext cpxac = new ClassPathXmlApplicationContext("applicationContext.xml");
cpxac.registerShutdownHook();

//注意:registerShutdownHook在ApplicationContext中也没有。
<2> 两种方式异同
相同点:这两种都能用来关闭容器。
不同点:close()是在调用的时候关闭,registerShutdownHook()是在JVM退出前调用关闭。
//close()方法只能写在代码最后,registerShutdownHook()方法可以写在代码任意位置。

(5) 小结

<1> 关于Spring中对bean生命周期控制提供了两种方式:
在配置文件中的bean标签中添加init-methoddestroy-method属性。
bean点对应类实现InitializingBeanDisposableBean接口,这种方式了解下即可。
<2> 关闭容器的两种方式:
close()方法
registerShutdownHook()方法
<3> 对于bean的生命周期控制在bean的整个生命周期中所处的位置如下:
<3.1> 初始化容器
创建对象
执行构造方法(构造方法)
//因为先要创建对象,而创建对象后,会先执行构造方法。
执行属性注入(setter方法)
//因为所创建的对象需要另一个对象,所以要执行setter方法后,该对象才算创建完成。
执行bean初始化方法(afterPropertiesSet方法)
//对象创建完全创建完成后,执行初始化方法。
<3.2> 使用bean
执行业务操作
<3.3> 销毁容器
执行bean销毁方法(destroy方法)

posted @ 2023-10-17 09:54  10kcheung  阅读(49)  评论(0)    收藏  举报