1. 介绍
IoC
IoC—Inversion of Control,即“控制反转”,它不是什么技术,而是一种设计思想。在 Java 开发中, IoC意味着将设计好的对象交给容器控制,而不是传统的在对象内部直接控制。
传统Java程序中,我们直接在对象内部通过new进行创建对象,是程序去主动创建依赖对象;而IoC是有专门的一个容器来创建这些对象,即由IoC容器来控制对象的创建。反转则是由容器来帮忙创建及注入依赖对象。
IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
DI
DI—Dependency Injection,即“依赖注入”:是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
对于IoC和DI,其实它们是同一个概念的不同角度的描述。
IoC容器
IoC容器就是具有依赖注入功能的容器,IoC容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。应用程序无需直接在代码中new相关的对象,应用程序由IoC容器进行组装。在Spring中BeanFactory是IoC容器的实际代表者。
在 Spring Ioc 容器的代表就是 org.springframework.beans 包中的BeanFactory 接口, BeanFactory 接口提供了 IoC 容器最基本功能;而org.springframework.context 包下的 ApplicationContext 接口扩展了 BeanFactory ,还提供了与Spring AOP 集成、国际化处理、事件传播及提供不同层次的 context 实现 (如针对 web 应用的 WebApplicationContext )。简单说, BeanFactory 提供了 IoC 容器最基本功能,而 ApplicationContext 则增加了更多支持企业级功能支持。 ApplicationContext 完全继承 BeanFactory ,因而 BeanFactory 所具有的语义也适用于 ApplicationContext。
2. 使用
2.1 直接使用,给Bean注入value
- 首先创建一个Maven项目,创建方法可以参考我之前的博客。然后在pom.xml中注入spring相关的包。
- 创建一个类Person,再创建一个测试类Main,在resources文件夹中创建一个spring.xml文件。
- 运行输出结果,就可以把注入的value输出。
Person.java
package com.liu5599.test;
public class Person {
private String name;
private int age;
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 "name: " + name + " age: " + age;
}
}
Main.java
package com.liu5599.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
Person p = (Person)context.getBean("person");
System.out.println(p);
}
}
spring.xml
- 这里使用了三种方式给Bean注入value,第三种方式需要设置命名空间xmlns:p="http://www.springframework.org/schema/p"。
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id = "person" class = "com.liu5599.test.Person">
<property name="name" value="liuyang" />
<property name="age" value="21" />
</bean>
<!--<bean id = "person" class = "com.liu5599.test.Person">-->
<!--<property name="name">-->
<!--<value>liuyang</value>-->
<!--</property>-->
<!--<property name="age">-->
<!--<value>21</value>-->
<!--</property>-->
<!--</bean>-->
<!--<bean id = "person" class = "com.liu5599.test.Person"-->
<!--p:name = "liuyang" p:age = "21">-->
</beans>
输出结果:
name: liuyang age: 21
2.2 内部嵌套的Bean
接着讲述一个Bean中嵌套着另外一个Bean。
1.第一种方式是在Student中通过ref属性引用Person的Bean,但是一旦Person被引用到了Student下,也就不会被其他的Bean引用了。
2.第二种方式是在Student的Bean中声明一个内部的Bean。
3.第三种方式是通过构造函数注入。
将测试类进行修改如下:
package com.liu5599.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
Student p = (Student)context.getBean("student");
System.out.println(p);
}
}
输出结果:
Student name: liuyang age: 21
2.3 Bean的作用域
在Spring中,支持如下5种类型的作用域:
- singleton — 单例模式,由 IOC 容器返回一个唯一的 bean 实例。
- prototype — 原型模式,被请求时,每次返回一个新的 bean 实例。
- request — 每个 HTTP Request 请求返回一个唯一的 Bean 实例。
- session — 每个 HTTP Session 返回一个唯一的 Bean 实例。
- globalSession — Http Session 全局 Bean 实例。
默认情况下,作用域是单例。
单例模式与原型模式的区别
单例模式
- 编写一个消息类,在Bean中将此类设置为默认情况,也就是单例模式。
public class Message {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
Message m1 = (Message) context.getBean("message");
m1.setMessage("Message1");
System.out.println("Message1: " + m1.getMessage());
Message m2 = (Message)context.getBean("message");
System.out.println("Message2: " + m2.getMessage());
}
}
输出结果:
Message1: Message1
Message2: Message1
原型模式
- 将Bean的作用域改为prototype。还是上述测试类,运行。
<bean id = "message" class = "com.liu5599.test.Message" scope="prototype" />
运行结果:
Message1: Message1
Message2: null
- 第二次输出为null,所以证明在原型模式下,每一次请求返回一个新的Bean。
2.4 集合类型的Bean
这里主要讲述List、Set、Map和Properties四种集合类型。
Properties类型类似于特殊的Map,不同之处是Map的key可以为任意类型对象,而Properties类型的key只能是字符串。
1.首先修改Student类,修改如下。
public class Student {
private List<Object> lists;
private Set<Object> sets ;
private Map<Object, Object> maps ;
private Properties pros;
public List<Object> getLists() {
return lists;
}
public void setLists(List<Object> lists) {
this.lists = lists;
}
public Set<Object> getSets() {
return sets;
}
public void setSets(Set<Object> sets) {
this.sets = sets;
}
public Map<Object, Object> getMaps() {
return maps;
}
public void setMaps(Map<Object, Object> maps) {
this.maps = maps;
}
public Properties getPros() {
return pros;
}
public void setPros(Properties pros) {
this.pros = pros;
}
private Person person;
public Student() {}
public Student(Person person) {
this.person = person;
}
public void setPerson(Person person) {
this.person = person;
}
@Override
public String toString() {
return "Student " + person;
}
}
2.修改spring.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-3.0.xsd">
<bean id = "person" class = "com.liu5599.test.Person">
<property name = "name" value = "liuyang" />
<property name = "age" value = "21" />
</bean>
<bean id = "student" class="com.liu5599.test.Student">
<property name = "lists">
<list>
<value>1</value>
<ref bean = "person" />
<bean class = "com.liu5599.test.Person">
<property name = "name" value = "abc" />
<property name = "age" value = "20" />
</bean>
</list>
</property>
<property name="sets">
<set>
<value>1</value>
<ref bean = "person" />
<bean class = "com.liu5599.test.Person">
<property name = "name" value = "abc" />
<property name = "age" value = "20" />
</bean>
</set>
</property>
<property name="maps">
<map>
<entry key = "key1" value = "123" />
<entry key = "key2" value-ref = "person" />
<entry key = "key3">
<bean class = "com.liu5599.test.Person">
<property name = "name" value = "abcd" />
<property name = "age" value = "19" />
</bean>
</entry>
</map>
</property>
<property name="pros">
<props>
<prop key = "name">liuyang</prop>
<prop key = "age">21</prop>
</props>
</property>
<property name="person">
<bean class = "com.liu5599.test.Person">
<property name = "name" value = "liuyang" />
<property name = "age" value = "21" />
</bean>
</property>
</bean>
<bean id = "message" class = "com.liu5599.test.Message" scope="prototype" />
</beans>
3.修改测试类,如下。
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
Student s = (Student)context.getBean("student");
System.out.println(s.getLists().toString());
System.out.println(s.getSets().toString());
System.out.println(s.getMaps().toString());
System.out.println(s.getPros().toString());
}
}
输出结果:
[1, name: liuyang age: 21, name: abc age: 20]
[1, name: liuyang age: 21, name: abc age: 20]
{key1=123, key2=name: liuyang age: 21, key3=name: abcd age: 19}
{age=21, name=liuyang}
以上就是Spring Bean的简单使用方法,但是全都是手动配置的,难以维护,下次我会写一篇关于注解配置和自动扫描装配的文章。