Spring IoC学习笔记

前几天抽空重温了Spring参考手册,IoC部分做了笔记,AOP部分因为结合了AspectJ和XML Schemed Support,暂时没做笔记,除此之外,还看了看Spring的事务管理以及对Hibernate的底层支持.现在整理出我的Spring IoC学习笔记.

 

1 学习Spring的切入点----反向控制,学习反向控制,Spring IoC容器的包开始.

org.springframework.beansorg.springframeword.context包是Spring IoC容器的基础.BeanFactory提供了配置框架及基本功能,ApplicationContext则增加了更多支持企业核心内容的功能.

2 了解bean

简单地讲,bean就是由Spring容器初始化、装配及被管理地对象,除此之外,bean就没有特别之处了.

3 了解BeanFactory

Spring,BeanFactoryIoC容器的核心接口.它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖.

实际运行机制是这样的:

ApplicationContext context=new ClassPathXmlApplicationContext(new String[]{“applicationContext.xml”,”applicationContext-part2.xml”});

BeanFactory factory=(BeanFactory)context;

 

我们需要BeanFactory实例,BeanFactory实例中最主要的一组方法就是getBean的重载方法.

 

4 <bean> 标签

Class:采用标准java命名机制.

Alias:别名.

构造器参数:<constructor-arg index=”0” value=”40” type=”java.lang.Integer”>

4.1 bean的实例化:

两种方法:

第一种是直接调用构造函数.

<bean name=”example” class=”src.Example”/>

第二种是静态工厂的工厂方法,其中,工厂方法必须是静态方法.

<bean name=”example” class=”src.ExampleFactory” factory-method=”createExample”/>

第三种是动态(实例)工厂的工厂方法

<bean name=”myFactory”/>

<bean name=”example” factory-bean=”myFactory” factory-method=”createExample”/>

这种方法的好处是工厂也被IoC.

4.2 反向注入的类型

1 setter()方法 2 构造器

使用构造器注入可能会导致循环依赖,A在构造器中注入B,B在构造器中注入A,此时,会抛出BeanCurrentlyInCreationException异常.对于此问题,一个可能的解决方法就是修改源代码,将构造器注入改为setter注入.另一个解决方法就是完全放弃使用构造器注入,只使用setter注入.

4.3 注入的时机

注入默认采用singleton模式,在容器创建时便创建,这样的好处是可以及时发现配置问题,坏处是增加了不必要的内存和时间开销.

4.4 ref的三种用法

<ref bean=”beanname”>

<ref local=”beanname”>

<ref parent=”beanname”>不常见

其中beanname可以是id也可以是name,为了优化,还可以把ref改成idref,这时会在创建容器时对beanname进行检验.

4.5 内部类的配置

4.6 集合的配置 <list/><set/><map/><props/>

4.7 父子bean的集合属性合并(merge=true)

4.8 强类型集合(tiger适用)

SpringIoC支持tiger的强类型集合

4.9 null””的注入(<null/><value><value/>)

4.10 valueref的简写形式,包括在map

4.11 组合属性赋值

需要注意,组合中的任一级在初始化后不能为空,否则抛NullPointerException

4.12 depends-on

显示指定多个依赖,被指定的bean需要在该bean之前初始化,但并不一定该类与被指定的bean存在关联.

4.13 延迟初始化 <bean laze-init=”true”/> or <beans default-laze-init=”true”>…<beans/>

如果一个非延迟初始化bean依赖于一个延迟初始化bean,那么初始化前者的时候,会顺带初始化后者,也就是说,laze-init=”true”会实效.

4.14 自动装配 五种形式no byName byType constructor autodetect

优点:1 省事

     2 同步更新

缺点:确定你用的对,否则可能抛异常.

4.15

形式:方法注入

手段:<lookup-method name=”methodName” bean=”返回类型的bean定义”>

目的:一个singleton类的某个方法每次调用需要创建不同的非singleton类实例.

注意:singleton类实例必须是prototype.

 

5 bean的作用域

五种:singleton,prototype,request,session,global session,其中后三种是针对web.

如果采用后三种,只需要在web.xml中加入:

<listener>

  <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>

