Spring Framework的核心:Spring IoC

1  Spring IoC容器概述

       IoC(控制反转:Inverse of Control)是Spring容器的内核,AOP以及声明事务等功能都是在此基础上开花结果。

       控制反转是面向对象编程的一种设计原则,可以用来降低计算机代码之间的耦合度。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。所以,控制反转是关于一个对象如何获取它所依赖的对象的应用。

1.1  主要形式:

        依赖查找:容器提供回调接口和上下文条件给组件。这样一来,组件就必须使用容器提供的API来查找资源和协作对象,仅有的控制反转只体现在那些回调方法上。容器将调用这些回调方法,从而让应用代码获得相关资源。

       依赖注入:组件不做定位查询,只提供普通的Java方法让容器去决定依赖关系。容器全权负责组件的装配,它会把符合依赖关系的对象通过JavaBean属性或者构造函数传递给需要的对象。通过JavaBean属性注射依赖关系的做法称为设值方法注入(Setter Injection);将依赖关系作为构造函数参数传入的做法称为构造器注入(Constructor Injection)。

1.2  技术描述:

        Class A 中用到了Class B的对象b,一般情况下,需要在A的代码中显式地new一个B的对象。

        采用依赖注入技术之后,A的代码只需要定义一个私有的B对象,不需要直接new来获得这个对象,而是通过相关的容器控制程序来讲B对象在外部new出来并注入到A类里的引用中。而具体获取的方法以及对象被获取是的状态由配置文件(如XML)来指定。

1.3  应用场景:

        在JavaEE企业应用开发中,IoC(控制反转)设计模式,是解耦组件之间复杂关系的利器,Spring IoC模块就是这个模式的一种实现。在Spring中,Spring IoC提供了一个基本的JavaBean这样的POJO对象赋予事务管理以及生命周期管理等功能。

        使用IoC容器,把资源获取的方向反转,让IoC容器主动管理这些依赖关系,将这些依赖关系注入到组件中,那么会让这些依赖关系的适配和管理更加灵活。在Spring的IoC设计中,setter注入和构造器注入是主要的注入方式,相对而言,使用Spring是setter注入是常见的注入方式,而且为了防止注入异常,Spring IoC容器还提供了对特定依赖的检查。

       另一方面,在应用管理依赖关系时,可以通过IoC容器将控制进行反转,在反转的实现中,如果能通过可读的文本来完成配置,并且还能通过工具对这些配置信息进行可视化的管理和浏览,那么肯定的是能够提高对组件关系的管理水平,并且如果耦合关系需要变动,并不需要重新修改和编译Java源代码,这符合在面向对象设计中的开闭准则,并且能够提高组件系统设计的灵活性,同时,如果结合OSGi的使用特性,还可以提高应用的动态部署能力。

        Spring IoC容器已经是一个产品实现。作为产品实现,它对多种应用场景的适配是通过Spring设计的IoC容器系列来实现的,比如在某个容器系列中可以看到各种带有不同容器特性的实现,可以读取不同配置信息的各种日期,从不同I/O源读取配置信息的各种容器设计,更加面向框架的容器应用上下文的容器设计等。这些丰富的容器设计,已经可以满足广大用户对IoC容器的各种使用需求,这时的Spring IoC容器已经不是原来简单的Interface21框架了,已经成为一个IoC容器的工业级实现。

2  相关的Java基础知识

        Java语言允许通过程序化的方式间接对Class的对象实例操作,Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息:如构造函数,属性和方法等。Java允许用户借由这个Class相关的元信息对象间接调用Class对象的功能,这就为使用程序化方式操作Class对象开辟了途径。

扫描二维码关注公众号,回复: 3172086 查看本文章

2.1  简单实例:

package com.hmz.reflect;

public class Car {

    private String brand;
    private String color;
    private int maxSpeed;

    public Car() {

    }

