【Spring总结】基于配置的方式来写Spring
本篇文章是对这两天所学的内容做一个总结,涵盖我这两天写的所有笔记:
- 【Spring】 Spring中的IoC(控制反转)
- 【Spring】Spring中的DI(依赖注入)Dependence Import
- 【Spring】bean的基础配置
- 【Spring】bean的实例化
- 【Spring】bean的生命周期
- 【Spring】依赖注入方式,DI的方式
- 【Spring】使用三方包进行数据源对象(数据库)管理
- 【Spring】加载properties文件
- 【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这个实现类,我们就需要再次修改代码,重新修改BookServiceImpl中bookDao属性的初始化。这样子的修改方式就不是太合理,耦合度较高。
如果我们可以将这种主动式产生对象转换为由外部指定产生对象,就可以减少这种修改,降低耦合度,通俗的说,就是将对象的创建控制权由程序转移到外部,这就是控制反转(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标签标示配置beanid属性标示给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. 获取beanBookService bookService = (BookService) ctx.getBean("bookService");bookService.save();}
}
为什么需要依赖注入?
在上面给出的BookServiceImpl.java代码中,有一个bookDao的属性,我们定义为:
private BookDao bookDao = new BookDaoImpl();
我们想要通过IoC去管理bean,为了真正实现解耦,我们不再保留new形式创建的Dao对象,这时候我们就需要使用依赖注入来完成这一点
如何进行依赖注入?
思考两个问题:
Service中需要的Dao对象如何进入到Service中?(提供方法,使得我们可以修改Service中的Dao对象)Service与Dao间的关系如何描述?(通过配置的形式来描述)
基于以上两个问题及答案,我们总结了不同的注入方式
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;@Overridepublic 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>{@Overridepublic BookDao getObject() throws Exception {return new BookDaoImpl();}@Overridepublic 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类下多实现InitializingBean和DisposableBean这两个接口,并实现其中的afterPropertiesSet和destroy方法:
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...");}@Overridepublic void destroy() throws Exception {System.out.println("接口destroy");}@Overridepublic 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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://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>
其他问题
- 假如在配置文件中写的是
username=root,而不是jdbc.username=root,则可能使用${username}得到的结果并不会是root
是因为系统环境变量里面有和properties里面定义的变量重名,则properties里面的变量不会被加载,也就是说系统环境变量的优先级会比properties里面的变量优先级高。
为了使用properties里面面的变量,而不是系统环境变量,我们可以对xml配置文件,让系统环境变量不被使用:
<context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>
- 加载多个properties文件可以用逗号分隔
<context:property-placeholder location="jdbc.properties, msg.properties"/>
- 加载所有properties文件可以用正则*.properties
<context:property-placeholder location="*.properties"/>
- 以上写法不够标准,标准的是classpath:*.properties
<context:property-placeholder location="classpath:*.properties"/>
- 如果不止要从工程中加载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. 复习内容


相关文章:
【Spring总结】基于配置的方式来写Spring
本篇文章是对这两天所学的内容做一个总结,涵盖我这两天写的所有笔记: 【Spring】 Spring中的IoC(控制反转)【Spring】Spring中的DI(依赖注入)Dependence Import【Spring】bean的基础配置【Spring】bean的实…...
Unity在Windows选项下没有Auto Streaming
Unity在Windows选项下没有Auto Streaming Unity Auto Streaming插件按网上说的不太好使最终解决方案 Unity Auto Streaming插件 我用的版本是个人版免费版,版本号是:2021.2.5f1c1,我的里边Windows下看不到Auto Streaming选项,就像下边这张图…...
下厨房网站月度最佳栏目菜谱数据获取及分析
目录 概要 源数据获取 写Python代码爬取数据 Scala介绍与数据处理 1.Sacla介绍...
【Java 进阶篇】深入理解 JQuery 事件绑定:标准方式
在前端开发中,处理用户与页面的交互是至关重要的一部分。JQuery作为一个广泛应用的JavaScript库,为我们提供了简便而强大的事件绑定机制,使得我们能够更加灵活地响应用户的行为。本篇博客将深入解析 JQuery 的标准事件绑定方式,为…...
某app c++层3处魔改md5详解
hello everybody,本期是安卓逆向so层魔改md5教学,干货满满,可以细细品味,重点介绍的是so层魔改md5的处理. 常见的魔改md5有: 1:明文加密前处理 2:改初始化魔数 3:改k表中的值 4:改循环左移的次数 本期遇到的是124.且循环左移的次数是动态的,需要前面的加密结果处理生成 目录…...
安装MongoDB
查看MongoDB版本可以执行如下命令 mongod --version 如果是Ubuntu,则直接安装 sudo apt-get install -y mongodb如果是其他,比如Amazon Linux2。 查看Linux系统发行版类型 grep ^NAME /etc/*release 如果是 Amazon Linux 2,则创建一个r…...
C++加持让python程序插上翅膀——利用pybind11进行c++和python联合编程示例
目录 0、前言1、安装 pybind11库c侧python侧 2、C引入bybind11vs增加相关依赖及设置cpp中添加头文件及导出模块cpp中添加numpy相关数据结构的接收和返回编译生成dll后改成导出模块同名文件的.pyd 3、python调用c4、C引入bybind11 0、前言 在当今的计算机视觉和机器学习领域&am…...
ubuntu20.04安装cv2
查看ubuntu的版本 cat /etc/lsb-release DISTRIB_IDUbuntu DISTRIB_RELEASE20.04 DISTRIB_CODENAMEfocal DISTRIB_DESCRIPTION"Ubuntu 20.04.3 LTS"更改镜像源 cp /etc/apt/sources.list /etc/apt/sources.list.bak cat > /etc/apt/sources.listdeb http://mirr…...
Android 13.0 recovery出厂时清理中字体大小的修改
1.前言 在13.0的系统rom定制化开发中,在recovery模块也是系统中比较重要的模块,比如恢复出厂设置,recovery ota升级, 清理缓存等等,在一些1080p的设备,但是density只是240这样的设备,会在恢复出厂设置的时候,显示的字体有点小, 产品要求需要将正在清理的字体调大点,这…...
spring+pom-注意多重依赖时的兼容问题[java.lang.NoSuchMethodError]
背景: 项目中同时引入了依赖A和依赖B,而这两个依赖都依赖于项目C,但它们指定的C版本不一致,导致运行时出现了错误。 报错如: java.lang.NoSuchMethodError 解决方案: 需要在项目pom文件中引入依赖C并指定需…...
Matalab插值详解和源码
转载:Matalab插值详解和源码 - 知乎 (zhihu.com) 插值法 插值法又称“内插法”,是利用函数f (x)在某区间中已知的若干点的函数值,作出适当的特定函数,在区间的其他点上用这特定函数的值作为函数f (x)的近似值,这种方…...
Flask 接口
目录 前言 代码实现 简单接口实现 执行其它程序接口 携带参数访问接口 前言 有时候会想着开个一个接口来访问试试,这里就给出一个基础接口代码示例 代码实现 导入Flask模块,没安装Flask 模块需要进行 安装:pip install flask 使用镜…...
Vue3 toRef函数和toRefs函数
当我们在setup 中的以读取对象属性单独交出去时,我们会发现这样会丢失响应式: setup() {let person reactive({name: "张三",age: 18,job: {type: "前端",salary:10}})return {name: person.name,age: person.age,type: person.jo…...
【论文阅读】(VAE-GAN)Autoencoding beyond pixels using a learned similarity metric
论文地址;[1512.09300] Autoencoding beyond pixels using a learned similarity metric (arxiv.org) / 一、Introduction 主要讲了深度学习中生成模型存在的问题,即常用的相似度度量方式(使用元素误差度量)对于学习良好的生成模型存在一定…...
verilog之wire vs reg区别
文章目录 一、wire vs reg二、实例一、wire vs reg wire线网: 仅支持组合逻辑建模必须由assign语句赋值不能在always块中驱动用于连接子模块的输出用于定义模块的输入端口reg寄存器: 可支持组合逻辑或时序逻辑建模必须在always块中赋值二、实例 wire [7:0] cnt; assign cnt …...
力扣面试经典150题详细解析
刷题的初心 众所周知,算法题对于面试大厂是必不可缺的一环,而且对于提高逻辑思维能力有着不小的提升。所以,对于程序员来讲,无论刚入行,还是从业多年,保持一个清醒的头脑,具备一个良好的设计思…...
【Java 进阶篇】唤醒好运:JQuery 抽奖案例详解
在现代社交网络和电商平台中,抽奖活动成为吸引用户、提升用户参与度的一种常见手段。通过精心设计的抽奖页面,不仅可以增加用户的互动体验,还能在一定程度上提高品牌的知名度。本篇博客将通过详细解析 JQuery 抽奖案例,带领你走进…...
数据处理生产环境_利用MurmurHash3算法在Spark和Scala中生成随机颜色
需求 根据给定的轨迹编号在这一列后面生成随机颜色_16 输入数据 ("吃饭", "123"), ("吃饭", "宋江"), ("郭靖", "宋江"), ("杨过", "奥特曼"), ("周芷若", "张无忌"),…...
便利工具分享:一个proto文件的便利使用工具
最近在研究序列化,每次的proto文件手敲生成代码指令都很麻烦,干脆自己写一个泛用脚本,这样以后使用时候就方便了。 废话不多说,首先上代码: #!/bin/bash # 检查是否提供了文件名参数 if [ -z "$1" ]; then…...
LeetCode704.二分查找及二分法
每日一题:LeetCode704.二分查找 LeetCode704.二分查找知识点:二分法解题代码 LeetCode704.二分查找 问题描述:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...
日常一水C
多态 言简意赅:就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过,当子类和父类的函数名相同时,会隐藏父类的同名函数转而调用子类的同名函数,如果要调用父类的同名函数,那么就需要对父类进行引用&#…...
认识CMake并使用CMake构建自己的第一个项目
1.CMake的作用和优势 跨平台支持:CMake支持多种操作系统和编译器,使用同一份构建配置可以在不同的环境中使用 简化配置:通过CMakeLists.txt文件,用户可以定义项目结构、依赖项、编译选项等,无需手动编写复杂的构建脚本…...