</listener>

 

如果需要将后三种作用域的bean注入到其它bean,还需要加入

<aop:scoped-proxy/>

之所以加入这条语句的原因,就是将这个bean配成动态代理模式,这样可以保证每次注入进依赖类中的对象不总是那一个,配上这个代理后,被注入类就就智能了了.

 

6 定制bean特性

6.1 Lifecycle接口

包括两个接口,InitializingBeanDisposableBean,实现这两个接口可的bean在初始化和析构时会调用前者的afterPropertiesSet()方法,以及后者的destroy()方法.

前者称为初始化回调,Spring建议我们不要采用实现接口InitializingBean来实现初始化回调,因为这样会带来与业务Bean的耦合,最好的方法是采用 beaninit-method属性.

后者称为析构回调,同样, Spring建议我们不要采用实现接口DisposableBean来实现析构回调,

因为这样会带来与业务Bean的耦合,最好的方法是采用 beandestroy-method属性.

 

default-init-method defalult-destroy-method的用法.

 

7 web环境优雅的关闭Spring IoC

JVM里注册一个关闭钩子

AbstractApplicationContext ctx=new ClassPathXmlApplicationContext(new String[](“beans.xml”));

Ctx.registerShutdownHook();

 

8 BeanFactoryAware接口

BeanFactory实例管理的bean是可怜的,因为他们默认不能感知BeanFactory实例的存在,但是,有了BeanFactoryAware接口,让这一切都变得可能.

实际上BeanFactoryAware接口里只有一个方法:setBeanFactory(),但就是这个方法,使bean可以获得BeanFactory的实例,进而获取其它的bean实例,当然这会影响耦合性.

现在来用BeanFactoryAware接口,其实还有更简单的方法,就是用org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean,该类实现了BeanFactoryAware接口,在使用时,bean包含一个ObjectFactory(怀疑这是前者的一个窄接口),然后把前者注入到这个接口对象里就可以,在注入的时候还需指定目标bean(就是你想通过beanFactory实例得到的bean),继而在程序中调用getObject()就可以得到你想要的bean.来看程序:

NewsFeed:

package org.test.objectFactoryCreatingFactoryBean;

 

public class NewsFeed {

  private String news;

 

public String getNews() {

       return this.toString()+news;

}

 

public void setNews(String news) {

       this.news = news;

}

}

 

NewsFeedManager:

package org.test.objectFactoryCreatingFactoryBean;

 

import org.springframework.beans.factory.ObjectFactory;

 

public class NewsFeedManager {

private ObjectFactory factory;

 

public void setFactory(ObjectFactory factory) {

       this.factory = factory;

}

 

public void printNews(){

       NewsFeed news=(NewsFeed)factory.getObject();

       System.out.println(news.getNews());

}

}

 

NewsFeedTest:

package org.test.objectFactoryCreatingFactoryBean;

 

import org.springframework.beans.factory.BeanFactory;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

 

public class NewsFeedTest {

  public static void main(String[] args) throws Exception{

         ApplicationContext ctx=new ClassPathXmlApplicationContext("org//test//objectFactoryCreatingFactoryBean//beans.xml");

         BeanFactory factory=(BeanFactory)ctx;

         NewsFeedManager manager=(NewsFeedManager)factory.getBean("newsFeedManager");

         manager.printNews();

         manager.printNews();

  }

}

 

Beans.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="newsFeedManager" class="org.test.objectFactoryCreatingFactoryBean.NewsFeedManager">

 <property name="factory">

   <bean class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">

     <property name="targetBeanName">

     <idref local="newsFeed"/>

     </property>

   </bean>

 </property>

 </bean>

 

 <bean id="newsFeed" class="org.test.objectFactoryCreatingFactoryBean.NewsFeed" singleton="false">

 <property name="news">

 <value>"that's too fit"</value>

 </property>

 </bean>

</beans>

 

运行后得到:

org.test.objectFactoryCreatingFactoryBean.NewsFeed@e5855a"that's too fit"

org.test.objectFactoryCreatingFactoryBean.NewsFeed@95fd19"that's too fit"

 

