Spring学习笔记1

Spring 初探

1. Action 接口:
Action 接口定义了一个 execute 方法,在我们示例中,不同的 Action 实现提供了各自的execute 方法,以完成目标逻辑。

public interface Action {
    public String execute(String str);
}

2. Action 接口的两个实现 UpperAction、LowerAction

public class UpperAction implements Action {
    private String message;

    public String getMessage() {
        return message;
    }
    public void setMessage(String string) {
        message = string;
    }
    public String execute(String str) {
        return (getMessage() + str).toUpperCase();
    }
}

UpperAction将其message属性与输入字符串相连接,并返回其大写形式。

public class LowerAction implements Action {

    private String message;

    public String getMessage() {
        return message;
    }
    public void setMessage(String string) {
        message = string;
    }
    public String execute(String str) {
        return (getMessage() + str).toLowerCase();
    }
}

LowerAction将其message属性与输入字符串相连接,并返回其小写形式。

Spring 配置文件(bean.xml)

<beans>
    <description>
        Spring Quick Start
    </description>
    <bean id="TheAction" class="net.xiaxin.spring.qs.UpperAction">
        <property name="message">
            <value>
                HeLLo
            </value>
        </property>
    </bean>
</beans>

测试代码:

public void testQuickStart() {

    ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");
    Action action = (Action) ctx.getBean("TheAction");
    System.out.println(action.execute("Rod Johnson"));

}

可以看到,上面的测试代码中,我们根据”bean.xml”创建了一个ApplicationContext实
例,并从此实例中获取我们所需的Action实现。

运行测试代码,我们看到控制台输出:

HELLO ROD JOHNSON

我们将bean.xml中的配置稍加修改:

<bean id="TheAction" class="net.xiaxin.spring.qs.LowerAction"/>

再次运行测试代码,看到:

……
hello rod johnson

仔细观察一下上面的代码,可以看到:

1. 我们的所有程序代码中(除测试代码之外) ,并没有出现Spring中的任何组件。
2. UpperAction和LowerAction的Message属性均由Spring通过读取配置文件(bean.xml)动态设置。
3. 客户代码(这里就是我们的测试代码)仅仅面向接口编程,而无需知道实现类的具体名称。同时,我们可以很简单的通过修 改配置文件来切换具体的底层实现类。


上面所说的这些,对于我们的实际开发有何帮助?

  • 组件并不需要实现框架指定的接口,因此可以轻松的将组件从Spring中脱离,甚至不需要任何修改(这在基于EJB框架实现的应用中是难以想象的) 。

  • 其次,组件间的依赖关系减少,极大改善了代码的可重用性。

Spring的依赖注入机制,可以在运行期为组件配置所需资源,而无需在编写组件代码时就加以
指定,从而在相当程度上降低了组件之间的耦合。

上面的例子中,我们通过Spring,在运行期动态将字符串 “HeLLo” 注入到Action实现类的
Message属性中。

Spring通过依赖注入模式,将依赖关系从编码中脱离出来,从而大大降低了组件之间的耦合,实现了组件真正意义上的即插即用。这也是Spring最具价值的特性之一。

  • 面向接口编程。
    Spring使得接口的定义和使用不再像传统编码过程中那么繁琐(传统编码过程中,引入一个接口,往往也意味着同时要引入一个Factory类,也许还有一个额外的配置文件及其读写代码) 。

Spring是一个从实际项目开发经验中抽取的,可高度重用的应用框架。
Spring Framework中目前最引人注目的,也就是名为控制反转(IOC =Inverse Of Control)或者依赖注入(DI =Dependence Injection)的设计思想,这的确是相当优秀的设计理念。


Spring 基础语义

IoC,用白话来讲,就是由容器控制程序之间的关系,而非传统实现中,由程序代码直接操控。这也就是所谓“控制反转”的概念所在:控制权由应用代码中转到了外部容器,控制权的转移,是所谓反转。

从名字上理解,所谓依赖注入,即组件之间的依赖关系由容器在运行期决定,形象的来说,即由容器动态的将某种依赖关系注入到组件之中。

回顾 Quick Start 中的示例,UpperAction/LowerAction 在运行前,其 Message 节点为空。运行后由容器将字符 串“HeLLo”注入。此时 UpperAction/LowerAction 即与内存中的“HeLLo”字符串对象建立了依赖关系。也许区区一个字符串我们无法感受出依赖关系的存在。如果把这里的 Message 属性换成一个数据源(DataSource) ,可能更有感觉:

