IOC 与DI 系列:1 Spring 给我们带来了什么

1.Spring的作用

程序员就是Spring的用户,Spring到底能带给我们什么呢?大部分材料都会说,Spring的核心的功能是简化企业级应用程序开发,让程序员更多关注业务如何实现,通用的技术只要集成相关框架即可。那句话到底是什么意思呢?
看一个例子:
1.输入:我在bean.xml中提供了一段Bean关系的配置,希望Spring给我完成创建等工作。

<bean id="user" class="com.liuqingchao.spring5.User"></bean>
<bean id="book" class="com.liuqingchao.spring5.Book">
    <property name="bname" value="易筋经"></property>
    <property name="bauthor" value="达摩老祖"></property>
    <property name="address" value="北京"></property>
    <property name="user" ref="user"></property>
</bean>

业务里需要的时候,我不用自己写一堆的new,特别是自己进行复杂的递归创建,只要导入就可以用了:

@Test
public void testBook1() {
    ApplicationContext context =
            new ClassPathXmlApplicationContext("bean1.xml");
    Book book = context.getBean("book", Book.class);
    System.out.println(book.getAddress());
    System.out.println(book.getUser());
}

为了能够完成这些功能,Spring至少具有以下功能:
(1)创建和管理对象之间的依赖关系。
(2)除了管理Bean,Spring框架最好还能提供通用日志记录,性能统计,安全控制,异常处理等面向切面的能力,最好还能帮我们处理数据库事务。从而使我们将更多的精力放在业务开发上。

如果要完成上面的功能,该如何做呢?如果我们来做一个框架,比如叫Summer,该如何做呢?
基本需求:
1.要有读取配置文件的功能。
2.为了应对各种变化,通过工厂框架专门负责创建对象,并在用户需要的时候提供对应的对象
3.如何创建? new? 反射?最好使用反射,因为更容易对创建过程进行抽象。

另外,要保证框架的通用性,一个基本任务是将掉用和被调用方的耦合度最低。
为此可以这么做:
(1)利用工厂模式创建被调用方,从而隔离了调用方和被调用方之间的耦合。但是工厂和被调用方的耦合度仍然很高,所以:
(2) 通过xm 解析获得要装载哪些对象,条件和参数等是什么。
(3)反射,通过获得类的字节码文件来操作类对象的内容。 就是创建了类的代理对象来完成相关的内容。
一句话就是通过工厂模式来隔离调用者和被调用者,工厂通过配置获得被调用者的信息和参数,然后通过反射构造出来。
由此,我们也看到了Spring的几个基本设计:
1.读取配置使用BeanDefinition
2.读取之后的信息存放到一个HashMap中,内部使用BeanFactory来管理。
3.通过ApplicationContext统一给用户提供服务。
上面第1 和2的过程就是IOC,第2和3的过程就是DI。

2.一起从Bean开始

2.1 Spring Beans

1.什么是 Spring beans?
官方的定义是:Spring beans 是那些形成 Spring 应用的主干的 java 对象。被 Spring IOC 容器实例化,装配,和管理的对象。
这些 beans 通过容器中配置的元数据创建。比如以 XML 文件中 的形式定义。
2.Spring Bean的特征是什么?
bean是对象,一个或者多个不限定
bean由Spring中的IoC管理
我们的应用程序由一个个bean实体构成
Bean是用户提供给容器的的元配置信息。一个 Spring Bean 的定义包含容器必知的所有配置元数据,包括如何创建一个 bean,它的生命周期详情及它的依赖。

3. Spring的思想和关键策略

Spring的基础是BOP:面向Bean(普通的Java类)。而实现过程中主要是IOC和ID两个部分。IOC(控制反转),主要是通过配置文件,注解等描述对象和对象之间的关系,并以此为基础进行创建。 DI(依赖注入)就是对象不是从容器中查找它依赖的类,而是在容器实例化时主动将对象依赖的类注入给它(不是依赖什么创建什么,而是需要什么给什么)。
Spring的关键策略有四个,听着名字就吓人,但非常重要:
1.基于POJO的轻量级和最小侵入性编程
当我们接触过java编程一段时间之后就会发现很多框架会强迫应用继承他们的类或实现他们的接口从而让应用和框架进行绑死。比如代理模式,如果使用jdk需要目标对象实现一个接口,然后在生成新的类的时候实现接口。
我找到一个EJB的例子:

package eric.example;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
public class HelloWorldEJBBean implements SessionBean{
    public void ejbActive(){         //我们不需要这些方法
    }  
    public void ejbPassivate(){
    }
    public void ejbRemove(){
    }
    public void SetSessionContext(SessionContext ctx){
    }
    public String sayHello(){       //EJB的核心业务逻辑
        return "Hello World"  ; 
    }
    public void ejbCreate(){
    }
    }

上面的代码,用户真正关心的是sayHello方法,这需要实现EJB的无状态会话接口SessionBean。SessionBean接口强迫你参与到ejb的生命周期里来,这时候就不得不实现该接口的很多方法,这增加了用户的工作量,同时代码也也不可能从EJB移植到其他框架上。
早期版本的Struts、WebWork等等,这些重量级框架都存在这些问题:强迫开发者编写大量冗余代码、应用与框架绑定不易于迁移,通常也很难编写测试代码。Spring竭力避免因为自身的API弄乱开发者的代码。Spring不会强迫你实现Spring的一些接口或者集成Spring规范的类,通常我们在使用Spring的时候没有任何痕迹说明你使用了Spring除了注解。
而Spring框架的代码如何写呢?

