Spring入门-IOC和DI

接下来学习Spring相关知识IOC和DI,即控制反转和依赖注入。

什么是IOC和DI

IOC(Inversion of Control 控制反转),即对象之间的依赖关系由Spring容器来建立

DI(Dependency Injection 依赖注入),Spring容器可以通过调用set方法或者构造器来建立对象之间的依赖关系

简单来说,对象之间关系由最初人来建立,变成交给Spring容器来建立,这样造成控制角色的转变,人只需要等着就可以了,容器给什么对象我就使用什么对象。为了实现IOC,DI是必不可少的,其可以实现对象动态注入,根据接口实现类的不同可以调用不同的对象,这样可以大大提高代码可维护性。

DI依赖注入对象

依赖注入有两种方式,即set方法注入和构造器注入,一般使用set方法注入。

set方法注入

(1)需要注入对象的类中,添加对应对象set方法,属性一般使用接口类型,这样可以实现动态注入

接口

 1 package com.boe;
 2 /**
 3  * 员工接口
 4  */
 5 public interface Employee {
 6     
 7     //接口方法,就是工作
 8     public void work();
 9 
10 }
View Code

实现类

 1 package com.boe;
 2 
 3 public class Engineer implements Employee{
 4     
 5     public Engineer() {
 6         System.out.println("创建了一个新的Engineer");
 7     }
 8 
 9     public void work() {
10         System.out.println("call厂商,写报告,被怼");
11     }
12 
13 }
View Code
 1 package com.boe;
 2 
 3 public class Leader implements Employee{
 4     
 5     public Leader() {
 6         System.out.println("新创建了一个Leader");
 7     }
 8 
 9     public void work() {
10         System.out.println("开会,坐办公室,专职怼下属");
11     }
12 
13 }
View Code

测试类中添加要注入的属性emp,类型为接口Employee。

 1 package com.boe;
 2 
 3 public class Company {
 4     //属性为员工
 5     private Employee emp;
 6     
 7     //添加set,get方法
 8     public Employee getEmp() {
 9         return emp;
10     }
11 
12     public void setEmp(Employee emp) {
13         this.emp = emp;
14     }
15     
16     //构造方法
17     public Company() {
18         System.out.println("创建了一个新的公司");
19     }
20     
21     //调用员工工作的方法
22     public void runCompany() {
23         //根据注入的对象不同,执行得到不同的结果
24         emp.work();
25     }
26 }

(2)在配置文件中,使用<property name="" ref="">来配置属性,name即类中注入对象对应的属性名,ref为注入对象的bean id

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3     xmlns:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util"  
 4     xmlns:jee="http://www.springframework.org /schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
 5     xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:mvc="http://www.springframework.org/schema/mvc"
 6     xsi:schemaLocation="
 7         http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
 8         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
 9         http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd
10         http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
11         http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
12         http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
13         http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">    
14         
15        <!--Set方式注入 -->
16        <bean id="engineer" class="com.boe.Engineer"></bean>
17        <bean id="leader" class="com.boe.Leader"></bean>       
18        <!-- 开始注入 -->
19        <bean id="company" class="com.boe.Company">
20         <property name="emp" ref="leader"></property>
21        </bean>
22        
23 </beans>
View Code

使用junit测试

 1 import org.junit.Test;
 2 import org.springframework.context.ApplicationContext;
 3 import org.springframework.context.support.ClassPathXmlApplicationContext;
 4 
 5 import com.boe.Company;
 6 
 7 public class TestDI {
 8     
 9     //测试set方法注入
10     @Test
11     public void testSet() {
12         String path="myIOC.xml";
13         ApplicationContext ac=new ClassPathXmlApplicationContext(path);
14         Company company=ac.getBean("company",Company.class);
15         company.runCompany();
16     }
17 
18 }
View Code

测试结果如下,当property中ref指向的bean id发生变化时,对应就有不同的执行效果,实现了对象的依赖注入,配置什么ref就有什么对象注入进来。

构造器注入

(1)需要注入对象的类中添加带有参数的构造器,参考下面代码。

注入类,宅男类

 1 package com.boe;
 2 /**
 3  * 宅男类
 4  * @author yangchaolin
 5  */
 6 public class Otaku {
 7     //构造方法
 8     public Otaku() {
 9         System.out.println("创建一个新的宅男");
10     }
11 }
View Code