为了测试prototype,我又做了另一个测试,在这个例子中,我并不打算通过beanFactoryAware来得到其它的bean,我就想通过注入来得到其它的bean,但我还想每次得到的实例不同,就像上面这个练习的结果一样,于是我这样做:

 

NewsFeed:

package org.test.prototype;

 

public class NewsFeed {

  private String news;

 

public String getNews() {

       return this.toString()+news;

}

 

public void setNews(String news) {

       this.news = news;

}

}

 

 

NewsFeedManager:

package org.test.prototype;

 

import org.springframework.beans.factory.ObjectFactory;

 

public class NewsFeedManager {

private NewsFeed newsFeed;

 

 

 

public void printNewsFeed(){

      

       System.out.println(newsFeed.getNews());

}

 

 

 

public void setNewsFeed(NewsFeed news) {

       this.newsFeed = news;

}

}

 

 

NewsFeedTest:

package org.test.prototype;

 

import org.springframework.beans.factory.BeanFactory;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

 

public class NewsFeedTest {

  public static void main(String[] args) throws Exception{

         ApplicationContext ctx=new ClassPathXmlApplicationContext("org//test//prototype//beans.xml");

         BeanFactory factory=(BeanFactory)ctx;

         NewsFeedManager manager=(NewsFeedManager)factory.getBean("newsFeedManager");

         manager.printNewsFeed();

         manager.printNewsFeed();

  }

}

 

Beans.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="newsFeedManager" class="org.test.prototype.NewsFeedManager">

 <property name="newsFeed">

   <ref bean="newsFeed"/>

 </property>

 </bean>

 

 <bean id="newsFeed" class="org.test.prototype.NewsFeed" singleton="false">

 <property name="news">

 <value>"that's too fit"</value>

 </property>

 </bean>

</beans>

结果很失望:

org.test.prototype.NewsFeed@60420f"that's too fit"

org.test.prototype.NewsFeed@60420f"that's too fit"

 

9 bean的继承

目的:一个子bean定义可以从父bean继承构造器参数值、属性值、以及覆盖父bean的方法,并且可以有选择地增加新地值.如果指定了init-method,destroy-method/或静态factory-method,它们就会覆盖父bean相应的设置.

剩余的设置将总是从子bean定义处得到:依赖、自动装配模式、依赖检查、singleton、作用域和延迟初始化.

 

10 Spring IoC的扩展点

10.1 BeanPostProcessor bean后置处理器

你想在beaninit()之前或之后再做些事情麽,请实现这个接口.

然后只需要在配置文件里加上<bean class=”xxxx.MyBeanPostProcessor”/>

 

10.2 BeanFactoryPostProcessor bean工厂后置处理器

BeanPostProcessor相似,只是它的发生时间是在所有bean被初始化前

两个重要的实现:

PropertyPlaceholderConfigurer

PropertyOverrideConfigurer

SS里是怎么配的.

<!-- 属性文件读入 bean工厂后置处理器实现类-->

       <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

              <property name="locations">

                     <list>

                            <value>classpath*:/config/jdbc.properties</value>

                            <value>classpath*:/config/hibernate.properties</value>

                            <value>classpath*:/config/mail.properties</value>

                     </list>

              </property>

       </bean>

然后就可以放心大胆的用配置文件里的key-value值了.

 

10.3 factoryBean接口

三个方法:

Object getObject()

Boolean isSingleton();

Class getObjectType()

 

11 messageSource接口

该接口实现了i18n,Spring提供了两个接口实现

org.springframework.context.support. MessageSourceResourceBundle

org.springframework.context.support. StaticMessageSource

其中前者较常用,它继承自java标准类库的ResourceBundle.

所以只需要配置:

<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">

 <property name="basenames">

   <list>

     <value>org.test.messageSource.format</value>

     <value>org.test.messageSource.exception</value>

   </list>

 </property>

 </bean>

注意,id必须是messageSource,这样在容器加载的时候,由于ApplicationContext实现了MessageSource接口,所以该bean被注入到ApplicationContext实例中:

ApplicationContext ctx=new ClassPathXmlApplicationContext("org//test//messageSource//beans.xml");

                   System.out.println(ctx.getMessage("message", null, Locale.UK));

                   System.out.println(ctx.getMessage("argument.request", new String[]{"name"}, Locale.UK));

 

