Spring Action 笔记(一) 初识Spring

前言

决定写博客,是受了《软技能-代码之外的生存之道》的启发,深知写博客利人利已的好处。

本人之前主要使用的语言并不是java,而是c#,也不是因为想转java而来学习Spring。我认为学习一种框架,不光知道如何使用框架,更重要的是理解框架背后的思想,而这个思想是不受语言限制的。在读了《Spring Action》第一章后,收获很多,也理解了之前一直模糊的一些思想,可以说收获很大,我会坚持这个博客系列,将我所收获的分享出来。下面切入正题。

Spring使命

简化java开发是Spring框架的终极目标,看似简单的几个字,实现起来并不简单,Spring一举颠覆java之前的众多框架,划出一道清新亮丽的风景线。Spring从以下几个方面做了努力使代码更简洁:

  • 激发POJO的潜能,不侵入代码。
  • 通过依赖注入和面向接口实现松耦合
  • 基于切面和惯例进行声明式编程
  • 通过切面和模板减少样板式代码

激发POJO的潜能,不侵入代码。

    之前很多框架都要求使用者继承框架接口或类,使得开发者需要更多的代码来适配框架。Spring可以让开发者只关注业务本身,POJO(Plain Ordinary Java Object)是一个简单的java类,无需为适配框架来写过多的不需要的代码。之下是两个例子:

程序1:EJB2.1 强迫实现你根本不需要的方法

public class HelloWorldBean implements SessionBean {
    public void ejbActivate(){}  // 业务无关的代码
    public void ejbPassivate(){}
    public void ejbRemove(){}
    public void setSessionContext(SessionContext ctx){}
    public void ejbCreate(){}

    public String sayHello() {
        return "Hello World";
    }
}

程序2:Spring简洁的代码

public class HelloWorldBean {
    public String sayHello() {
        return "Hello World";
    }
}

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

要理解理解依赖注入,首先搞清楚面向接口编程和面向实现编程的区别。

下面是一个简单的例子,面向接口编程的汽车类(CarA)依赖的是具有跑(Run)功能的轮子(IWheel),只要符合接口的任何厂商的轮子都能装入汽车A,以后想更换轮子,不需要重新来修改汽车架构。而面向实现编程的汽车B(CarB)依赖特定的轮子实现(WhellA),若以后出来更先进的轮子,汽车B也只能相望,除非重新改造自己。所以面向接口编程使得类之间是松耦合的。

程序3:面向接口编程和面向实现编程比较

// 面向接口编程
public class CarA{
    Iwheel whell;  
    
    public Run(){
        whell.Run();
    }
}

// 面向实现编程
public class CarB{
    WhellA whell;  

    public Run(){
        whell.Run();
    }
}

那么依赖注入和面向接口编程是什么关系呢。上例中的汽车A的轮子从何而来?如果是汽车内部new出来,还是依赖了具体实现类,以后换个轮子仍然要修改汽车类,下面的例子,汽车A依赖的轮子由构造函数注入进来,这样把具体使用哪种轮子交给汽车的装配者,汽车框架的制造者不关心轮子选用何种,从何而来,只知道轮子能实现自己想要的Run功能就够了。这样汽车装配者可以根据自己的喜好装出各种汽车(跑车、普通汽车、越野车),所以依赖注入是面向接口编程的一种实现方式。依赖注入除了构造函数注入,还会有其他的注入方式,会在后面的博客中一一解释,这里只需了解依赖注入的思想就够了。

// 面向接口编程
public class Car{
    Iwheel whell;

    public Car(Iwhell whellA){ // 构造函数依赖注入
        this.whell= whellA;
    }

    public Run(){
        whell.Run();
    }
}

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

依赖注入让相互协作的软件组件保持松散耦合,而AOP编程允许你把遍布应用各处的功能分离出来形成可重用的组件。哪些功能是遍布应用各处的?比如日志功能,权限功能等,这些功能被称为切面功能,因为他们横切各个业务模块。

