【Spring总结】基于配置的方式来写Spring

news2025/1/18 9:04:24

本篇文章是对这两天所学的内容做一个总结,涵盖我这两天写的所有笔记:

  1. 【Spring】 Spring中的IoC(控制反转)
  2. 【Spring】Spring中的DI(依赖注入)Dependence Import
  3. 【Spring】bean的基础配置
  4. 【Spring】bean的实例化
  5. 【Spring】bean的生命周期
  6. 【Spring】依赖注入方式,DI的方式
  7. 【Spring】使用三方包进行数据源对象(数据库)管理
  8. 【Spring】加载properties文件
  9. 【Spring】IoC容器的一些总结与补充

文章目录

  • 1. 控制反转与依赖注入的定义
    • 为什么需要控制反转?
    • Spring中怎么做控制反转?
      • 第一步:pom.xml引入相关依赖
      • 第二步:在Resource文件下创建Spring Config File(也就是配置IoC容器的内容),并配置bean
      • 第三步:在代码中调用IoC容器,并获取具体的bean
    • 为什么需要依赖注入?
    • 如何进行依赖注入?
      • Setter注入引用类型和简单类型
        • 第一步:删除`ServiceImpl`中的`new`创建方式,建立set方法,使得可以对`ServiceImpl`中的属性做修改
        • 第二步:在配置文件中写明使用什么类别给bookDao属性做注入
        • 第三步:测试
      • 构造器注入简单类型和引用类型
        • 第一步:删除`ServiceImpl`中的`new`创建方式,无需set方法,通过构造方法对`ServiceImpl`中的属性做修改
        • 第二步:在配置文件中写明使用什么类别给构造方法
        • 第三步:测试
      • 构造器注入的参数适配(了解,通常不用这些方法)
      • 依赖注入方式选择
      • 依赖自动装配,通过`autowire`属性指定类型
        • 按类型注入:`autowire="byType"`
        • 按名称注入:`autowire="byName"`
        • 自动装配注意事项
      • 集合注入
        • 在集合中注入简单类型——`value`
        • 在集合中注入引用类型——`ref`
    • 控制反转与依赖注入的关系
  • 2. bean的配置
    • bean的别名配置——`name`属性配置
    • bean的单例多例——`scope`属性配置
      • bean为什么默认为单例?
      • bean的作用范围说明
    • bean的实例化
      • 构造方法
      • 静态工厂
      • 实例工厂
      • FactoryBean
    • bean的生命周期
      • 在类中提供生命周期控制方法,并在配置文件中配置init-method&destroy-method(配置)
        • 通过ctx.close()执行IoC容器关闭
        • 通过注册关闭钩子执行IoC容器关闭
      • 实现接口来做和init和destroy(接口)
      • bean的生命周期执行顺序
  • 3. 使用第三方包进行数据源的管理(例子:使用com.alibaba.druid进行数据库连接)
    • 第一步:在pom.xml中导入坐标
    • 第二步:在bean中配置连接
  • 4. 在配置文件中读取properties文件中的属性
    • 第一步:建立jdbc.properties文件,内容如下
    • 第二步:开启context命名空间
    • 第三步:使用context命名空间加载指定properties文件
    • 第四步:使用`${}`读取加载的属性值
    • 其他问题
  • 5. 关于IoC容器其他内容的总结与补充
    • 创建容器的两种方式
      • 相对路径导入
      • 绝对路径导入
    • 获取Bean的三种方式
      • 获取后强转类型
      • 在获取时指定类型
      • 通过类型获取Bean
    • 容器类层次结构图
    • BeanFactory
  • 6. 复习内容

1. 控制反转与依赖注入的定义

为什么需要控制反转?

我们在传统写业务层的时候,需要给业务层中的属性通过new操作生成对象。

public class BookServiceImpl implements BookService{

	private BookDao bookDao = new BookDaoImpl();
	
	public void save(){
		bookDao.save()
	}
}

假如某一天我不再希望使用BookDaoImpl这个实现类,而是希望使用BookDaoImpl2这个实现类,我们就需要再次修改代码,重新修改BookServiceImplbookDao属性的初始化。这样子的修改方式就不是太合理,耦合度较高。
如果我们可以将这种主动式产生对象转换为由外部指定产生对象,就可以减少这种修改,降低耦合度,通俗的说,就是将对象的创建控制权由程序转移到外部,这就是控制反转(IoC, Inversion of Control)

Spring中怎么做控制反转?

Spring实现了IoC,此处介绍基于配置来写IoC:

第一步:pom.xml引入相关依赖