    public Car(String brand, String color, int maxSpeed) {
        super();
        this.brand = brand;
        this.color = color;
        this.maxSpeed = maxSpeed;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public int getMaxSpeed() {
        return maxSpeed;
    }

    public void setMaxSpeed(int maxSpeed) {
        this.maxSpeed = maxSpeed;
    }

    @Override
    public String toString() {
        return "Car [brand=" + brand + ", color=" + color + ", maxSpeed=" + maxSpeed + "]";
    }

}
View Code
package com.hmz.reflect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ReflectTest {

    public static void main(String[] args) {
        Car car1 = new Car("红旗", "黑色", 400);
        System.out.println(car1);

        try {
            Class clazz = Class.forName("com.hmz.reflect.Car");
            Car car2 = (Car) clazz.newInstance();

            Method setBrand = clazz.getMethod("setBrand", String.class);
            setBrand.invoke(car2, "黑旗");
            Method setColor = clazz.getMethod("setColor", String.class);
            setColor.invoke(car2, "红色");
            Method setMaxSpeed = clazz.getMethod("setMaxSpeed", int.class);
            setMaxSpeed.invoke(car2, 200);

            System.out.println(car2);
        } catch (InstantiationException | IllegalAccessException | ClassNotFoundException | NoSuchMethodException
                | SecurityException | IllegalArgumentException | InvocationTargetException e) {
            e.printStackTrace();
        }

    }

}
View Code

运行以上程序,在控制台上将打印出以下信息:

Car [brand=红旗, color=黑色, maxSpeed=400]
Car [brand=黑旗, color=红色, maxSpeed=200]

2.2  Java反射机制

        Class反射对象描述类语义结构,可以从Class对象中获取构造函数,成员变量,方法类等类元素的反射对象,并以编程的方式通过这些反射对象对目标类对象进行操作。这些反射对象类在java.reflect包中定义,下面是最主要的三个反射类:

        Constructor:类的构造函数反射类。通过Class#getConstructors()方法可以获得类的所有构造函数反射对象数组。Constructor的一个主要方法是newInstance(Object[] initargs),通过该方法可以创建一个对象类的实例,相当于new关键字。

        Method:类方法的反射类。通过Class#getMethod()方法可以获得类的所有方法反射类对象数组。

        Field:类的成员变量的反射类,通过Class#getDeclaredFields()方法可以获得类的成员变量反射对象数组。

3  IoC基础

3.1  思维理解

        控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法。在没有IoC的程序中,我们使用面向对象编程对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方。

3.2  具体实现

        IoC是Spring框架的核心内容,使用多种方式完美地实现了IoC,在实际开发中,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从IoC容器中取出需要的对象。

4  使用XML配置的方式实现IOC

        假设项目中需要完成对图书的数据访问服务,我们定义好IBookDAO接口和BookDAO实现类。

        创建maven项目:

        IBookDAO接口如下:

 1 package com.hmz.spring.ioc;
 2 
 3 /*
 4  *@Description:  图书数据访问接口
 5  *@Author:  hmz
 6  *@CreateDate:  2018/9/13 15:42
 7  *@Version:  1.0
 8  */
 9 public interface IBookDAO {
10 
11     /*
12      *@Description:  添加图书
13      *@author:  hmz
14      *@param:
15      *@return:
16      *@exception:
17      *@date:  2018/9/13 15:57
18      */
19     public String addBook(String bookname);
20 
21 }
IBookDAO

        BookDAO实现类如下:

 1 package com.hmz.spring.ioc;
 2 
 3 /*
 4  *@Description:  图书数据访问实现类
 5  *@Author:  hmz
 6  *@CreateDate:  2018/9/13 15:59
 7  *@Version:  1.0
 8  */
 9 public class BookDAO implements IBookDAO {
10 
11     /*
12      *@Description:  添加图书
13      *@author:  hmz
14      *@param:
15      *@return:
16      *@exception:
17      *@date:  2018/9/13 16:01
18      */
19     public String addBook(String bookname) {
20         return "Book " + bookname + "adds successfully!";
21     }
22 }
BookDAO

        Maven项目的pom.xml如下:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project xmlns="http://maven.apache.org/POM/4.0.0"
 3          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 5     <modelVersion>4.0.0</modelVersion>
 6 
 7     <groupId>com.hmz</groupId>
 8     <artifactId>IoC</artifactId>
 9     <version>1.0-SNAPSHOT</version>
10 
11     <properties>
12         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
13         <spring.version>4.3.0.RELEASE</spring.version>
14     </properties>
15 
16     <dependencies>
17         <dependency>
18             <groupId>junit</groupId>
19             <artifactId>junit</artifactId>
20             <scope>test</scope>
21             <version>4.10</version>
22         </dependency>
23         <dependency>
24             <groupId>org.springframework</groupId>
25             <artifactId>spring-context</artifactId>
26             <version>${spring.version}</version>
27         </dependency>
28         <dependency>
29             <groupId>org.aspectj</groupId>
30             <artifactId>aspectjweaver</artifactId>
31             <version>1.8.9</version>
32         </dependency>
33         <dependency>
34             <groupId>cglib</groupId>
35             <artifactId>cglib</artifactId>
36             <version>3.2.4</version>
37         </dependency>
38         <dependency>
39             <groupId>org.junit.jupiter</groupId>
40             <artifactId>junit-jupiter-api</artifactId>
41             <version>5.3.0-M1</version>
42         </dependency>
43         <dependency>
44             <groupId>org.testng</groupId>
45             <artifactId>testng</artifactId>
46             <version>RELEASE</version>
47         </dependency>
48     </dependencies>
49 
50 </project>
pom.xml

