Java Web系列:Spring依赖注入基础

一、Spring简介

1.Spring简化Java开发

Spring Framework是一个应用框架,框架一般是半成品,我们在框架的基础上可以不用每个项目自己实现架构、基础设施和常用功能性组件,而是可以专注业务逻辑。因此学习Spring Framework在架构和模式方面的结构和原理,对我们在架构和模块级别的理解帮助极大。Spring Framework(参考1)的宗旨是简化Java开发,主要的手段如下:

(1)在架构上解耦:通过DI(依赖注入)管理类型依赖,通过AOP分离关注点,减少重复代码。

(2)在设计上广泛采用DIP(依赖倒置)和ISP(接口隔离)等原则和Facade(外观)等模式:提供简化的调用接口并封装了众多出色的第三方组件。

(3)在语言层面上采用注解:通过配置文件和Annotation(参考.NET Attribute)简化应用配置。

2.Spring Framework的架构和模块:

Spring Framework本身的架构是典型的松散分层,外层可以按需引用全部内层,内层不能引用外层。Spring的基础组件如下图所示:

从图中可以看出,开始的模块只有从core\beans\aop\context四个组件,后来添加了context-support【1.2】扩展模块、expression【3.0】扩展模块和beans-groovy【4.0】扩展模块。

Spring上述模块的基础上,内建和封装了众多的实用的通用组件,主要的组件如图所示:

从图中可以看出,spring-oxm、spring-jdbc和spring-web是众多模块依赖的核心,spring-oxm提供了Object和XML的映射支持。

二、基础知识

1.DIP:DIP(依赖倒置原则)是DI(依赖注入)的核心(参考2)。

(1)高层模块不应该依赖于低层模块。两者都应该依赖于抽象。

(2)抽象不应该依赖于细节。细节应该依赖于抽象。

说人话就是:将对具体类的引用转换成对其接口的引用,具体类只引用接口(引用==依赖,接口==接口或抽象类)。事实上我们调用具体类的时候在头脑里也是只关心其提供的API而非实现,DIP则通过在设计和重构阶段在技术手段上保证了解耦。

2.DI:DI(依赖注入)让我们不必手写工厂代码来管理接口和实现类的映射、对象的创建和生命周期的管理。

(1)接口注入:必须实现特定的接口才可以,侵入性太强,现在已经无人关心和使用。

(2)构造函数注入:依赖体现在构造函数的参数上。

(3)属性注入:依赖体现在属性上。

由于在实现时,可以将类型注册为自己的兼容类型,这样依赖注入就可以直接替代new实例化对象,这样理解和使用依赖注入工具还不如不使用或手写工厂了。依赖注入工具在实现时肯定会实现成一个支持不同配置和不同生命周期的对象工厂,但即使没有提供一套添加依赖倒置原则限制的API,也不意味着我们把它当成new的替代品。如同映射工具虽然在实现时可以任意映射,但不是用来取代赋值的,而是用来处理领域实体和视图模型等有实际对应关系的对象之间的映射。

(1)依赖配置:依赖配置是依赖注入实现的基础。依赖注入工具都至少支持代码配置和文件配置。Java中可以通过Annotation(.NET中通过Attribute)简化配置。

(2)对象工厂:根据配置返回一个或多个对象。这是核心功能。

(3)生命周期管理:一般提供至少4种级别的支持:作用域、单例、线程、HTTP请求范围。

大多数依赖注入工具在支持依赖倒置原则的基础上,在技术手段上实现了更多的功能,如类型的兼容转换、对依赖命名、在配置时直接传入对象等。

三、Spring依赖注入

Bean在Spring中就是POJO(.NET的POCO),Bean在Spring中就是POJO,Bean在Spring中就是POJO,重要的事情要说3遍。可以简单的认为Bean就是对象。

Spring依赖注入的核心是BeanFactory和BeanDefinition。分别对应对象工厂和依赖配置的概念。虽然我们通常使用的是ApplicationContext的实现类,但ApplicationContext只是封装和扩展了BeanFactory的功能。