引入相关依赖,刷新maven:

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context</artifactId>
</dependency>

第二步:在Resource文件下创建Spring Config File(也就是配置IoC容器的内容),并配置bean

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--1. 导入spring的坐标spring-context-->

    <!--2. 配置bean-->
    <!--bean标签标示配置bean
    id属性标示给bean起名字
    class属性标示给bean定义的类型
    -->
    <bean id="bookDao" class="com.example.demo231116.dao.impl.BookDaoImpl" />

    <bean id="bookService" class="com.example.demo231116.service.impl.BookServiceImpl" />
</beans>

第三步:在代码中调用IoC容器,并获取具体的bean

public class Demo231116Application2 {
    public static void main(String[] args) {
        // 3. 获取IoC容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 4. 获取bean
        BookService bookService = (BookService) ctx.getBean("bookService");
        bookService.save();
    }
}

为什么需要依赖注入?

在上面给出的BookServiceImpl.java代码中,有一个bookDao的属性,我们定义为:

private BookDao bookDao = new BookDaoImpl();

我们想要通过IoC去管理bean,为了真正实现解耦,我们不再保留new形式创建的Dao对象,这时候我们就需要使用依赖注入来完成这一点

如何进行依赖注入?

思考两个问题:

  1. Service中需要的Dao对象如何进入到Service中?(提供方法,使得我们可以修改Service中的Dao对象)
  2. ServiceDao间的关系如何描述?(通过配置的形式来描述)
    基于以上两个问题及答案,我们总结了不同的注入方式

Setter注入引用类型和简单类型

第一步:删除ServiceImpl中的new创建方式,建立set方法,使得可以对ServiceImpl中的属性做修改
public class BookServiceImpl implements BookService {
	// 删除原本的new方法,给bookDao设置一个setter方法
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    private BookDao bookDao;
	private String bookName;

    public void save(){
        System.out.println("book service save... bookName:" + this.bookName);
        bookDao.save();
    }

}
第二步:在配置文件中写明使用什么类别给bookDao属性做注入

在配置文件中,在bookService的bean内部配置property,该标签表示配置当前bean的属性,其中name表示配置哪一个具体属性,ref表示引用类型参照的具体bean,value则是简单类型的对应值

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="bookDao" class="com.example.demo231116.dao.impl.BookDaoImpl" />

    <bean id="bookService" class="com.example.demo231116.service.impl.BookServiceImpl">
        <property name="bookDao" ref="bookDao"/>
        <property name="bookName" value="bkName!!!" />
    </bean>
</beans>
第三步:测试

运行如下代码:

public class Demo231116Application2 {
    public static void main(String[] args) {
        // 获取IoC容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        
        BookService bookService = (BookService) ctx.getBean("bookService");
        bookService.save();
    }
}

结果为:

book service save... bookName:bkName!!!
book dao save...

构造器注入简单类型和引用类型

第一步:删除ServiceImpl中的new创建方式,无需set方法,通过构造方法对ServiceImpl中的属性做修改
public class BookServiceImpl implements BookService {

    private BookDao bookDao;
	private String bookName;

	// 删除原本的new方法,给bookDao设置一个setter方法
	public BookServiceImpl(BookDao bookDao, String bookName){
		this.bookDao = bookDao;
		this.bookName = bookName;
	}

    public void save(){
        System.out.println("book service save... bookName:" + this.bookName);
        bookDao.save();
    }
}
第二步:在配置文件中写明使用什么类别给构造方法

在配置文件中,在bookService的bean内部配置constructor-arg,该标签表示构造方法的参数,其中name表示配置哪一个具体属性,ref表示引用类型参照的具体bean,value则是简单类型的对应值

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="bookDao" class="com.example.demo231116.dao.impl.BookDaoImpl" />

    <bean id="bookService" class="com.example.demo231116.service.impl.BookServiceImpl">
        <constructor-arg name="bookDao" ref="bookDao"/>
        <constructor-arg name="bookName" value="bkName!!!" />
    </bean>
</beans>
第三步:测试

运行如下代码:

public class Demo231116Application2 {
    public static void main(String[] args) {
        // 获取IoC容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        
        BookService bookService = (BookService) ctx.getBean("bookService");
        bookService.save();
    }
}

结果为:

book service save... bookName:bkName!!!
book dao save...

