Spring framework内容整理

来源:互联网 发布:艾媒咨询数据 编辑:程序博客网 时间:2024/05/21 14:26

1、Spring framework简介

Spring 是一个轻量级的控制反转(IoC)和面向切面编程(AOP)的容器框架。Spring具有非侵入性,通过IoC促进了松耦合。

2、IoC:控制反转(Inversion of control)解说

在传统程序开发过程中,如果在一个对象要使用另一个对象,就必须得到它(自己new一个,或者从JNDI中查询一个),使用完之后还要将对象销毁(比如数据库连接对象Connection等),这样对象始终会和其他的接口或类耦合起来。

而使用Spring,所有的类都会在Spring容器中登记,告诉Spring自己是什么,需要什么,然后Spring会在系统运行到适当的时候把要的东西主动给你,同时也把你交给其他需要你的对象。所有类的创建、销毁都由Spring来控制,控制对象生存周期的不再是引用它的对象,而是Spring。IoC是Spring的核心,控制权的转移即反转。

3、DI:依赖注入(Dependency Injection)介绍

IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点通过DI实现。

比如对象A需要操作数据库,以前我们是在A中自己编写代码来获得一个Connection对象,而使用Spring,只需告诉我们需要一个Connection,在系统运行时,Spring会在适当的时候制造一个Connection,注入到A中,这样就完成了对各个对象之间的关系控制。

A需要依赖Connection才能正常运行,而Connection由Spring注入到A中,即为依赖注入。

4、实例说明

HelloWorldImpl.java

package com.iholtek;import java.util.Date;public class HelloWorldImpl {   public String sayHello( String message ){   return new Date() + " hello," + message;   }}

HelloWorldTest.java