虽然说xml配置是Spring依赖注入中支持早、功能强大和容易修改的特点,但xml配置只是Spring依赖注入读取配置的一种方式,仅此而已。无论以前、现在和以后支持和即将支持什么样的配置,Spring依赖注入的核心是BeanDefinition接口,有通过代码使用BeanDefinition经验的人可能早就意识到了这一点个:各种配置最后会映射到BeanDefinition。因此我们应该按需使用适合的配置,不要把细枝末节当作核心,至少无论.NET还是Java中,依赖倒置、对象工厂、生命周期等概念是通用的,掌握了这些核心概念,再去学习具体的类库,会事半功倍。ApplicationContext的实现类AnnotationConfigWebApplicationContext配合Annotation注解和泛型,早已经提供了更简易的配置方式。如下代码所示,现在的Spring依赖注入在使用上已经很接近.NET中的用法了。

现在让我们关注AnnotationConfigApplicationContext和AnnotationConfigWebApplicationContext。这两个实现类是实现无xml配置的核心。

1.Java一般应用程序:

(1)依赖注入代码:

package me.test.spring_ioc;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class App {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext container = new AnnotationConfigApplicationContext();
        container.register(AppService.class, Repository.class);
        container.refresh();
        String message = container.getBean(IAppService.class).print();
        System.out.println(message);
        container.close();
    }
}

(2)接口和实现代码:

package me.test.spring_ioc;

public interface IAppService {
    String print();
}
package me.test.spring_ioc;

public interface IRepository<T> {
    String print();
}
package me.test.spring_ioc;

public class SimpleEntity {

}
package me.test.spring_ioc;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;

@Scope("request")
public class AppService implements IAppService {

    private IRepository<SimpleEntity> _repo;

    @Autowired

    public AppService(IRepository<SimpleEntity> repo) {
        this._repo = repo;
    }

    @Override
    public String print() {
        return this._repo.print();
    }
}
package me.test.spring_ioc;

public class Repository<T> implements IRepository<T> {
    @Override
    public String print() {
        return "Test";
    }
}

从代码中我们可以看到,不需要配置文件,不需要使用字符串,不需要对接口类有任何副作用。

2.Java Web应用程序:

(1)依赖注入代码:

package swp;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;

public class MyWebAppInitializer implements WebApplicationInitializer{

    @Override
    public void onStartup(ServletContext container) throws ServletException {
        AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
        rootContext.register(AppService.class,Repository.class);
        container.addListener(new ContextLoaderListener(rootContext));
    }
}

(2)JSP中调用(接口和实现代码同上):

<%@page import="swp.IAppService"%>
<%@page import="org.springframework.web.context.WebApplicationContext"%>
<%@page
    import=" org.springframework.web.context.support.WebApplicationContextUtils"%>
<html>
<body>
    <%
        WebApplicationContext context = WebApplicationContextUtils
                .getRequiredWebApplicationContext(this.getServletContext());
        String message = context.getBean(IAppService.class).print();
        out.print(message);
    %>
</body>
</html>

随着Java中泛型和注解的添加以及版本的更新,Spring中包含了众多的ApplicationContext,从最基础的ClassPathXmlApplicationContext、FileSystemXmlApplicationContext、XmlWebApplicationContext等实现,又陆续与时俱进的添加了GenericWebApplicationContext【1.2】、XmlPortletApplicationContext【2.0】、ResourceAdapterApplicationContext【2.5】、GenericXmlApplicationContext【3.0】、【AnnotationConfigWebApplicationContext】、GenericGroovyApplicationContext【4.0】、GroovyWebApplicationContext【4.1】。阅读Spring源代码是最好的学习方式没有之一,但如果你只想拷贝一段pom.xml+applicationContext.xml+ClassPathXmlApplicationContext的调用代码又另当别论。

四、Spring依赖注入的要点

1.BeanFactory的层次结构