Format_en.properties:

message=hellohello!!!!

 

Exception_en.properties:

argument.request={0} is request!

 

输入如下:

hellohello!!!!

name is request!

 

当然还可以把messageSource这个bean注入到其它bean.

也就说,因为有了messageSource接口以及MessageSourceResourceBundle接口实现, 利用IoC反向注入的模式,我们可以把i18n分离出业务.

 

12 ApplicationContext实现事件机制

Spring中的事件机制是典型的Observer模式的实现.一个事件的发生会触发它所有的监听器.参考文档中的例子少了一个event,我从网上找了一个例子.

MessageEvent:

package org.test.applicationEvent;

 

import org.springframework.context.ApplicationEvent;

 

public class MessageEvent extends ApplicationEvent {

       public MessageEvent(Object source) {

              super(source);

              System.out.println(this.getTimestamp() + ":" + source);

       }

}

 

 

MessageListener:

package org.test.applicationEvent;

 

import org.springframework.context.ApplicationEvent;

import org.springframework.context.ApplicationListener;

 

public class MessageListener implements ApplicationListener {

       public void onApplicationEvent(ApplicationEvent event) {

              if (event instanceof MessageEvent) {

                     System.out.println("I got the message:" + event.getSource());

              }

       }

}

 

 

SenderBean:

package org.test.applicationEvent;

 

import org.springframework.beans.BeansException;

import org.springframework.context.ApplicationContext;

import org.springframework.context.ApplicationContextAware;

 

public class SenderBean implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public void setApplicationContext(ApplicationContext applicationContext)

              throws BeansException {

          this.applicationContext = applicationContext;

    }

    public String sendMessage(String msg) {

          MessageEvent event = new MessageEvent(msg);

          this.applicationContext.publishEvent(event);

          return msg;

    }

}

 

Build.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="sender" class="org.test.applicationEvent.SenderBean" />

       <bean id="listener" class="org.test.applicationEvent.MessageListener" />

</beans>

 

 

MessageTest:

package org.test.applicationEvent;

 

import java.util.Locale;

 

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

 

public class MessageTest {

 

       /**

        * @param args

        */

       public static void main(String[] args) {

              // TODO Auto-generated method stub

 

               ApplicationContext ctx=new ClassPathXmlApplicationContext("org//test//applicationEvent//beans.xml");

               SenderBean sender = (SenderBean)ctx.getBean("sender");

         String msg = sender.sendMessage("test message");

        System.out.println(msg);

               

       }

 

}

最后的输出为:

1183099914140:test message

I got the message:test message

test message

 

13 ApplicationContextweb.xml中的配置

利用ContextLoader,可以实现非编程,声明式的初始化ApplicationContext实例.

 

Spring IoC总结:

作为Spring的双核之一,Spring IoC----反向注入做了一件非常了不起的事情,Spring IoC容器对应用程序中的实例进行统一管理,它负责初始化、配置、销毁甚至建立实例之间的联系,Spring IoC管理的实例与普通的基本上没有什么区别,Spring IoC管理的实例(我们还是叫它Bean,因为虽然Bean与普通类没太大区别,但是它要满足被注入的条件要么setter,要么构造器),通常情况下感知不到它们是被管理的,它们只需关心它们应该关心的,当然在必要的时候,它们也可以感知到容器的存在.

Spring IoC最大的特点是低耦合.通过反向的注入使我们可以随意拆卸通常的类之间的依赖关系.

利用Spring IoC几乎支持所有的类初始化方式,包括工厂模式,singleton,propotype,通过MessageSource接口以及ResourceBundleMessageSource接口实现你甚至可以把i18n都独立出你的业务.利用LifeCycle的两个接口(虽然Spring不鼓励你利用这两个接口,取而代之的是两个方法)你可以定制Bean的初始化及析构行为.利用bean工厂后置处理器的实现类,你还可以将容器的配置做的更软.最后,Spring IoC容器的接口BeanFactory的扩展ApplicationContext还可以实现基于观察者模式的事件机制.

 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值