构造器注入的参数适配(了解,通常不用这些方法)

  • 配置中使用constructor-arg的标签type属性按形参类型注入(为了解耦,避免配置和name耦合)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="bookDao" class="com.example.demo231116.dao.impl.BookDaoImpl" />

    <bean id="bookService" class="com.example.demo231116.service.impl.BookServiceImpl">
        <constructor-arg type="com.example.demo231116.dao.bookDao" ref="bookDao"/>
        <constructor-arg type="java.lang.String" value="bkName!!!" />
    </bean>
</beans>
  • 配置中使用constructor-arg的标签index属性按形参位置注入(为了解决有同样类型的参数的问题)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="bookDao" class="com.example.demo231116.dao.impl.BookDaoImpl" />

    <bean id="bookService" class="com.example.demo231116.service.impl.BookServiceImpl">
        <constructor-arg index="0" ref="bookDao"/>
        <constructor-arg index="1" value="bkName!!!" />
    </bean>
</beans>

依赖注入方式选择

在这里插入图片描述
建议使用setter注入
第三方技术根据情况选择


依赖自动装配,通过autowire属性指定类型

根据bean所依赖的资源在容器中自动查找并注入到bean的过程为自动装配
自动装配的方式通过autowire指定,有四种类型:

  • 按类型(常用)
  • 按名称
  • 按构造方法
  • 不启用自动装配

注意,想要让IoC容器实现自动装配,必须给这些属性配置setter方法!

按类型注入:autowire="byType"

在这里插入图片描述
此时配置BookServiceImpl的代码应该这么写:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="bookDao" class="com.example.demo231116.dao.impl.BookDaoImpl" />
    <bean id="userDao" class="com.example.demo231116.dao.impl.UserDaoImpl" />

    <bean id="bookService" class="com.example.demo231116.service.impl.BookServiceImpl" autowire="byType" />
</beans>

bean的类型指向必须唯一!,推荐使用。
假如配置了两个指向同一类型,id不同的bean,执行报错会提示找到了两个bean,不知道匹配哪一个。

按名称注入:autowire="byName"

在这里插入图片描述
此时配置BookServiceImpl的代码应该这么写:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="bookDao" class="com.example.demo231116.dao.impl.BookDaoImpl" />
    <bean id="userDao" class="com.example.demo231116.dao.impl.UserDaoImpl" />

    <bean id="bookService" class="com.example.demo231116.service.impl.BookServiceImpl" autowire="byName" />
</beans>

属性的名称要能够在配置的bean中的id对应的上。此方式因变量名与配置耦合,不推荐使用。

自动装配注意事项
  • 自动装配用于引用类型依赖注入,不能对简单类型操作(因为简单类型你可以有很多不同的值,除非一个一个写)
  • 自动装配优先级低于setter注入和构造器注入,同时出现时自动装配无效

集合注入

关于array、list、set、map、properties的注入
假如BookDaoImpl.java代码如下:

package com.example.demo.dao.impl;

import com.example.demo.dao.BookDao;

import java.util.*;

public class BookDaoImpl implements BookDao {

    private int[] array;
    private List<String> list;
    private Set<String> set;
    private Map<String, String> map;
    private Properties properties;

