`

对依赖注入技术的初学指导

阅读更多

http://www.yeeyan.com/articles/view/2091/838

 

序:依赖注入技术(Dependency Injection)被世人所认知和使用有一段时间了。近来,越来越多的人开始使用它的原理来设计、开发、和单元测试Java程序。一些很优秀很有意思的开源框架也由此开发出来,比如SpringFramework和Google Guice等等。译者翻译的这篇近两年前的文章,对于人们了解和学习Dependency Injection的基本原理应该是有裨益的。

就Dependency Injection目前在业界的直译(依赖注入)而言,译者认为不明了。所以以下就还一直使用英文。另请注意,这项技术并不是将依赖性进行注入,而是一些功能和资源的注入,请读者不要望文生义。

原文:

范畴
这篇文章从较高的层次来一览Dependency Injection(DI)技术。它的目的是在如何使用几种DI容器的背景下,把DI技术概念性地介绍给初学者。

是DI还是IOC(反向控制)?
当前的许多资料通常将DI和IOC混为一谈。就我个人而言,DI应该是一个更具体的名字。我能想像在许多情况下使用IOC的时候,不见得最后的模式就是以解决依赖性为重心(比如从一个控制台应用程序到一个窗口事件循环)。但是,如果你更习惯IOC,你基本上可以将以下文章里的DI换成IOC来读。IOC有多种类别,像一类,二类,三类。这些类别间的不同不
在本文的讨论范围之内。
另请注意我在本文里大量使用了服务端(Service)词。请不要将它混淆为以服务为导向的框架(SOA)。我这里的意思只是用用户端来代表用户端组件,服务端来代表服务端组件。

DI的简要介绍

简要介绍

情境一
假设你在一个将出差当成家常便饭的公司工作。通常来说,你乘飞机来旅行。每次你赶飞机时,总是要安排taxi。你认识航空公司订票的人员,出租车公司安排taxi送你到机场的人员。你知道他们的电话,你知道通常订票的谈话内容。你的典型的旅行步骤如:

  • 决定目的地,和希望到达时间;
  • 给航空公司打电话,将必要的旅行信息传达出去,用以订票
  • 给出租公司打电话,要求taxi从你住地送你到机场去赶某一具体航班(出租公司可能需要联络航空公司来得到航班的起飞时间表,机场信息,并计算从你住地至机场的的具体时间,及相应的到达你住地的时间)
  • 获取机票,赶上taxi,开始出差之旅

如果现在你的公司突然更换原先订票的经纪以及相应的交流手段,你可能被迫进入重新熟悉的境地:

  • 新的经纪公司,他们的交流方式(比如说,如果新的经纪通过互联网来做生意,而不是原来的电话)
  • 用以成交的典型的谈话方式的次序(是数据,而不再是声音)

不仅仅是你,很可能你的许多同事也要对此变化进行适应。适应的过程往往要花上可观的时间。

情境二
现在让我们来假设整个程序有一点点不同。你们公司有一个行政部门。当你需要出差旅行的时候,行政部门的互动电话系统会给你打电话(事实上是将你和订票经纪公司挂起钩来)。在电话上,你只需回答特定的一套问题,来讲出你的目的地和需要的到达时间。机票订票系统是专为你们设计的,出租公司将计划好taxi的时间,同时,机票也会给你送上门来。

如果现在订票的经纪更换了的话,行政部门会知会这个变更,也许他们会相应调整订票经纪的工作流程。互动的电话系统可以重新程序化,以便于和经纪在互联网上沟通。但是,你和你的同事们不需要有任何的重新适应过程。你仍旧只需照先前的程序走就行了(所有的调整都由行政部门去做了)

依赖注入(Dependency Injection)

在上述两个情境中,你是客户,你依赖于经纪提供的服务。但是,情境二有些不同:

  • 你不需要知道订票经纪人和电话 - 必要的话,公司行政部门会打电话给你
  • 你不需要知道行政部门和订票经纪的交流程序和方式,虽然你知道如何与行政部门一个特定的交流方式
  • 你不需要作出任何调整,即使当给你提供服务的经纪有变更的时候

这就是现实生活中的依赖性注入(DI)。这种方式看上去省不了你什么,但是当你把它应用到一个大公司里去时,他的节省是很可观的。

在软件领域里的DI

软件的组件(用户端)通常是一整套相互作用的组件中的一部分,这一整套组件又会依赖于一些其他的组件(服务端)来完成它们的预定目标。在许多情况下,它们需要知道和哪些组件打交道,在哪里可以找到那些组件,以及如何和那些组件交流。如果获取到这些组件(服务端)的方式改变时,这些变化可能迫使大量用户端的组件作出调整。

有鉴于此,把代码结构化的一个方法是,将找到和实例化服务端组件的逻辑嵌入到用户端的组件里。另一个方法是将客户端对服务端的依赖性声明,让一些‘外在’的代码来承担找到和实例化服务端组件的任务,并提供相关的服务端的引用(reference)给客户端。后一种方式里,当寻找外在依赖性的方式变更时,客户端的代码通常不需要调整。这种实施的方法被认为是DI,前面提到过的那些’外在‘的代码可能是自己亲自写的,也可能是按一些现成的DI框架来实施的。

那些试着将上述情况在设计术语中得到印证的人会发现他们之间的可比性适用于三个不同的术语 - DI,工厂(Factory),和程序接口(不是具体的实施)。在这个比较中,这三个术语通常被用到(不一定同时用到)。

一直到几年前,传统的方法总是将接口和实施分离开来。工厂设计模式甚至允许将复杂的实例化隐藏起来。但是,找到服务端组件的机制就交给客户端去做了。而且,部分的服务端组件需要晓得其内部各组件之间的依赖关系,在实例化的过程中暗含的次序问题,以及跟踪和管理他们的生命周期。

举例来说,J2EE中使用了JNDI来标准化找寻对象引用的机制。这样的实施在一些简单的,没有JNDI的环境中就会迫使客户端的代码作出调整。

DI的使用要求我们在写软件时,把依赖性搞清楚,让DI框架里的容器替我们去做那些将服务端部件实例化、初始化、次序化、然后提供用户端需要的服务端的引用的复杂工作。

DI框架举例

现在有不少对开发人员有帮助的框架。我发现确实有用的有:

  • Spring框架:一个相当大的框架,提供包括DI在内的很多功能
  • Pico框架:一个很小,但是集中于DI容器的框架
  • HiveMind:另一个DI容器的框架
  • XWork:一个非常有效地利用DI的,主要基于命令模式的框架。虽然它是独立存在的,但它通常和Webwork结合起来用

在下面的章节里,我们将取一种情境,先不用DI来实施一次,再用上述提到的每一种DI框架来实施。这样,我们就能体会到如何使用这些框架。这里,我尽量少用代码,而且这些代码的主要重心也在DI相关的功能上。

实施情境

随附的源代码基本上实施了我之前提到的那个旅行订票情境。总的流程图(从每一个"*Harness"类里的主方法开始)如下:

  • Harness类被实例化,其中的runHarness()方法被调用。
  • runHarness()方法(在AbstractHarness类中)首先激活configure()方法。configure()方法再来对DI容器进行初始化,并注册它的实施类。
  • runHarness()方法随后激活getPlanner()方法。这通常会激活DI框架底层的Lookup方法。目前暂不明瞭的是,这个Lookup方法是否会引发DI容器对AirlineAgency,CabAgency,和TripPlanner等接口的实施类的实例化,并解决好它们之间的相互依赖关系。
  • runHarness()方法随后在调用planTrip()方法来实际完成订票和计划taxi接人的时间问题。

值得注意的依赖关系如下:

  • AirlineAgency和CabAgency是代表相应服务的接口程序。CabAgency需要对AirlineAgency有一个引用才能完成它的功能。
  • TripPlanner是一个在与AirlineAgency和CabAgency结合起来后来完成旅行计划的接口程序。TripPlanner需要对AirlineAgency和CabAgency的引用。

基于源代码的UML图如下所示:



在各个不同框架下应注意到的显著的差别是:

  • harness类里的configure()和getPlanner()方法
  • 接口和实施类的关系是如何描述的(有的使用Java,有的使用XML文件)
  • 不同服务组件间的依赖关系是如何声明的(有的使用XML文件,有的使用constructor,有的使用setter的方法)
  • 其他的需求期望(比如说,xwork里的接口推动者-enabler interfaces)

无容器实施

对于无容器实施而言,无一定之规。一些实施会做服务组件的查询(比如JNDI)或建立一组工厂的接口程序(可能通过单态模式来实施)。在我们这个case里,我选择的是首先从属性文件里读取类名,然后使用映射的方式来将各服务组件里的对象实例化。

属性文件如下(只有类名):

airline-agency-class = com.dnene.ditutorial.common.impl.SimpleAirlineAgency

cab-agency-class = com.dnene.ditutorial.common.impl.ConstructorBasedCabAgency



用来实例化AirlineAgency,CabAgency,和TripPlanner的源代码如下:

AirlineAgency airlineAgency = null;

CabAgency cabAgency = null;

TripPlanner tripPlanner = null;

// Get class names from property file

InputStream propertyFile = getClass().getClassLoader().

        getResourceAsStream("nocontainer-agency.properties");

Properties properties = new Properties();

properties.load(propertyFile);

                

Class airlineAgencyClass = Class.forName

            (properties.getProperty("airline-agency-class"));

Class cabAgencyClass = Class.forName

             (properties.getProperty("cab-agency-class"));

Class tripPlannerClass = Class.forName

               (properties.getProperty("trip-planner-class"));

         

if (AirlineAgency.class.isAssignableFrom(airlineAgencyClass))

{

        // Instantiate airline agency

     airlineAgency = (AirlineAgency) airlineAgencyClass.newInstance();

}

if (CabAgency.class.isAssignableFrom(cabAgencyClass))

{

        // Instantiate CabAgency, and satisfy its dependency on an airlineagency.

                 

  Constructor constructor = cabAgencyClass.getConstructor

           (new Class[]{AirlineAgency.class});

       cabAgency = (CabAgency) constructor.newInstance

           (new Object[]{airlineAgency});

}   

if (TripPlanner.class.isAssignableFrom(tripPlannerClass))

{

    Constructor constructor = tripPlannerClass.getConstructor

         (new Class[]{AirlineAgency.class,CabAgency.class});

       tripPlanner = (TripPlanner) constructor.newInstance

               (new Object[]{airlineAgency,cabAgency});

}

return tripPlanner;



请注意对AirlineAgency的引用被传递给了CabAgency的constructor(这表示两服务组件间的依赖关系)。


实际旅行规划操作的代码是在AbstractHarness和AbstractTripPlanner中。它在所有的harness类重复使用。


AbstractHarness.java的部分代码如下:

public void runHarness() throws Exception

{

   this.configure();

 this.getPlanner().planTrip(

               "1, Acme Street",

               "11111",

                "22222",

                new Date(System.currentTimeMillis() + 7200000));

}

AbstractTripPlanner.java的部分代码如下:

public void planTrip(String departingAddress, String departingZipCode, 
                                String arrivalZipCode, Date preferredArrivalTime)
{

    FlightSchedule schedule = airlineAgency.bookTicket

                        (departingZipCode,arrivalZipCode,preferredArrivalTime);         

  cabAgency.requestCab(departingAddress ,schedule.getFlightNumber());

}



Pico容器的实施

容器的实例化

这里要求一个简单的对DefaultPicoContainer类的实例化。
pico = new DefaultPicoContainer();

组件的注册

在Pico的容器里注册实施类。

pico.registerComponentImplementation(SimpleAirlineAgency.class);

pico.registerComponentImplementation(ConstructorBasedCabAgency.class);

pico.registerComponentImplementation(ConstructorBasedTripPlanner.class);



依赖性的声明

依赖性是将包含着某一特定服务组件所依赖的接口程序列表的一个constructor里声明的。在下面的情况里,

ConstructorBasedCabAgency的服务件是依赖于AirlineAgency的接口程序的。

public ConstructorBasedCabAgency(AirlineAgency airlineAgency)

{

        this.airlineAgency = airlineAgency;

}

类似地,ConstructorBasedTripPlanner也在它的constructor里声明对AirlineAgency和CabAgency的依赖。



依赖性的解决方案

Pico通过查看constructors来解决依赖性的问题。服务组件借助于对具体实施类的注册而完成在Pico容器中的注册。用户组件

通过那些接口类的名字来调用服务组件。智能化的Pico容器将遍历接口实施的层次和等级来返回适合用户需求的实施。(请注意在前面讨论过的

无容器实施方案也是通过类名来实例化实施类的)。



因为TripPlanner接口是依赖于Airline和CabAgency两个接口的,CabAgency接口又是依赖于AirlineAgency接口的,Pico容器

会自动将AirlineAgency首先实例化,然后在建CabAgency时,将对AirlineAgency的引用传递过去;在建TripPlanner时,

将对AirlineAgency和CabAgency的引用传递过去。在下面的需求提出时,上面整个的次序得以执行:



pico.getComponentInstanceOfType(TripPlanner.class);



HiveMind的实施



XML的声明

在这个例子里,接口和实施类都在xml文件里来声明。请注意各服务端组件间的依赖性不在xml文件里声明,而是在随后的代码里。

xml文件里的一个服务点将记录下一个服务组件所需的必要数据。

<?xml version="1.0"?>

<module id="com.dnene.ditutorial.hivemind" version="1.0.0">

  <service-point id="AirlineAgency" interface="com.dnene.ditutorial.common.AirlineAgency">

    <create-instance class="com.dnene.ditutorial.common.impl.SimpleAirlineAgency"/>

  </service-point>

  <service-point id="CabAgency" interface="com.dnene.ditutorial.common.CabAgency">

    <invoke-factory>

      <construct class="com.dnene.ditutorial.common.impl.SetterBasedCabAgency"/>

    </invoke-factory>

   </service-point>

   <service-point id="TripPlanner" interface="com.dnene.ditutorial.common.TripPlanner">

    <invoke-factory>

      <construct class="com.dnene.ditutorial.common.impl.SetterBasedTripPlanner"/>

    </invoke-factory>

   </service-point>  

</module>



容器的初始化



这一步会自动按照上面xml文件里注明的值将注册目录调配妥当。

Registry registry = RegistryBuilder.constructDefaultRegistry();



依赖性声明

CabAgency对AirlineAgency的依赖性通过在相关setter方法中的定义暗中传达给了HiveMind容器。

public void setAirlineAgency(AirlineAgency airlineAgency)
{

 this.airlineAgency = airlineAgency;

} 



依赖性的解决方案


当调用TripPlanner的引用时,依赖性的解决方案同样是自动进行的(见以下部分代码)。HiveMind能够

遍历所有依赖性的结构,从而来依次对AirlineAgency,CabAgency,和TripPlanner接口来进行实例化,并

在CabAgency中激活setter(setAirlineAgency),在TripPlanner中激活两个setter(setAirlineAgency, 

setCapAgency),从而来解决它们之间的依赖性。



registry.getService(TripPlanner.class);



Spring框架的实施


XML声明


需要实例化的实施都将在XML里声明。各服务组件间的依赖性作为属性元素来声明。Spring框架将使用这些信息来激活相应的

setter方法。



<beans>

  <bean id="AirlineAgency" class="com.dnene.ditutorial.common.impl.SimpleAirlineAgency" singleton="true"/>

      <bean id="CabAgency" class="com.dnene.ditutorial.common.impl.SetterBasedCabAgency" singleton="true">

          <property name="airlineAgency">

                   <ref bean="AirlineAgency"/>

               </property>

 </bean>

     <bean id="TripPlanner" class="com.dnene.ditutorial.common.impl.SetterBasedTripPlanner" singleton="true">

              <property name="airlineAgency">

                   <ref bean="AirlineAgency"/>

               </property>

         <property name="cabAgency">

                       <ref bean="CabAgency"/>

           </property>

 </bean>

</beans>



容器的初始化



容器的初始化通常需要对xml文件的引用和对bean工厂的实例化。

ClassPathResource res = new ClassPathResource("spring-beans.xml");

BeanFactory factory = new XmlBeanFactory(res);

依赖性的解决方案

调出对服务端组件的引用是基于定义在xml文件里的‘id’,而不是接口类。同样地,所有的服务组件按照准确的次序隐性地实例化,setters得以调用来解决依赖性问题。

factory.getBean("TripPlanner");



XWork实施



XWork其实是有一定DI功能的命令行模式的框架。它可能是在所有DI框架中最不成熟的了。但是,我发现如果你已经决定使用

webwork,特别是你要把依赖性注入到操作类中去的时候,XWork是一个很有用的框架。



XML声明

<components>

    <component>

        <scope>application</scope>

        <class>com.dnene.ditutorial.common.impl.SimpleAirlineAgency</class>

        <enabler>com.dnene.ditutorial.xwork.AirlineAgencyAware</enabler>

    </component>

    <component>

        <scope>application</scope>

        <class>com.dnene.ditutorial.common.impl.SetterBasedCabAgency</class>

        <enabler>com.dnene.ditutorial.xwork.CabAgencyAware</enabler>

    </component>

    <component>

        <scope>application</scope>

        <class>com.dnene.ditutorial.common.impl.SetterBasedTripPlanner</class>

        <enabler>com.dnene.ditutorial.xwork.TripPlannerAware</enabler>

    </component>

</components>

请注意XWork依赖于一个叫做‘推动者‘的接口(enabler interface)。它基本上是一个为服务组件定义setter方法的接口。

public interface CabAgencyAware

{

     public void setCabAgency(CabAgency cabAgency);

}

和其他的容器不同的是,XWork要求我们定义AirlineAgencyAware,CabAgencyAware和TripPlannerAware 接口,并
正确地实施它们。从这个角度来看,XWork要比其他框架烦人些。

容器的初始化

这看上去会有一点复杂。

ComponentConfiguration config = new ComponentConfiguration();

InputStream in = 

      XworkHarness.class.getClassLoader().getResourceAsStream("components.xml");

config.loadFromXml(in);

cm = new DefaultComponentManager();

config.configure(cm,"application");

依赖性的声明

各类通过对必要的’推动者接口‘的实施来声明它们之间的依赖性。因此每一个服务组件通过实施相应的’推动者接口‘来声明
它对其他组件的依赖性。XWork使用’推动者接口‘来解决依赖性。

public class SetterBasedCabAgency extends AbstractCabAgency implements AirlineAgencyAware { ... }

依赖性的解决方案

很自然的,从一个组件管理器引发的一个查询就可以自动解决所有的依赖性问题。

cm.getComponent(TripPlannerAware.class);

XWork和WebWork

XWork通常和WebWork被绑在一起使用。在那样的情况下,你需要做的就是定义并实施推动者接口类,写出XML文件,

并使用组件拦截器。组件管理器间的互动得以通过WebWork来实施,所以只要将操作类的依赖性加以解决,组件管理器的

代码是不需要的。此外,对不同的组件管理器设立不同的范畴(比如说,应用,session,或request)是很容易的。



结论



正如你所看到的,每一个DI框架在实施它的功能上都或有不同。平心而论,它们在使用上也有不同(比如说,Pico容器

是基于Setter的DI,而Spring框架则是基于constructor的DI)。我刚刚决定要记录下其中的一种已被支持的机制。

从全面的角度来考量,要说一个框架比另一个框架好有点难。我想你需要根据你自身的实际情况来使用最合适的。
从以上情境来看,最清晰不过的是,使用DI框架将显著地降低实例化不同组件以及解决它们之间的依赖性的复杂程度。



额外功能

请注意我主要把重心放在下面两方面:
  • 解决用户端对服务端组件的依赖性
  • 解决服务端各组件间的相互依赖性

DI框架其实提供了更多的功能-主要在服务组件的配置和生命周期的管理上。就这些功能,你应该读相关的文档以了解得更多些。就大多数具有中小型复杂度的应用而言,DI本身就已经足够好了。但是,当你的应用变得更复杂的时候,你可能就需要探询些其他的功能。本文之后的一些资源列表可以作为获取更多信息的出发点。


一些有争议的话题

在一些领域里,有着相当程度的争论(通常来自于不同DI框架的作者或支持者)。其中的一些如:

基于constructor或基于setter的DI?

这里是指用constructor和用setter来声明依赖性,哪一个更好些?我不确信是否能干净利落地证明一个比另一个好。但是我

注意到,当整体的依赖性结构变得更复杂时,基于setter的DI看上去更易于管理。

XML或Java?
一些人偏好将依赖性的关系用Java代码来编织起来,更多的人则偏向于XML。我认为用Java来做更简单些。但是至少在

一个境况下,XML是唯一可行的选择。那就是当你推出二进制的产品时,为了系统管理员、集成人员、最终用户能够通过插入

为服务组件而做的不同的实施(为布署系统做的一些特别改动)而修改和添加默认的功能。你的最终用户更可能来改动你的XML

文件(假设你的文档做得好的话),而不是你的源代码(假设你已经公布了)。

在上述情况下,更重要的是需要指出,争议是关于方法,而不是结果。不管你用何种方法来实施,你都能利用DI框架。注意在

许多情况下,你会有两个选择(你可能需要好好研究一下API的文档)。如果框架的文档推荐使用XML,你很可能可以使用Java

来串起来,只要你能搞清楚那些能这么做的API就行。如果文档建议你用的框架只支持基于constructor或setter的DI,那么

一定要深度探索一下,很有可能你会发现对其他方法的支持。

使用DI能得到什么?

对DI从整体来看,无论是作为一个设计模式,还是不同的框架,你可能都会问,我能从DI上得到些什么?以下就是一些好处
  • 如果审视你的服务组件依赖性,它会包含两个子依赖性。一个是基于API或服务组件自身协议的专于某一领域的依赖性。另一个是泛指基于服务组件间实例、配置、和查询的技术协议上的依赖性。DI允许你将处理后者的代码转移到附带的代码边界上去(如XML文件或一些离你的’主‘方法不远的方法里)。这样,一些技术上的更改得以较容易地处理,因为客户端组件的开发人员可以集中精力于他们最拿手的-服务端某一特定领域的API。
  • 如果你想创建即插即用的结构,从而你的最终用户和系统集成人员可能会要求延伸或更改服务端的功能,那么你用DI的方法来做会让事情变得很容易。DI让按开闭原理来建造软件变得容易。在举例中,我提到作为一个DI容器的Spring,其实Spring本身也大量地使用了DI。建造进Spring的一系列功能(如AOP,交易处理等)实际上是用DI来插入这些即插即用的服务的。Spring框架也可以靠写一些额外的服务并用DI来插入它们得以延伸和拓展(比如说,spring模块,参见http://springmodules.dev.java.net)。
  • 如果你真的在应用中,建立大量的细分的彼此间相互依赖的服务,很可能你要重新布局的次数,比你预计的要来的多。如果你真的用了DI的框架的话,你会很庆幸你当初的选择。
  • 当你最初的假设开始变更的时候,你需要开始加强你的应用。比如,你有建在J2EE平台上的服务组件,它们之间靠JNDI来相互查询。如果你现在把代码移植到一个没有JNDI的环境中去,如果你当初用了DI的话,就没有一点问题。
分享到:
评论

相关推荐

    Servlet,JSP和Spring MVC 初学指南源码

    这份初学指南源码涵盖了这三个重要技术的基础知识,为初学者提供了一个良好的学习起点。 Servlet是Java平台上的一个标准,用于扩展服务器的功能。它是一个Java类,用于接收和响应来自Web客户端的请求。Servlet生命...

    thinkphp5.1框架容器与依赖注入实例分析

    在实际开发中,我们可以借助容器和依赖注入技术,将应用程序的不同部分解耦合,使得每个模块的职责更加单一,同时也便于测试和扩展。例如,当需要替换一个服务的实现时,只需要在容器中重新绑定新的类或者闭包即可,...

    Servlet JSP和SpringMVC初学指南

    Servlet、JSP(JavaServer Pages)和SpringMVC是Java Web开发中不可或缺的三大核心技术,它们各自承担着不同...通过阅读"Servlet JSP和SpringMVC初学指南.pdf",可以系统地学习这三个技术,并结合实践提升自己的技能。

    Dependency Injection in NET

    《依赖注入在.NET》这本书由Mark Seemann编写,旨在深入探讨依赖注入的概念和技术细节,并提供一系列实用的指南帮助开发者更好地理解和运用这一设计模式。书中不仅包含了依赖注入的核心模式介绍,还提供了大量的实际...

    Java初学者指南

    - **Spring框架**:一种流行的Java EE应用开发框架,支持依赖注入、面向切面编程等功能。 通过以上梳理,我们可以看到《Java初学者指南》涵盖了Java语言的基础知识、核心特性以及高级应用等多个方面,非常适合Java...

    Gradle依赖包的spring依赖

    Spring框架是Java开发中的核心组件,它提供了依赖注入(DI)和面向切面编程(AOP)等关键特性,用于简化企业级应用的开发。依赖注入是Spring的核心特性之一,允许开发者在运行时动态地将对象和服务连接起来,无需硬...

    Servlet+JSP和Spring+MVC初学指南 书籍配套源码

    本指南将深入介绍这两种技术及其整合应用,通过配套的源码帮助初学者更好地理解概念和实践操作。 Servlet是Java平台上的一个标准接口,允许开发者编写服务器端的Java程序,处理HTTP请求并返回响应。Servlet API提供...

    spring框架,技术详解及使用指导.pdf

    ### Spring框架技术详解及使用指导 #### Spring框架简介 Spring框架是一个开源的轻量级Java应用框架,由Rod Johnson创建并由Pivotal Software维护。它最初被设计为解决企业级应用开发中常见的问题,例如复杂的配置...

    spring 教程 对初学者够了

    Spring框架的核心特性包括依赖注入(Dependency Injection, DI)和面向切面编程(Aspect-Oriented Programming, AOP),这两个特性极大地简化了Java应用的开发。 **2. Spring模块** Spring框架由多个模块组成,包括...

    Spring框架基础包和依赖包.zip

    Spring框架是Java开发中最常用的轻量级框架之一,它的核心特性是依赖注入(Dependency Injection,简称DI)和面向切面编程(Aspect Oriented Programming,简称AOP)。在"Spring框架基础包和依赖包.zip"中,包含的...

    spring教程-经典官方教程(适合初学者)

    - Spring高级特性章节讲解了Spring框架的高级用法,例如Web应用与MVC、SpringMVC指南、Web应用中模板技术与JSP技术的对比,输入验证与数据绑定,异常处理以及国际化支持等内容。 - 数据持久层章节可能会深入探讨如何...

    开源项目-quii-learn-go-with-tests.zip

    4. 教程:可能是一系列步骤或指南,指导用户如何逐步学习和实现依赖注入。 通过参与这个开源项目,你不仅可以提升Go语言技能,还能了解到如何使用TDD和依赖注入来编写高质量、可测试的代码。这是一个绝佳的学习资源...

    Google.Guice.Agile.Lightweight.Dependency.Injection.Framework

    总之,《Google Guice:敏捷轻量级依赖注入框架》不仅是一本技术指南,更是一本启发性的读物,它将带领你进入Guice的世界,让你从一个XML编码者转变为真正的Guice专家。无论你是初学者还是有经验的开发人员,本书都...

    多功能DLL注入器源码 附带注释

    4. **"必看文件"和"通用注入器(测试DLL)"**:这两个文件可能包含了更深入的使用指南或示例,例如如何使用这个注入器,以及一个预编译的测试DLL,用于验证注入器的功能。通过阅读和分析这些文件,开发者可以了解...

    对于Spring初学者的学习建议

    - **面向对象的基础**:熟练掌握面向对象的思想,这对于理解Spring中的依赖注入(Dependency Injection, DI)和面向切面编程(Aspect-Oriented Programming, AOP)至关重要。 - **设计模式**:虽然不需要学习所有...

    Spring开发指南.pdf

    - **依赖注入的概念**:依赖注入是一种设计模式,旨在降低组件之间的耦合度。Spring通过DI实现了组件之间的解耦,使得组件更容易测试、维护和扩展。 - **依赖注入的几种实现类型** - **Type1 接口注入**:通过接口...

    spring 教程,初学者的好帮手

    Spring框架是Java开发中最常用的轻量级开源框架之一,它以依赖注入(Dependency Injection, DI)为核心,旨在简化企业级应用的开发。本教程专为初学者设计,旨在帮助你快速掌握Spring的基本概念和核心功能。 一、...

    DLL依赖查看工具

    这些帮助文档对于初学者尤其有用,他们可以从中学习如何查找和解析DLL依赖关系。 DEPENDS.CNT可能是一个包含上下文敏感帮助内容的文件,通常与.HLP文件一起使用,提供快速访问特定主题的功能。用户在使用工具时遇到...

    spring开发指南(电子书)

    总的来说,《Spring开发指南》会引导你逐步掌握Spring的基本概念和关键组件,包括依赖注入、AOP、数据访问以及Web开发。无论你是刚接触Spring的新手,还是希望巩固Spring基础的开发者,这本书都将是你宝贵的参考资料...

Global site tag (gtag.js) - Google Analytics