        业务类BookService如下:

 1 package com.hmz.spring.ioc;
 2 
 3 import org.springframework.context.ApplicationContext;
 4 import org.springframework.context.support.ClassPathXmlApplicationContext;
 5 
 6 /*
 7  *@Description:  图书业务类
 8  *@Author:  hmz
 9  *@CreateDate:  2018/9/13 16:13
10  *@Version:  1.0
11  */
12 public class BookService {
13 
14     private IBookDAO bookDAO;
15 
16     public BookService() {
17         //容器
18         ApplicationContext applicationContext = new ClassPathXmlApplicationContext("IoCBeans01.xml");
19         //从容器中获得id为bookDAO的bean
20         bookDAO = (IBookDAO) applicationContext.getBean("bookDAO");
21     }
22 
23     public void storeBook(String bookname) {
24         System.out.println("Book " + bookname + " is stored.");
25         String result = bookDAO.addBook(bookname);
26         System.out.println(result);
27     }
28 
29 }
BookService

        容器的配置文件IoCBeans01.xml如下:

1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
5 
6     <bean id="bookDAO" class="com.hmz.spring.ioc.BookDAO"/>
7 
8 </beans>
IoCBeans01.xml

        测试类如下:

 1 package com.hmz.spring.ioc;
 2 
 3 import org.testng.annotations.Test;
 4 
 5 /**
 6  * Created by 黄茂展 on 2018/9/13.
 7  */
 8 public class IoCTest {
 9 
10     @Test
11     public void testStoreBook() {
12         BookService bookService = new BookService();
13         bookService.storeBook("《Spring MVC权威指南 第一版》");
14     }
15 
16 }
IoCTest

        运行结果:

        Book 《Spring MVC权威指南 第一版》 is stored.
        Book 《Spring MVC权威指南 第一版》adds successfully!

4.1  通过属性赋值

        Address地址类:

 1 package com.hmz.spring.ioc;
 2 
 3 /**
 4  * Created by 黄茂展 on 2018/9/13.
 5  */
 6 public class Address {
 7 
 8     private String country;
 9     private String city;
10 
11     public Address() {
12 
13     }
14 
15     public String getCountry() {
16         return country;
17     }
18 
19     public void setCountry(String country) {
20         this.country = country;
21     }
22 
23     public String getCity() {
24         return city;
25     }
26 
27     public void setCity(String city) {
28         this.city = city;
29     }
30 
31     @Override
32     public String toString() {
33         return "Address{" +
34                 "country='" + country + '\'' +
35                 ", city='" + city + '\'' +
36                 '}';
37     }
38 
39 }
Address

        配置文件IoCBeans02.xml如下:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
 5 
 6     <bean name="zhuhai" class="com.hmz.spring.ioc.Address">
 7         <property name="country" value="中国"/>
 8         <property name="city" value="珠海"/>
 9     </bean>
10 
11 </beans>
IoCBeans02.xml

       测试代码:

 1 package com.hmz.spring.ioc;
 2 
 3 import org.springframework.context.ApplicationContext;
 4 import org.springframework.context.support.ClassPathXmlApplicationContext;
 5 import org.testng.annotations.Test;
 6 
 7 /**
 8  * Created by 黄茂展 on 2018/9/13.
 9  */
10 public class AddressTest {
11 
12     @Test
13     public void testAddress() {
14         ApplicationContext applicationContext = new ClassPathXmlApplicationContext("IoCBeans02.xml");
15         Address zhuhai = applicationContext.getBean("zhuhai", Address.class);
16         System.out.println(zhuhai);
17     }
18 
19 }
AddressTest

        运行结果:

        Address{country='中国', city='珠海'}

4.2  回调方法

4.2.1  初始化函数

        Student学生类:

 1 package com.hmz.spring.ioc;
 2 
 3 /**
 4  * Created by 黄茂展 on 2018/9/13.
 5  */
 6 public class Student {
 7 
 8     private String name;
 9     private int height;
10 
11     public Student() {
12 
13     }
14 
15     public String getName() {
16         return name;
17     }
18 
19     public void setName(String name) {
20         this.name = name;
21     }
22 
23     public int getHeight() {
24         return height;
25     }
26 
27     public void setHeight(int height) {
28         this.height = height;
29     }
30 
31     public void init() {
32         System.out.println("init......");
33     }
34 
35     public void destory() {
36         System.out.println("destory........");
37     }
38 
39     @Override
40     public String toString() {
41         return "Student{" +
42                 "name='" + name + '\'' +
43                 ", height=" + height +
44                 '}';
45     }
46 
47 }
Student

        配置文件IoCBeans03.xml如下:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
 5 
 6     <bean id="student" class="com.hmz.spring.ioc.Student" init-method="init" destroy-method="destory">
 7         <property name="name" value="张柏芝"/>
 8         <property name="height" value="195"/>
 9     </bean>
10 
11 </beans>
IoCBeans03.xml

       测试代码:

 1 package com.hmz.spring.ioc;
 2 
 3 import org.springframework.context.ApplicationContext;
 4 import org.springframework.context.support.ClassPathXmlApplicationContext;
 5 import org.testng.annotations.Test;
 6 
 7 /**
 8  * Created by 黄茂展 on 2018/9/13.
 9  */
10 public class StudentTest {
11 
12     @Test
13     public void testAddress() {
14         ApplicationContext applicationContext = new ClassPathXmlApplicationContext("IoCBeans03.xml");
15         Student student = applicationContext.getBean("student", Student.class);
16         System.out.println(student);
17     }
18 
19 }
StudentTest

        运行结果:

        init......
        Student{name='张柏芝', height=195}

5  使用Spring注解配置IoC

5.1  修改BookDAO

 1 package com.hmz.spring.ioc;
 2 
 3 import org.springframework.stereotype.Component;
 4 
 5 /*
 6  *@Description:  图书数据访问实现类
 7  *@Author:  hmz
 8  *@CreateDate:  2018/9/13 15:59
 9  *@Version:  1.0
10  */
11 @Component("bookDAO")
12 public class BookDAO implements IBookDAO {
13 
14     /*
15      *@Description:  添加图书
16      *@author:  hmz
17      *@param:
18      *@return:
19      *@exception:
20      *@date:  2018/9/13 16:01
21      */
22     public String addBook(String bookname) {
23         return "Book " + bookname + "adds successfully!";
24     }
25 }
BookDao

        在类上增加了一个注解Component,在类的开头使用了@Component注解,它可以被Spring容器识别,启动Spring后,会自动把它转成容器管理的Bean。

        除了@Component外,Spring还提供了3个功能和@Compont等效的注解:

        @Repository  用于对DAO实现类进行注解

        @Service  用于对业务层进行注解

        @Repository  用于对控制层进行注解

5.2  修改BookService

 1 package com.hmz.spring.ioc;
 2 
 3 import org.springframework.context.ApplicationContext;
 4 import org.springframework.context.support.ClassPathXmlApplicationContext;
 5 import org.springframework.stereotype.Component;
 6 
 7 /*
 8  *@Description:  图书业务类
 9  *@Author:  hmz
10  *@CreateDate:  2018/9/13 16:13
11  *@Version:  1.0
12  */
13 @Component
14 public class BookService {
15 
16     private IBookDAO bookDAO;
17 
18     public void storeBook(String bookname) {
19         ApplicationContext applicationContext = new ClassPathXmlApplicationContext("IoCBeans01.xml");
20         bookDAO = applicationContext.getBean("bookDAO", BookDAO.class);
21         System.out.println("Book " + bookname + " is stored.");
22         String result = bookDAO.addBook(bookname);
23         System.out.println(result);
24     }
25 
26 }
BookService

        将构造方法中的代码直接写在storeBook方法中,避免循环加载的问题。

5.3  修改IoC配置文件IoCBeans01.xml

1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4        xmlns:context="http://www.springframework.org/schema/context"
5        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">
6 
7     <context:component-scan base-package="com.hmz.spring.ioc"/>
8 
9 </beans>
IoCBeans01.xml

5.4  测试类

