IOC(DI)是一个用来管理对象的容器,用来管理对象获取的方式。
DI又叫做依赖注入,可以判断依赖关系。
一个A类,A类有一个父类B类,在javaSE中在创建A类前要先创建B类,再将B类注入到A类中,在Spring中,这一步骤可以由IOC容器来实现。
IOC与DI概述:
- IOC:其思想是反转资源获取的方向。传统的资源查找方式要求组件向容器发起请求查找资源。作为为回应,容器适时的返回资源。而应用了IOC之后,则是容器主动的将资源推送给它所管理的组件,组件所要做的仅是选择一种合适的方式来接受资源。这种行为也被称为查找的被动形式。
- DI:IOC的另一种表述方式,即组件以一些预先定义好的方式(例如 set方法)接受来自资源容器的注入。相对于IOC而言,这种表述更直接。
本文目录
一、初步使用IOC容器
1、hello IOC
创建一个普通的maven工程,导入jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
创建一个User类
package bean;
public class User {
private String name;
private int age;
public User(){}
public User(int age,String name){
this.name=name;
this.age=age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
在resources下创建一个spring文件,这个文件就是用来管理对象的容器
<?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">
<!--属性注入-->
<!-- id唯一,若是没设id,则通过class="bean.User"来获取容器对象-->
<bean id="user" class="bean.User">
<!-- 实际上是在调用User类中的set方法-->
<!-- name实际上不是属性,而是方法名,只不过将setName中的set去掉且将Name变成小写-->
<property name="name" value="张三"/>
<property name="age" value="20"/>
</bean>
</beans>
在主方法中创建IOC容器对象
package test;
import bean.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Demo01 {
public static void main(String[] args){
ApplicationContext context=new ClassPathXmlApplicationContext("spring01.xml");
User user= (User) context.getBean("user");
System.out.println(user);
}
}
运行结果:
2、springIOC的三种注入方式
- 属性注入
<bean id="user" class="bean.User">
<property name="name" value="张三"/>
<property name="age" value="20"/>
</bean>
- 构造器注入
<bean id="user1" class="bean.User">
<!-- 有name属性时,可以不用按照构造器的传参顺序,没有name时,value需要和有参构造器中的参数对应上-->
<!-- 没有name时,也可以指定index来强制规定value对应第几个参数-->
<!-- 向构造器中参数传值是以数组的方式-->
<!-- <constructor-arg value="张三" index="1"/>-->
<!-- <constructor-arg value="20" index="0"/>-->
<constructor-arg name="name" value="张三"/>
<constructor-arg name="age" value="20"/>
</bean>
- 通过工厂方法注入
(1)静态工厂方式
调用静态工厂方法创建Bean是将对象创建过程封装到静态方法中。
工厂类:
package test;
import bean.Car;
public class CarFactory {
private static Car car;
public static Car getCar(String kind,int price){
car=new Car();
car.setKind(kind);
car.setPrice(price);
return car;
}
}
xml:
<!-- class指向工厂类-->
<bean id="car" class="test.CarFactory" factory-method="getCar">
<constructor-arg name="kind" value="audi"/>
<constructor-arg name="price" value="20000"/>
</bean>
(2)实例工厂方式
实例工厂方式:将对象的创建过程封装到另一个对象的实例方法里
工厂类:
package test;
import bean.Car;
public class BeanFactory {
private Car car;
public Car getCar(String kind,int price){
car=new Car();
car.setPrice(price);
car.setKind(kind);
return car;
}
}
xml:
<bean id="beanFactory" class="test.BeanFactory"/>
<bean id="car" class="bean.Car" factory-bean="beanFactory" factory-method="getCar">
<constructor-arg name="kind" value="audi"/>
<constructor-arg name="price" value="20000"/>
</bean>
3、当对象间存在引用关系
(1)添加引用对象属性
Car类:
package bean;
public class Car {
private String kind;
private int price;
public Car(){}
public Car(String kind,int price){
this.kind=kind;
this.price=price;
}
public void setKind(String kind) {
this.kind = kind;
}
public void setPrice(int price) {
this.price = price;
}
public String getKind() {
return kind;
}
public int getPrice() {
return price;
}
@Override
public String toString() {
return "Car{" +
"kind='" + kind + '\'' +
", price=" + price +
'}';
}
}
spring01.xml:
<bean id="user" class="bean.User">
<property name="age" value="20"/>
<property name="name" value="tom"/>
<property name="car" ref="car"/>
</bean>
<bean id="car" class="bean.Car">
<property name="kind" value="audi"/>
<property name="price" value="20000"/>
</bean>
(2)还可以通过内部bean的方式来管理
<bean id="user" class="bean.User">
<constructor-arg name="age" value="20"/>
<constructor-arg name="name" value="tom"/>
<constructor-arg name="car">
<bean class="bean.Car">
<property name="price" value="20000"/>
<property name="kind" value="audi"/>
</bean>
</constructor-arg>
</bean>
(3)IOC容器支持级联属性
在外层User类中先将Car初始化
直接通过car对象调用其中的set方法
<bean id="user" class="bean.User">
<property name="name" value="tom"/>
<property name="age" value="20"/>
<property name="car.kind" value="audi"/>
<property name="car.price" value="20000"/>
</bean>
(4)IOC容器支持集合属性
在User类中添加集合属性
- 内部list标签
<bean id="user" class="bean.User">
<property name="name" value="tom"/>
<property name="age" value="20"/>
<property name="cars">
<list>
<bean id="car" class="bean.Car">
<property name="kind" value="audi"/>
<property name="price" value="20000"/>
</bean>
<bean id="car" class="bean.Car">
<property name="kind" value="BMW"/>
<property name="price" value="50000"/>
</bean>
</list>
</property>
</bean>
- 外部list标签
<bean id="car" class="bean.Car">
<property name="kind" value="audi"/>
<property name="price" value="20000"/>
</bean>
<uril:list id="cars">
<ref bean="car"/>
<ref bean="car"/>
<ref bean="car"/>
<ref bean="car"/>
</uril:list>
<bean id="user" class="bean.User">
<property name="age" value="20"/>
<property name="name" value="tom"/>
<property name="cars" ref="cars"/>
</bean>
(5)IOC容器支持Map
<bean id="user" class="bean.User">
<property name="age" value="20"/>
<property name="name" value="tom"/>
<property name="map" ref="map"/>
</bean>
<uril:map id="map">
<entry key="k1" value-ref="car"/>
<entry key="k2" value-ref="car"/>
<entry key="k3" value-ref="car"/>
</uril:map>
(6)IOC容器支持Set
<bean id="user" class="bean.User">
<property name="age" value="20"/>
<property name="name" value="tom"/>
<property name="carSet" ref="set"/>
</bean>
<uril:set id="set">
<ref bean="car"/>
</uril:set>
(7)IOC容器支持P标签
首先在xml文件首部加上P标签的命名空间
xmlns:uril=“http://www.springframework.org/schema/util”
<bean id="car" class="bean.Car" p:kind="audi" p:price="50000"/>
<bean id="user" class="bean.User" p:car-ref="car"/>
(8)IOC容器支持autowire自动装配
Spring IOC 容器可以自动装配 Bean. 需要做的仅仅是在 < bean> 的 autowire 属性里指定自动装配的模式
- byType(根据类型自动装配):
拿User与Car类来说明
User类中含有需要Car类型的属性,当在< bean>标签中的autowire指定byType后,会在xml文件中,查找所有含有Car类型的< bean>。若能查找到一个,则将User类中,所有含有Car类型的属性都装配上Car的< bean>;若查找到多个,在这种情况下, Spring 将无法判定哪个 Bean 最合适该属性, 所以不能执行自动装配. - byName(根据名称自动装配): 必须将目标 Bean 的名称和属性名设置的完全相同.
(9)IOC支持父子bean
在< bean>中添加parent属性,不支持多继承。
当父bean中abstract设为true时,父bean以模板形式存在,不会实例化。
(10)IOC支持depends-on依赖关系
前置依赖的bean会在本bean实例化前创建好。
如果前置依赖多个bean可以通过逗号或空格的方式配置bean的名称。
4、IOC容器对象实例化顺序
在配置文件中,谁先配置谁先初始化
下面的示例中,User被叫做为外层对象,Car叫做被引用的对象
当对象间存在引用关系,且被引用的对象配置在了后面,则先初始化先配置的对象,然后初始化被引用的对象,调用被引用的对象中的set方法,最后调用先配置的对象的set方法。
当对象间存在引用关系,且被引用的对象配置在了前面,则先初始化被引用的对象,然后调用被引用的对象中的set方法,初始化外层的对象,最后调用外层对象的set方法。
当对象间不存在引用关系,按照配置顺序依次实例化,先初始化前面的对象并调用他的set方法,然后再初始化后面的对象并调用他的set方法。
5、IOC容器中bean的生命周期
IOC容器可以管理bean的生命周期。
Spring IOC允许在 Bean 生命周期的特定点执行定制的任务.
Spring IOC 容器对 Bean 的生命周期进行管理的过程:
- 通过构造器或工厂方法创建 Bean 实例
- 为 Bean 的属性设置值和对其他 Bean 的引用
- 调用 Bean 的初始化方法
- Bean 可以使用了
- 当容器关闭时, 调用 Bean 的销毁方法
在 Bean 的声明里设置 init-method 和 destroy-method 属性, 为 Bean 指定初始化和销毁方法.
初始化:
销毁:
6、创建Bean后置处理器
Bean后置处理器允许在调用初始化方法前后对Bean进行额外处理,处理原理类似于Servlet中的过滤器
package test;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class CarPost implements BeanPostProcessor {
//o是bean实例化出来的对象
//s是bean中的id
public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
System.out.println("Before o是:"+o+" s是:"+s);
return o;
}
public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
System.out.println("After o是:"+o+" s是:"+s);
return null;
}
}
xml:
<?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="car" class="bean.Car" init-method="init" destroy-method="destroy">
<property name="price" value="20000"/>
<property name="kind" value="audi"/>
</bean>
<bean class="test.CarPost"/>
</beans>
二、通过IOC容器配置数据库连接池
xml文件:
<?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/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">
<!--加载外部文件-->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
<property name="initialSize" value="${db.initNum}"/>
<property name="maxActive" value="${db.maxNum}"/>
</bean>
</beans>
外部文件:
三、spring组件扫描
spring能够从classpath下自动扫描,侦测和实例化具有特定注解的组件。
可以将带有注解的类,注入到IOC容器当中,这些类受IOC容器管理
<?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/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">
<!-- 自动扫描指定包以及子包下所有添加了注解的类,然后加入到IOC容器当中-->
<context:component-scan base-package="spring"></context:component-scan>
</beans>
可以设置只扫描特定的类:
<!-- 设置只扫描时,需更改扫描规则use-default-filters="false"-->
<context:component-scan base-package="spring" use-default-filters="false">
<!-- 只扫描特定注解的类-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<!-- 只扫描自己写的某个类-->
<context:include-filter type="assignable" expression="spring.controller.UserController"/>
</context:component-scan>
可以设置扫描时排除的类
<context:component-scan base-package="spring">
<!-- 只排除特定注解的类-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<!-- 只排除自己写的某个类-->
<context:exclude-filter type="assignable" expression="spring.controller.UserController"/>
</context:component-scan>
特定组件包括:
- @Component:基本注解,标识了一个受spring管理的组件
- @Repository:标识持久层组件
- @Service:标识服务层(业务层)组件
- @Controller:标识表现层组件
- @Autowired:自动注入,会将IOC容器中的对象自动注入给我们要使用的对象当中。注意,使用这个注解的前提是,要被注入的那个对象所在的类要受IOC容器管理。
使用getBean获取类对象的方法是让类名第一个字母小写,等价于< bean>中的id属性