一. IOC和DI的概念
IOC(Inversion of Control):反转控制。
在应用程序中的组件需要获取资源时,传统的方式是组件主动的从容器中获取所需要的资源,在这样的模式下开发人员往往需要知道在具体容器中特定资源的获取方式,增加了学习成本,同时降低了开发效率。
反转控制的思想完全颠覆了应用程序组件获取资源的传统方式:反转了资源的获取方向——改由容器主动的将资源推送给需要的组件,开发人员不需要知道容器是如何创建资源对象的,只需要提供接收资源的方式即可,极大的降低了学习成本,提高了开发的效率。这种行为也称为查找的被动形式。
DI(Dependency Injection):依赖注入。
IOC的另一种表述方式:即组件以一些预先定义好的方式(例如:setter 方法)接受来自于容器的资源注入。相对于IOC而言,这种表述更直接。
IOC容器在Spring中的实现
-
在通过IOC容器读取Bean的实例之前,需要先将IOC容器本身实例化。
-
Spring提供了IOC容器的两种实现方式
(1)BeanFactory:IOC容器的基本实现,是Spring内部的基础设施,是面向Spring本身的,不是提供给开发人员使用的。
(2)ApplicationContext:BeanFactory的子接口,提供了更多高级特性。面向Spring的使用者,几乎所有场合都使用ApplicationContext而不是底层的BeanFactory。 -
ApplicationContext的主要实现类
[1]ClassPathXmlApplicationContext:对应类路径下的XML格式的配置文件
[2]FileSystemXmlApplicationContext:对应文件系统中的XML格式的配置文件
[3]在初始化时就创建单例的bean,也可以通过配置的方式指定创建的Bean是多实例的。 -
ConfigurableApplicationContext
[1]是ApplicationContext的子接口,包含一些扩展方法
[2]refresh()和close()让ApplicationContext具有启动、关闭和刷新上下文的能力。 -
WebApplicationContext
专门为WEB应用而准备的,它允许从相对于WEB根目录的路径中完成初始化工作
二. Bean的赋值
- 通过setXxx()属性赋值
<bean id="person1" class="com.hello.Person">
<!-- 利用getter setter方法创建对象并赋值 -->
<property name="lastName" value="张三"></property>
<property name="age" value="28"></property>
<property name="gender" value="男"></property>
<property name="email" value="ddd@163.com"></property>
</bean>
- 通过多参数构造器赋值
<bean id="person2" class="com.hello.Person">
<!-- 利用有参构造器创建对象并赋值 -->
<constructor-arg name="lastName" value="李四"></constructor-arg>
<constructor-arg name="age" value="23"></constructor-arg>
<constructor-arg name="email" value="lisi@163.com"></constructor-arg>
<constructor-arg name="gender" value="男"></constructor-arg>
</bean>
- 指定索引赋值
<bean id="person3" class="com.hello.Person">
<!-- name可以省略 但是顺序要保持和构造器一样,否则使用index指定顺序 -->
<constructor-arg value="王五"></constructor-arg>
<constructor-arg value="23"></constructor-arg>
<constructor-arg value="wangwu@163.com" index="3"></constructor-arg>
<constructor-arg value="男" index="2"></constructor-arg>
</bean>
- 指定类型
</bean>
<bean id="person4" class="com.hello.Person">
<!-- 当出现有着相同参数个数的构造器,可以用 type指定类型 -->
<constructor-arg value="小红"></constructor-arg>
<constructor-arg value="23" type="java.lang.Integer"></constructor-arg>
<constructor-arg value="xiaohong@163.com"></constructor-arg>
</bean>
- 通过p名称空间赋值
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
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">
<!-- 通过p名称空间为bean赋值-->
<bean id="person5" class="com.hello.Person"
p:age="18" p:email="hh@163.com" p:lastName="哈哈">
</bean>
- 特殊值赋值(null, 特殊符号)
<!-- 通过标签<null/>表示null值-->
<!-- 通过标签<![CDATA[XXX]]>表示特殊符号XXX -->
<bean id="person6" class="com.hello.Person">
<property name="lastName" value="Tom"></property>
<property name="gender"><null/></property>
<property name="email">
<value><![CDATA[%%%hehe%%%^]]></value>
</property>
</bean>
- 引用外部的值
<bean id="car1" class="com.hello.Car">
<property name="carName" value="宝马"></property>
<property name="color" value="Grenn"></property>
<property name="price" value="200000.0"></property>
</bean>
<bean id="person7" class="com.hello.Person">
<property name="car" ref="car1"></property>
</bean>
- 内部值
<!--内部Bean-->
<bean id="person8" class="com.hello.Person">
<property name="car">
<bean id="car2" class="com.hello.Car">
<property name="carName" value="奔驰"></property>
<property name="color" value="Black"></property>
<property name="price" value="300000.0"></property>
</bean>
</property>
</bean>
- 对List或者数组赋值
<bean id="book2" class="com.hello.Book">
<property name="bookName" value="C++ book"></property>
<property name="price" value="200"></property>
<property name="author" value="Jack"></property>
</bean>
<!--对List<Book> 赋值 Book又为一个类-->
<bean id="person9" class="com.hello.Person">
<property name="books">
<list>
<bean id="book1" class="com.hello.Book"
p:bookName="Java book" p:author="Tom" p:price="120"></bean>
<ref bean="book2"></ref>
</list>
</property>
</bean>
- 对Map赋值
<bean id="person10" class="com.hello.Person">
<property name="maps">
<map>
<entry key="key1" value="value1"></entry>
<entry key="key2" value="value2"></entry>
<entry key="key3" value="value3"></entry>
</map>
</property>
</bean>
- 对Properties赋值
<bean id="person11" class="com.hello.Person">
<property name="properties">
<props>
<prop key="user01">root</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
- 利用util名称空间配置集合类型
<util:map id="myMap">
<entry key="key1" value="value1"></entry>
<entry key="key2" value="value2"></entry>
</util:map>
<bean id="person12" class="com.hello.Person">
<property name="maps" ref="myMap"></property>
</bean>
三. 工厂方法配置Bean
- 静态工厂
import com.hello.Book;
public class BookStaticFactory {
public static Book getInstance(String bookName){
System.out.println("静态工厂创建");
Book book = new Book();
book.setBookName(bookName);
book.setAuthor("zhangsan");
book.setBookId(123);
return book;
}
}
<!-- 静态工厂方法 -->
<bean id="StaticFactory" class="com.factory.BookStaticFactory" factory-method="getInstance">
<constructor-arg value="bookName1"></constructor-arg>
</bean>
- 实例工厂
import com.hello.Book;
public class BookInstanceFactory {
public Book getInstance(String bookName){
System.out.println("实例工厂创建");
Book book = new Book();
book.setBookName(bookName);
book.setAuthor("zhangsan");
book.setBookId(123);
return book;
}
}
<!-- 实例工厂方法 -->
<bean id="InstanceFactory" class="com.factory.BookInstanceFactory"></bean>
<bean id="book" class="com.hello.Book" factory-bean="InstanceFactory" factory-method="getInstance">
<constructor-arg value="bookName2"></constructor-arg>
</bean>
- FactoryBean
import com.hello.Book;
import org.springframework.beans.factory.FactoryBean;
/**
* Spring中的FactoryBean
* */
public class BookFactoryBean implements FactoryBean<Book> {
@Override
public Book getObject() throws Exception {
System.out.println("FactoryBean 创建");
Book book = new Book();
book.setBookName("BookName123");
return book;
}
@Override
public Class<?> getObjectType() {
return Book.class;
}
@Override
public boolean isSingleton() {
return false; // false为多实例, true为单实例
}
}
<!-- FactoryBean工厂方法 -->
<bean id="FactoryBean1" class="com.factory.BookFactoryBean"></bean>
FactoryBean 对象创建时在getBean时创建,不管单实例还是多实例
四. Bean的高级配置
1. Bean的继承
这里的继承只是说继承Bean的配置,被继承的bean称为父bean。继承这个父bean的bean称为子bean,子bean从父bean中继承配置,包括bean的属性配置,子bean也可以覆盖从父bean继承过来的配置。
例如:
<bean id="dept" class="com.atguigu.parent.bean.Department">
<property name="deptId" value="100"/>
<property name="deptName" value="IT"/>
</bean>
<bean id="emp01" class="com.atguigu.parent.bean.Employee">
<property name="empId" value="1001"/>
<property name="empName" value="Tom"/>
<property name="age" value="20"/>
<!-- 重复的属性值 -->
<property name="detp" ref="dept"/>
</bean>
<bean id="emp02" class="com.atguigu.parent.bean.Employee">
<property name="empId" value="1002"/>
<property name="empName" value="Jerry"/>
<property name="age" value="25"/>
<!-- 重复的属性值 -->
<property name="detp" ref="dept"/>
</bean>
避免重复可以让 emp02 继承 emp01:
<!-- 以emp01作为父bean,继承后可以省略公共属性值的配置 -->
<bean id="emp02" parent="emp01">
<property name="empId" value="1002"/>
<property name="empName" value="Jerry"/>
<property name="age" value="25"/>
</bean>
注意:
父bean可以作为配置模板,也可以作为bean实例。若只想把父bean作为模板,可以设置的abstract属性为true,这样Spring将不会实例化这个bean。
并不是元素里的所有属性都会被继承。比如:autowire,abstract等。也可以忽略父bean的class属性,让子bean指定自己的类,而共享相同的属性配置。但此时abstract必须设为true。
2. Bean的依赖创建
有的时候创建一个bean的时候需要保证另外一个bean也被创建,这时我们称前面的bean对后面的bean有依赖。例如:要求创建Employee对象的时候必须创建Department。这里需要注意的是依赖关系不等于引用关系,Employee即使依赖Department也可以不引用它。
<bean id="emp03" class="com.atguigu.parent.bean.Employee" depends-on="dept">
<property name="empId" value="1003"/>
<property name="empName" value="Kate"/>
<property name="age" value="21"/>
</bean>
3. Bean的作用域
作用域决定Bean是否是单实例还是多实例。
作用域有:
prototype: 多实例,容器启动并不会创建对象,获取的时候创建
singleton: 单实例 默认的,容器启动就已经创建好对象
request: 和Web请求相关
session: 和会话相关
可以在元素的scope属性里设置bean的作用域,以决定这个bean是单实例的还是多实例的。
4. Bean的生命周期
Spring IOC容器可以管理bean的生命周期,Spring允许在bean生命周期内特定的时间点执行指定的任务。Spring IOC容器对bean的生命周期进行管理的过程:
[1] 通过构造器或工厂方法创建bean实例
[2] 为bean的属性设置值和对其他bean的引用
[3] 调用bean的初始化方法
[4] bean可以使用了
[5] 当容器关闭时,调用bean的销毁方法
在配置bean时,通过 init-method
和 destroy-method
属性为bean指定初始化和销毁方法。
而bean后置处理器允许在调用初始化方法前后对bean进行额外的处理。bean后置处理器对IOC容器里的所有bean实例逐一处理,而非单一实例。其典型应用是:检查bean属性的正确性或根据特定的标准更改bean的属性。bean后置处理器时需要实现接口:org.springframework.beans.factory.config.BeanPostProcessor
。在初始化方法被调用前后,Spring将把每个bean实例分别传递给上述接口的以下两个方法:
postProcessBeforeInitialization(Object, String)
postProcessAfterInitialization(Object, String)
添加bean后置处理器后bean的生命周期过程:
[1] 通过构造器或工厂方法创建bean实例
[2] 为bean的属性设置值和对其他bean的引用
[3] 将bean实例传递给bean后置处理器的postProcessBeforeInitialization()方法
[4] 调用bean的初始化方法
[5] 将bean实例传递给bean后置处理器的postProcessAfterInitialization()方法
[6] bean可以使用了
[7] 当容器关闭时调用bean的销毁方法
后置处理器实现类:
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class BeanProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化前处理工作");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化后处理工作");
return bean;
}
}
生命周期控制的XML文件配置:
<bean id="car1" class="com.hello.Car" init-method="inintMethod" destroy-method="destroyMethod">
<property name="price" value="12345"></property>
<property name="color" value="red"></property>
</bean>
<bean id="beanPostProcesser" class="com.hello.BeanProcessor">
</bean>
5. 引用外部属性文件
当bean的配置信息逐渐增多时,查找和修改一些bean的配置信息就变得愈加困难。这时可以将一部分信息提取到bean配置文件的外部,以properties格式的属性文件保存起来,同时在bean的配置文件中引用properties属性文件中的内容,从而实现一部分属性值在发生变化时仅修改properties属性文件即可。这种技术多用于连接数据库的基本信息的配置。
Xml中可以直接配置C3P0数据库连接池,如:
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="root"></property>
<property name="password" value="tyltyl"></property>
<property name="jdbcUrl">
<value>jdbc:mysql://localhost:3306/test?serverTimezone=GMT&characterEncoding=utf8</value>
</property>
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
<property name="minPoolSize" value="5"></property>
<property name="initialPoolSize" value="5"></property>
</bean>
通过context命名空间加载属性文件来配置连接池:
<context:property-placeholder location="jdbc.properties"></context:property-placeholder>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
</bean>
其中jdbc.properties文件中写入
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?characterEncoding=utf8&serverTimezone=GMT
jdbc.user=root
jdbc.password=tyltyl
6. 自动装配
手动装配:以value或ref的方式明确指定属性值都是手动装配。
自动装配:根据指定的装配规则,不需要明确指定,Spring自动将匹配的属性值注入bean中。
自动装配通过Bean设置autowire
选项,其中有四种模式:
- 不自动装配(autowire=“default”)
- 根据名字(autowire=“byName”)
通过属性名作为id去容器中寻找,找到了自动给装配,找不到装配null
- 根据类型(autowire=“byType”)
通过属性的类型作为依据去容器中寻找,找到了自动给装配,找不到装配null,如果有多个相同的类型,报错。
- 根据构造器(autowire=“constructor”)
按照构造器进行赋值,其中规则是先按照构造器类型,没有就装配null,如果按类型找到多个,那么按照参数名找,找不到就装配null
五. SpEl(Spring表达式语言)
SpEl支持运行时查询并可以操作对象图,和JSP页面上的EL表达式、Struts2中用到的OGNL表达式类似。SpEl使用 #{…}
作为定界符,所有在大框号中的字符都将被认为是SpEL表达式
- 字面量
<property name="age" value="#{23}"></property> <!--整数-->
<property name="score" value="#{98.4}"></property> <!--浮点数-->
<property name="sex" value="#{'男'}"></property> <!--字符串-->
<property name="capcity" value="#{1e4}"></property> <!--科学记数法-->
<property name="enabled" value="#{true}"></property> <!--Boolean类型-->
2.引用其他bean或者引用其他bean的属性值
<bean id="department1" class="com.hello.Department">
<property name="name" value="IT"></property>
</bean>
<bean id="employee" class="com.hello.Employee">
<property name="department" value="#{department1}"></property>
<property name="departName" value="#{department1.name}"></property>
</bean>
- 调用非静态方法
<bean id="book" class="com.hello.Book">
<property name="bookName" value="XXXX"></property>
</bean>
<bean id="person" class="com.hello.Person">
<property name="lastName" value="#{book.getBookName()}"
</bean>
- 调用静态方法
静态方法使用#{T(全类名).静态方法名()}
模式
<bean id="employee" class="com.hello.Employee">
<property name="circle" value="#{T(java.lang.Math).PI*2}"/>
</bean>
5.运算符
Spring表达式语言还包含各种常见的运算符
算术运算符:+、-、*、/、%、^
字符串连接:+
比较运算符:<、>、==、<=、>=、lt、gt、eq、le、ge
逻辑运算符:and, or, not, |
三目运算符:判断条件?判断结果为true时的取值:判断结果为false时的取值
正则表达式:matches