被注入类,女神类

 1 package com.boe;
 2 /**
 3  * 女神类
 4  * @author yangchaolin
 5  */
 6 public class Gakki {    
 7     //属性
 8     private Otaku otaku;
 9     //带参数的构造方法
10     public Gakki(Otaku otaku) {
11         System.out.println("创建一个女神,有宅男");
12         this.otaku = otaku;
13     }
14     //默认构造方法
15     public Gakki() {
16         System.out.println("创建一个女神,无宅男");
17     }    
18     //toString方法
19     @Override
20     public String toString() {
21         return "Gakki [otaku=" + otaku + "]";
22     }
23 }
View Code

(2)在配置文件中,使用<constructor-arg index="" ref="" />元素来配置,其中index代表构造方法中参数的位置,位置从0开始,ref为bean id。

1        <!-- 构造器方式注入 -->
2        <bean id="otaku" class="com.boe.Otaku"></bean>
3        <bean id="gakki" class="com.boe.Gakki">
4            <constructor-arg index="0" ref="otaku"></constructor-arg>
5        </bean>
View Code

junit测试类

1     //测试构造器方法注入
2     @Test
3     public void testConstructor() {
4         String path="myIOC.xml";
5         ApplicationContext ac=new ClassPathXmlApplicationContext(path);
6         Gakki gakki=ac.getBean("gakki",Gakki.class);
7         System.out.println(gakki);
8     }
View Code

测试结果

自动装配

默认情况下容器不会自动装配对象只有依据某些规则自动帮我们建立对象之间的依赖关系,需设置autowire属性,有三种类型。

(1)byName:表示依据属性名查找对应的bean,即通过属性名,查找xml配置文件中是否有bean id等于这个属性名的,有就将bean注入,没有就不注入,参考将人注入到国家。

人对应的类

1 package com.boe;
2 
3 public class People {
4     //默认构造方法
5     public People() {
6         System.out.println("创建一个人");
7     }
8 }
View Code

国家对应的类

 1 package com.boe;
 2 
 3 public class Country {
 4     //属性
 5     private People people;
 6     //get set方法
 7     public People getPeople() {
 8         return people;
 9     }
10 
11     public void setPeople(People people) {
12         this.people = people;
13     }
14     //构造方法
15     public Country() {
16         System.out.println("创建一个新的国家");
17     }
18     //重写toString方法
19     @Override
20     public String toString() {
21         return "Country [people=" + people + "]";
22     }
23 }
View Code

xml中配置信息

1        <!-- byName -->
2        <bean id="people" class="com.boe.People"></bean><!-- 属性名为people,bean id也为people -->
3        <bean id="country" class="com.boe.Country" autowire="byName"></bean>

junit测试

1     //测试使用自动装配-byName&byType
2     @Test
3     public void testAutowireByName() {
4         String path="myIOC.xml";
5         ApplicationContext ac=new ClassPathXmlApplicationContext(path);
6         Country country=ac.getBean("country",Country.class);
7         System.out.println(country);
8     }
View Code

当bean id等于属性名people时,autowire="byName"是可以将People类实例对象注入的,否则不会注入。

修改bean id为其他名字,不会注入。

(2)byType:依据属性的类型来查找对应的bean,即查看bean的class属性是否匹配,如果一致就注入。

在上述修改基础上,将autowire属性值设置为byType

1        <!-- byType -->
2        <bean id="people1" class="com.boe.People"></bean>
3        <bean id="country" class="com.boe.Country" autowire="byType"></bean>

即使bean id不匹配,class属性是匹配的,因此也可以注入。

但是byType属性只能有一个bean与之对应,如果有多个class类型一样的bean,这种自动装配会有致命错误发生。

1        <!-- byType -->
2        <bean id="people1" class="com.boe.People"></bean>
3        <bean id="people2" class="com.boe.People"></bean>
4        <bean id="country" class="com.boe.Country" autowire="byType"></bean>

(3)constructor:与byType类似,只不过使用构造器来注入,将Country换成Province测试。

省对应的类

 1 package com.boe;
 2 
 3 public class Province {
 4     //属性
 5     private People people;
 6     //带参数构造方法
 7     public Province(People people) {
 8         System.out.println("新建一个省,有人民");
 9         this.people = people;
10     }
11     //默认构造方法
12     public Province() {
13         System.out.println("新建一个省,没有人民");
14     }
15     //重写toString
16     @Override
17     public String toString() {
18         return "Province [people=" + people + "]";
19     }    
20 }
View Code

