• Spring 之 IOC


    本文主要参考:http://www.cnblogs.com/wing011203/archive/2013/05/15/3078849.html

    推荐事务文章:https://my.oschina.net/u/2362627/blog/868490

    IoC的全称是Inversion of Control,中文称为控制反转, Martin Flower由根据它创造了一个新词:Dependency Injection,中文称为依赖注入。这两个词讲的是一回事儿。

    控制反转模式(也称作依赖性介入)的基本概念是:不创建对象,但是描述创建它们的方式。在代码中不直接与对象和服务连接,但在配置文件中描述哪一个组件需要哪一项服务。

    容器 (在 Spring 框架中是 IOC 容器) 负责将这些联系在一起。IoC的实质是如何管理对象,传统意义上我们使用new方式来创建对象,但在企业应用开发的过程中,大量的

    对象创建都在程序中维护很容易造成资源浪费,并且不利于程序的扩展。

    实现IoC通常有三种方式:

    1)利用接口或者继承,一般以接口较多。这种实现方式和我们平时提到的lazy load有异曲同工之妙。

    2)构造函数注入。

    3)属性注入。

    IoC简单示例

      我们先来定义一个简单的接口和实现:

    复制代码
     1 public interface UserDao {
     2     void save();
     3 }
     4 
     5 public class UserDaoImpl implements UserDao
     6 {
     7 
     8     public void save() {
     9         System.out.println("save() is called.");
    10     }
    11 
    12 }
    复制代码

      然后是在classpath下创建一个beans.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"
            xmlns:context="http://www.springframework.org/schema/context"
            xmlns:tx="http://www.springframework.org/schema/tx"
            xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
                    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
    
        <bean id="userDaoImpl" class = "sample.spring.ioc.UserDaoImpl"/>
    </beans>    
    复制代码

      接下来是测试代码:

    复制代码
    1 private static void test1()
    2 {
    3     ApplicationContext ctx = new ClassPathXmlApplicationContext("sample/spring/ioc/beans.xml");
    4     UserDao userDao = (UserDao)ctx.getBean("userDaoImpl");
    5     userDao.save();
    6 }
    复制代码

      输出结果如下:

    save() is called.

      我们还可以通过工厂方式来创建对象。

      通过静态工厂创建Bean

      添加一个类,如下:

    复制代码
    1 public class UserDaoFactory {
    2 
    3     public static UserDao getUserDao()
    4     {
    5         return new UserDaoImpl();
    6     }
    7 }
    复制代码

      在beans.xml中,添加如下内容:

    1 <bean id="userDaoImpl2" class = "sample.spring.ioc.UserDaoFactory" factory-method = "getUserDao"/>

      测试代码和执行结果和上面类似,不再赘述。

      通过实例工厂创建Bean

      添加如下类:

    复制代码
    1 public class UserDaoFactory2 
    2 {
    3     public UserDao getUserDao()
    4     {
    5         return new UserDaoImpl();
    6     }
    7 }
    复制代码

      这个类和UserDaoFactory唯一的区别是这里的getUserDao是实例方法,而不是静态方法。

      在beans.xml中追加如下内容:

    1 <bean id="factory" class="sample.spring.ioc.UserDaoFactory2"/>
    2 <bean id="userDaoImpl3" factory-bean="factory" factory-method="getUserDao"/>

      测试方法和结果同上。

      对象的生命周期

      我们可以通过设置bean节点的scope属性来控制对象的声明周期,它包含两个可选值:

      1)singleton,表明系统中对于同一个对象,只保留一个实例。

      2)prototype,表明系统中每次获取bean时,都新建一个对象。

      我们修改beans.xml文件:

    1 <bean id="userDaoImpl" class = "sample.spring.ioc.UserDaoImpl" scope="singleton"/>
    2 <bean id="userDaoImpl2" class = "sample.spring.ioc.UserDaoImpl" scope="prototype"/>

      这两个bean指向同一个类型,但是scope的设置不同。

      下面是测试方法:

    复制代码
     1 private static void scopeTest()
     2 {
     3     ApplicationContext ctx = new ClassPathXmlApplicationContext("sample/spring/ioc/scope.xml");
     4     System.out.println("=====Singleton test=====");
     5     UserDao userDao1A = (UserDao)ctx.getBean("userDaoImpl");
     6     UserDao userDao1B = (UserDao)ctx.getBean("userDaoImpl");
     7     System.out.println("userDao1A == userDao1B:" + (userDao1A==userDao1B));
     8     System.out.println("=====Prototype test=====");
     9     UserDao userDao2A = (UserDao)ctx.getBean("userDaoImpl2");
    10     UserDao userDao2B = (UserDao)ctx.getBean("userDaoImpl2");
    11     System.out.println("userDao2A == userDao2B:" + (userDao2A==userDao2B));
    12 }
    复制代码

      执行结果如下:

    =====Singleton test=====
    userDao1A == userDao1B:true
    =====Prototype test=====
    userDao2A == userDao2B:false

      如何设置对象属性

      上面的示例中,我们的对象中没有包含属性,对于业务对象来说,这一般是不现实。实际中的对象或多或少都会有一些属性。

      Spring支持两种方式对属性赋值:set方式和构造函数。

      下面我们会分别描述两种方式,但首先我们需要展示业务对象:

    public class UserServiceBean 
     2 {
     3     private int userID;
     4     private String userName;
     5     private UserDao userDao;
     6     private List<String> hobbies;
     7     private Map<String, Integer> scores;
     8     
     9     public UserServiceBean(int userID, String userName, UserDao userDao, List hobbies,Map scores)
    10     {
    11         this.userID = userID;
    12         this.userName = userName;
    13         this.userDao = userDao;
    14         this.hobbies = hobbies;
    15         this.scores = scores;
    16     }
    17     
    18     public UserServiceBean(){}
    19     
    20     public void setUserID(int userID) {
    21         this.userID = userID;
    22     }
    23     public int getUserID() {
    24         return userID;
    25     }
    26     public void setUserName(String userName) {
    27         this.userName = userName;
    28     }
    29     public String getUserName() {
    30         return userName;
    31     }
    32     public void setUserDao(UserDao userDao) {
    33         this.userDao = userDao;
    34     }
    35     public UserDao getUserDao() {
    36         return userDao;
    37     }
    38     public void setHobbies(List<String> hobbies) {
    39         this.hobbies = hobbies;
    40     }
    41     public List<String> getHobbies() {
    42         return hobbies;
    43     }
    44     public void setScores(Map<String, Integer> scores) {
    45         this.scores = scores;
    46     }
    47     public Map<String, Integer> getScores() {
    48         return scores;
    49     }
    50 }

      这是一个典型的学生信息,包括学号、姓名、爱好和成绩。

      通过Set方式为对象属性赋值

      我们在beans.xml中追加如内容:

    复制代码
     1 <bean id="userService" class="sample.spring.ioc.UserServiceBean">
     2     <property name="userID" value="1"/>
     3     <property name="userName" value="张三"/>
     4     <property name="userDao" ref="userDaoImpl"/>
     5     <property name="hobbies">
     6         <list>
     7             <value>羽毛球</value>
     8             <value>看电影</value>
     9             <value>弹吉他</value>
    10         </list>
    11     </property>
    12     <property name="scores">
    13         <map>
    14             <entry key="数据结构" value="90"/>
    15             <entry key="编译原理" value="85"/>
    16             <entry key="离散数学" value="82"/>
    17         </map>
    18     </property>
    19 </bean>
    复制代码

      上面是典型的为属性赋值的示例,其中属性不仅包括简单属性(整数、字符串),也包含了复杂属性(List、Map),还有其他的bean。

      下面是测试代码:

    复制代码
     1 private static void propertyTest1()
     2 {
     3     ApplicationContext ctx = new ClassPathXmlApplicationContext("sample/spring/ioc/beans.xml");
     4     UserServiceBean userService = (UserServiceBean)ctx.getBean("userService");
     5     printUserService(userService);
     6 }
     7 
     8 private static void printUserService(UserServiceBean userService)
     9 {
    10     System.out.println("编号:" + userService.getUserID());
    11     System.out.println("姓名:" + userService.getUserName());
    12     System.out.println("爱好:");
    13     for(String hobby:userService.getHobbies())
    14     {
    15         System.out.println(hobby);
    16     }
    17     System.out.println("学习成绩:");
    18     for(Entry<String,Integer> entry:userService.getScores().entrySet())
    19     {
    20         System.out.println(entry.getKey() + "	" + entry.getValue());
    21     }
    22     userService.getUserDao().save();
    23 }
    复制代码

      输出结果如下:

    复制代码
    编号:1
    姓名:张三
    爱好:
    羽毛球
    看电影
    弹吉他
    学习成绩:
    数据结构    90
    编译原理    85
    离散数学    82
    save() is called.
    复制代码

      通过构造函数为对象属性赋值

      我们也可以通过构造函数来为对象赋值,在上面定义UserServiceBean时,我们已经添加了一个构造函数。下面来看beans.xml中的配置:

    复制代码
     1 <bean id="userService2" class="sample.spring.ioc.UserServiceBean">
     2     <constructor-arg index="0" value="1"/>
     3     <constructor-arg index="1" value="张三"/>
     4     <constructor-arg index="2" ref="userDaoImpl"/>
     5     <constructor-arg index="3">
     6         <list>
     7             <value>羽毛球</value>
     8             <value>看电影</value>
     9             <value>弹吉他</value>
    10         </list>
    11     </constructor-arg>
    12     <constructor-arg index="4">
    13         <map>
    14             <entry key="数据结构" value="90"/>
    15             <entry key="编译原理" value="85"/>
    16             <entry key="离散数学" value="82"/>
    17         </map>
    18     </constructor-arg>
    19 </bean>
    复制代码

      测试代码和输出结果同上。

      需要注意:我们定义的业务对象应该保留默认的构造函数。(如果新增构造方法,需要手动添加默认的构造方法)

      使用Annotation来定位Bean

      在Spring中,除了在xml配置文件中定义对象,我们还可以使用Annotation来定位,这位我们提供了很大的方便。

      这里我们使用的Annotation主要包括:@Resource/@Autowried/@Qualifier。

      来看下面的示例:

          

    1 public class UserServiceBean2 
     2 {
     3     private String userID;
     4     private String userName;
     5     @Resource(name="userDaoImpl")
     6     private UserDao userDao1;
     7     private UserDao userDao2;
     8     
     9     @Autowired(required=false)
    10     @Qualifier("userDaoImpl")
    11     private UserDao userDao3;
    12     
    13     @Autowired(required=false)
    14     @Qualifier("userDaoImpl3")
    15     private UserDao userDao4;
    16     
    17     public void setUserID(String userID) {
    18         this.userID = userID;
    19     }
    20     public String getUserID() {
    21         return userID;
    22     }
    23     public void setUserName(String userName) {
    24         this.userName = userName;
    25     }
    26     public String getUserName() {
    27         return userName;
    28     }
    29     @Resource
    30     public void setUserDao2(UserDao userDao2) {
    31         this.userDao2 = userDao2;
    32     }
    33     public UserDao getUserDao2() {
    34         return userDao2;
    35     }
    36     
    37     public void test()
    38     {
    39         userDao1.save();
    40         userDao2.save();
    41         System.out.println(userDao3.getClass().getName());
    42         userDao3.save();
    43     }
    44 }

    测试方法:

    复制代码
    1 private static void annotationTest()
    2 {
    3     ApplicationContext ctx = new ClassPathXmlApplicationContext("sample/spring/ioc/annotation.xml");
    4     UserServiceBean2 userService = (UserServiceBean2)ctx.getBean("userService");
    5     
    6     userService.test();
    7 }
    复制代码

      输出结果如下:

    save() is called.
    save() is called.
    sample.spring.ioc.UserDaoImpl
    save() is called.

      我们来对上面示例中出现的Annotation来进行说明。

    1 @Resource(name="userDaoImpl")
    2 private UserDao userDao1;

      这是定义在字段上的Annotation,是指userDao1使用xml配置文件中定义的名为“userDaoImpl”的bean进行填充。

    1 @Autowired(required=false)
    2 @Qualifier("userDaoImpl")
    3 private UserDao userDao3;

      这是第二种类型的Annotation,它把Autowired和Qualifier组合在一起使用,Qualifier来设置bean的名称,Autowired来设置bean找不到时的行为,required为true时会抛出异常,required为false时会返回null。

    1 @Resource
    2 public void setUserDao2(UserDao userDao2) {
    3      this.userDao2 = userDao2;
    4 }

      这是作用在setter上的Annotation,@Resource 可以不写明name参数,这时Spring会首先按照名字然后按照数据类型的方式去定位bean。

      自动加载对象定义

      对于大型系统来说,我们可能会创建大量的类,如果这些类的声明都需要写在xml文件里的话,会产生额外大量的工作。

      Spring提供了一种简单的机制让我们的对象可以自动注册。

      我们可以在beans.xml中添加如下内容:

    1 <context:component-scan base-package="sample.spring.ioc"/>

      然后我们可以在sample.spring.ioc包下的对象,添加@Component/@Service/@Controller/@repository,这样Spring会自动将带有这些Annotation的类进行注册。

      下面是一个示例:

          

    1 @Service("userService")
     2 public class UserServiceBean3 
     3 {
     4     private String userID;
     5     private String userName;
     6     @Resource(name="userDaoImpl")
     7     private UserDao userDao1;
     8     
     9     @Autowired(required=true)
    10     @Qualifier("userDaoImpl")
    11     private UserDao userDao3;
    12     
    13     public void setUserID(String userID) {
    14         this.userID = userID;
    15     }
    16     public String getUserID() {
    17         return userID;
    18     }
    19     public void setUserName(String userName) {
    20         this.userName = userName;
    21     }
    22     public String getUserName() {
    23         return userName;
    24     }
    25 //    @Resource
    26 //    public void setUserDao2(UserDao userDao2) {
    27 //        this.userDao2 = userDao2;
    28 //    }
    29 //    public UserDao getUserDao2() {
    30 //        return userDao2;
    31 //    }
    32     
    33     public void test()
    34     {
    35         userDao1.save();
    36 //        userDao2.save();
    37         System.out.println(userDao3.getClass().getName());
    38         userDao3.save();
    39     }
    40 }

    这个类和上面定义的UserServiceBean2非常相似,需要注意在类前面添加的Annotation信息。

      我们不需要在xml文件中手动定义这个bean,Spring会进行自动注册,注册的bean名称是userService。

  • 相关阅读:
    C++中pair的用法
    DFS例题:力扣200:岛屿数量
    DFS例题:力扣695:岛屿的最大面积
    DFS深度优先遍历
    java AQS源码分析
    spring实现事务原理
    java常见并发面试题目+示例代码
    java并发锁
    ThreadPoolExecutor
    线程通信
  • 原文地址:https://www.cnblogs.com/Jtianlin/p/4456200.html
Copyright © 2020-2023  润新知