Spring框架——IOC

1.IOC是什么?

ioc(inverse of controll ) 控制反转。
所谓控制反转就是把**创建对象(bean)和维护对象(bean)**的关系的权利从程序中转移到spring的容器(applicationContext.xml),而程序本身不再维护。
DI是什么?
di(dependency injection) 依赖注入: 实际上di和ioc是同一个概念,spring设计者认为di更准确表示spring核心技术

2.IOC如何获取bean

 从ApplicationContex 应用上下文容器中获取bean和从bean工厂容器中获取bean

1
2
3
4
5
6
7
8
9
10
11
12
//从ApplicationContext中取bean
ApplicationContext ac=new ClassPathXmlApplicationContext("com/hsp/ioc/beans.xml");
//当我们去实例化beans.xml,该文件中配置的bean被实例(该bean scope是 singleton)从bean中取出student


//如果我们使用beanfactory去获取bean,当你只是实例化该容器, 那么
//容器的bean不被实例化,只有当你去使用getBean某个bean时,才会实时的创建.

BeanFactory factory = new XmlBeanFactory(
new ClassPathResource("com/hsp/ioc/beans.xml"));
factory.getBean("student");

两者的区别:
1.如果使用ApplicationContext ,则配置的bean如果是 singlton不管你用不用,都被实例化.(好处就是可以预先加载,缺点就是耗内存)
2.如果是 BeanFactory ,则当你获取beanfacotry时候,配置的bean不会被马上实例化,当你使用的时候,才被实例(好处节约内存,缺点就是速度)

3.IOC处理对象的依赖关系

set注入的缺点是无法清晰表达哪些属性是必须的,哪些是可选的,构造注入的优势是通过构造强制依赖关系,不可能实例化不完全的或无法使用的bean。

3.1 set方法注入值

  • 普通字段赋值
  • 集合属性 (list/map/property)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//Department类:
public class Department {

private String name;
private String [] empName;
private List<Employee> empList;
private Set<Employee> empsets;
private Map<String,Employee> empMaps;

public Set<Employee> getEmpsets() {
return empsets;
}
public void setEmpsets(Set<Employee> empsets) {
this.empsets = empsets;
}
public String[] getEmpName() {
return empName;
}
public void setEmpName(String[] empName) {
this.empName = empName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Employee> getEmpList() {
return empList;
}
public void setEmpList(List<Employee> empList) {
this.empList = empList;
}
public Map<String, Employee> getEmpMaps() {
return empMaps;
}
public void setEmpMaps(Map<String, Employee> empMaps) {
this.empMaps = empMaps;
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//Employeel类 
package com.hsp.collection;
public class Employee {
private String name;
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<bean id="department" class="com.hsp.collection.Department">
<property name="name" value="财务部"/>
<!-- 给数组注入值 -->
<property name="empName">
<list>
<value>小明</value>
<value>小明小明</value>
<value>小明小明小明小明</value>
</list>
</property>
<!-- 给list注入值 list 中可以有相当的对象 -->
<property name="empList">
<list>
<ref bean="emp2" />
<ref bean="emp1"/>
<ref bean="emp1"/>
</list>
</property>
<!-- 给set注入值 set不能有相同的对象 -->
<property name="empsets">
<set>
<ref bean="emp1" />
<ref bean="emp2"/>
<ref bean="emp2"/>
</set>
</property>
<!-- 给map注入值 map只有key不一样,就可以装配value -->
<property name="empMaps">
<map>
<entry key="11" value-ref="emp1" />
<entry key="22" value-ref="emp2"/>
<entry key="33" value-ref="emp1"/>
</map>
</property>
<!-- 给属性集合配置 -->【点http协议 referer 】
<property name="pp">
<props>
<prop key="pp1">abcd</prop>
<prop key="pp2">hello</prop>
</props>
</property>
</bean>
<bean id="emp1" class="com.hsp.collection.Employee">
<property name="name" value="北京"/>
<property name="id" value="1"/>
</bean>
<bean id="emp2" class="com.hsp.collection.Employee">
<property name="name" value="天津"/>
<property name="id" value="2"/>
</bean>
1
2
3
4
5
6
7
<!--内部bean-->
<bean id=”foo” class=”....Foo”>
<property name=”属性”>
<!—第一方法引用-->
<ref bean=’bean对象名’/>
</property>
</bean>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//继承配置
public class Student {
private String name;
private String age;

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return name;
}
public void setAge(String age) {
this.name = name;
}
}
public class Gradate extends Student{
....
}
1
2
3
4
5
6
7
8
9
10
11
12
在beans.xml文件中体现配置 
<!-- 配置一个学生对象 -->
<bean id="student" class="com.hsp.inherit.Student">
<property name="name" value="顺平" />
<property name="age" value="30"/>
</bean>
<!-- 配置Grdate对象 -->
<bean id="grdate" parent="student" class="com.hsp.inherit.Gradate">
<!-- 如果自己配置属性name,age,则会替换从父对象继承的数据 -->
<property name="name" value="小明"/>
<property name="degree" value="学士"/>
</bean>

3.2 构造函数赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Employee {

private String name;
//默认构造函数
public Employee(){

}

public Employee(String name, int age) {

System.out.println("Employee(String name, int age) 函数被调用..");
this.name = name;

}

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}

}

index:第几个参数
type:参数类型
value:参数
spring会根据传入的参数,自动调用相应的狗崽函数

1
2
3
4
5
<!-- 配置一个雇员对象 -->
<bean id="employee" class="com.hsp.constructor.Employee">
<!-- 通过构造函数来注入属性值 -->
<constructor-arg index="0" type="java.lang.String" value="大明" />
</bean>

3.3自动装配bean的属性值(autowire=”….”)

Spring官方文档说明

原理

(1) byName: byName:寻找和属性名相同的bean,找不到,装不上
(如下面的例子:当master和dog对象加载到内存中时,master中的dog其实是个null。当在获取bean的时候master.getDog().getName (),ioc会根据你设置autowire=“byName” ,来从内存中查找对象名=dog的,然后把dog对象的内存地址给master的dog属性)
(2) byType: byType:寻找和属性类型相同的bean,找不到,装不上,找到多个抛异常。
(同byName原理一样,只是要查找类型是dog类型的对象,名字可以不相同,但是类型相同就行)
(3) constructor: autowire=”constructor”
说明 : 查找和bean的构造参数一致的一个或多个bean,若找不到或找到多个抛异常。按照参数的类型装配
(查找master中有没有狗杂函数,用于接受dog对象)

1
2
3
public Master(Dog dog){
this.dog = dog;
}

(4) autodetect
说明 : autowire=”autodetect”(3)和(2)之间选一个方式。不确定性的处理与(3)和(2)一致。
(5) defualt
autowire默认是defualt,而defualt-autorwire默认是no
这个需要在
当你在指定了 default-atuowrite后, 所有的bean的 默认的autowire就是 指定的装配方法;
如果没有在 没有 defualt-autorwire=“指定” ,则默认是
defualt-autorwire=”no”

调用xml配置文件

1
2
3
4
5
6
7
8
9
10
11

public static void main(String[] args) {
// TODO Auto-generated method stub
ApplicationContext ac=new ClassPathXmlApplicationContext("com/hsp/autowire/beans.xml");
//获取
Master master=(Master) ac.getBean("master");
System.out.println(master.getName()+" 养 "+master.getDog().getName());
}

}

Master和Dog对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//Master
public class Master {

private String name;
private Dog dog;




public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
}

//Dog
public class Dog {
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;
}
}

