当前位置: 代码迷 >> Web前端 >> Spring的IOC(统制反转Inversion of Control)
  详细解决方案

Spring的IOC(统制反转Inversion of Control)

热度:294   发布时间:2012-11-06 14:07:00.0
Spring的IOC(控制反转Inversion of Control)

所谓控制反转就是应用本身不负责依赖对象的创建及维护,依赖对象的创建及维护是由外部容器负责的。这样控制权就由应用转移到了外部容器,控制权的转移就是所谓反转。

依赖注入(Dependency Injection)

所谓依赖注入就是指:在运行期,由外部容器动态地将依赖对象注入到组件中

spring的配置文件模版

<?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="userId"class="biz.impl.UserBizImpl"/>
</beans>

实例化spring容器

ApplicationContextapp=new ClassPathXmlApplicationContext("bean.xml");

从spring容器中得到bean

当spring容器启动后,因为spring容器可以管理bean对象的创建,销毁等生命周期,所以我们只需从容器直接获取Bean对象就行,而不用编写一句代码来创建bean对象。从容器获取bean对象的代码如下:

ApplicationContextapp=new ClassPathXmlApplicationContext("bean.xml");

UserBizuserBiz=app.getBean("userId",UserBiz.class);

Bean的作用域

.singleton

 在每个SpringIoC容器中一个bean定义只有一个对象实例。默认情况下会在容器启动时初始化bean,但我们可以指定Bean节点的lazy-init=“true”来延迟初始化bean,这时候,只有第一次获取bean会才初始化bean。如:

 <bean id="xxx"class="cn.itcast.OrderServiceBean" lazy-init="true"/>

如果想对所有bean都应用延迟初始化,可以在根节点beans设置default-lazy-init=“true“,如下:

<beans default-lazy-init="true“...>

.prototype

 每次从容器获取bean都是新的对象。<bean id=“” class=“”  scope=“prototype”/>

 每次从容器获取bean都是新的对象。

 根据经验,对有状态的bean应该使用prototype作用域,

 而对无状态的bean则应该使用singleton作用域

.request

 表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTPrequest内有效

.session

  表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTPsession内有效

.globalSession

 不过它仅仅在基于portlet的web应用中才有意义。Portlet规范定义了全Session

 的概念,它被所有构成某个portletweb应用的各种不同的portlet所共享。

以上3种均基于web的SpringApplicationContext情形下有效

延迟初始化bean

默认情况下会在容器启动时初始化bean,但我们可以指定Bean节点的lazy-init=“true”来延迟初始化bean,这时候,只有第一次获取bean时才初始化bean。如:

<bean id="xxx"class="cn.itcast.OrderServiceBean"                                                          lazy-init="true"/>

如果想对所有bean都应用延迟初始化,可以在根节点beans设置default-lazy-init=“true“,如下:

    <beans default-lazy-init="true“...>

Bean的初始化方法和销毁方法

<bean id="xxx"class=“biz.OrderServiceBean" init-method="init" destroy-method="close"/>
//init-method初始化方法   destroy-method销毁方法
  public void init() {
      System.out.println(“已经初始化了”);
  }
  public void close() {
      System.out.println(“被销毁了”);
  }
  //ApplicationContext不具有close()方法来调用销毁函数,需要使用ApplicationContext 才可以调用close()
  AbstractApplicationContext ctx = newClassPathXmlApplicationContext("beans.xml");
  PersonService person =(PersonService)ctx.getBean("personServiceBean");
  ctx.close();

注入依赖对象

public class UserBizImpl implements UserBiz {
   private UserDao userDao;   //需要注入的对象
}
方式一
    <bean id="userDaoId "class="cn.itcast.service.OrderDaoBean"/>
    <bean id="orderService"class="biz.impl.UserBizImpl">
          <propertyname="userDao " ref="userDaoId "/>
</bean>
//这里使用id定义的userDaoId可以被其他bean使用
方式二(使用内部bean,但该bean不能被其他bean引用)
    <bean id="orderService"class=" biz.impl.UserBizImpl">
       <property name="userDao ">
    <!--没有id(name)属性,所以不能被其它引用-->
              <bean class=“dao.impl.userDaoImpl”/>
       </property>
    </bean>