相信很多人都看到过一个业务模块被淹没在日志、权限控制、事务这些跟业务逻辑无关的代码中的情况,即使这些功能已经很好的封装,但调用代码还是嵌在了业务逻辑代码中。AOP不但很好地将公共功能封装成组件,还使这些公共功能不会嵌在业务逻辑代码中,使得POJO更简单。如何使得切面功能代码不侵入业务代码中却能实现切面功能呢,Spring采用的是配置的方式,在需要的地方配置需要的切面功能。 比较下面两份代码,你可以很明显的看到切面编程使得POJO非常简洁,只关注业务逻辑,不关心切入功能,使得切面功能和业务模块耦合的更松散。

// 没有使用AOP,依赖日志接口,日志代码侵入业务逻辑
public class Car{
    Iwheel whell;
    ILog log;

    public Run(){
        log.info("before Run");
        whell.Run();
        log.info("after Run");
    }
}

// 使用AOP,POJO根本不知道有日志,日志是否需要和使用何种日志交给使用者,POJO只关心业务逻辑
public class Car{
    Iwheel whell;

    public Run(){
        whell.Run();
    }
}

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

相信大家有过这种经历,在编写某些代码的时候,感觉跟别的代码很相似,比如数据库代码,或者远程调用,或socket。先连接,后处理业务,然后关闭,还要catch exception等一系列重复性的代码,看下面这个例子,业务代码被样板代码淹没了。

Connection con = null;
        try {
            con = DriverManager.getConnection(dbUrl1, dbUserName, dbPassword);
            var stmt = con.prepareStatement("select * from users");  // 真正的业务代码只有这一行
            var rs = stmt.executeQuery();
            System.out.println("获取数据库连接成功!");
            System.out.println("进行数据库操作!");
        } catch (SQLException e) {
            e.printStackTrace();
            System.out.println("获取数据库连接失败!");
        }finally{
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
Spring旨在通过模板封装来消除样板式代码,使用jdbcTemplate,样板代码消除了,只需要关心业务逻辑。
jdbcTemplate.queryForObject(
        "select * from users",  // 查询语句
        new RowMapper<User>{} // 结果映射
  )

Bean容器

在spring中,POJO只关心自己的业务逻辑,不关心依赖的创建,实现。那有谁复杂依赖的创建,谁负责依赖的生命周期呢,那就是Bean容器。Bean容器装载所有的Bean(POJO类实例),通过注入bean来组装复杂bean。

 
 

Bean在容器中有一系列的生命周期节点,Bean开发者可以定义节点的钩子函数来定制Bean,比如销毁的时候,同时释放一些资源。

  • Bean的建立, 由BeanFactory读取Bean定义文件,并生成各个实例
  • Setter注入,执行Bean的属性依赖注入
  • BeanNameAware的setBeanName(), 如果实现该接口,则执行其setBeanName方法
  • BeanFactoryAware的setBeanFactory(),如果实现该接口,则执行其setBeanFactory方法
  • BeanPostProcessor的processBeforeInitialization(),如果有关联的processor,则在Bean初始化之前都会执行这个实例的processBeforeInitialization()方法
  • InitializingBean的afterPropertiesSet(),如果实现了该接口,则执行其afterPropertiesSet()方法
  • Bean定义文件中定义init-method
  • BeanPostProcessors的processAfterInitialization(),如果有关联的processor,则在Bean初始化之前都会执行这个实例的processAfterInitialization()方法
  • DisposableBean的destroy(),在容器关闭时,如果Bean类实现了该接口,则执行它的destroy()方法
  • Bean定义文件中定义destroy-method,在容器关闭时,可以在Bean定义文件中使用“destory-method”定义的

Spring模块

Spring框架由几个不同的模块组成,为开发企业级应用提供了所需的一切,下面是模块的概览图,会在后面的博客中详细介绍各个模块,这里只需要了解Spring的模块组成和各模块的功能就够了。


总结

以上是spring action第一章的总结,主要理解Spring的目标以及通过哪些手段来实现这些目标。相信你对Spring已经有了大概了解。后续博客将对Spring进行全面详细介绍。



猜你喜欢

转载自blog.csdn.net/xuefeiliuyuxiu/article/details/79587580