简介
Spring:春天->给款件行业带来了春天!
2002,首次推出了Spring框架的谁形:nterface21推架!
Spring框架即以interface.21架为其础经过年新设计并不断丰富其内涵,于2004年3月24日发布了1.0正式版。
Rod Johnson,Spring Framework创始人,著名作者。很难想象Rod Johnson的学历,真的让好多人大吃一惊,他是悉尼达学的博士,然而他的专业不是计算机,而是音乐学。
Spring理念:使现有的技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架!
为什么要学
- Spring技术是JavaEE开发必备技能,企业开发技术选型命中率>90%
- 专业角度
-
简化开发,降低企业级开发的复杂性
-
框架整合,高效整合其他技术,提高企业级应用开发与运行效率
优点
- spring是一个开源的免费软件(容量)!
- spring是一个轻量级的,非入侵式的框架!
- 控制反转(IOC),面向切面编程(AOP)!
- 支持事物的处理,对框架整合的支持!
总结:Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架!
初识Spring
了解Spring家族
- 官网:spring.io
- Spring发展到今天已经形成了一种开发的生态圈,Spring提供了若干个项目,每个项目用于完成特定的功能
Spring发展史
Spring Framework系统架构
- Spring Framework是Spring生态圈中最基础的项目,是其他项目的根基
核心概念
目前我们代码存在的问题
- 使用对象时,在程序中不要主动使用new产生对象,转换为由外部提供对象
IoC(Inversion of Control)控制反转
- 对象的创建控制权由程序转移到外部,这种思想称为控制反转
IoC/DI
IoC(Inversion of Control)控制反转
- 使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部,此思想称为控制反转
Spring技术对IoC思想进行了实现
- Spring提供了一个容器,称为IoC容器,用来充当IoC思想中的“外部”
- IoC容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在IoC容器中统称为Bean
DI(Dependency Injection)依赖注入
在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入
目的:充分解耦
- 使用IoC容器管理bean(IoC)
- 在IoC容器内将有依赖关系的bean进行关系绑定(DI)
最终效果
- 使用对象时不仅可以直接从IoC容器中获取,并且获取到的bean已经绑定了所有的依赖关系
IoC入门案例
①:导入Spring坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
②:定义Spring管理的类(接口)
BookDao接口和BookDaoImpl实现类
public interface BookDao {
public void save();
}
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
}
BookService接口和BookServiceImpl实现类
public interface BookService{
public void save();
}
public class BookServiceImpl implements BookService {
private BookDao bookDao = new BookDaoImpl();
public void save() {
System.out.println("book dao save...");
bookDao.save();
}
}
③:创建Spring配置文件,配置对应类作为Spring管理的bean
Bean标签中的属性:
- bean标签:表示配置bean
- id属性:表示给bean起名字
- class属性:表示给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">
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl"></bean>
</beans>
注意:bean定义时id属性在同一个上下文中不能重复
④:初始化IoC容器(Spring核心容器/Spring容器),通过容器获取bean
public class App {
public static void main(String[] args) {
//1.创建IoC容器对象,加载spring核心配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//2 获取资源,从IOC容器中获取Bean对象(BookService对象)
BookService bookService= (BookService)ctx.getBean("bookService");
//3 调用Bean对象(BookService对象)的方法
bookService.save();
}
}
DI入门案例
①:删除使用new的形式创建对象的代码
public class BookServiceImpl implements BookService {
private BookDao bookDao; //删除 (= new BookDao();)
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
②:提供依赖对象对应的setter方法
public class BookServiceImpl implements BookService {
private BookDao bookDao;
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
//提供依赖对象对应的setter方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}v
③:配置service与dao之间的关系
property标签中的属性:
- property标签:表示配置当前bean的属性
- name属性:表示配置哪一个具体的属性
- ref属性:表示参照哪一个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">
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
图解演示:
Bean
Bean基础配置
Bean别名配置
注意事项
获取bean无论是通过id还是name获取,如果无法获取到,将抛出异常NoSuchBeanDefinitionException:No bean named 'bookServiceImpl' available
代码展示:
输出结果:
Bean作用范围配置
配置说明:
注意:scope的取值不仅仅只有singleton和prototype,还有request、session、application、 websocket ,表示创建出的对象放置在web容器(tomcat)对应的位置。比如:request表示保存到request域中。
代码演示
打印结果:
注意:在我们的实际开发当中,绝大部分的Bean是单例的,也就是说绝大部分Bean不需要配置scope属性。
为什么默认bean为单例?
- 减少了新生成实例的消耗
- 减少jvm垃圾回收
- 可以快速获取到bean
以上问题答案出处:(面试题:Spring为什么默认bean为单例? - 简书 (jianshu.com))
Bean实例化
bean本质上就是对象,创建bean使用构造方法完成
实例化Bean的三种方式
提供可访问的构造方法
BookDaoImpl实现类
public class BookDaoImpl implements BookDao {
public BookDaoImpl() {
System.out.println("book dao constructor is running ....");
}
public void save() {
System.out.println("book dao save ...");
}
}
applicationContext.xml配置
<!--方式一:构造方法实例化bean-->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
AppForInstanceBook测试类
public class AppForInstanceBook {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
}
}
运行结果
注意:无参构造方法如果不存在,将抛出异常BeanCreationException
静态工厂(了解)
public class OrderDaoFactory{
public static OrderDao getOrderDao(){
return new OrderDaoImpl();
}
}
applicationContext.xml配置
<bean id="orderDao"
class="com.itheima.factory.OrderDaoFactory"
factory-method="getOrderDao"
/>
图片演示:
运行结果:
实例工厂(了解)
public class UserDaoFactory {
public UserDao getUserDao(){
return new UserDaoImpl();
}
}
applicationContext.xml配置
<bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/>
<bean id="userDao"
factory-method="getUserDao"
factory-bean="userFactory"
/>
图片演示:
运行结果:
FactoryBean
//FactoryBean创建对象
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
//代替原始实例工厂中创建对象的方法
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
public Class<?> getObjectType() {
return UserDao.class;
}
}
applicationContext.xml
<bean id="userDao"
class="com.itheima.factory.UserDaoFactoryBean"
/>
Bean生命周期
- 生命周期:从创建到消亡的完整过程
- bean生命周期:bean从创建到销毁的整体过程
- bean生命周期控制:在bean创建后到销毁前做一些事情
Bean生命周期控制
提供生命周期控制方法
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
//表示bean初始化对应的操作
public void init(){
System.out.println("init...");
}
//表示bean销毁前对应的操作
public void destory(){
System.out.println("destory...");
}
}
applicationContext.xml
<!--init-method:设置bean初始化生命周期回调函数,此处填写init方法名-->
<!--destroy-method:设置bean销毁生命周期回调函数,仅适用于单例对象,此处填写destory方法名-->
<bean id="bookDao"
class="com.itheima.dao.impl.BookDaoImpl"
init-method="init"
destroy-method="destory"
/>
Bean生命周期控制--接口控制(了解)
实现InitializingBean, DisposableBean接口
public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
System.out.println("set .....");
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
public void destroy() throws Exception {
System.out.println("service destroy");
}
public void afterPropertiesSet() throws Exception {
System.out.println("service init");
}
}
Bean生命周期
- 创建对象(内存分配)
- 执行构造方法
- 执行属性注入(set操作)
- 执行Bean初始化方法
- 使用Bean
1.执行业务操作
- 关闭/销毁容器
1.执行Bean销毁方法
Bean销毁时机
- 手工关闭容器
ConfigurableApplicationContext接口close()操作
- 注册关闭钩子,在虚拟机退出前先关闭容器再退出虚拟机
ConfigurableApplicationContext接口registerShutdownHook()操作
public class AppForLifeCycle {
public static void main( String[] args ) {
//此处需要使用实现类类型,接口类型没有close方法
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
//注册关闭钩子函数,在虚拟机退出之前回调此函数,关闭容器
ctx.registerShutdownHook();
//关闭容器
//ctx.close();
}
}
依赖注入方式
- 思考:依赖注入描述了在容器中建立bean与bean之间依赖关系的过程,如果bean运行需要的是数字或字符串呢?
setter注入
引用类型
简单类型
在bean中定义引用类型属性并提供可访问的set方法
构造器注入
引用类型(了解)
简单类型(了解)
参数适配(了解)
配置中使用constructor-arg标签type属性设置按形参类型注入
配置中使用constructor-arg标签index属性设置按形参位置注入
总结:
依赖注入方式选择
- 强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现
- 可选依赖使用setter注入进行,灵活性强
- Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
- 如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入
- 实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
- 自己开发的模块推荐使用setter注入
依赖自动装配
- IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配
- 自动装配方式
- 按类型(常用)
- 按名称
- 按构造方法
- 不启用自动装配
- 配置中使用bean标签autowire属性设置自动装配的类型
依赖自动装配特征
- 自动装配用于引用类型依赖注入,不能对简单类型进行操作
- 使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用
- 使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推荐使用
- 自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效
集合注入
数组
注入数组对象
<property name="array">
<array>
<value>1</value>
<value>2</value>
</array>
</property>
List
注入List对象(重点)
<property name="list">
<list>
<value>hello</value>
<value>world</value>
</list>
</property>
Set
注入Set对象
<property name="set">
<set>
<value>hello</value>
<value>world</value>
</set>
</property>
Map
注入Map对象(重点)
<property name="set">
<map>
<entry key="country" value="china"/>
<entry key="name" value="XiaoXiao"/>
<entry key="city" value="Guangzhou"/>
</map>
</property>
Properties
<property name="set">
<props>
<prop key="country">china</prop>
<prop key="name">XiaoXiao</prop>
<prop key="city">Guangzhou</prop>
</props>
</property>
案例:数据源对象管理
说明:以管理DataSource连接池对象为例讲解第三方资源配置管理
管理DataSource连接池对象
数据库准备
create database if not exists spring_db character set utf8;
use spring_db;
create table if not exists tbl_account(
id int primary key auto_increment,
name varchar(20),
money double
);
insert into tbl_account values(null,'Tom',1000);
insert into tbl_account values(null,'Jerry',1000);
添加Druid连接池依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
注意:除了添加以上两个依赖之外,别忘了添加spring-context依赖。
配置DruidDataSource连接池Bean对象
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring_db"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
在测试类中从IOC容器中获取连接池对象并打印
public class App {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource dataSource = (DataSource) ctx.getBean("dataSource");
System.out.println(dataSource);
}
}
管理c3p0连接池
添加c3p0连接池依赖
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
配置c3p0连接池Bean对象
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring_db"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
<property name="maxPoolSize" value="1000"/>
</bean>
注意:同一个Spring容器中不能有两个的连接池。
在测试类中从IOC容器中获取连接池对象并打印
public class App {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource dataSource = (DataSource) ctx.getBean("dataSource");
System.out.println(dataSource);
}
}
加载properties配置信息
开启context命名空间
使用context命名空间,加载指定properties文件
<context:property-placeholder location="jdbc.properties"/>
使用${}读取加载的属性值
<property name="username" value"${jdbc.username}"/>
加载properties文件
不加系统属性载
加载多个properties文件
<context:property-placeholder location="jdbc.properties,MS.properties"/>
加载所有properties文件
<context:property-placeholder location="*.properties"/>
加性properties文件标准格式
<context:property-placeholder location="classpath:*.properties"/>
从类路径或jar包中搜索并加载properties文件
<context:property-placeholder location="classpath*:*.properties"/>