Spring(二): 基本用法



Spring容器

Spring容器是Spring的核心,是生成Bean的工厂。Spring有两个核心接口:BeanFactoryApplicationContext,后者是前者的子接口,二者都可以代表Spring容器。
ApplictionContext的实现类有:

  • FileSystemXmlApplicationContext: 基于文件系统的XML配置文件创建ApplicationContext
  • ClassPathXmlApplicationContext: 基于类加载路径下的xml配置文件创建ApplicationContext

所以一般可以这样创建Spring容器:

ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");
//或者
ApplicationContext context = new FileSystemXmlApplicationContext("file:绝对路径/spring.xml");

依赖注入的方式

依赖注入的方式主要有2种:

  • 通过Bean属性的setter方法注入(前提是有setter方法)
  • 通过Bean的构造方法注入(前提是有构造方法)
    <!-- 通过 setter 方法注入 -->
    <bean id="helloService" class="com.test.spring.service.impl.HelloServiceImpl">
        <property name="hello" ref="hello"/>  <!-- 若参数不是自定义类型,则使用value="**"的方式 -->
    </bean>
    <!-- 通过构造方法注入 -->
    <bean id="helloService" class="com.test.spring.service.impl.HelloServiceImpl">
        <!-- 下面着四种注入方式,只要一种就可以,最好通过name或者index来注入 -->
        <constructor-arg ref="hello" />  <!-- 当构造方法有多个参数时,这样会有问题 -->
        <constructor-arg name="hello" ref="hello" /> <!-- 通过参数名称注入 -->
        <constructor-arg index="0" ref="hello" />    <!-- 通过参数索引注入 -->
        <constructor-arg type="com.test.spring.dao.Hello" ref="hello" /> <!-- 通过参数类型注入 -->
        <!-- 通过参数类型注入的其他例子 -->
        <!--<constructor-arg type="java.lang.Double" value="100.00" />-->
    </bean>

Bean的scope属性

    <bean id="hello" class="com.test.spring.dao.impl.HelloImpl" scope="singleton"></bean>

bean元素的scope属性主要是用来指定如何创建bean对象的,系统已经实现的主要有五中类型,分别是:singletonprototyperequestsessionglobalSession。 其中requestsessionglobalSession只能在web环境中使用,当在非web环境中使用它们时,系统会抛出IllegalStateException异常。

  • singleton: 默认值,单例模式。在整个Spring IoC容器中只会创建一个对象,该对象创建以后是保存在singleton beans的缓存中的,每次都取得同一个bean对象。
  • prototype: 原型模式。每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新实例。
  • request: 对每次HTTP request都将产生一个新实例。
  • session: 对每个处于活跃状态的HttpSession都将创建一个对象。
  • globalSession: 一个全局的HttpSession下会拥有一个单独的实例,通常用于Portlet环境下。

注意:

  • 当一个singletonbean A依赖于一个prototypebean B时,A拥有的B就只会在A初始化时初始化一次,每次在A使用B的时候都是用的同一个对象B,这与B为prototype有点违背,不是我们想要的结果,其解决办法是,使bean A实现一个ApplicationContextAware接口,在每次A需要使用B的时候都从ApplicationContext里面取一个B对象,这个时候取的B对象每次都会是不一样的。
  • 当需要把一个http级别的scope的对象注入到其他bean中时,需要在声明的http级别的scope的对象中加入<aop:scoped-proxy/>如下面的userPreferences对象
    <bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
        <!-- <aop:scoped-proxy/> -->
        <aop:scoped-proxy proxy-target-class="false"/><!-- 为true则为开启对CGLIB的支持  -->
    </bean>  
    <bean id="userService" class="com.foo.SimpleUserService">  
        <property name="userPreferences" ref="userPreferences"/>  
    </bean>

这样做的原因就像在singletonBean中引用了prototypeBean一样,而使用<aop:scoped-proxy/>就会在实际调用的时候每次使用代理去代理userPreferences调用其对应的方法,代理访问的是对应的session中的对象,这样就可以实现每个session对应一个对象。而在代理的时候有两种方式,一种是基于JDK的interface的,一种是CGLIB形式的,如果要代理的类是面向对象的,就可以直接使用JDK的代理,否则就需要开启对CGLIB代理的支持,同时要引入CGLIB的jar包。

  • requestsessionglobalSession只在Web环境中, 并且在Web应用中增加了额外的配置(将HTTP请求对象绑定到为该请求提供服务的线程上)才会生效。具体做法是:
  • 若使用支持servlet2.4+的容器,则需要在web.xml中加入一个RequestContextListener监听器
  • 若使用只支持servlet2.4之前规范的容器,则该容器不支持Listener规范,故无法使用这种配置, 需要在web.xml中加入一个RequestContextFilter
  • 若Web应用直接使用Spring MVC作为MVC框架,则无需这些额外的配置,因为SpringDispatchServletDispatchPortlet 已经处理了所有和请求有关的状态处理。
<web-app>  
    ...  
    <!-- 支持servlet2.4及以上 RequestContextListener -->
    <listener>  
        <listener-class>  
            org.springframework.web.context.request.RequestContextListener  
        </listener-class>  
    </listener>
    <!-- 仅支持servlet2.4之前 RequestContextFilter -->
    <filter>
        <filter-name>requestContextFilter</filter-name>
        <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>requestContextFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    ...  
</web-app>