 1 package com.hmz.spring.ioc;
 2 
 3 import org.springframework.context.ApplicationContext;
 4 import org.springframework.context.support.ClassPathXmlApplicationContext;
 5 import org.testng.annotations.Test;
 6 
 7 /**
 8  * Created by 黄茂展 on 2018/9/13.
 9  */
10 public class IoCTest {
11 
12     @Test
13     public void testStoreBook() {
14         ApplicationContext applicationContext = new ClassPathXmlApplicationContext("IoCBeans01.xml");
15         BookService bookService = applicationContext.getBean(BookService.class);
16         bookService.storeBook("《Spring MVC权威指南 第一版》");
17     }
18 
19 }
IoCTest

5.5  运行结果:

        Book 《Spring MVC权威指南 第一版》 is stored.
        Book 《Spring MVC权威指南 第一版》adds successfully!

6  自动装配

6.1  修改BookDAO

 1 package com.hmz.spring.ioc;
 2 
 3 import org.springframework.stereotype.Repository;
 4 
 5 /*
 6  *@Description:  图书数据访问实现类
 7  *@Author:  hmz
 8  *@CreateDate:  2018/9/13 15:59
 9  *@Version:  1.0
10  */
11 @Repository
12 public class BookDAO implements IBookDAO {
13 
14     /*
15      *@Description:  添加图书
16      *@author:  hmz
17      *@param:
18      *@return:
19      *@exception:
20      *@date:  2018/9/13 16:01
21      */
22     public String addBook(String bookname) {
23         return "Book " + bookname + "adds successfully!";
24     }
25 }
BookDAO

6.2  修改BookService

 1 package com.hmz.spring.ioc;
 2 
 3 import org.springframework.beans.factory.annotation.Autowired;
 4 import org.springframework.context.ApplicationContext;
 5 import org.springframework.context.support.ClassPathXmlApplicationContext;
 6 import org.springframework.stereotype.Service;
 7 
 8 /*
 9  *@Description:  图书业务类
10  *@Author:  hmz
11  *@CreateDate:  2018/9/13 16:13
12  *@Version:  1.0
13  */
14 @Service
15 public class BookService {
16 
17     @Autowired
18     private IBookDAO bookDAO;
19 
20     public void storeBook(String bookname) {
21         ApplicationContext applicationContext = new ClassPathXmlApplicationContext("IoCBeans01.xml");
22         bookDAO = applicationContext.getBean("bookDAO", BookDAO.class);
23         System.out.println("Book " + bookname + " is stored.");
24         String result = bookDAO.addBook(bookname);
25         System.out.println(result);
26     }
27 
28 }
BookService

6.3  运行结果:

        Book 《Spring MVC权威指南 第一版》 is stored.
        Book 《Spring MVC权威指南 第一版》adds successfully!

6.4  装配注解

        主要有:@Autowired、@Qualifier、@Resource,它们的特点是:

        1、@Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入;

        2、@Autowired默认是按照类型装配注入的,如果想按照名称来转配注入,则需要结合@Qualifier一起使用;

        3、@Resource注解是又J2EE提供,而@Autowired是由spring提供,故减少系统对spring的依赖建议使用@Resource的方式;如果Maven项目是1.5的JRE则需换成更高版本的。

        4、@Resource和@Autowired都可以书写注解在字段或者该字段的setter方法之上

        5、@Autowired 可以对成员变量、方法以及构造函数进行注释,而 @Qualifier 的注解对象是成员变量、方法入参、构造函数入参。

        6、@Qualifier("XXX") 中的 XX是 Bean 的名称,所以 @Autowired 和 @Qualifier 结合使用时,自动注入的策略就从 byType 转变成 byName 了。

        7、@Autowired 注释进行自动注入时,Spring 容器中匹配的候选 Bean 数目必须有且仅有一个,通过属性required可以设置非必要。

        8、@Resource装配顺序
          8.1. 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
          8.2. 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
          8.3. 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
          8.4. 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;

7  总结

7.1

        @Compent 要求Spring容器管理,被Spring扫描,应用于不确定的功能(宽泛)
        @Repository 应用于数据访问层 DAO
        @Service 应用于业务层
        @Controller 应用于控制层
        注解要被Spring容器管理的类 -> 配置文件指定要扫描的包 ->初始化容器,获得bean

7.2

        @Autowired 自动装配,字段(成员变量)、方法、属性、构造, 不支持指定名称,配合@Qualifier
        @Resource 自动装配,指定名称,指定类型,不属于Spring javax
        @Qualifier 在自动装配时指定对象的名称,避免多个不唯一的实例

猜你喜欢

转载自www.cnblogs.com/HNewa/p/HMZ_Spring_IoC.html