xml中配置信息

1       <!-- constructor -->
2        <bean id="p" class="com.boe.People"></bean>
3        <!-- <bean id="p1" class="com.boe.People"></bean> -->
4        <bean id="province" class="com.boe.Province" autowire="constructor"></bean>

junit代码

1     //测试使用自动装配-constructor
2     @Test
3     public void testAutowireConstructor() {
4         String path="myIOC.xml";
5         ApplicationContext ac=new ClassPathXmlApplicationContext(path);
6         Province province=ac.getBean("province",Province.class);
7         System.out.println(province);
8     }
View Code

测试结果,可以看出跟byType类似,如果有多个同样类型的bean匹配,则不会注入且不报错,这点跟byType有点不同。

DI依赖注入值

Spring容器可以帮我们注入基本数据类型的值,可以注入的类型有:charintdouble等,此外还可以注入String

测试类valueBean

 1 package value;
 2 
 3 import java.util.List;
 4 import java.util.Map;
 5 import java.util.Properties;
 6 import java.util.Set;
 7 
 8 public class valueBean {
 9     //注入基本数据类型示例
10     private String name;
11     private int age;
12     //注入集合类型示例
13     private List<String> interests;//List集合中的值可以重复
14     private Set<String> city;//Set集合中的值不可以重复
15     private Map<String,Double> score;//Map集合的注入
16     private Properties db;
17     
18     
19     public Properties getDb() {
20         return db;
21     }
22     public void setDb(Properties db) {
23         this.db = db;
24     }
25     public Map<String, Double> getScore() {
26         return score;
27     }
28     public void setScore(Map<String, Double> score) {
29         this.score = score;
30     }
31     public Set<String> getCity() {
32         return city;
33     }
34     public void setCity(Set<String> city) {
35         this.city = city;
36     }
37     public List<String> getInterests() {
38         return interests;
39     }
40     public void setInterests(List<String> interests) {
41         this.interests = interests;
42     }
43     public String getName() {
44         return name;
45     }
46     public void setName(String name) {
47         this.name = name;
48     }
49     public int getAge() {
50         return age;
51     }
52     public void setAge(int age) {
53         this.age = age;
54     }
55     //无参数构造器
56     public valueBean() {
57         System.out.println("valueBean()");
58     }
59     @Override
60     public String toString() {
61         return "valueBean [name=" + name + ", age=" + age +"\n"+"interests=" + interests + "\n"+"city=" + city + "\n"+"score="
62                 + score + "\n"+"db=" + db + "]";
63     }    
64 }
View Code

注入基本数值xml配置

1         <!--  注入基本类型数据-->
2         <bean id="vb1" class="value.valueBean">
3            <!--  注入基本数据类型-->
4            <!-- 调用setName方法和setAge方法,将关关和22传入到对象中 -->
5            <property name="name" value="关关"></property>
6            <property name="age" value="22"></property>
7      </bean>

junit测试

 1    @Test
 2    /**
 3     * 测试注入-直接注入
 4     */
 5    public void test6() {
 6          //写配置路径
 7          String config="value.xml";
 8          //启动Spring容器
 9          ApplicationContext ac=new ClassPathXmlApplicationContext(config);          
10          valueBean valueBean=ac.getBean("vb1", valueBean.class);
11          System.out.println(valueBean);
12    }
View Code

测试结果

容器可以注入集合类型的值(List,Set,Map,Properties),有如下两种方式

(1)直接注入,在xml中写好

 1        <bean id="vb1" class="value.valueBean">
 2           <!-- 注入集合类型数据 -->
 3           <!-- 调用setInterests方法,将集合传入到对象中,list代表集合,其中的值就是集合中的元素 -->
 4           <property name="interests">
 5             <list>
 6               <value>玩电脑</value>
 7               <value>玩诛仙</value>
 8               <value>玩街头篮球</value>
 9             </list>