<beans>
    <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiName">
            <value>
                java:comp/env/jdbc/sample
            </value>
        </property>
    </bean>
    <bean id="SampleDAO" class="net.xiaxin.spring.dao.SampleDAO">
        <property name="dataSource">
            <ref local="dataSource" />
        </property>
    </bean>
</beans>

其中SampleDAO中的dataSource将由容器在运行期动态注入, 而DataSource的具体配置和初始化工作也将由容器在运行期完成。
对比传统的实现方式(如通过编码初始化DataSource实例) ,我们可以看到,基于依赖注入的系统实现相当灵活简洁。

通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定SampleDAO中所需的DataSource实例。SampleDAO只需利用容器注入的DataSource实例,完成自身的业务逻辑,而不用
关心具体的资源来自何处、由谁实现。

上面的实例中,我们假设SampleDAO是一个运行在J2EE容器中的组件(如 Weblogic) 。在运行期,通过JNDI从容器中获取DataSource实例。

现在假设我们的部署环境发生了变化,系统需要脱离应用服务器独立运行,这样,由于失去了容器的支持,
原本通过JNDI获取DataSource的方式不再有效。我们需要如何修改以适应新的系统环境?很简单,我们
只需要修改dataSource的配置:

<beans>
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
    destroy-method="close">
        <property name="driverClassName">
            <value>
                org.gjt.mm.mysql.Driver
            </value>
        </property>
        <property name="url">
            <value>
                jdbc:mysql://localhost/sample
            </value>
        </property>
        <property name="username">
            <value>
                user
            </value>
        </property>
        <property name="password">
            <value>
                mypass
            </value>
        </property>
    </bean>
    <bean id="SampleDAO" class="net.xiaxin.spring.dao.SampleDAO">
        <property name="dataSource">
            <ref local="dataSource" />
        </property>
    </bean>
</beans>

这里我们的DataSource改为由Apache DBCP组件提供。没有编写任何代码我们即实现了DataSource的切换。回想传统编码模式中,如果要进行同样的修改,我们需要付出多大的努力。

依赖注入机制减轻了组件之间的依赖关系,同时也大大提高了组件的可移植性,这意味着,组件得到重用的机会将会更多。


依赖注入的几种实现类型

Type1 接口注入

我们常常借助接口来将调用者与实现者分离。

public class ClassA {
    private InterfaceB clzB;
    public doSomething() {
        Ojbect obj = Class.forName(Config.BImplementation).newInstance();
        clzB = (InterfaceB) obj;
        clzB.doIt()
    }……
}

上面的代码中,ClassA依赖于InterfaceB的实现,如何获得InterfaceB实现类的实例?传统的方法是在代码中创建InterfaceB实现类的实例,并将起赋予clzB。
而这样一来,ClassA在编译期即依赖于*InterfaceB的实现。为了将调用者与实现者在编译期分离*,于是有了上面的代码,我们根据预先在配置文件中设定的实现类的类名(Config.BImplementation),动态加载实现类,并通过InterfaceB强制转型后为ClassA所用。这就是接口注入的一个最原始的雏形。
而对于一个Type1型IOC容器而言,加载接口实现并创建其实例的工作由容器完成。

如下面这个类:

public class ClassA {
    private InterfaceB clzB;
    public Object doSomething(InterfaceB b) {
        clzB = b;
        return clzB.doIt();
    }……
}

在运行期,InterfaceB实例将由容器提供。

Type1型IOC发展较早(有意或无意) ,在实际中得到了普遍应用,即使在IOC的概念尚未确立时,这样的方法也已经频繁出现在我们的代码中。


Type2 设值注入

在各种类型的依赖注入模式中,设值注入模式在实际开发中得到了最广泛的应用(其中很大一部分得力于Spring框架的影响)。
在笔者看来,基于设置模式的依赖注入机制更加直观、也更加自然。Quick Start中的示例,就是典型的设置注入,即通过类的setter方法完成依赖关系的设置。


Type3 构造子注入

构造子注入,即通过构造函数完成依赖关系的设定,如:

public class DIByConstructor {
    private final DataSource dataSource;
    private final String message;
    public DIByConstructor(DataSource ds, String msg) {
        this.dataSource = ds;
        this.message = msg;
    }……
}