方法三  也可以通过构造函数注入和set方法注入
<bean id="userDaoId "class="cn.itcast.service.OrderDaoBean"/>
 <bean id="orderService"class="cn.itcast.service.OrderServiceBean">
       <constructor-arg index=“0”  ref=”userDaoId”/>//构造器注入
    (构造函数注入,则构造函数的为这个对象赋值,index表示参数的索引,可以省略的)
       <property name=“userDao”  ref=” userDaoId”/>//属性setter方法注入
     (使用set方法注入当然得为对象设置set方法)
 </bean>

集合类型的装配

<bean id="order"class="cn.itcast.service.OrderServiceBean">
   <property name="lists">
         <list>
       <value>pkbest</value>
        </list>
     </property>       
     <property name="sets">
        <set>
           <value>pkbest</value>
       </set>
     </property>       
    <property name="maps">
       <map>
           <entry key=“pkbest" value="28"/>
      </map>
    </property>        
    <property name="properties">
       <props>
       <propkey=“pk">best</prop>
      </props>
      </property>
</bean>

依赖注入--手工装配

第一种方法:同上面依赖注入对象的第三种是一样的

第二种方法:

在java代码中使用@Autowired或@Resource注解方式进行装配。但我们需要在xml配置文件中配置以下信息:

<beansxmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context-3.0.xsd">
          <context:annotation-config/>
    </beans>

这个配置隐式注册了多个对注释进行解析处理的处理器:AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,PersistenceAnnotationBeanPostProcessor,RequiredAnnotationBeanPostProcessor

注: @Resource注解在spring安装目录的lib\j2ee\common-annotations.jar

在java代码中使用@Autowired或@Resource注解方式进行装配,这两个注解的区别是:@Autowired 默认按类型装配,@Resource默认按名称装配,当找不到与名称匹配的bean才会按类型装配。

    @Autowired

    private PersonDao  personDao;//用于字段上

    @Autowired

    public void setOrderDao(OrderDao orderDao){//用于setter方法上

        this.orderDao = orderDao;

    }

@Autowired注解是按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false。如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。如下:

    @Autowired  @Qualifier("personDaoBean")

    private PersonDao  personDao;

@Resource注解和@Autowired一样,也可以标注在字段或setter方法上,但它默认按名称装配。名称可以通过@Resource的name属性指定,如果没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在setter方法上,即默认取bean名称寻找依赖对象。

    @Resource(name=“personDaoBean”)

private PersonDao personDao;//用于字段上

注意:如果没有指定name属性,并且按照默认的名称找不到依赖对象时, @Resource注解会回退到按类型装配。但一旦指定了name属性,就只能按名称装配了。

通过在classpath自动扫描

前面的例子我们都是使用XML的bean定义来配置组件。在一个稍大的项目中,通常会有上百个组件,如果这些这组件采用xml的bean定义来配置,显然会增加配置文件的体积,查找及维护起来也不太方便。spring为我们引入了组件自动扫描机制,他可以在类路径底下寻找标注了@Component、@Service、@Controller、@Repository注解的类,并把这些类纳入进spring容器中管理。它的作用和在xml文件中使用bean节点配置组件是一样的。要使用自动扫描机制,我们需要打开以下配置信息:

<beansxmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.0.xsd">
 <context:component-scanbase-package="cn.itcast"/>
  // base-package指定需要扫描的路径
</beans>

其中base-package为需要扫描的包(含子包)。

@Service用于标注业务层组件、 @Controller用于标注控制层组件(如struts中的action)、@Repository用于标注数据访问组件,即DAO组件。而@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。

@Scope用于指定scope作用域的(用在类上)  @PostConstruct用于指定初始化方法(用在方法上)

@PreDestory用于指定销毁方法(用在方法上)

使用自动扫描以后在beam.xml中的beans中不需要再定义bean了,要使用时路径下的bean对象时只需要通过类名首字母小写获取即可,比如要获取UserBizImpl的对象

UserBiz biz = app.getBean("userBizImpl", UserBiz.class);

如果依赖的注入对象的接口有多个实现类的情况下需要,在@Resource中的name的值也类名首字母小写,如果只有一个实现类的话可以不需要指明@Resource的name属性

@Resource(name=“userDaoImpl”)

  相关解决方案