说明:该专题所说的Spring内容基于spring2.0,最Spring最基础,最原始,最核心的内容。
Spring框架是个轻量级的Java EE框架。所谓轻量级,是指不依赖于容器就能运行的。Struts、Hibernate也是轻量级的。
轻量级框架是相对于重量级框架而言的,重量级框架必须依赖特定的容器,如如EJB框架就必须运行在Glassfish、JBOSS等支持EJB的容器中,而不能运行在Tomcat中。
Spring框架
Spring是应用最广泛的轻量级Java EE框架之一,它以IOC,AOP为主要思想,能够协同Struts,Hibernate,WebWork,JSF,IBatis等众多的框架。
Spring背景
Spring并不是官方的框架,而是Rod Johnson领导的开源的、免费的、民间的框架。Rod Johnson曾是Servlet 2.4规范专家组的成员,不过他对EJB等官方标准表示怀疑,认为过于复杂、臃肿的重量级官方框架并不能解决问题提供便利。
为此,2002年他写了Expert one on one J2EE Design and Development(《精通J2EE设计开发》)一书,对现有的J2EE技术标准进行了反思。这是一本J2EE领域的畅销书。基于这本书的思想,Rod Johnson创立了一个实用的轻量级框架,这就是Spring。
Spring容器
Spring是一个轻量级的框架,不需要特殊容器的支持,不依赖于特定的规范如Java EE规范等。不同于Struts,Hibernate等,Spring不提供某种功能。它只是将所有的组件部署到Spring中,管理、维护、执行它们,因此Spring也被称为轻量级“容器”。官方网站为http://springframework.org
一个依赖注入的例子
Spring最主要的思想是IOC(Inversion of Control,控制反转、反向控制),或者称为DI(Dependency Injection,依赖注入)。IOC是对传统控制流程的一种颠覆。
一个普通程序的例子
在传统程序中,相互的依赖性是固定在程序中的,程序的运行也是一步一步的,完全按照程序代码执行,根据代码就能知道程序怎样运行。
例如一个传统的程序可能是这样的,先实例化一个ServiceExample对象,然后调用该对象的service()方法服务用户,代码如下
package com.helloweenvsfei.spring.example; public class Test { public static void main(String[] args) { String name = "Helloween"; ServiceExample serviceExample = new ServiceExample(); serviceExample.service(name); } }
而在ServiceExample中,先实例化一个DaoExample对象,通过DaoExample的sayHello()方法输出问候语。代码如下:
package com.helloweenvsfei.spring.example; public class ServiceExample { private DaoExample daoExample = new DaoExample(); public void service(String name) { System.out.println(daoExample.sayHello(name)); } }
ServiceExample输出了欢迎语,而最终欢迎语的内容是由DaoExample决定的。代码如下:
package com.helloweenvsfei.spring.example; import java.util.Calendar; public class DaoExample { public String sayHello(String name) { int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY); if (hour < 6) return "凌晨早, " + name; if (hour < 12) return "早上好, " + name; if (hour < 13) return "中午好, " + name; if (hour < 18) return "下午好, " + name; return "晚上好, " + name; } }
根据源代码就可以知道,本程序分为3层:应用层、Service层以及DAO层。应用层实例化了一个Service层对象,并调用其中的方法。Service层中实例化了一个DAO层对象,并调用了其中的方法。DAO层对象返回计算后的字符串。程序的执行流程可以从代码中阅读出来,都是正向运行的。
而利用Spring的反向控制(IOC)思想,可以使用另一种方式实现,完全不同的思路。下面看一下Spring中的实现。
Dao接口及实现
新建Web项目spring,添加Spring特性。方法同添加Hibernate特性一样。先定义DAO层的接口。Spring推荐使用接口,Java中也推荐使用接口编程。IDao接口只有一个方法sayHello,参数为人名。该方法将返回对该人名的问候语。代码如下:
package com.helloweenvsfei.spring.example; public interface IDao { public String sayHello(String name); }
实现IDao接口,并根据时间输出不同的问候语。代码如下:
package com.helloweenvsfei.spring.example; import java.util.Calendar; public class DaoImpl implements IDao { public String sayHello(String name) { int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY); if (hour < 6) return "凌晨早, " + name; if (hour < 12) return "早上好, " + name; if (hour < 13) return "中午好, " + name; if (hour < 18) return "下午好, " + name; return "晚上好, " + name; } }
Service接口及实现
定义Service层接口。Service层只有一个service()方法,没有任何DAO层的依赖,没有任何冗余的代码。IService代码如下:
package com.helloweenvsfei.spring.example; public interface IService { public void service(String name); }
Service层实现中,定义了一个IDao类型对象,以及对应的getter,setter方法,注意这里没有实例化一个DaoImpl对象。该对象将被Spring注射进来。而注射动作是发生在运行时的,是在ServiceImpl代码写完之后,这个方向是反向的。Service接口实现代码如下:
package com.helloweenvsfei.spring.example; public class ServiceImpl implements IService { private IDao dao; public void service(String name) { System.out.println(dao.sayHello(name)); } public IDao getDao() { return dao; } public void setDao(IDao dao) { this.dao = dao; } }
组装DAO与Service
注意前面的代码。IService中没有IDao的对象。ServiceImpl中只有IDao类型变量,及对应的getter、setter方法,也没有实例化任何的IService对象或者IDao。
不过如果运行ServiceImpl的service方法,仍然要依赖IDao对象。这种依赖关系并不是写在程序中的,而是配置在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.0.xsd"> <bean id="dao" class="com.helloweenvsfei.spring.example.DaoImpl"> bean> <bean id="serviceImpl" class="com.helloweenvsfei.spring.example.ServiceImpl"> <property name="dao" ref="dao">property> bean>
配置文件中用
service.setDao(daoImpl); //Spring将通过反射执行该setter方法
Spring会根据该配置在运行时使用setter、getter方法注入依赖的对象。
运行代码
Spring会负责IService、IDao对象的实例化,并设置彼此间的依赖。先创建一个Spring的工程,该工程会根据Spring配置文件加载配置文件中的Java Bean,并通过Java的反射机制调用getter、setter方法设置彼此间的依赖。然后从工程中获取Java对象,执行就可以了。代码如下:
package com.helloweenvsfei.spring.example; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; public class SpringTest { public static void main(String[] args) { XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource( "applicationContext.xml")); // 从配置文件中获取对象 IService hello = (IService) factory.getBean("service"); hello.service("Helloween"); factory.destroySingletons(); } }
运行程序,会输出相同的结果:晚上好,Helloween。
程序中没有实例化任何的DAO、Service对象,而是从Spring工程中直接获取的。也就是说,Spring实例化并维护这此对象。因此,Spring实际上是一个轻量级的容器,能生产、管理、维护各种实例。
反向控制的原理
在本例中,Service、DAO层是作为独立的组件出现的。在编码阶段,既没有实例化对象,也没有设置依赖关系,而把它交给Spring,由Spring在运行阶段实例化、组装对象。这种做法颠覆了传统的写代码实例化、组装对象、然后一步步执行的做法,因此被称为反向控制(Inverse of Control),或者反转控制。
面向切面编程的原理
由于组件是在运行期间组装、调用的,因此Spring即可以在执行完A组件后执行组件B,也可以执行完A组件后执行B组件前再执行C组件。也就是说,将C组件插入到A组件与B组件之间。
如果把A、B、C看成是切面,这就是AOP、面向切面的编程。面向切面编程的思想就是在执行某些代码前执行另外的代码,使程序更灵活、扩展性更好,可以随便地添加,删除某些功能。
Java Web机制中的Filter就是面向切面编程的例子。Tomcat会在程序运行的某个时机(即Servlet执行前后),执行与Servlet、JSP等毫无关系的Filter代码。
一个面向切面编程的例子
Spring的另一个重要思想是AOP(Aspect Oriented Programming,面向切面编程),切面编程提供了一种机制,在执行业务前后执行另外的代码。Servlet中的Filter便是一种AOP思想的实现。Spring提供非常灵活的AOP机制。
实现拦截器接口
跟Filter一样,Spring的AOP也需要实现特定的接口。Spring把这些实现了特定AOP接口的类称为拦截器(Interceptor)。Spring有若干种拦截器,例如在某些方法前执行的拦截器,在某些方法后执行的拦截器、异常拦截器等。例如下面的代码是在方法前执行拦截器:
package com.helloweenvsfei.spring.example; import java.lang.reflect.Method; import java.util.Arrays; import org.springframework.aop.MethodBeforeAdvice; public class MethodBeforeAdviceImpl implements MethodBeforeAdvice { public void before(Method method, Object[] args, Object obj) throws Throwable { System.out.println("运行前检查... "); System.out.println("Method: " + method.getName()); System.out.println("Args: " + Arrays.asList(args)); System.out.println("Object: " + obj); } }
Spring在执行某个实例的方法之前,会调用MethodBeforeAdvice的before()方法,参数分别为即将被调用的方法、方法的参数、该实例。该拦截器中没有定义执行时机,执行时机是在Spring配置文件中配置的。
AOP配置拦截器
修改上例中Spring的配置文件,使程序在执行sayHello()方法之前执行拦截器的代码。将上面的拦截器配置在applicationContext.xml中,并用通配符"*“配置在ServiceImpl的任意方法前执行拦截器代码。代码如下:
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.0.xsd"> <bean id="methodBeforeAdviceImpl" class="com.helloweenvsfei.spring.example.MethodBeforeAdviceImpl"> bean> <bean id="theAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <property name="advice"> <ref local="methodBeforeAdviceImpl" /> property> <property name="mappedName" value="*">property> bean> <bean id="dao" class="com.helloweenvsfei.spring.example.DaoImpl"> bean> <bean id="serviceImpl" class="com.helloweenvsfei.spring.example.ServiceImpl"> <property name="dao"> <bean class="com.helloweenvsfei.spring.example.DaoImpl">bean> property> bean> <bean id="service" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="interceptorNames" value="theAdvisor">property> <property name="target"> <ref local="serviceImpl" /> property> bean> beans>
配置完毕后,不用对SpringTest代码做任何的修改。运行SpringTest,Spring会先执行拦截器代码,后执行ServiceImpl的方法。输出如下:
Spring提供的AOP机制异常灵活,能够在任何类、任何方法的执行前、后添加拦截器。Spring不仅给开发者提供了IOC与AOP功能,Spring内置的许多功能也是基于IOC与AOP技术实现的。
Spring框架的组成
Spring是个轻量级的框架,但是它的功能却很庞大。Spring框架的所有组成如图:
图中显示了Spring的7大模块。各个模块的功能如下:
1、Core模块
Core模块是框架的核心类库,Spring所有的功能均依赖于该类库。Core模块主要实现了IOC功能。Spring的所有功能都是借助IOC实现的。
2、AOP模块
Spring的AOP库,提供了AOP(也就是拦截器)机制,并提供各种常用的拦截器,允许自定义、配置方法拦截器、拦截的对象。
3、ORM模块
ORM模块提供对常用ORM框架的管理、辅助支持。Spring支持Hibernate、iBatis、JDO等各种ORM框架。Spring并不提供自己的ORM实现,只是对现有的ORM框架进行封装,并提供对它们的管理,例如事务管理等。
4、DAO模块
DAO模块提供JDBC支持,对JDBC进行封装,允许JDBC使用Spring的资源,并能统一管理JDBC的事务,Spring也不提供JDBC实现。
5、Web模块
Web模块提供对Struts、WebWork、JSF等各种Web框架的支持。Spring能够管理这些框架,将Spring的资源如数据源、Bean等注射给框架,也能在执行框架方法前后插入Spring的拦截器。
6、Context模块
Context模块提供框架式Bean访问方式,其他程序可以通过Context访问Spring的Bean资源。类似于JNDI。
7、Web MVC模块
Spring提供一套轻量级的MVC实现。在Spring框架中,开发者可以选择Struts作为MVC框架,也可以使用Spring自带的MVC框架。Spring MVC与Struts等框架相比,更加简洁、灵活。
小结
Spring是一个轻量级的框架,包括7个核心的模块。Spring最核心的思想是依赖注入(或者称为返向控制),它把离散的组件在运行时组装到一块。由于程序流程是在运行时组装的,因此可以很方便地添加功能,例如拦截器等。Spring是一种面向切面编程的框架。