10           </property>
11           <property name="city">
12             <set>
13                <value>长沙</value>
14                <value>佛山</value>
15                <value>北京</value>
16                <value>北京</value><!-- 写两个北京,set只会显示一个,不允许重复的值 -->
17             </set>
18           </property>
19           
20           <!-- Map集合的注入 -->
21           <property name="score">
22               <map>
23                  <entry key="物理" value="59"></entry>
24                  <entry key="工程力学" value="49"></entry>
25                  <entry key="材料科学与工程" value="58"></entry>
26               </map>
27           </property>
28           
29           <!-- Properties集合的注入 -->
30           <property name="db">
31                <props>
32                   <prop key="username">yangchaolin</prop>
33                   <prop key="password">1234</prop>
34                </props>
35           </property>
36        </bean>        

注入结果

(2)采用引用的方式注入

 step1 将集合类型的值先配置成一个bean

 step2 再将这个bean注入到对应的bean里面

测试类ExampleBean

 1 package value;
 2 
 3 import java.util.List;
 4 import java.util.Map;
 5 import java.util.Properties;
 6 import java.util.Set;
 7 /**
 8  * 采用另外一种注入方式注入集合,即引用的方式,将集合先配置成bean,然后注入到ExampleBean里面来
 9  * @author yangchaolin
10  */
11 public class ExampleBean {
12     
13     private List<String> interests;
14     private Set<String> city;
15     private Map<String,Double> score;
16     private Properties db;
17     //无参数构造器
18     public ExampleBean() {
19         System.out.println("ExampleBean()");
20     }
21     public List<String> getInterests() {
22         return interests;
23     }
24     public void setInterests(List<String> interests) {
25         this.interests = interests;
26     }
27     public Set<String> getCity() {
28         return city;
29     }
30     public void setCity(Set<String> city) {
31         this.city = city;
32     }
33     public Map<String, Double> getScore() {
34         return score;
35     }
36     public void setScore(Map<String, Double> score) {
37         this.score = score;
38     }
39     public Properties getDb() {
40         return db;
41     }
42     public void setDb(Properties db) {
43         this.db = db;
44     }
45     @Override
46     public String toString() {
47         return "ExampleBean [interests=" + interests + "\n"+"city=" + city + "\n"+"score=" + score + "\n"+"db=" + db + "]";
48     }   
49 }
View Code

xml配置

 1 <!-- 将集合类型的值配置成一个bean,util:list其实就是一个bean,因此可以作为ref来注入-->
 2        <!-- util:list中的util是命名空间(namespace),是为了区分同名的元素而设定的前缀,如list是上面spring框架下的list,不是java.util下的list -->
 3        <util:list id="interestBean">
 4          <value>喝酒</value>
 5          <value>玩游戏</value>
 6          <value>逃课</value>
 7          <value>挂科</value>
 8        </util:list>
 9        <util:set id="cityBean">
10           <value>长沙</value>
11           <value>佛山</value>  
12           <value>中山</value>  
13           <value>北京</value>        
14        </util:set>
15        <util:map id="scoreBean">
16          <entry key="大学数学" value="79"></entry>
17          <entry key="C语言" value="76"></entry>
18          <entry key="大学计算机基础" value="75"></entry>
19        </util:map>
20        <util:properties id="dbBean">
21          <prop key="物理化学">59</prop>
22          <prop key="弹性力学">55</prop>
23          <prop key="大学英语">80</prop>
24        </util:properties>
25        <!-- 采用引用的方式,注入集合类型的值 -->
26        <bean id="eb1" class="value.ExampleBean">
27          <property name="interests" ref="interestBean"></property>
28          <property name="city" ref="cityBean"></property>
29          <property name="score" ref="scoreBean"></property>
30           <property name="db" ref="dbBean"></property>
31        </bean>

junit测试

 1    @Test
 2    /**
 3     * 测试注入-使用引用注入
 4     */
 5    public void test7() {
 6          //写配置路径
 7          String config="value.xml";
 8          //启动Spring容器
 9          ApplicationContext ac=new ClassPathXmlApplicationContext(config);          
10          ExampleBean exampleBean=ac.getBean("eb1", ExampleBean.class);
11          System.out.println(exampleBean);
12    }
View Code

测试结果

结论

(1)IOC是目标,DI是手段

(2)DI注入有set方法注入和构造器方法注入,一般使用前者

(3)自动装配有三种类型,byName、byType和constructor

(4)DI注入除了可以注入对象外,还可以注入数据,如基本数据类型和集合,其中集合注入可以直接注入,也可以将集合配置成一个bean后注入

posted @ 2019-08-12 04:03  斐波那切  阅读(318)  评论(0编辑  收藏  举报