首页 > WEB开发 > 后台开发 > Spring入门:IoC
2014
10-05

Spring入门:IoC

09. Spring入门:IoC13

一、简介

Spring是一个开源的IoC容器(Spring的核心)和AOP编程框架。它的主要目的是简化企业开发。

09. Spring入门:IoC72

1、IoC:Inversion of Control,控制反转

下面的代码中personDao对象是在应用内部创建及维护的:

public class PersonServiceImpl {
     private PersonDao personDao = new PersonDaoImpl();

      public void save(Person person){
            personDao.save(person);
     }
}

所谓控制反转就是应用本身不负责依赖对象的创建及维护,依赖对象的创建及维护是由外部容器(即IoC容器,有时也称Spring容器)负责的。这样控制权就由应用转移到了外部容器,控制权的转移就是所谓反转(应用内部 –> IoC容器)。

当我们把依赖对象交给外部容器负责创建,那么PersonServiceImpl类可以改成如下:

public class PersonServiceImpl {
     private PersonDao personDao ;
     //让容器通过setter方法把创建好的依赖对象注入进PersonServiceImpl
     public void setPersonDao(PersonDao personDao){
         this.personDao=personDao;
     }
      public void save(Person person){
            personDao.save(person);
     }
}

所谓依赖注入(DI)就是指:在运行期,由外部容器动态地将依赖对象注入到组件中。因此可以说,Spring是通过DI实现IoC的。

可以发现在PersonServiceImpl中没有了PersonDaoImpl,即Service层已经和DAO层的具体实现解耦,在PersonServiceImpl中就可以直接面向接口编程。

关于AOP(面向切面编程)的概念见《动态代理技术》一文。

2、引入Spring之后带来的好处

  • 降低组件之间的耦合度,实现软件各层之间的解耦。
  • 可以使用容器提供众多的服务,如:事务管理服务、消息服务等等。当我们使用容器管理事务时,就不再需要手工控制事务,也不需处理复杂的事务传播。
  • 容器提供了AOP技术,利用它很容易实现如权限拦截、运行期监控等功能。

另外,Spring对于主流的应用框架提供了集成支持,如:集成Hibernate、JPA、Struts等,这样更便于应用的开发。这些好处在后面的学习中会逐渐体现。

3、轻量级与重量级概念的划分

Q:Spring属于轻量级框架,还是重量级框架?

其实划分一个应用属于轻量级还是重量级,主要看它使用了多少服务。使用的服务越多,容器要为普通java对象做的工作就越多,必然会影响到运行性能。

对于spring容器,它提供了很多服务,但这些服务并不是默认为应用打开的,应用需要某种服务,还需要指明使用该服务,如果应用使用的服务很少,如只使用了spring核心服务,那么我们可以认为此时应用属于轻量级的,如果应用使用了spring提供的大部分服务,这时应用就属于重量级。

EJB容器就因为它默认为应用提供了EJB规范中所有的功能,所以它属于重量级。

09. Spring入门:IoC1529

二、IoC容器

1、Helloworld — Spring编码步骤

新建一个Java Project,项目名为:spring_01_helloworld。

1)搭建开发环境

① 拷贝jar包:dist\spring.jar、lib\jakarta-commons\commons-logging.jar

② 创建Spring配置文件:applicationContext.xml,开发时置于src目录下即可(Spring默认加载位置和配置文件名),内容如下(可从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-2.5.xsd">

  <bean id="..." class="..."></bean>
</beans>

2)创建Bean

public class Helloworld {
	public void sayHello(){
		System.out.println("Hello,World!");
	}
}

在applicationContext.xml中配置bean元素

<bean id="helloWorld" class="org.flyne.bean.HelloWorld"></bean>

几点说明:

  • 把一个类放入到Spring容器中(即配置对应的bean元素),该类就是bean,Spring容器不仅限于管理JavaBean,可以是任意的类。
  • bean元素的配置:id属性为bean的标识符,class属性为对应的类的全名。id属性命名规范:类的第一个字母变小写,其他字母保持不变。

3)从Spring容器中得到Bean

@Test
public void testSayHello(){
	//启动Spring容器
	ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
	//从Spring容器中取Bean对象:id为helloWorld的那个
	HelloWorld helloWorld = (HelloWorld)ctx.getBean("helloWorld");

	helloWorld.sayHello();
}

几点说明:

  • 从ClassPathXmlApplicationContext的名称就可以看出Spring默认从类路径加载配置文件。
  • 当启动Spring容器时,会去解析Spring配置文件,利用反射机制创建对象(实例化Bean),并放入Spring容器中(可以将Spring容器想象成一个Map<String,Object>结构)。

Ok,这就是Spring的Helloworld,主要目的是演示:① 将一个类配置成为Bean ②从Spring容器中取得相应对象。

4)关于Spring所需jar包的几点说明:

① 如果只是用到IoC(Spring的核心),只需导入上面两个包即可。

② 如果使用了切面编程(AOP),还需要下列jar文件

  • lib/aspectj/aspectjweaver.jar和aspectjrt.jar
  • lib/cglib/cglib-nodep-2.1_3.jar

③ 如果使用了@Resource、@PostConstruct、@PreDestroy等注解,还需要lib\j2ee\common-annotations.jar,或引入Java ee库。
后面在使用相关功能时不再说明。

2、三种实例化bean的方式

1)默认使用构造方法(开发时足够了)

例如上面HelloWorld中的例子,当容器启动并实例化Bean时,就调用的是默认的无参构造方法。

2)使用静态工厂方法

