Spring之IOC
1.简介
IOC( Inversion of Control ),控制反转,也叫依赖注入DI(dependency injection),是Spring框架Core模块提供的核心功能。
“控制反转”:对象A运行需要对象B,由主动创建变为IOC容器注入,这便是控制反转。
“依赖注入”:获得依赖对象的过程被反转了,获取依赖对象的过程由自身创建变为由IOC容器注入,这便是依赖注入。
2.快速入门
-
引入依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.1</version> </dependency>
-
新建一个Person类
public class Person { private String name; private int age; public void sayHello(){ System.out.println("hello!"); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
-
创建bean1.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="person" class="com.apple.spring.bean.Person"></bean> </beans>
-
使用容器创建对象
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); Person person = context.getBean("person", Person.class); person.sayHello(); //hello!
3.IOC容器两种实现方式
3.1 BeanFactory
IOC容器基本实现,是Spring内部的使用接口,不提供开发人员进行使用
- 加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象
3.2 ApplicationContext
BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员进行使用
- 加载配置文件时候就会把在配置文件对象进行创建
- 有两个实现类接口:
- ClassPathXmlApplicationContext,加载类文件基于src目录下的类路径
- FileSystemXmlApplicationContext,加载类文件基于硬盘路径,C://…
4.Bean的常用属性配置
4.1 id
bean的唯一标识,同一个Spring容器中不允许重复
4.2 class
全类名,用于反射创建对象
4.3 scope
scope主要有两个值: singleton和prototype。
如果设置为singleton,则一个容器中只会有这个一个bean对象。默认容器创建的时候就会创建该对象.
如果设置为prototype,则一个容器中会有多个该bean对象。每次调用getBean方法获取时都会创建一个新对象
5.DI依赖注入
5.1 set方法注入
在要注入属性的bean标签中进行配置。前提是该类有提供属性对应的set方法
<bean id="person" class="com.apple.spring.bean.Person">
<property name="name" value="杰克"></property>
<property name="age" value="18"></property>
</bean>
创建容器查看注入的属性
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
Person person = context.getBean("person", Person.class);
System.out.println(person); //Person{name='杰克', age=18}
如果Person有个Cat属性
public class Cat {
private String type;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
bean1.xml配置,ref属性用于给引用类型的属性设置值,和Spring容器中bean的id相对应
<bean id="person" class="com.apple.spring.bean.Person">
<property name="name" value="杰克"></property>
<property name="age" value="18"></property>
<property name="cat" ref="Cat"></property>
</bean>
<bean id="Cat" class="com.apple.spring.bean.Cat">
<property name="type" value="奶牛猫"/>
</bean>
测试结果:Person{name=‘杰克’, age=18, cat=Cat{type=‘奶牛猫’}}
5.2 有参构造注入
在要注入属性的bean标签中进行配置。前提是该类有提供对应的有参构造
bean1.xml文件配置
<bean id="person" class="com.apple.spring.bean.Person">
<constructor-arg name="name" value="肉丝"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="cat" ref="Cat"></constructor-arg>
</bean>
<bean id="Cat" class="com.apple.spring.bean.Cat">
<property name="type" value="奶牛猫"/>
</bean>
测试结果:Person{name=‘肉丝’, age=18, cat=Cat{type=‘奶牛猫’}}
5.3 复杂类型属性注入
Person类增加了这些属性
public class Person {
private String name;
private int age;
private Cat cat;
private List<String> list;
private List<Cat> cats;
private Set<String> set;
private Map<String,Cat> map;
private int[] arr;
private Properties properties;
...//省略了get、set、toString方法
}
bean1.xml配置
<bean id="person" class="com.apple.spring.bean.Person">
<property name="name" value="Mary"></property>
<property name="age" value="10"></property>
<property name="cat" ref="Cat"></property>
<property name="list">
<list>
<value>小智</value>
<value>皮卡丘</value>
</list>
</property>
<property name="cats">
<list>
<ref bean="Cat"></ref>
</list>
</property>
<property name="set">
<set>
<value>小光</value>
<value>波加曼</value>
</set>
</property>
<property name="map">
<map>
<entry key="黑猫警长" value-ref="Cat"></entry>
</map>
</property>
<property name="arr">
<array>
<value>1</value>
<value>2</value>
</array>
</property>
<property name="properties">
<props>
<prop key="小霞">可达鸭</prop>
<prop key="小刚">大岩蛇</prop>
</props>
</property>
</bean>
<bean id="Cat" class="com.apple.spring.bean.Cat">
<property name="type" value="奶牛猫"/>
</bean>
测试结果:Person{name=‘Mary’, age=10, cat=Cat{type=‘奶牛猫’}, list=[小智, 皮卡丘], cats=[Cat{type=‘奶牛猫’}], set=[小光, 波加曼], map={黑猫警长=Cat{type=‘奶牛猫’}}, arr=[1, 2], properties={小刚=大岩蛇, 小霞=可达鸭}}
6.SPEL
我们可以在配置文件中使用SPEL表达式,写法如下:
<property name="age" value="#{10}"></property>
<property name="cat" value="#{Cat}"></property>
注意: SPEL需要写到value属性中,不能写到ref属性
7.读取使用properties配置文件
7.1 创建jdbc.properties
prop.driverClass=com.mysql.jdbc.Driver
prop.url=com.mysql.jdbc.Driver
prop.userName=root
prop.password=root
7.2 引入context命名空间
<bean xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
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/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"/>
7.3 引入properties配置文件,进行配置
<!--引入外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${prop.driverClass}"></property>
<property name="url" value="${prop.url}"></property>
<property name="username" value="${prop.userName}"></property>
<property name="password" value="${prop.password}"></property>
</bean>
8.引入Spring配置文件
我们可以在主的配置文件中通过import标签的resource属性,引入其它的xml配置文件
<import resource="classpath:jdbc.xml"></import>
9.bean的不常用配置
9.1 name属性
我们可以用name属性来给bean取名,一个多个都可以
<bean id="person" class="com.apple.spring.bean.Person" name="person2,person3"/>
获取的时候就可以使用这个名字来获取了
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
Person person = (Person) context.getBean("person3");
9.2 lazy-init
可以控制bean的创建时间,如果设置为true就是在第一次获取该对象的时候才去创建。
<bean id="person" class="com.apple.spring.bean.Person" lazy-init="true"/>
9.3 init-method
可以用来设置初始化方法,设置完后容器创建完对象就会自动帮我们调用对应的方法
<bean id="person" class="com.apple.spring.bean.Person" init-method="sayHello"/>
注意:配置的初始化方法只能是空参的
9.4 destroy-method
可以用来设置销毁之前调用的方法,设置完后容器销毁对象前就会自动帮我们调用对应的方法
<bean id="person" class="com.apple.spring.bean.Person" destroy-method="sayBye"/>
注意:配置的初始化方法也只能是空参的
9.5 factory-bean和factory-method
现实中一辆汽车是由很多零件构成的,汽车工厂负责将各种零件组装起来
//Car.java
public class Car {
//方向盘
private SteeringWheel steeringWheel;
//轮胎
private Tyre tyre;
//发动机
private Motor motor;
public SteeringWheel getSteeringWheel() {
return steeringWheel;
}
public void setSteeringWheel(SteeringWheel steeringWheel) {
this.steeringWheel = steeringWheel;
}
public Tyre getTyre() {
return tyre;
}
public void setTyre(Tyre tyre) {
this.tyre = tyre;
}
public Motor getMotor() {
return motor;
}
public void setMotor(Motor motor) {
this.motor = motor;
}
}
//CarFactory.java
public class CarFactory {
public Car getCar(){
Car c = new Car();
c.setMotor(new Motor());
c.setSteeringWheel(new SteeringWheel());
c.setTyre(new Tyre());
return c;
}
}
bean2.xml配置
<!--创建实例工厂-->
<bean class="com.apple.spring.factory.CarFactory" id="carFactory"></bean>
<!--使用实例工厂创建Car放入容器-->
<!--factory-bean 用来指定使用哪个工厂对象-->
<!--factory-method 用来指定使用哪个工厂方法-->
<bean factory-bean="carFactory" factory-method="getCar" id="car"></bean>
测试使用
ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
Car car = (Car) context.getBean("car");
System.out.println(car);
也可以使用静态工厂创建Car
public class CarFactory {
public static Car getCar(){
Car c = new Car();
c.setMotor(new Motor());
c.setSteeringWheel(new SteeringWheel());
c.setTyre(new Tyre());
return c;
}
}
<bean class="com.apple.spring.factory.CarFactory" factory-method="getCar" id="car2"></bean>