package eric.example;
public class HelloWorldBean {
public String sayHello(){       //这就是我们的需要的。
	return "Hello World";
}
}

2.通过依赖注入和面向接口松耦合
通常在创建对象的时候需要手动设置大量的对象参数,而Spring中,大部分是可以通过Spring的依赖注入给你做好。而这个过程就是依赖注入。
这里有两个非常相关的名词,IOC和DI。前者可以理解为专注于描述对象如何被创建,而不用创建对象。后者是在容器中创建对象和对象之间的关系。
Spring通过applicationContext等配置文件确定对象之间的依赖关系,在IOC容器中将我们需要的对像创建并管理,而这也是Spring诞生的初衷。
3.基于切面和惯性进行声明式编程
声明式的代码增强,程序员只关心业务,重复事情不用重复做。例如scan,自动扫描包来实现一些功能
4.通过切面和模版减少样板式代码
典型的是操作数据库的时候, JdbcTemplate以及注解。
几乎Spring做的任何事情都可以追溯到上面的一条或者多条核心策略,我们可以通过一些案例进一步阐明这些理念。

4. Spring的特征

1.轻量——从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。此外,Spring是非侵入式的:典型地,Spring应用中的对象不依赖于Spring的特定类。
2.控制反转——Spring通过一种称作控制反转(IoC)的技术促进了低耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。
3.面向切面——Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。
容器——Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每个bean如何被创建——基于一个可配置原型(prototype),你的bean可以创建一个单独的实例或者每次需要时都生成一个新的实例——以及它们是如何相互关联的。然而,Spring不应该被混同于传统的重量级的EJB容器,它们经常是庞大与笨重的,难以使用。
4.框架——Spring可以将简单的组件配置、组合成为复杂的应用。在Spring中,应用对象被声明式地组合,典型地是在一个XML文件里。Spring也提供了很多基础功能(事务管理、持久化框架集成等等),将应用逻辑的开发留给了你。
5.MVC——Spring的作用是整合,但不仅仅限于整合,Spring 框架可以被看做是一个企业解决方案级别的框架。客户端发送请求,服务器控制器(由DispatcherServlet实现的)完成请求的转发,控制器调用一个用于映射的类HandlerMapping,该类用于将请求映射到对应的处理器来处理请求。HandlerMapping 将请求映射到对应的处理器Controller(相当于Action)在Spring 当中如果写一些处理器组件,一般实现Controller 接口,在Controller 中就可以调用一些Service 或DAO 来进行数据操作 ModelAndView 用于存放从DAO 中取出的数据,还可以存放响应视图的一些数据。 如果想将处理结果返回给用户,那么在Spring 框架中还提供一个视图组件ViewResolver,该组件根据Controller 返回的标示,找到对应的视图,将响应response 返回给用户。

5.Spring的组件和功能

Spring目前有20多个模块,大致可以分为四个主要的部分,IOC和DI解决了基础容器的构建和管理;AOP解决了面向切面的应用;数据库管理相关;web,另外还有工具、消息等模块。
Spring的组件
上面大致可以分为四个主要部分:

  1. Spring-core和spring-beans和context模块是Spring的核心模块,包含了控制反转(IOC)和依赖注入(DI)。基础是BeanFactory接口,使用控制反转对应用程序的配置和依赖性规范与实际的应用程序代码分离,但是容器是懒加载的,只有当BeanFactory容器被使用时才会对该Bean进行实例化和装配依赖关系。getBean是IOC和DI的分界线。
  2. ApplicationContext以core和bean为基础,扩展了BeanFacory,为其添加了生命周期,框架事件体系以及资源加载等功能。Java服务启动时ApplicationContext容器也会启动,完成加载配置等一系列任务,使之处于怠速状态。
  3. spring-aop,spring-aspects和spring-instument3个模块实现了AOP的相关功能。AOP以动态代理为基础,设计出了一系列的AOP横切实现,比如前置通知,返回通知,异常通知等。aspects模块集成了AspectJ框架,主要是为SpringAOP提供多种AOP实现方法。spring-instument,Java在1.5引入java.lang.instrument,你可以由此实现一个Java agent,通过此agent来修改类的字节码即改变一个类,从而实现AOP。
    4.数据访问和集成。由Spring-jdbc,Spring-tx,spring-orm,spring-jms和spring-oxm 5个模块组成。jdbc模块用于简化jdbc操作,orm等。

5.一个基于XML的完整例子

现在我们再看一个完整的基于XML的例子作为本篇收尾:
基于xml创建对象的基本结构是:

<bean id="user" class="com.liuqingchao.spring5.User"></bean>

(1)在 spring 配置文件中,使用 bean 标签,标签里面添加对应属性,就可以实现对象创建
(2)在 bean 标签有很多属性,介绍常用的属性,例如:
id 属性:唯一标识
class 属性:类全路径(包类路径)
(3)创建对象时候,默认也是执行无参数构造方法完成对象创建
看一个完整的例子:
创建Bean:

public class Book {
    //创建属性
    private String bname;
    private String bauthor;
    private String address;
    // getter setter
    }

配置文件的写法:

<bean id="book" class="com.liuqingchao.spring5.Book">
    <property name="bname" value="易筋经"></property>
    <property name="bauthor" value="达摩老祖"></property>
    <property name="address">
        <value=“南京“></value>
    </property>
</bean>

调用方法:

@Test
public void testBook1() {
    //1 加载spring配置文件
    ApplicationContext context =
            new ClassPathXmlApplicationContext("bean1.xml");
    //2 获取配置创建的对象
    Book book = context.getBean("book", Book.class);
    System.out.println(book.getAddress());
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

纵横千里,捭阖四方

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值