可以看到,在Type3类型的依赖注入机制中,依赖关系是通过类构造函数建立,容器通过调用类的构造方法,将其所需的依赖关系注入其中。


Spring Bean 封装机制

Spring 从核心而言,是一个 DI 容器,其设计哲学是提供一种无侵入式的高扩展性框架。即无需代码中涉及 Spring 专有类,即可将其纳入 Spring 容器进行管理。

为了避免这种情况,实现无侵入性的目标。Spring 大量引入了 Java 的 Reflection 机制,通过动态调用的方式避免硬编码方式的约束,并在此基础上建立了其核心组件 BeanFactory,以此作为其依赖注入机制的实现基础。

org.springframework.beans 包中包括了这些核心组件的实现类, 核心中的核心为 BeanWrapperBeanFactory 类。

所谓依赖注入,即在运行期由容器将依赖关系注入到组件之中。讲的通俗点,就是在运行期,由Spring根据配置文件,将其他对象的引用通过组件的提供的setter方法进行设定。

我们知道,如果动态设置一个对象属性,可以借助Java的Reflection机制完成:

Class cls = Class.forName("net.xiaxin.beans.User");
Method mtd = cls.getMethod("setName", new Class[] {
    String.class
});
Object obj = (Object) cls.newInstance();
mtd.invoke(obj, new Object[] {
    "Erica"
});
return obj;

Spring BeanWrapper基于同样的原理,提供了一个更加完善的实现。
看看如何通过Spring BeanWrapper操作一个JavaBean:

Object obj = Class.forName("net.xiaxin.beans.User").newInstance();
BeanWrapper bw = new BeanWrapperImpl(obj);
bw.setPropertyValue("name", "Erica");
System.out.println("User name=>"+bw.getPropertyValue("name"));

通过BeanWrapper,我们可以无需在编码时就指定JavaBean的实现类和属性值,通过在配置文件加以设定,就可以在运行期动态创建对象并设定其属性(依赖关系) 。

ApplicationContext

ApplicationContext覆盖了BeanFactory的所有功能,并提供了更多的特性。此外,
ApplicationContext为与现有应用框架相整合,提供了更为开放式的实现(如对于Web应用,我们可以在
web.xml中对ApplicationContext进行配置) 。

对于Web应用,Spring提供了可配置的ApplicationContext加载机制。

加载器目前有两种选择:ContextLoaderListenerContextLoaderServlet。这两者在功能上完全等同,只是一个是基于Servlet2.3版本中新引入的Listener接口实现,而另一个基于Servlet接口实现。开发中可根据目标Web容器的实际情况进行选择。

配置非常简单,在web.xml中增加:

<listener>
    <listener-class>
        org.springframework.web.context.ContextLoaderListener
    </listener-class>
</listener>

<servlet>
    <servlet-name>
        context
    </servlet-name>
    <servlet-class>
        org.springframework.web.context.ContextLoaderServlet
    </servlet-class>
    <load-on-startup>
        1
    </load-on-startup>
</servlet>

通过以上配置,Web容器会自动加载/WEB-INF/applicationContext.xml初始化ApplicationContext实例,如果需要指定配置文件位置,可通过context-param加以指定:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/myApplicationContext.xml</param-value>
</context-param>

配置完成之后,即可通过WebApplicationContextUtils.getWebApplicationContext方法在Web应用中获取ApplicationContext引用。


Spring 高级特性 Web 应用与 MVC

从较偏向设计的角度出发,WebWork2 的设计理念更加先进,其代码与 Servlet API 相分离,这使得单元测试更加便利,同时系统从 BS 结构转向 CS 接口也较为简单。另外,对于基于模板的表现层技术(Velocity、Freemarker 和 XSLT)的支持,也为程序员提供了除 JSP之外的更多的选择(Struts 也支持基于模板的表现层技术,只是实际中不太常用)。

而对于 Spring 而言,首先,它提供了一个相当灵活和可扩展的 MVC 实现,与 WebWork2相比,它在依赖注入方面、AOP 等方面更加优秀,但在 MVC 框架与底层构架的分离上又与Webworks 存在着一定差距(Spring 的 MVC 与 Servlet API 相耦合,难于脱离 Servlet容器独立运行,在这点的扩展性上,比 Webwork2 稍逊一筹)。

