《Spring实战》学习笔记(1)——Spring介绍

最近准备重头开始再系统地学习一遍Spring框架,使用《Spring实战》这本书为了加深印象并方便之后高效的复习,所以有了写这个文章的打算。如果有哪里写的不好还请各位大神批评指正。


Spring是为了解决企业级应用开发的复杂性而创建的一个开源框架。采取了以下四种策略来降低Java开发复杂性:

  • 基于POJO的轻量级和最小侵入性编程
  • 通过依赖注入和面向接口实现松耦合
  • 基于切面和惯例进行声明式编程
  • 通过切面和模板减少样板式代码
一、非侵入编程模型:

  Spring不会强制我们去实现Spring规范的接口或类,最多需要我们去使用注解。对于一个普通的Java类,我们不需要使用侵入性的编程去破坏它。

二、依赖注入(DI)

用以下几个类做例子:

+---java
|   +---enemy
|   |       ArtilleryCorps.java  炮车兵
|   |       Enemy.java           敌人接口
|   |       MeleeCreeps.java     近战兵
|   |       RemoteSoldier.java   远程兵
|   |
|   \---hero
|           ADHero.java          AD英雄
|           APHero.java          AP英雄
|           Hero.java            英雄接口
|
\---resources

注意Hero与Enemy的关系:

public class APHero implements Hero {
    private MeleeCreeps enemy;

    public APHero() {
        this.enemy = new MeleeCreeps(); // 与MeleeCreeps(近战兵)高耦合
    }

    public void killEnemy() {
        enemy.dead();
    }
}

  可以看到,APHero在构造函数中创建了MeleeCreeps,使得它与MeleeCreeps类耦合度很高,导致APHero只能击杀(killEnemy方法)MeleeCreeps,如果想要击杀其他小兵就无法做到了。
而使用依赖注入,不需要Hero再来创建或管理对Enemy的依赖关系了,Spring将会自动将Enemy交给Hero。

所以代码将可以变成这样:

public class ADHero implements Hero{
    private Enemy enemy;

    public ADHero(Enemy enemy) {
        this.enemy = enemy;
    }

    public void killEnemy() {
        enemy.dead();
    }
}

  对比之前的APHero,ADHero并没有自己创建enemy,而是将其作为构造方法的参数传入进来,并且传入的类型是Enemy,所以不论传入的是什么类型的小兵,ADHero都能将其击杀。这种注入方式称为构造器注入

  既然ADHero可以接收任意一种Enemy的实现类了,那就要考虑如何将Enemy传给他。

这是将要注入到ADHero中的MeleeCreeps类

public class MeleeCreeps implements Enemy {
    public void dead() {
        System.out.println("近战兵被击杀");
    }
}

创建应用组件之间协作的行为通常称为装配(wiring)

Spring有主要三种装配方式:

  • 在Java中显式配置
  • 在XML中显式配置
  • 隐式bean发现机制和自动装配

暂时只介绍前两种

1.在Java中显式配置

@Configuration
public class HeroConfig {

    // 创建MeleeCreeps bean
    @Bean
    public Enemy enemy(){
        return new MeleeCreeps();
    }
    
    // 注入MeleeCreeps到ADHero中
    @Bean
    public Hero hero(){
        return new ADHero(enemy());
    }
    
}

2.在XML中显式配置

<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">

    <!--  创建MeleeCreeps bean  -->
    <bean id="meleeCreeps" class="enemy.MeleeCreeps">
    </bean>

    <!--  注入MeleeCreeps到ADHero中  -->
    <bean id="adHero" class="hero.ADHero">
        <constructor-arg ref="meleeCreeps"></constructor-arg>
    </bean>
</beans>
三、面向切面编程(AOP)

如果要增加一个新闻播报功能,在英雄击杀敌人前后给出提示,该怎么样实现呢?

public class Broadcast {
    // killEnemy方法之前调用
    public void promptBeforeKill(){
        System.out.println("英雄准备击杀敌人了");
    }

    // killEnemy方法之后调用
    public void promptAfterKill(){
        System.out.println("英雄击杀了敌人");
    }
}

使用之前的依赖注入修改ADHero,如下:

public class ADHero implements Hero{
    private Enemy enemy;
    private Broadcast broadcast;

    public ADHero(Enemy enemy,Broadcast broadcast) {
        this.enemy = enemy;
        this.broadcast = broadcast;
    }

    public void killEnemy() {
        broadcast.promptBeforeKill();
        enemy.dead();
        broadcast.promptAfterKill();
    }
}

这样实现乍一看似乎没有问题,但仔细想想,Broadcast不该由ADHero去管理。播报本身就是Broadcast该做的事情,不需要由ADHero去控制它来做事。

所以要解决这个问题,就需要使用AOP,将Broadcast抽象成一个切面。

 <!--  创建MeleeCreeps bean  -->
    <bean id="meleeCreeps" class="enemy.MeleeCreeps">
    </bean>

    <!--  注入MeleeCreeps到ADHero中  -->
    <bean id="adHero" class="hero.ADHero">
        <constructor-arg ref="meleeCreeps"></constructor-arg>
    </bean>

    <!--  创建Broadcast bean  -->
    <bean id="broadcast" class="broadcast.Broadcast"></bean>

    <!--  AOP配置  -->
    <aop:config>
        <!--   将  Broadcast bean声明为切面   -->
        <aop:aspect ref="broadcast">
            <!--     定义切点       -->
            <aop:pointcut id="killEnemy" expression="execution(* *.killEnemy(..))"/>
            <!--     前置通知       -->
            <aop:before method="promptBeforeKill" pointcut-ref="killEnemy"></aop:before>
            <!--     后置通知       -->
            <aop:before method="promptAfterKill" pointcut-ref="killEnemy"></aop:before>
        </aop:aspect>
    </aop:config>

在killEnemy方法前调用的,称为前置通知,在killEnemy方法后调用的,称为后置通知

对比之前的方式,ADHero不需要再去管理Broadcast,可以说ADHero完全不知道Broadcast,而且我们不需要去改动Broadcast的代码,只是在xml里进行配置就可以达到抽象成切面的目的。

四、使用模板消除样板式代码

  比如编写JDBC代码进行查询数据库操作时,会出现大量的样板式代码:创建数据库连接,创建语句对象,查询之后还要关闭连接等等。而使用Spring的JdbcTemplate可以避免这样的重复代码。

五、关于Bean

  在Spring应用中,对象都生存在Spring容器中。Spring要创建对象,装配对象并管理对象的整个生命周期。

bean的生命周期:

六、总结

  这一章对Spring的功能特点有了整体的认识了解,核心还是DI和AOP。之后会依次对每个知识点展开学习。

posted @ 2020-08-12 19:41  当代艺术家  阅读(170)  评论(2)    收藏  举报