    @Override
    public void save() {
        System.out.println("book dao save...");

        System.out.println("遍历数组:" + Arrays.toString(array));

        System.out.println("遍历List:" + list);

        System.out.println("遍历set:" + set);

        System.out.println("遍历Map:" + map);

        System.out.println("遍历properties:" + properties);
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public void setMap(Map<String, String> map) {
        this.map = map;
    }

    public void setSet(Set<String> set) {
        this.set = set;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public void setArray(int[] array) {
        this.array = array;
    }
}
在集合中注入简单类型——value

配置文件需要如下配置:

<bean id="bookDao" class="com.example.demo.dao.impl.BookDaoImpl">
    <property name="array">
        <array>
            <value>100</value>
            <value>200</value>
            <value>300</value>
        </array>
    </property>
    <property name="list">
        <list>
            <value>a</value>
            <value>b</value>
            <value>c</value>
        </list>
    </property>
    <property name="set">
        <set>
            <value>c</value>
            <value>c</value>
            <value>d</value>
        </set>
    </property>
    <property name="map">
        <map>
            <entry key="country" value="china" />
            <entry key="province" value="guangdong" />
            <entry key="city" value="shenzhen" />
        </map>
    </property>
    <property name="properties">
        <props>
            <prop key="country">china</prop>
            <prop key="province">henan</prop>
            <prop key="city">kaifeng</prop>
        </props>
    </property>
</bean>

其中,set属性自动过滤元素,如果执行以下java代码:

public class Demo231116Application2 {
    public static void main(String[] args) {
        // 获取IoC容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        
        BookService bookService = (BookService) ctx.getBean("bookService");
        bookService.save();
    }
}

则结果如下:

book service save...
book dao save...
遍历数组:[100, 200, 300]
遍历List:[a, b, c]
遍历set:[c, d]
遍历Map:{country=china, province=guangdong, city=shenzhen}
遍历properties:{country=china, province=henan, city=kaifeng}
在集合中注入引用类型——ref

只需去掉简单类型的value属性,使用ref属性

<bean id="bookDao" class="com.example.demo.dao.impl.BookDaoImpl">
    <property name="array">
        <array>
            <ref bean="beanId" />
            <ref bean="beanId" />
            <ref bean="beanId" />
        </array>
    </property>
</bean>

控制反转与依赖注入的关系

这部分我也思考了一下,认为网上有句话说的很对:简单地说,控制反转是一种设计思想,而依赖注入是控制反转思想的一种实现方式
我认为,实际上控制反转就是我们将已有的类别放置到IoC容器中,便于让外部进行处理。而依赖注入是真正将具体的类别给属性完成注入的方式。

2. bean的配置

bean的别名配置——name属性配置

通过name属性进行name的别名配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
       
    <bean id="bookDao" name="dao" class="com.example.demo231116.dao.impl.BookDaoImpl" />
    
</beans>

这样,当我们在主代码里调用ctx.getBean("bookDao")ctx.getBean("dao"),起到的效果是相同的:

package com.example.demo231116;

import com.example.demo231116.dao.BookDao;
import com.example.demo231116.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Demo231116Application2 {
    public static void main(String[] args) {
        // 获取IoC容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        bookDao = (BookDao) ctx.getBean("dao");
        bookDao.save();
    }
}

bean的单例多例——scope属性配置

若保持别名配置里那样的bean配置,我们生成出来的bean是单例的,当我们执行以下代码:

BookDao bookDao = (BookDao) ctx.getBean("dao");
BookDao bookDao1 = (BookDao) ctx.getBean("dao");
System.out.println(bookDao);
System.out.println(bookDao1);

会得到结果:

com.example.demo231116.dao.impl.BookDaoImpl@309e345f
com.example.demo231116.dao.impl.BookDaoImpl@309e345f

两个对象出自同一个实例,但如果我们不希望它是以单例形式创建,而是以多例形式创建的时候,我们需要配置scope属性:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
       
    <bean id="bookDao" name="dao" class="com.example.demo231116.dao.impl.BookDaoImpl" scope="prototype" />
    
</beans>

scope有两个属性:singleton单例(默认),prototype非单例
配置scope属性后再执行上面的代码,会得到结果:

com.example.demo231116.dao.impl.BookDaoImpl@309e345f
com.example.demo231116.dao.impl.BookDaoImpl@56a6d5a6

两个对象不出自同一个实例

bean为什么默认为单例?

如果每创建一个对象,都是不同的实例,那么对内存的消耗会很大

bean的作用范围说明

适合交给容器管理的bean:表现层对象、业务层对象、数据层对象、工具对象
不适合交给容器管理的bean:封装实体的域对象

bean的实例化

构造方法

当我们在Spring Config文件中配置:

<bean id="bookDao" name="dao" class="com.example.demo231116.dao.impl.BookDaoImpl" />

这种bean的实例化默认调用无参构造方法,对于写有参构造,对有参构造中参数的注入则是上面依赖注入的部分

静态工厂

假如写了一个BookDao工厂BookDaoFactory.java

package com.example.demo231116.factory;

import com.example.demo231116.dao.BookDao;
import com.example.demo231116.dao.impl.BookDaoImpl;

public class BookDaoFactory {
    public static BookDao getBookDao(){
        System.out.println("Factory method....");
        return new BookDaoImpl();
    }
}

我们想让bean通过调用这个工厂方法返回里面的BookDaoImpl,则需要如下配置:

<bean id="bookDaoFactory" class="com.example.demo231116.factory.BookDaoFactory" factory-method="getBookDao" />

id属性和class属性与之前差异不大,分别为bean名称和bean类别,而factory-method指定具体地工厂方法

实例工厂

注意:实例工厂与静态工厂的区别是,实例工厂中的方法不是静态方法!
静态方法和非静态方法的区别是:

  • 静态方法可以在不建立该类对象的情况下,通过类名.方法进行调用
  • 非静态方法需要建立该类对象后,通过对象名.方法进行调用

详细可以看第4个笔记中的实验

假如写了一个BookDao工厂BookDaoFactory.java

package com.example.demo231116.factory;

import com.example.demo231116.dao.BookDao;
import com.example.demo231116.dao.impl.BookDaoImpl;

public class BookDaoFactory {
    public BookDao getBookDao(){
        System.out.println("Factory method....");
        return new BookDaoImpl();
    }
}

在配置文件中需要如下写:

<bean id="bookDaoFactory2" class="com.example.demo231116.factory.BookDaoFactory" />
<bean id="bd" factory-method="getBookDaoUnstatic" factory-bean="bookDaoFactory2"  />

即先建立出工厂类的bean,再建立下面这个使用该工厂方法的bean。factory-method指定其中具体的工厂方法,factory-bean指定工厂方法所属的父类,在最后调用的时候,直接调用bd就可以完成初始化:

BookDao bookDao3 = (BookDao) ctx.getBean("bd");
System.out.println(bookDao3);

在这种方法里,实际上我们创建出的bookFactory2这个bean没有被用到,所以还有如下的方法FactoryBean

FactoryBean

直接写一个工厂方法,implement FactoryBean。FactoryBean是一个泛型方法,指定其中的类型,实现其方法:
第一个getObject()返回具体地对象
第二个getObjectType()返回对象的类型

package com.example.demo231116.factory;

import com.example.demo231116.dao.BookDao;
import com.example.demo231116.dao.impl.BookDaoImpl;
import org.springframework.beans.factory.FactoryBean;

public class BookDaoFactoryBean implements FactoryBean<BookDao>{
    @Override
    public BookDao getObject() throws Exception {
        return new BookDaoImpl();
    }

    @Override
    public Class<?> getObjectType() {
        return BookDao.class;
    }
}

如此,在bean配置中,只需要像原本普通的一行即可:

<bean id="bookDaoFactoryMethod" class="com.example.demo231116.factory.BookDaoFactoryBean" />

注意,当我们执行以下代码后,会发现打印出来的结果相同,说明这是单例模式创建的。

BookDao bookDao4 = (BookDao) ctx.getBean("bookDaoFactoryMethod");
BookDao bookDao5 = (BookDao) ctx.getBean("bookDaoFactoryMethod");
System.out.println(bookDao4);
System.out.println(bookDao5);

如果我们想要该工厂方法返回的不是单例模式,而是不同的实例,则我们需要补充FactoryBean中的isSingleton()方法,当这个方法设置为True时,就是用单例模式创建的对象,如果这个方法返回为False,就不会使用单例模式,每一次构造都会创建出新的对象:

@Override
public boolean isSingleton() {
    return true;
}

bean的生命周期

假如我们希望在建立bean时候执行一些资源的初始化,在bean销毁之前执行一些资源的销毁,我们就会需要考虑到bean的声明周期,通过以下方式进行生命周期的控制。

在类中提供生命周期控制方法,并在配置文件中配置init-method&destroy-method(配置)

定义实现类如下:

package com.example.demo231116.dao.impl;

import com.example.demo231116.dao.BookDao;

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

    public void init(){
        System.out.println("book dao init...");
    }

    public void destroy(){
        System.out.println("book dao destroy...");
    }
}

配置时,我们只需要指定具体的初始化方法init-method和销毁方法destroy-method即可:

<bean id="bookDaoCycle" class="com.example.demo231116.dao.impl.BookDaoImpl" init-method="init" destroy-method="destroy" />

这样,当我们在主函数中执行:

// IoC容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

BookDao bookDao = (BookDao) ctx.getBean("bookDaoCycle");
System.out.println(bookDao);

结果是:

book dao init...
com.example.demo231116.dao.impl.BookDaoImpl@5dd6264

这时候的疑问可能是,为什么destroy方法没有被执行?
答案是:在程序执行结束之后,IoC容器还没有执行关闭操作,java虚拟机就已经强行关闭了。那么应该如何在java虚拟机关闭之前执行IoC容器关闭呢?如下有两种方法

通过ctx.close()执行IoC容器关闭

ApplicationContext并没有close方法,ApplicationContext下的一个接口ClassPathXmlApplicationContext才有定义close方法,所以这里想要使用close方法,需要修改IoC容器定义,然后在末尾调用ctx.close()

// IoC容器
ClassPathXmlApplicationContextctx = new ClassPathXmlApplicationContext("applicationContext.xml");

BookDao bookDao = (BookDao) ctx.getBean("bookDaoCycle");
System.out.println(bookDao);

ctx.close()

输出结果为:

book dao init...
com.example.demo231116.dao.impl.BookDaoImpl@5dd6264
book dao destroy...

但是如果是这样的话,ctx.close()只能在程序的末尾写,因为在开头定义结束就写的话,这个IoC容器就被销毁了,也不可能执行一些getBean的操作,还有一个方法是通过注册钩子关闭IoC容器

通过注册关闭钩子执行IoC容器关闭

注册一个关闭钩子,在不用强行关闭IoC容器的情况下,设置在java虚拟机关闭之前让程序执行销毁的方法:

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

BookDao bookDao = (BookDao) ctx.getBean("bookDaoCycle");
System.out.println(bookDao);

这样就不再需要强硬地执行ctx.close()方法了

实现接口来做和init和destroy(接口)

只需要在bean类下多实现InitializingBeanDisposableBean这两个接口,并实现其中的afterPropertiesSetdestroy方法:

package com.example.demo231116.dao.impl;

import com.example.demo231116.dao.BookDao;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

public class BookDaoImpl implements BookDao, InitializingBean, DisposableBean {
    public void save(){
        System.out.println("book dao save...");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("接口destroy");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("接口init");
    }
}

然后配置bean只需要简单地配置如下:

<bean id="bookDaoCycle" class="com.example.demo231116.dao.impl.BookDaoImpl" />

这个afterPropertiesSet的init方法,是在先执行属性设置后再执行init方法

bean的生命周期执行顺序