xml配置文件

1
2
3
4
5
6
7
8
9
10
<bean id="master" class="com.hsp.autowire.Master" autowire="byName">
<property name="name">
<value>顺平</value>
</property>
</bean>
<!-- 配置dog对象 -->
<bean id="dog111" class="com.hsp.autowire.Dog">
<property name="name" value="小黄"/>
<property name="age" value="3"/>
</bean>

3.4 通过注解配置ioc

配置方式与注解方式区别:
1. 配置, 便于维护(配置过多,比较繁琐)
2. 注解, 开发方便(简化配置,不利于后期维护,如果修改对象创建、关系处理,需要改代码!)
3.4.1 将bean注入到ioc中
@Component
表示一个组件(类),把当前组件加入ioc容器加入容器的组件的名称默认是类名第一个字母小写,
@Component("classname")指定加入ioc容器的组件类的类名
相当于如下所示

1
<bean id="classname" class="..."><bean/>

以下注解作用和@Component一样,只是更容易区分组件类型
@Repository 标识是一个持久层的组件
@Service 标识是一个业务逻辑层的组件
@Controller 标识是一个控制层的组件
3.4.1 处理bean中的依赖关系
@Resource

  1. 默认根据修饰的字段名称会取ioc容器找对象自动注入找到后注入
  2. 如果名称没有找到,再根据类型查找 找到后就立刻注入。如果改类型在ioc容器中有多个对象,报错!
  3. 根据类型也没有找到对象,报错!
    @Resource(name ="") 会根据指定的名称去容器找对象自动注入
    相当于如下所示
    1
    2
    3
    <bean id="classname" class="...">
    <property name="" ref="beanid"/>
    <bean/>

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//@Component("userService")  // 把当前类加入IOC容器
//@Service("userService") // 把当前类加入IOC容器

@Service
public class UserService implements IUserService {

//@Resource(name = "userDao") // 根据”userDao“去ioc容器找对象,找到后注入到当前方法参数

@Resource // 默认会根据private IUserDao userDao 这里的名称去容器找; 如果没有找到,再根据类型找,再没有找到就报错!
private IUserDao userDao;

public void save() {
userDao.save();
}
}