我们还要注意到,Spring 对于 Web 应用开发的支持,并非只限于框架中的 MVC 部分。即使不使用其中的 MVC 实现, 我们也可以从其他组件, 如事务控制、 ORM 模板中得益。 同时, Spring也为其他框架提供了良好的支持,如我们很容易就可以Struts 与 Spring 甚至 WebWork与 Spring 搭配使用(与 WebWork 的搭配可能有些尴尬,因为两者相互覆盖的内容较多,如
WebWork 中的依赖注入机制、AOP 机制等与 Spring 中的实现相重叠)。因此,对于 Spring在 Web 应用中的作用,应该从一个更全面的角度出发。

对于现有较成熟的 Model-View-Control(MVC)框架而言,其解决的主要问题无外乎下面几部分:

1. 将 Web 页面中的输入元素封装为一个(请求)数据对象。
2. 根据请求的不同,调度相应的逻辑处理单元,并将(请求)数据对象作为参数传入。
3. 逻辑处理单元完成运算后,返回一个结果数据对象。
4. 将结果数据对象中的数据与预先设计的表现层相融合并展现给用户。

下面的实例,实现了一个常见的用户登录逻辑,即用户通过用户名和密码登录,系统对用户名和密码进行检测,如果正确,则在页面上显示几条通知信息。如果登录失败,则返回失败界面。

index.html:

<html>

    <body>
        <form method="POST" action="/login.do">
            <p align="center">
                登录
            </p>
            <br>
            用户名:
            <input type="text" name="username">
            <br>
            密 码 :
            <input type="password" name="password">
            <br>
            <p>
                <input type="submit" value="提交" name="B1">
                <input type="reset" value="重置" name="B2">
            </p>
        </form>
    </body>
</html>

很简单的一个登录界面,其中包含了一个用以输入用户名密码的 form,针对此 form 的提交将被发送到”/login.do”
MVC 关键流程的第一步,即收集页面输入参数,并转换为请求数据对象。这个静态页面提供了一个基本的输入界面,下面这些输入的数据将被发送至何处,将如何被转换为请求数据对象?

现在来看接下来发发生的事情:

当用户输入用户名密码提交之后,此请求被递交给 Web 服务器处理,上面我们设定 form提交目标为”/login.do”,那么 Web 服务器将如何处理这个请求?显然,标准 Http 协议中,并没有以.do 为后缀的服务资源,这是我们自己定义的一种请求匹配模式。此模式在 web.xml 中设定:

<?xml version="1.0" encoding="ISO-8859-1" ?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
    <servlet><servlet-name> Dispatcher</servlet-name>
        <servlet-class>
            org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <init-param>
            <param-name> contextConfigLocation </param-name>
            <param-value>/WEB-INF/Config.xml </param-value>
        </init-param>
    </servlet>
    <servlet-mapping><servlet-name> Dispatcher </servlet-name>
        <url-pattern> *.do </url-pattern>
    </servlet-mapping>
</web-app>

⑴ Servlet 定义
这里我们定义了请求分发 Servlet,即:
org.springframework.web.servlet.DispatcherServlet
DispatcherServlet 是 Spring MVC 中负责请求调度的核心引擎,所有的请求将由此 Servlet 根据配置分发至各个逻辑处理单元。其内部同时也维护了一个ApplicationContext 实例。

我们在节点中配置了名为“contextConfigLocation”的Servlet 参数, 此参数指定了 Spring 配置文件的位置 “/WEB-INF/Config.xml”。

果忽略此设定,则默认为“/WEB-INF/-servlet.xml”,其中 以 Servlet 名 替 换 ( 在 当 前 环 境 下 , 默 认 值 也 就 是“/WEB-INF/Dispatcher-servlet.xml)。

⑵ 请求映射
我们将所有以.do 结尾的请求交给 Spring MVC 进行处理。当然,也可以设为其他值,
如.action、.action 等。

通过以上设定,Web 服务器将把登录界面提交的请求转交给 Dispatcher 处理,Dispatcher 将提取请求(HttpServletRequest)中的输入数据,分发给对应的处理单元,各单元处理完毕后,将输出页面返回给 Web 服务器,再由 Web 服务器返回给用户浏览器。

Dispatcher 根据什么分发这些请求?显然,我们还需要一个配置文件加以设定。这也就是上面提及的 Config.xml,此文件包含了所有的“请求/处理单元”关系映射设定,以及返回时表现层的一些属性设置

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值