public class HelloWorldFactory1 {
	public static HelloWorld getInstance(){
		return new HelloWorld();
	}
}

applicationContext.xml中的配置如下-------------
<bean id="helloWorld1" class="org.flyne.bean.HelloWorldFactory1" factory-method="getInstance"></bean>

当采用静态工厂方法创建bean时,通过factory-method属性来指定创建bean实例的工厂方法。此时在程序中就可以通过ctx.getBean(“helloWord1″);来获取HelloWorld对象了。

3)使用实例工厂方法

public class HelloWorldFactory2 {
	public HelloWorld getInstance(){
		return new HelloWorld();
	}
}

applicationContext.xml中的配置如下-------------
<bean id="helloWorldFactory2" class="org.flyne.bean.HelloWorldFactory2" ></bean>
<bean id="helloWorld2" factory-bean="helloWorldFactory2" factory-method ="getInstance"/>

此时在程序中就可以通过ctx.getBean(“helloWord2″);来获取HelloWorld对象了。

说明:当使用实例工厂方法创建Bean对象之前必须先拿到该工厂的实例,因此必须配置一个工厂Bean,在使用静态工厂方法时就不需要。

可以做个小实验:在上面两个工厂方法的默认构造函数中增加一条System.out.println(“实例化了”);。可以发现使用静态工厂方法实例化bean时,工厂类没有实例化;而使用实例工厂方法实例化bean时,工厂类被实例化了。

实际开发中一般用第一种,写第三方框架和Spring整合时会见到第二种和第三种创建方式。

3、bean元素相关配置

1)给bean设置别名:alias元素

<bean id="helloWorld" class="com.itheima11.spring.ioc.alias.HelloWorld" ></bean>

<alias name="helloWorld" alias="hw"/>

则在程序中可以通过ctx.getBean(“helloWorld”)取得HelloWorld对象,也可通过ctx.getBean(“hw”)取。

2)bean的延迟初始化(延迟加载):lazy-init属性

lazy-init属性告诉Spring容器是在启动时还是在对象第一次被用到时实例化(即getBean调用时)。

  • false:lazy-init的默认值为false,Spring容器在启动时会实例化所有singleton bean
  • true:如果想让一个singleton bean在容器启动的时候不被实例化,可以配置bean元素的lazy-init属性为true,此时将在对象第一次被用到时实例化。

开发中保持lazy-init为默认就OK了,因为这种形式的配置更安全:如果spring的配置文件中有错误,在启动spring容器的时候就会报错(否则容器启动时发现不了错误)。

需要说明的是,如果一个beanA被设置为延迟初始化,而另一个非延迟初始化的singleton beanB依赖于它,那么当Spring容器启动时,beanA也需要被实例化。因此,如果Ioc容器在启动的时候创建了那些设置为延迟实例化的bean的实例,你也不要觉得奇怪,因为那些延迟初始化的bean可能在配置的某个地方被注入到了一个非延迟初始化的singleton bean里面。

3)bean的作用域:scope属性

  • singleton:默认值,表示Spring容器产生的对象是单例的
  • prototype:多例,每次从容器取bean对象时都会产生新的实例。如:将Struts2中的action类交给Spring容器管理时,需配置为多例bean。

注:lazy-init属性只对singleton bean起作用。

小结:Spring容器实例化Bean时间

① lazy-init=”false”,即默认情况下,singleton bean在容器启动时被实例化,prototype bean在第一次被调用时实例化。

② lazy-init=”true”,即设置了延迟初始化,singleton bean和prototype bean都在第一次被调用时才实例化。

③ 还有一点需要注意的是,当一个lazy-init=”true”的beanA被另一个lazy-init=”false”的singleton beanB所引用时(依赖),则beanA在容器启动时也会被实例化。

4)定义bean初始化和销毁时的回调函数:init-method、destroy-method属性

① 初始化回调

Spring容器允许在设置好bean的所有必要属性后(☆),处理初始化事宜,即初始化回调。

怎么做:在Bean定义中指定一个普通的初始化方法,然后在XML配置文件中指定init-method属性。如下:

public class ExampleBean {
    public void init() {
        // 在setter方法后调用,做一些初始化工作
    }
}

applicationContext.xml中的配置如下-------------
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>

② 析构回调(销毁时调用)

同理,Spring容器也提供了容器关闭时的相关处理(析构销毁),通过destroy-method配置销毁函数。在此不作演示。

③ 配置缺省的初始化和销毁方法

像init、destroy这种生命周期相关的方法最好在一个项目范围内标准化,这样团队中的开发人员就可以使用同样的方法名称。

怎么做:指定在顶级的<beans />元素中的”default-init-method”属性。

<beans default-init-method="init">
    ………………
</beans>

这样Spring IoC容器在bean创建和装配的时候会将’init’方法作为初始化回调方法,如果类有这个方法,就会在适当的时候执行(没有就不执行)。

销毁回调方法配置是相同的,在顶级的<beans />元素中使用 ‘default-destroy-method’ 属性。

5)配置继承关系(☆):parent属性

业务场景:在《DAO层设计(泛型反射)》一文中,设计了BaseDao接口定义DAO层的公共操作:增删改以及查询一条记录。此时在实现CustomerDao时,继承BaseDaoImpl并增加自己独有的方法即可。这种继承关系的在Spring容器中的配置如下:

<bean id="baseDao" class="org.flyne.dao.impl.BaseDaoImpl" />

<bean id="customerDao" class="org.flyne.dao.impl.CustomerDaoImpl" parent="baseDao"/>

留下一个回复

你的email不会被公开。