Spring实战(第4版) 学习(1)-依赖注入

前言:本文仅用于博主学习记录,可以批评指正。

1.spring是什么?

Spring是一个开源框架,最早由Rod Johnson创建,并在《Expert One-onOne:J2EE Design and Development》(http://amzn.com/076454385)这本著 作中进行了介绍。Spring是为了解决企业级应用开发的复杂性而创建的,使 用Spring可以让简单的JavaBean实现之前只有EJB才能完成的事情。但Spring 不仅仅局限于服务器端开发,任何Java应用都能在简单性、可测试性和松耦 合等方面从Spring中获益。 

2.为什么要用spring?

为了降低Java开发的复杂性,Spring采取了以下4种关键策略:

• 基于POJO的轻量级和最小侵入性编程;

• 通过依赖注入和面向接口实现松耦合;

• 基于切面和惯例进行声明式编程;

• 通过切面和模板减少样板式代码。


几乎Spring所做的任何事情都可以追溯到上述的一条或多条策略。以此来证明Spring是 如何完美兑现它的承诺的,也就是简化Java开发。

3.开始学习spring

程序清单1.1   Spring不会在HelloWorldBean上有任何不合理的要求 

可以看到,这是一个简单普通的Java类——POJO。没有任何地方表明它是一 个Spring组件。Spring的非侵入编程模型意味着这个类在Spring应用和非 Spring应用中都可以发挥同样的作用。 

尽管形式看起来很简单,但POJO一样可以具有魔力。Spring赋予POJO魔力 的方式之一就是通过DI来装配它们。让我们看看DI是如何帮助应用对象彼此 之间保持松散耦合的。


1.1.2 依赖注入  依赖注入


依赖注入这个词让人望而生畏,现在已经演变成一项复杂的编程技巧或设计 模式理念。但事实证明,依赖注入并不像它听上去那么复杂。在项目中应用 DI,你会发现你的代码会变得异常简单并且更容易理解和测试。

任何一个有实际意义的应用(肯定比Hello World示例更复杂)都会由两个或 者更多的类组成,这些类相互之间进行协作来完成特定的业务逻辑。按照传 统的做法,每个对象负责管理与自己相互协作的对象(即它所依赖的对象) 的引用,这将会导致高度耦合和难以测试的代码。
举个例子,考虑下程序清单1.2所展现的Knight类。
程序清单1.2   DamselRescuingKnight只能执行 只能执行RescueDamselQuest探险任务

可以看到,DamselRescuingKnight在它的构造函数中自行创建了Rescue DamselQuest。这使得DamselRescuingKnight紧密地和RescueDamselQuest耦合到了一起,因此极大地限制了这个骑士执行探险的能力。如 果一个少女需要救援,这个骑士能够召之即来。但是如果一条恶龙需要杀 掉,或者一个圆桌……额……需要滚起来,那么这个骑士就爱莫能助了。

更糟糕的是,为这个DamselRescuingKnight编写单元测试将出奇地困难。 在这样的一个测试中,你必须保证当骑士的embarkOnQuest()方法被调用的 时候,探险的embark()方法也要被调用。但是没有一个简单明了的方式能 够实现这一点。很遗憾,DamselRescuingKnight将无法进行测试。

耦合具有两面性(two-headed beast)。一方面,紧密耦合的代码难以测试、 难以复用、难以理解,并且典型地表现出“打地鼠”式的bug特性(修复一个 bug,将会出现一个或者更多新的bug)。另一方面,一定程度的耦合又是必 须的——完全没有耦合的代码什么也做不了。为了完成有实际意义的功能, 不同的类必须以适当的方式进行交互。总而言之,耦合是必须的,但应当被 小心谨慎地管理。
通过DI,对象的依赖关系将由系统中负责协调各对象的第三方组件在创建对 象的时候进行设定。对象无需自行创建或管理它们的依赖关系,如图1.1所 示,依赖关系将被自动注入到需要它们的对象当中去。

为了展示这一点,让我们看一看程序清单1.3中的BraveKnight,这个骑士不 仅勇敢,而且能挑战任何形式的探险。
程序清单1.3   BraveKnight足够灵活可以接受任何赋予他的探险任务 足够灵活可以接受任何赋予他的探险任务

我们可以看到,不同于之前的DamselRescuingKnight,BraveKnight没有 自行创建探险任务,而是在构造的时候把探险任务作为构造器参数传入。这 是依赖注入的方式之一,即构造器注入(constructor injection)。 

更重要的是,传入的探险类型是Quest,也就是所有探险任务都必须实现的 一个接口。所以,BraveKnight能够响应RescueDamselQuest、 SlayDragonQuest、 MakeRound TableRounderQuest等任意的Quest实 现。
这里的要点是BraveKnight没有与任何特定的Quest实现发生耦合。对它来 说,被要求挑战的探险任务只要实现了Quest接口,那么具体是哪种类型的 探险就无关紧要了。这就是DI所带来的最大收益——松耦合。如果一个对象 只通过接口(而不是具体实现或初始化过程)来表明依赖关系,那么这种依 赖就能够在对象本身毫不知情的情况下,用不同的具体实现进行替换。
 

构造器注入的xml实现方式

这是一个简单Dao类 有三个属性name,sex,age

public class Dao {
    private String name;
    private String sex;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public int getAge() {
        return this.age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

这是一个service构造方法 传入dao类

public class Service {

    private Dao dao;

    public Service(){

    }

    public Service(Dao dao){
         this.dao=dao;
    }

    public int getAge(){
        dao.setAge(12);
        return dao.getAge();
    }

}

这是测试类test 实例化service 并调用getAge()方法。

public class test {
    public static void main(String[] args) {
        testDI();
    }
    public static void testDI() {
        ApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");

        Service service = ac.getBean(Service.class, "Service");

        System.out.println(service.getAge());
    }
}

application.xml

<?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.xsd">
    <bean id="Dao" class="test.springtest.TestDao.Dao"></bean>

    <bean id="Service" class="test.springtest.TestService.Service">
        <constructor-arg name="dao" ref="Dao" ></constructor-arg>
    </bean>


</beans>

上面是xml配置 <bean id="Dao" class="test.springtest.TestDao.Dao"></bean>实例化dao类

<bean id="Service" class="test.springtest.TestService.Service">
        <constructor-arg name="dao" ref="Dao" ></constructor-arg>
    </bean>

实例化service类 并向service类构造器注入dao类。

运行test类 输出

12

依赖注入实现方式二

setter注入

还是刚才那个dao类

public class Dao {
    private String name;
    private String sex;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public int getAge() {
        return this.age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

service 里setDao方法

public class Service {

    private Dao dao;

    public int getAge(){
        dao.setAge(12);
        return dao.getAge();
    }

    public void setDao(Dao dao){
        this.dao=dao;
    }

}

测试类不变

public class test {
    public static void main(String[] args) {
        testDI();
    }
    public static void testDI() {
        ApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");

        Service service = ac.getBean(Service.class, "Service");

        System.out.println(service.getAge());
    }
}

application.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    <!--<context:annotation-config/>-->
    <bean id="Dao" class="test.springtest.TestDao.Dao"></bean>

    <bean id="Service" class="test.springtest.TestService.Service">
        <property name="dao" ref="Dao"></property>
        <!--<constructor-arg name="dao" ref="Dao" ></constructor-arg>-->
    </bean>
</beans>

这里构造器的配置已经注释掉了,不再用,添加了一个<property>

然后根据名字dao去匹配set方法

   public void setDao(Dao dao) {
        this.dao=dao;
    }

类一致则将Dao类注入到set方法中。

运行测试类,输出

12

依赖注入实现方式三

注解注入

上面dao,test都不变,只有service,xml改变

public class Service {
    @Autowired
    private Dao dao;

    public int getAge(){
        dao.setAge(12);
        return dao.getAge();
    }

}

这里直接把@Autowired加到私有变量上,也是可以注入的。

application.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    <context:annotation-config/>
    <bean id="Dao" class="test.springtest.TestDao.Dao"></bean>

    <bean id="Service" class="test.springtest.TestService.Service">
        <!--<property name="dao" ref="Dao"></property>-->
        <!--<constructor-arg name="dao" ref="Dao" ></constructor-arg>-->
    </bean>
    
</beans>

这里构造器 property都不再用,增加了一个<context:annotation-config/>自动扫描,

有了这个,spring会自动扫描找到@Autowired,然后把实例化的dao注入到service的私有变量dao中。

运行测试类

输出 12

成功,完美!

如果有错误,随着实践,再来纠正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值