  • 初始化容器:
    创建对象(内存分配)
    执行构造方法
    执行属性注入(setter注入)
    执行bean的初始化方法
  • 使用bean
    执行业务操作
  • 关闭/销毁容器
    执行bean的销毁方法

3. 使用第三方包进行数据源的管理(例子:使用com.alibaba.druid进行数据库连接)

第一步:在pom.xml中导入坐标

<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>druid</artifactId>
	<version>1.1.21</version>
</dependency>

第二步:在bean中配置连接

在这里,首先应该查看这个Druid包给我们提供了一些什么方法。
先查看构造方法能否让我们通过构造注入写入一些配置信息,但在具体查看后发现是没有的(详细可见笔记7)。
但观察到可以通过setter注入来写一些配置信息,所以这里就使用setter注入:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="jdbc:mysql://localhost:3306/ecommercedb"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    </bean>
</beans>

4. 在配置文件中读取properties文件中的属性

第一步:建立jdbc.properties文件,内容如下

jdbc.url=jdbc:mysql://localhost:3306/ecommercedb
jdbc.username=root
jdbc.password=123456
jdbc.driverClassName=com.mysql.jdbc.Driver

第二步:开启context命名空间

我理解需要开辟新的命名空间,是读取非工程文件中的内容,而是读取外部文件中的内容

第三步:使用context命名空间加载指定properties文件

在这一步中需要通过location写清楚加载properties文件的位置

第四步:使用${}读取加载的属性值

第二——第四步具体代码如下,最终只需要通过${}读取加载的属性值即可:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
                http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans.xsd
                http://www.springframework.org/schema/context
                http://www.springframework.org/schema/context/spring-context.xsd">

<!--    1. 开启context命名空间-->
<!--    2. 使用context空间加载properties文件-->
    <context:property-placeholder location="jdbc.properties"/>
<!--    3. 使用属性占位符${}读取properties文件中的属性-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="driverClassName" value="${jdbc.url}" />
    </bean>
</beans>

其他问题