BeanFactory是spring中依赖注入的核心,其设计主要采用了ISP(接口隔离原则),通过多层次的接口继承即保证了单个接口的内聚又保证了整个体系的简洁。这里我们要关注的核心是DefaultListableBeanFactory。

如图所示,查看XmlBeanFactory代码,可以看到XmlBeanFactory只是通过XmlBeanDefinitionReader载入了BeanDefinition配置,XmlBeanDefinitionReader负责将配置解析到BeanDefinition。DefaultListableBeanFactory是真正的实现类,其中定义了类型为Map<String, BeanDefinition>的beanDefinitionMap列表用于存储依赖配置。

2.BeanDefinitionReader的层次结构:

除了上文提到的XmlBeanDefinitionReader,BeanDefinitionReader还有2个实现类PropertiesBeanDefinitionReader和GroovyBeanDefinitionReader,还有1个单独定义的AnnotatedBeanDefinitionReader。ApplicationContext的实现类中,有些是固定搭配这些BeanDefinitionReader,有些没有没有绑定BeanDefinitionReader,我们可以通过手动搭配方式,搭配一个或多个BeanDefinitionReader。


3.ApplicationContext的层次结构

ApplicationContext的各种实现类太多了,但核心都是将对象工厂功能委托给BeanFactory的实现类DefaultListableBeanFactory,解析配置都是BeanDefinitionReader的4个相关类。

如图所示,各种各样的ApplicationContext有2个继承分支。

(1)AbstractRefreshableApplicationContext抽象类的实现类:

AbstractRefreshableApplicationContext在refreshBeanFactory中使用了DefaultListableBeanFactory对象并将其作为参数传递给loadBeanDefinitions方法。

FileSystemXmlApplicationContext和ClassPathXmlApplicationContext是在基类AbstractXmlApplicationContext实现loadBeanDefinitions方法时使用了XmlBeanDefinitionReader。

XmlWebApplicationContext和XmlPortletApplicationContext使用XmlBeanDefinitionReader,GroovyWebApplicationContext使用的是GroovyBeanDefinitionReader。

AnnotationConfigWebApplicationContext使用的是AnnotatedBeanDefinitionReader。

(2)GenericApplicationContext类及其子类:

GenericApplicationContext没有和特定的BeanDefinitionReader绑定,我们可以直接使用该类型实例作为构造参数传递给BeanDefinitionReader,再手动调用BeanDefinitionReader的loadBeanDefinitions方法。这意味着我们可以使用多个BeanDefinitionReader。

GenericGroovyApplicationContext和GenericXmlApplicationContext分别使用了GroovyBeanDefinitionReader和XmlBeanDefinitionReader。

AnnotationConfigApplicationContext使用的是AnnotatedBeanDefinitionReader。

ResourceAdapterApplicationContext和GenericWebApplicationContext和基类GenericApplicationContext一样,没有绑定特定的实现。

Spring的依赖注入虽然也有对象工厂和依赖配置的概念,但其依赖配置是以对象为核心而不是类型,BeanDefinition直接对应的是对象的信息及其依赖,这在通过代码进行配置的时候有明显的体现。google guice(参考3)在依赖倒置支持和代码配置上是明显优于Spring的。

参考

1.http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/overview.html

2.https://en.wikipedia.org/wiki/Dependency_inversion_principle

3.http://www.ibm.com/developerworks/cn/java/j-guice.html

时间: 2024-10-03 14:27:28

Java Web系列:Spring依赖注入基础的相关文章

(转)Java Web系列:Spring依赖注入基础

一.Spring简介 1.Spring简化Java开发 Spring Framework是一个应用框架,框架一般是半成品,我们在框架的基础上可以不用每个项目自己实现架构.基础设施和常用功能性组件,而是可以专注业务逻辑.因此学习Spring Framework在架构和模式方面的结构和原理,对我们在架构和模块级别的理解帮助极大.Spring Framework(参考1)的宗旨是简化Java开发,主要的手段如下: (1)在架构上解耦:通过DI(依赖注入)管理类型依赖,通过AOP分离关注点,减少重复代码

