【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 中…...

2023年R1快开门式压力容器操作证模拟考试题库及R1快开门式压力容器操作理论考试试题
题库来源:安全生产模拟考试一点通公众号小程序 2023年R1快开门式压力容器操作证模拟考试题库及R1快开门式压力容器操作理论考试试题是由安全生产模拟考试一点通提供,R1快开门式压力容器操作证模拟考试题库是根据R1快开门式压力容器操作最新版教材&#…...

探索NLP中的核心架构:编码器与解码器的区别
❤️觉得内容不错的话,欢迎点赞收藏加关注😊😊😊,后续会继续输入更多优质内容❤️ 👉有问题欢迎大家加关注私戳或者评论(包括但不限于NLP算法相关,linux学习相关,读研读博…...

解决:Error: Missing binding xxxxx\node_modules\node-sass\vendor\win32-x64-83\
一、具体报错 二、报错原因 这个错误是由于缺少 node-sass 模块的绑定文件引起的。 三、导致原因 3.1、环境发生了变化 3.2、安装过程出现问题 四、解决方法步骤: 4.1、重新构建 node-sass 模块 npm rebuild node-sass 4.2、清除缓存并重新安装依赖 npm c…...

科研学习|科研软件——面板数据、截面数据、时间序列数据的区别是什么?
一、数据采集方式不同 面板数据是通过在多个时间点上对同一组体进行观测而获得的数据。面板数据可以是横向面板数据,即对同一时间点上不同个体的观测,也可以是纵向面板数据,即对同一个体在不同时间点上的观测。采集面板数据需要跟踪相同的个体…...

【UE5】物体沿样条线移动
目录 效果 步骤 一、使用样条线创建路径 二、创建沿样条线路径移动的物体 三、定义可移动物体的生成器 效果 步骤 一、使用样条线创建路径 先创建一个Actor蓝图,这里命名为“BP_Line” 该蓝图中只需添加一个样条组件 将“BP_Line”拖入场景中 按住Alt鼠标左键…...

Qt控件按钮大全
按钮 在 Qt 里,最常用使用的控件就是按钮了,有了按钮,我们就可以点击,从而响应事件,达到人机交互的效果。不管是嵌入式或者 PC 端,界面交互,少不了按钮。Qt 按钮部件是一种常用的部件之一,Qt 内置了六种按钮部件如下: (1) QPushButton:下压按钮 (2) QToolBu…...

软件工程--软件过程学习笔记
本篇内容是对学校软件工程课堂内容的记录总结,部分也来源于网上查找的资料 软件过程基础 软件过程是指在软件开发过程中,经过一系列有序的步骤和活动,从问题定义到最终软件产品交付和维护的全过程。这个过程旨在确保软件项目能够按时、按预…...

高校教师资格证备考
高等教育制度 关于人的全面发展和个体发展的关系,说法正确的是(ABC)。 A.个体发展是在全面发展基础上的选择性发展 B.全面发展是个体发展的前提和基础 C.个体发展又是全面发展的动力 D.个体发展是全面发展的前提和基础...

Git通过rebase合并多个commit
在使用 Git 作为版本控制的时候,我们可能会由于各种各样的原因提交了许多临时的 commit,而这些 commit 拼接起来才是完整的任务。那么我们为了避免太多的 commit 而造成版本控制的混乱,通常我们推荐将这些 commit 合并成一个。 1. 查看提交历…...

ROS 学习应用篇(八)ROS中的坐标变换管理之tf广播与监听的编程实现
偶吼吼胜利在望,冲冲冲 老规矩新建功能包 工作空间目录下/src下开启终端输入 catkin_create_pkg learning_tf roscpp rospy tf turtlesim 如何实现tf广播 引入库 c python …...