  1. 假如在配置文件中写的是username=root,而不是jdbc.username=root,则可能使用${username}得到的结果并不会是root
    是因为系统环境变量里面有和properties里面定义的变量重名,则properties里面的变量不会被加载,也就是说系统环境变量的优先级会比properties里面的变量优先级高。
    为了使用properties里面面的变量,而不是系统环境变量,我们可以对xml配置文件,让系统环境变量不被使用:
<context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>
  1. 加载多个properties文件可以用逗号分隔
<context:property-placeholder location="jdbc.properties, msg.properties"/>
  1. 加载所有properties文件可以用正则*.properties
<context:property-placeholder location="*.properties"/>
  1. 以上写法不够标准,标准的是classpath:*.properties
<context:property-placeholder location="classpath:*.properties"/>
  1. 如果不止要从工程中加载properties文件,还要从jar包等中加载,则写classpath*:*properties
<context:property-placeholder location="classpath*:*.properties"/>

第4、5种根据不同的需求来写

5. 关于IoC容器其他内容的总结与补充

创建容器的两种方式

相对路径导入

ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

绝对路径导入

ApplicationContext ctx = new FileSystemXmlApplicationContext("E:\\Code\\Java\\SpringStudy\\project1\\src\\main\\resources\\applicationContext.xml");

获取Bean的三种方式

获取后强转类型

BookDao bookDao = (BookDao) ctx.getBean("bookDao");

在获取时指定类型

BookDao bookDao = (BookDao) ctx.getBean("bookDao", BookDao.class);

通过类型获取Bean

BookDao bookDao = ctx.getBean(BookDao.class);

容器类层次结构图

在这里插入图片描述

BeanFactory

这是最早的加载IoC容器的方法,使用BeanFactory的方法如下:

Resource resource = new ClassPathResource("applicationContext.xml");
BeanFactory bf = new XmlBeanFactory(resource);
BookDao bookDao = bf.getBean(BookDao.class);
bookDao.save();

BeanFactory与我们现在ApplicationContext的区别在于:
BeanFactory是延迟加载bean,ApplicationContext是立即加载bean
即前者在没有getBean的时候是不会提前执行类的构造方法的,而Application就算没有getBean也会执行构造方法
如果想在ApplicationContext上实现延迟加载bean,只需要加参数:lazy-init="true"

<bean id="bookDao" class="com.example.project1.dao.impl.BookDaoImpl" lazy-init="true"/>

6. 复习内容

在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1224939.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机msvcr120.dll丢失的解决方法,分享多种亲测可靠的方法

在使用计算机的过程中&#xff0c;我们有时可能会遇到一些技术问题&#xff0c;其中之一就是提示丢失msvcr120.dll文件。当计算机提示丢失msvcr120.dll文件时&#xff0c;可能是由于某些程序无法找到这个文件&#xff0c;从而导致程序无法正常运行。那么我们需要如何解决修复好…

一文读懂:testcafe框架和页面元素交互

一、互动要求 使用 TestCafe 与元素进行交互操作&#xff0c;元素需满足以下条件&#xff1a;☟ 元素在 body 页面窗口或 iframe 窗口的元素内。如果某个元素在视口之外&#xff0c;则 TestCafe 通过滚动可以滚动到元素可见。 元素是可见的&#xff0c;具有以下属性&#…

Leetcode—剑指OfferII LCR 022.环形链表II【中等】

2023每日刷题&#xff08;三十三&#xff09; Leetcode—LCR 022.环形链表II 算法思想 参考k神的博客 实现代码 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/ struct ListNode *detectCycle(struct List…

2.3 调用智谱 API

调用智谱 API 1 申请调用权限2 调用智谱 AI API3 使用 LangChain 调用智谱 AI参考&#xff1a; 智谱 AI 是由清华大学计算机系技术成果转化而来的公司&#xff0c;致力于打造新一代认知智能通用模型。公司合作研发了双语千亿级超大规模预训练模型 GLM-130B&#xff0c;并构建了…

Python------列表 集合 字典 推导式(本文以 集合为主)

推导式&#xff1a; 推导式comprehensions&#xff08;又称解析式&#xff09;&#xff0c;是Python的一种独有特性。推导式是可以从一个数据序列 构建 另一个 新的数据序列&#xff08;一个有规律的列表或控制一个有规律列表&#xff09;的结构体。 共有三种推导&#xff…

HUAWEI华为笔记本MateBook X 2021款i5集显(EULD-WFH9,WXX9)原装出厂Windows11系统工厂模式包

下载链接&#xff1a;https://pan.baidu.com/s/1gQ_O203SSm83Nc-zDk1iNA?pwd4exz 提取码&#xff1a;4exz 系统带F10一键智能还原功能隐藏恢复分区、所有驱动、Office办公软件、华为电脑管家等预装程序 所需要工具&#xff1a;32G或以上的U盘 文件格式&#xff1a;zip …

TensorRt推理加速框架Python API服务器部署教程以及运行Helloworld程序

一、确认cuda工具包和n卡相关驱动是否安装 在终端中输入以下命令&#xff1a; nvcc -V如果出现以下提示&#xff0c;则已经成功安装 在终端中输入以下命令&#xff1a; nvidia-smi如果出现即为成功&#xff0c;我在这里就不去介绍怎么下载cuda和驱动怎么下载了&#xff0c;…

【Linux系统化学习】进程的父子关系 | fork 进程

个人主页点击直达&#xff1a;小白不是程序媛 Linux专栏&#xff1a;Linux系统化学习 目录 前言&#xff1a; 父子进程 父子进程的引入 查看父子进程 查询进程的动态目录 更改进程的工作目录 fork创建进程 fork的引入 fork的使用 fork的原理 fork如何实现的&#…

2023-2024华为ICT大赛-计算赛道-广东省省赛初赛-高职组-部分赛题分析【2023.11.18】

2023-2024华为ICT大赛 计算赛道 广东省 省赛 初赛 高职组 部分赛题 分析【2023.11.18】 文章目录 单选题tpcds模式中存在表customer&#xff0c;不能成功删除tpcds模式是&#xff08; &#xff09;以下哪个函数将圆转换成矩形&#xff08; &#xff09;下列哪个选项表示依赖该D…

一键云端,AList 整合多网盘,轻松管理文件多元共享!

hello&#xff0c;我是小索奇&#xff0c;本篇教大家如何使用AList实现网盘挂载 可能还是有小伙伴不懂&#xff0c;所以简单介绍一下哈 AList 是一款强大的文件管理工具&#xff0c;为用户提供了将多种云存储服务和文件共享协议集成在一个平台上的便利性。它的独特之处在于&am…

cesium雷达效果(脉冲圆)

cesium雷达效果(脉冲圆) 下面富有源码 实现思路 使用ellipse方法加载圆型,修改ellipse中‘material’方法重写glsl来实现当前效果 示例代码 index.html <!DOCTYPE html> <html lang="en"><head>

Typora——优雅的排版也是一种品味

电脑中用于编辑文本的软件&#xff0c;一直以来可谓是层出不穷&#xff0c;大家脑海中一定会浮现出很多名字&#xff1a;word&#xff0c;OneNote&#xff0c;记事本&#xff0c;wps&#xff0c;LaTeX&#xff0c;还有各种小众的office工具&#xff0c;等等等等。今天学长将介绍…

java回调函数

在java中是存在回调函数的&#xff0c;我们可以把回调函数理解为一个被作为参数传递的函数。 类似于&#xff0c;我可以设置一个功能给系统&#xff0c;但是只有特定时候才会触发&#xff0c;触发的时候就会把函数作为参数的形式传递到另外的函数中。一般都是使用系统中写好的…

科研学习|科研软件——面板数据、截面数据、时间序列数据的区别是什么?

一、数据采集方式不同 面板数据是通过在多个时间点上对同一组体进行观测而获得的数据。面板数据可以是横向面板数据&#xff0c;即对同一时间点上不同个体的观测&#xff0c;也可以是纵向面板数据&#xff0c;即对同一个体在不同时间点上的观测。采集面板数据需要跟踪相同的个体…

文章分类列表进行查询(实体类日期格式设置)

categoryController GetMappingpublic Result<List<Category>> list(){List<Category> cs categoryService.list();return Result.success(cs);} categoryService //列表查询List<Category> list(); categoryServiceImpl Overridepublic List<Cat…

深度学习交通车辆流量分析 - 目标检测与跟踪 - python opencv 计算机竞赛

文章目录 0 前言1 课题背景2 实现效果3 DeepSORT车辆跟踪3.1 Deep SORT多目标跟踪算法3.2 算法流程 4 YOLOV5算法4.1 网络架构图4.2 输入端4.3 基准网络4.4 Neck网络4.5 Head输出层 5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; *…

kafka入门(一):kafka消息消费

安装kafka&#xff0c;创建 topic&#xff1a; Windows安装kafka, 详情见&#xff1a;https://blog.csdn.net/sinat_32502451/article/details/133067851 Linux 安装kafka&#xff0c;详情见&#xff1a;https://blog.csdn.net/sinat_32502451/article/details/133080353 添…

python的文件目录操作 1

我们在实际开发中&#xff0c;经常需要对文件进行读取、遍历、修改等操作&#xff0c;通过 python 的标准内置os模块&#xff0c;能够以简洁高效的方式完成这些操作。常见的操作整理如下&#xff1a; 文件夹操作&#xff1a;包括文件夹的创建、修改&#xff08;改名/移动&…

redis cluster搭建

k8s部署 Redis Insight k8s部署redis集群_mob6454cc6c6291的技术博客_51CTO博客 占用的内存竟然这么小&#xff0c;才200M左右 随便选个节点进去&#xff0c;看能否连接上其他节点 redis-cli -h redis-cluster-v1-0.redis-cluster.project-gulimall.svc.cluster.local 再创建个…

Typecho用宝塔面板建站(保姆级教程)

提前准备&#xff1a; 1 已备案域名 注意:在腾讯云备案的域名部署阿里云服务器的话还需要在阿里云备案&#xff0c;反之亦然 2 服务器 服务器操作系统设置为windows 服务器实例设置&#xff1a;依次开放8888/888/443/3000-4000/21/22端口 个人用的阿里云&#xff0c;到安全组配…