前言
1.spring大家族有很多,我们现在学的是spring-framework,因为比较基础
2.spring发展史
Spring1.0:采用纯配置开发
Spring2.0:加入了注解的形式
Spring3.0:注解配置的开发模式,大幅度的提升开发效率
Spring4.0:紧跟jdk版本,对个别API做了调整
Spring5.0:全面支持jdk8,如果想使用要把jdk升到8以上
3.spring系统架构图及学习顺序
正文
IOC案例(Spring基础项目的搭建)
Bean:一个java对象,一般被spring管理的对象统称为bean。
IoC:一种控制反转的设计思想,把对象的创建权由对象本身转交给容器,由主动new对象变为外部提供对象
1.先创建一个spring项目,把一些不需要的文件删除,只保留pom.xml文件、src文件夹下的java文件夹和resources文件夹,里面什么都可以没有,先在resources文件里创建spring配置文件(名字就叫spring配置)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
2.在java文件夹下的org.example文件夹下新建一个dao包,里面写一个接口配上一个实现类,一会要把这个实现类做成bean
package com.example.bookDao;
public interface BookDao {
public void save();
}
package com.example.bookDao.impl;
import com.example.bookDao.BookDao;
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao ...");
}
}
3.在resources文件夹下新建一个在xml配置文件,在里面配置bean对象信息
<!--2.配置bean-->
<!--bean标签标示配置bean
id属性标示给bean起名字
class属性表示给bean定义类型-->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
4.在工程类中就可以获取IOC容器中的Bean
编写代码时要注意包别导错,使用getBean方法后记得强转为指定类型
import com.itheima.dao.BookDao;
import com.itheima.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App2 {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao =(BookDao) ctx.getBean("bookDao");
bookDao.save();
}
}
4.然后运行工程类就能运行对应的语句了
DI案例
DI:依赖注入,在容器中创建bean与bean之间的依赖关系的整个过程
在想要依赖别的bean的内部写入被依赖的bean,然后就能依赖了
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<!--7.配置server与dao的关系-->
<!--property标签表示配置当前bean的属性
name属性表示配置哪一个具体的属性
ref属性表示参照哪一个bean-->
<property name="bookDao" ref="bookDao"/>
</bean>
Bean的配置
基本配置
id属性标示给bean起名字
class属性表示给bean定义类型
别名配置
name:为bean指定别名,别名可以有多个,使用逗号,分号,空格进行分隔
作用范围配置
scope:为bean设置作用范围
singleton : 唯一 bean 实例,Spring 中的 bean 默认都是单例的。
prototype : 每次请求都会创建一个新的 bean 实例。
request : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP request 内有效。
session : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP session 内有效。
global-session: 全局 session 作用域,仅仅在基于 Portlet 的 web 应用中才有意义,Spring5 已经没有了。Portlet 是能够生成语义代码(例如:HTML)片段的小型 Java Web 插件。它们基于 portlet 容器,可以像 servlet 一样处理 HTTP 请求。但是,与 servlet 不同,每个 portlet 都有不同的会话。
Bean的实例化
-
方式一:构造方法实例化bean
使用了无参的构造方法,还用了反射原理(不管私有还是公有都能访问)
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
-
方式二:使用静态工厂实例化bean
工厂模式要在代码中显式地创建工厂对象,然后使用工厂对象来创建具体的对象,这种方式需要我们自己手动创建和管理工厂对象,代码冗余和维护成本依然较高。
import com.itheima.dao.OrderDao;
import com.itheima.dao.impl.OrderDaoImpl;
//静态工厂创建对象
public class OrderDaoFactory {
public static OrderDao getOrderDao(){
System.out.println("factory setup....");
return new OrderDaoImpl();
}
}
<bean id="orderDao" class="com.itheima.factory.OrderDaoFactory" factory-method="getOrderDao"/>
-
方式三:使用实例工厂实例化bean
因为方法二中的工厂方法只能调用静态方法,假如工厂方法非静态就能使用这个方法
import com.itheima.dao.UserDao;
import com.itheima.dao.impl.UserDaoImpl;
//实例工厂创建对象
public class UserDaoFactory {
public UserDao getUserDao(){
return new UserDaoImpl();
}
}
<bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/>
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
-
方式四:使用FactoryBean实例化bean
由于方法三和方法一过于繁琐,就有了方法四,此方法要实现FactoryBean接口,泛型的类型写要创建的bean的方法,实现接口里的getObject和getObjectType方法
getObject方法的返回值为bean对象(相当于替换了工厂类的方法),getObjectType方法的返回值为bean的类名,例如:UserDao.class
import com.itheima.dao.UserDao;
import com.itheima.dao.impl.UserDaoImpl;
import org.springframework.beans.factory.FactoryBean;
//FactoryBean创建对象
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
//代替原始实例工厂中创建对象的方法
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
public Class<?> getObjectType() {
return UserDao.class;
}
}
<bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean"/>
bean的生命周期
- 实例化(Instantiation)
init-method:设置bean初始化生命周期回调函数,destroy-method:设置bean销毁生命周期回调函数,仅适用于单例对象,也可以实现InitializingBean, DisposableBean接口,效果一样
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" init-method="init" destroy-method="destory"/>
1.创建对象(内存分配)
2.执行构造方法
3.执行属性注入(set操作)
4.执行bean初始化方法
- 属性赋值(Populate)
- 初始化(Initialization)
- 销毁(Destruction)
ClassPathXmlApplicationContext的close方法,直接关闭IOC容器
ClassPathXmlApplicationContext的registerShutdownHook方法,告诉程序在程序关闭前先关闭IOC容器
import com.itheima.dao.BookDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppForLifeCycle {
public static void main( String[] args ) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
//注册关闭钩子函数,在虚拟机退出之前回调此函数,关闭容器
//ctx.registerShutdownHook();
//关闭容器
ctx.close();
}
}
依赖注入方式
用构造方法注入基本数据类型、string
在bean中定义基本数据类型并提供可访问的set方法
import com.itheima.dao.BookDao;
public class BookDaoImpl implements BookDao {
private String databaseName;
private int connectionNum;
public BookDaoImpl(String databaseName, int connectionNum) {
this.databaseName = databaseName;
this.connectionNum = connectionNum;
}
public void save() {
System.out.println("book dao save ..."+databaseName+","+connectionNum);
}
}
配置中使用property标签value属性注入基本数据类型的值
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
根据构造方法参数名称注入
<constructor-arg name="connectionNum" value="10"/>
<constructor-arg name="databaseName" value="mysql"/>
</bean>
除了这种还有两种方式
这种是降低了因为名称相同而造成的高耦合,但当同一类型的值有多个时无法使用
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
根据构造方法参数类型注入
<constructor-arg type="int" value="10"/>
<constructor-arg type="java.lang.String" value="mysql"/>
</bean>
这种通过索引找值的方式虽然不用名称,也不用判断类型,但限制了参数的顺序,增高了耦合度
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<!--根据构造方法参数位置注入-->
<constructor-arg index="0" value="mysql"/>
<constructor-arg index="1" value="100"/>
</bean>
用构造方法注入引用类型
在bean中定义引用类型属性并提供可访问的构造方法
import com.itheima.dao.BookDao;
import com.itheima.dao.UserDao;
import com.itheima.service.BookService;
public class BookServiceImpl implements BookService{
private BookDao bookDao;
private UserDao userDao;
public BookServiceImpl(BookDao bookDao, UserDao userDao) {
this.bookDao = bookDao;
this.userDao = userDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
userDao.save();
}
}
配置中用constructor-arg标签ref属性注入引用类型的值
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<constructor-arg name="userDao" ref="userDao"/>
<constructor-arg name="bookDao" ref="bookDao"/>
</bean>
用setter方法注入基本数据类型、string
在bean中定义基本数据类型并提供可访问的set方法
import com.itheima.dao.BookDao;
public class BookDaoImpl implements BookDao {
private String databaseName;
private int connectionNum;
//setter注入需要提供要注入对象的set方法
public void setConnectionNum(int connectionNum) {
this.connectionNum = connectionNum;
}
//setter注入需要提供要注入对象的set方法
public void setDatabaseName(String databaseName) {
this.databaseName = databaseName;
}
public void save() {
System.out.println("book dao save ..."+databaseName+","+connectionNum);
}
}
配置中使用property标签value属性注入基本数据类型的值
<!--注入简单类型-->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<!--property标签:设置注入属性-->
<!--name属性:设置注入的属性名,实际是set方法对应的名称-->
<!--value属性:设置注入简单类型数据值-->
<property name="connectionNum" value="100"/>
<property name="databaseName" value="mysql"/>
</bean>
用setter方法注入引用类型
在bean中定义引用类型并提供可访问的set方法
private UserDao userDao;
//setter注入需要提供要注入对象的set方法
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
配置中使用property标签ref属性注入引用类型对象
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<!--注入引用类型-->
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<!--property标签:设置注入属性-->
<!--name属性:设置注入的属性名,实际是set方法对应的名称-->
<!--ref属性:设置注入引用类型bean的id或name-->
<property name="bookDao" ref="bookDao"/>
<property name="userDao" ref="userDao"/>
</bean>
依赖注入方式的选择
强制依赖私用构造器,可选依赖用setter注入
spring框架推荐构造器(严谨),个人使用推荐setter
有必要可同时使用
具体情况具体分析
依赖自动装配
IoC容器根资源在容据bean所依赖的器中自动查找并注入到bean中的过程称为自动装配
<bean class="com.itheima.dao.impl.BookDaoImpl"/>
<!--autowire属性:开启自动装配,通常使用按类型装配-->
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byType"/>
autowire的一些值分别有
特征
自动装配用于引用类型依赖注入,不能对简单类型操作
使用按类型装配时必须保障容器中相同类型的bean唯一,推荐
使用按名称·装配时必须保障...有指定名称的bean,因变量名与配置耦合,不推荐
自动装配优先级低于setter注入/构造器注入,同时出现时自动装配失效
集合注入
之前学习的都是一些单个的数据注入,现在开始写多个数据:集合,这种类型的注入,这种类型又分别分成以下几种类型,分别有:
数组注入
<!--数组注入-->
<property name="array">
<array>
<value>100</value>
<value>200</value>
<value>300</value>
</array>
</property>
list集合注入
<!--list集合注入-->
<property name="list">
<list>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>chuanzhihui</value>
</list>
</property>
set集合注入
<!--set集合注入-->
<property name="set">
<set>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>boxuegu</value>
</set>
</property>
map集合注入
<!--map集合注入-->
<property name="map">
<map>
<entry key="country" value="china"/>
<entry key="province" value="henan"/>
<entry key="city" value="kaifeng"/>
</map>
</property>
Properties注入
<!--Properties注入-->
<property name="properties">
<props>
<prop key="country">china</prop>
<prop key="province">henan</prop>
<prop key="city">kaifeng</prop>
</props>
</property>
注意
list和数组的标签可以互相使用,注入引用类型用ref标签
案例:数据源对象管理
一个第三方的类的对象的管理
步骤:
1.先导依赖(c3p0的坐标可以去mvnrepository网站上搜)
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
2.再配置xml配置文件
<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>
<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>
3.最后在驱动类中就可以调用这个bean了
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource dataSource = (DataSource) ctx.getBean("dataSource");
System.out.println(dataSource);
加载properties文件
要想让配置的bean生效,就必须先让properties文件生效
加载properties文件的步骤
1.开启一个namespace,命名空间叫context,开启的方法就是将原来的xmlns这行还有下面的两行http开头的复制,然后粘贴到跟复制的那段代码同级的位置上,然后把beans改为context
未开启:
<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
">
已开启:
<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
">
2.使用context空间加载properties文件
property-placeholder叫做属性占位符,location的值用来指定所配置的文件名,当文件多个时,可以用引号包住,中间用逗号分隔,支持通配符,默认的系统文件的优先级比自己配置的文件的优先级高,system-properties-mode属性表示是否加载系统属性,never说明不加载系统属性,在文件名前加classpath:,可以不加但推荐加,现在默认只扫描当前工程里的配置文件,如果在给classpath:加上星号classpath*:,则表示可以扫描所有依赖的jar包
<context:property-placeholder location="classpath*:*.properties" system-properties-mode="NEVER"/>
3.使用属性占位符${}读取properties文件中的属性
说明:idea自动识别${}加载的属性值,需要手工点击才可以查阅原始书写格式
<bean class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
容器
两种创建容器的方法,这两种创建方法都支持配置多个文件
1.加载类路径下的配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
System.out.println(bookDao);
2.从文件系统下加载配置文件
ApplicationContext ctx = new FileSystemXmlApplicationContext("D:\\workspace\\spring\\spring_10_container\\src\\main\\resources\\applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
System.out.println(bookDao);
三种获取bean的方法
1.使用bean名称获取
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
2.使用bean名称获取并指定bean的类型
BookDao bookDao = ctx.getBean("bookDao",BookDao.class);
3.使用eban的类型获取(使用此方法时,容器内这种类型的bean只能有一个)
BookDao bookDao = ctx.getBean(BookDao.class);
还有一种获取bean的方法比较老(顶层接口初始化)(了解即可)
BeanFactory初始化的bean全部都是延迟加载,而ApplicationContext初始化出来的是立即加载的,lazy-init="true",给配置的bean加上这个属性可以设置让bean延迟加载
import com.itheima.dao.BookDao;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
//初始化BeanFactory
public class AppForBeanFactory {
public static void main(String[] args) {
Resource resources = new ClassPathResource("applicationContext.xml");
BeanFactory bf = new XmlBeanFactory(resources);
BookDao bookDao = bf.getBean(BookDao.class);
bookDao.save();
}
}