package com.iholtek;import org.apache.log4j.Logger;import org.apache.log4j.PropertyConfigurator;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class HelloWorldTest {  static final Logger log = Logger.getLogger( HelloWorldTest.class);    ApplicationContext appContext;  public void loadXML() {    // 載入XML    appContext = new ClassPathXmlApplicationContext( "springtest.xml" );  }  public void testHello() {   // 利用Application Context 的getBean建立Instance.    HelloWorldImpl helloworld =(HelloWorldImpl) appContext.getBean( "HelloWorld" );    String s = helloworld.sayHello( "Tom");    System.out.println( s );   }   public static void main( String [] args ){     HelloWorldTest test = new HelloWorldTest();     test.loadXML();     test.testHello();   }}

springtest.xml

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"><beans>  <bean id="HelloWorld" class="com.iholtek.HelloWorldImpl" >  </bean></beans>

    分析:在上面的例子中,实作一个类,并且在springtest.xml配置文件中定义,之后就可以通过在测试类中调用ClassPathXmlApplicationContext方法载入XML配置文件,找到并装载完成ApplicationContext对象的实例化工作,并调用ApplicationContextgetBean方法建立类实例。(与构造方法来构建相比,可以发现Spring能在不修改代码的情况下抽换实作的类)

    不过HelloWorldImpl类没有接口,不利于抽象,所以定义一个HelloWorld接口,由HelloWorldImpl实现。这样在配置文件中抽换不同的实作,在测试类中调用getBean后强制类型转换为接口类型,不需更改代码,这样的抽象化才有较大意义。

5、处理bean的依赖关系步骤:

(1)根据定义bean的配置文件创建并初始化BeanFactory实例(大部分Spring用户使用支持XML格式配置文件的BeanFactory或ApplicationContext实现)。

(2)每个bean的依赖将以属性、constructor参数、或静态工厂方法参数的形式出现。当这些bean被实际创建时,这些依赖也将提供给该bean。

(3)每个属性或constructor参数既可以是一个实际的值,也可以是对该容器中另一个bean的引用。

(4)每个指定的属性或constructor参数值必须能够被转换成属性或参数所需类型。默认情况下,Spring能够以String类型提供值转换成各种内置类型,如int、long、String、boolean等。

【Spring会在容器被创建时验证容器中每个bean的配置,包括验证bean所引用的属性是否指向一个有效的bean(即在容器中被定义)。但是,在bean被实际创建之前,bean的属性并不会被设置,伴随着bean被实际创建,作为该bean的依赖bean以及依赖bean的依赖bean(类推)也将被创建和分配。对于singleton类型和被设置为提前实例化的bean(如ApplicationContext中的singleton bean),bean实例将与容器同时创建。】

6、循环依赖

    一个类A,需要通过constructor注入类B,类B又需要通过constructor注入类A。如果配置的bean被互相注入的话,那么Spring IoC容器将在运行时检测出循环引用,并抛出异常。

    解决方法一,修改源码为setter注入;方法二,完全放弃使用constructor注入,只使用setter注入。

7、延迟初始化bean

    在默认情况下,ApplicationContext实现中的bean采用启动时提前实例化的singleton模式,在实际需要前创建bean会带来时间和内存开销,不过ApplicationContext被加载的时候可以尽早发现一些配置问题(也可以根据需要采用延迟实例化替代默认singleton模式)。

    延迟加载(延迟实例化,延迟初始化):在对象第一次被使用时实例化,而不是在启动时。

    在XML配置文件中,延迟初始化通过<bean/>元素中的lazy-init属性控制。

<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true">    <!-- various properties here... --></bean>  <bean name="not.lazy" class="com.foo.AnotherBean">    <!-- various properties here... --></bean>

    在容器层次中通过在<beans/>元素上使用'default-lazy-init'属性来控制延迟初始化也是可能的。如下面的配置:

<beans default-lazy-init="true">    <!-- no beans will be eagerly pre-instantiated... --></beans>

【 如果一个bean被设置为延迟初始化,而另一个非延迟初始化的singleton bean依赖于它,那么当ApplicationContext提前实例化singleton bean时,必须确保所有上述singleton 依赖的bean也被预先初始化,包括设置为延迟实例化的bean】

8、引用其他bean——ref标签

第一种:<ref bean=”someBean”/>

    最常见,通过ref的bean属性指定目标bean,通过该标签可以引用同一容器或父容器内的任何bean(无论是否在同一配置文件),ref内bean属性的值既可以是指定的bean的id、也可以是name。

第二种:<ref local=”someBean”/>

    通过ref的local属性指定目标bean,local属性值必须是目标bean的id值。如果在同一配置文件中没有找到引用的bean,XML解析器会抛出一个例外。如果目标bean在同一文件,则local方式是最好的选择(可以尽早发现错误)。

第三种:

<!-- in the parent context --><bean id="accountService" class="com.foo.SimpleAccountService">    <!-- insert dependencies as required as here --></bean>

<!-- in the child (descendant) context --><bean id="accountService"  <!--和父context中bean的id一样      class="org.springframework.aop.framework.ProxyFactoryBean">      <property name="target">          <ref parent="accountService"/>  <-- notice how we refer to the parent bean      </property>    <!-- insert other configuration and dependencies as required as here --></bean>   

    通过ref的parent属性来引用当前容器的父容器中的bean。Parent属性值既可以是目标bean的id、也可以是name。而且目标bean必须在当前容器的父容器中。主要用途是为了用某个与父容器中的bean同名的代理来包装父容器中的一个bean。

9、内部bean

    所谓内部bean(inner bean)是指在一个bean的<property/>或<constructor-arg/>元素中使用<bean/>元素定义的bean。内部bean中的singleton标记和id或name属性将被忽略。

<bean id="outer" class="...">  <property name="target">    <bean class="com.mycompany.Person"> <!-- this is the inner bean -->      <property name="name" value="Fiona Apple"/>。。。    </bean>  </property></bean>

【 内部bean总是匿名的,而且总是prototype模式的;将内部bean注入的包含该内部bean之外的bean是不可能的】

10、Collection类型的配置

通过<list/>,<set/>, <map/>, <props/>元素可以定义和设置于Java Collection类型对于的List,Set,Map,Properties的值。

<bean id="moreComplexObject" class="example.ComplexObject">  <property name="adminEmails">    <props>        <prop key="administrator">administrator@somecompany.org</prop>        <prop key="support">support@somecompany.org</prop>        <prop key="development">development@somecompany.org</prop>    </props>  </property>  <property name="someList">    <list>        <value>a list element followed by a reference</value>        <ref bean="myDataSource" />    </list>  </property>  <property name="someMap">    <map>        <entry>            <key>                <value>yup an entry</value>            </key>            <value>just some string</value>        </entry>    </map>  </property>  <property name="someSet">    <set>        <value>just some string</value>        <ref bean="myDataSource" />    </set>  </property></bean>

11、null标签

<null/>用于处理null值。因为Spring会吧属性的空参数当做空字符串处理,所以

<bean class="ExampleBean">  <property name="email"><value></value></property></bean>

等同于exampleBean.setEmail(“”)。所以用<null/>取代。

12、自动装配(autowire)

    Spring IoC容器可以自动装配相互合作bean之间的关联关系。当Spring在读取bean的配置文件时,读到某个bean的autowire属性时会去找class指定的类,然后根据类中的属性或方法在的参数的参数类型<autowire=bytype>(或名字autowire=byname)查找相应的类实例化,然后完成依赖注入(要用相应set方法)。Autowire的方便之处在于减少或消除属性或constructor参数的设置,简化配置文件。

Autowire一个有5种类型:

(1)no:不用自动装配,必须通过ref指定,这是默认设置。显示指定合作者是配置更灵活、清晰,相对于大的部署配置,推荐。

(2)byName:根据属性名自动装配,将检查容器并跟名字查找与属性完全一致的bean自动装配。

(3)byType:如果容器中存在一个与指定属性类型相同的bean,那么将与该属性自动装配。如果存在多个该类型的bean,会抛出异常,并指出不能用byType进行自动装配。如果没有找到相匹配的bean,则什么事都不发生,属性也不会被设置,如果不希望这样,可以通过设置dependency-check=”objects”让spring抛出异常。

(4)constructor:与byType类似,不过这里用于constructor参数。如果在容器中没有找到与constructor参数类型一致的bean,会抛出异常。

(5)autodetect:通过bean的自省机制(introspection)决定是使用constructor还是byType。如果发现默认的constructor,则使用byType。

13、定制bean特性

Spring提供了几个接口用来改变容器中bean的行为,包括InitializingBean,DisposableBean

(1)InitializingBean

实现org.springframework.beans.factory.InitializingBean接口允许容器在设置好bean的所有必要属性后,执行初始化事宜。该接口仅提供一个方法:

void afterPropertiesSet() throws Exception;

通常要避免使用InitializingBean,因为会将代码和Spring耦合。可以在bean定义中指定一个普通的初始化方法,即在XML配置文件通过指定init-method属性来完成。

(2)DisposableBean

实现org.springframework.beans.factory.DisposableBean接口允许在容器销毁该bean的时候获得一次callback。只提供一个方法: 

void destroy() throws Exception;

同样会耦合,可以定义一个普通析构方法,在配置文件通过destroy-method指定。

(3)BeanFactoryAware

对于实现了spring的org.springframework.beans.factory.BeanFactoryAware接口的类,只要实现setBeanFactory方法就可以以编码的方式获取创建它们的BeanFactory。当类被BeanFactory创建后,会拥有一个指向创建它的BeanFactory的引用,能够取得BeanFactory所管理的其他bean。不过会使代码与Spring耦合,而且违反Ioc原则(所需的其他bean应该作为属性提供),所以要避免使用。

public interface BeanFactoryAware {    void setBeanFactory(BeanFactory beanFactory) throws BeansException;}

(4)BeanNameAware

让Bean获取自己在BeanFactory配置中的名字(根据情况是id 或 name)

实现org.springframework.beans.factory.BeanNameAware接口的唯一方法setBeanName()

public class LogginBean implements BeanNameAware{private String beanName=null;public void setBeanName(String beanName){this.beanName=beanName;}}

该方法由Spring自动调用,在Spring自身完成Bean配置后,且在调用任何Bean生命周期回调(初始化或销毁)方法之前就调用该方法。所以,在程序中使用BeanFactory.getBean(StringbeanName)之前,Bean的名字就已经设定好了,不用但是没被初始化。

不过由于耦合问题,最好让Bean自己实现某个方法,如setName(),之后使用依赖注入给每个Bean注入一个名字。

原创粉丝点击