java框架篇---spring 依赖注入

spring依赖注入的方式有4种 构造方法注入 属性注入 工厂注入 注解注入 下面通过一个实例统一讲解: User.java package com.bjsxt.model; public class User { private String username; private String password; public User(){} public User(String username, String password) { super(); this.username = use

JAVA入门[3]—Spring依赖注入

Spring支持属性注入和构造器注入,它支持XML和注解两种方式.本文介绍Spring控制反转容器加载包含beans的XML文件,实现依赖注入. 一.创建bean实例 暂且抛开对象依赖,我们先看下如何通过Spring容器创建bean实例.这里要用到Spring的控制反转容器ApplicationContext,它的getBean方法可以创建bean实例 1.在Maven项目的pom.xml添加spring依赖项. <dependencies> <!-- mybatis核心包 -->

Java Web系列:Spring MVC 基础

1.Web MVC基础 MVC的本质是表现层模式,我们以视图模型为中心,将视图和控制器分离出来.就如同分层模式一样,我们以业务逻辑为中心,把表现层和数据访问层代码分离出来是一样的方法.框架只能在技术层面上给我们帮助,无法在思考和过程上帮助我们,而我们很多人都不喜欢思考和尝试. 2.实现Web MVC的基础 实现Web MVC基础可以概括为1个前段控制器和2个映射. (1)前端控制器FrontController ASP.NET和JSP都是以Page路径和URL一一对应,Web MVC要通过URL

Spring依赖注入

Spring依赖注入基础 一.Spring简介 1.Spring简化Java开发 Spring Framework是一个应用框架,框架一般是半成品,我们在框架的基础上可以不用每个项目自己实现架构.基础设施和常用功能性组件,而是可以专注业务逻辑.因此学习Spring Framework在架构和模式方面的结构和原理,对我们在架构和模块级别的理解帮助极大.Spring Framework(参考1)的宗旨是简化Java开发,主要的手段如下: (1)在架构上解耦:通过DI(依赖注入)管理类型依赖,通过AO

SSH深度历险(八) 剖析SSH核心原理+Spring依赖注入的三种方式

在java开发中,程序员在某个类中需要依赖其它类的方法,则通常是new一个依赖类再调用类实例的方法,这种开发存在的问题是new的类实例不好统一管理,spring提出了依赖注入的思想,即依赖类不由程序员实例化,而是通过spring容器帮我们new指定实例并且将实例注入到需要该对象的类中.依赖注入的另一种说法是"控制反转",通俗的理解是:平常我们new一个实例,这个实例的控制权是我们程序员,而控制反转是指new实例工作不由我们程序员来做而是交给spring容器来做. Spring依赖注入(

Spring依赖注入 --- 简单使用说明

Spring依赖注入 --- 简单使用说明 本文将对spring依赖注入的使用做简单的说明,enjoy your time! 1.使用Spring提供的依赖注入 对spring依赖注入的实现方法感兴趣的同学可以参考我的日志:http://www.cnblogs.com/kodoyang/p/Frame_Imitate_Spring.html 我们会用spring提供的ClassPathXmlApplicationContext实现来代替这篇日志中相应的实现 为导入的jar文件配置api 对应的L

研究 Spring MVC 将请求分发到 Spring 依赖注入的类实例

太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转载请保留此句:太阳火神的美丽人生 -  本博客专注于 敏捷开发及移动和物联设备研究:iOS.Android.Html5.Arduino.pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作. 一上午时间,碰了 N 个钉子,不断地把钉子拨掉,记录一下选择的工具和方法: 1.首先 Spring Mvc 框架的下载,那么有三个包是必不可少的:

腾讯分分彩源码带龙虎和玩法自言自语Spring依赖注入(XML配置)

至于基于XML依赖注入的过程,首先要找一个比较合适的入口,那就是getBean.那么具体是怎么实现的呢?首先写个测试方法: ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("application-common.xml","application-beans.xml","application_jdbc.xml");Object obj = app.g