Spring的个人理解

作者: alonwang | 来源:发表于2018-08-04 21:30 被阅读33次

    前言

    Java后端开发是必须要掌握Spring框架的.这半年的工作中遇到了很多Spring相关的问题,都是网上搜索寻找解决办法,虽然能解决问题,但是对原因还是一知半解,这段时间把Spring的官方文档过了一遍,同时跟随刘欣(码农翻身作者)的spring课程搭建一个微型Spring,扫清了很多疑问,在此总结一下,也希望对新手起到一点帮助作用.

    Spring的目的

    Spring 官方图片

    以上就是Spring的终极目标,为了完成上图的功能,Spring有以下三个技术要点

    • Dependency Inject,依赖注入
    • Aspect Oriented Programing,面向切面编程
    • Spring容器

    DI

    如果没有Spring,对于两个有依赖关系的对象,我们会这样处理

    class A{
    }
    
    class B{
      private A a;
      public B(A a){
      this.a=a;
      }
    

    有了Spring,我们就可以这样处理

    class A{
    }
    
    class B{
    @Autowired
    private A a;
    }
    

    两者的区别就是,B的依赖不再又B自己来处理,而是由Spring来处理,即依赖注入,在我看来依赖注入最大的意义的就是解耦

    想一下,在构建大型系统时,一个对象A可能被十几其他的对象objs依赖,并且真实环境中一个对象需要其他辅助信息来完成创建而不是简单的new出来,比如下面这个例子,A可能需要一个文件来完成初始化,如果由对象自己处理依赖,那么所有依赖于A的对象都要完成A的初始化逻辑(这也违背了DRY原则),如果后续A发生变化,那么他们都要修改.

    class A{
      public A(File f){
      ...
      }
    }
    
    

    通过依赖注入,也完成了将变化汇集到一处的目的,只要在定义A的地方修改对应的逻辑即可,依赖于A的对象不会感知到这些,下面是Spring中Bean定义的例子,如果A发生了变化,只修改这里就可以了.

    @Bean 
    public A initA(File f){
    ...
    }
    

    AOP

    AOP的含义这里不过多讲述,举一个最经典的例子,事务管理,如果没有Spring,要完成事务管理,我们会这样做

    class Service{
    public void doSomething(){
    TransactionUtil.startTransaction(...);
    
    ...
    
    TransactionUtil.commit(...);
    }
    

    对于所有需要事务的方法我们都要这样处理,显然这是不可接受的,事务管理这种事情和业务逻辑无关,不应该出现在业务代码中单又真实需要,怎么办? AOP!!!

    使用AOP,业务代码中不会掺杂无关的逻辑(当然需要提前进行全局匹配)

    class Service{
    @Transaction(...)
    public void doSomething(){
    ...
    }
    

    AOP的意义在于将事务管理/日志记录/安全控制这些必须处理但又和业务无关的逻辑抽离出来.

    Spring的AOP是基于代理的,还是以事务管理为例,下图中演示了基本原理,对于带有@Transactional注解的方法,Spring会为他生成一个代理对象,每当有调用时,代理对象先完成事务的开启,再调用真实对象的目标方法,最终提交事务.


    来自IBM开发者论坛

    这里有一个易错处:在目标方法内调用本类的方法会导致事务不会生效,如下

    class Service{
    @Transaction(...)
    public void methodA(){
    ...
    }
    public void mothodB(){
    ...
    methodA();
    }
    

    在methodB中调用methodA会导致事务无法生效,原因就是在方法内调用本类方法,相当于调用本类的私有方法,是不会经过代理的,也就不会有事务的处理,(如果methodB有@Transactional注解并且事务传播为Required等,methodA会继续methodB事务,还是像上面说的,就像调用了一个私有方法)

    Spring容器

    为了完成依赖注入,Spring需要解决下面这两个问题

    • 哪些对象需要使用Spring容器管理?
    • 对象之间的依赖关系是怎样的?

    为了解决上面的问题,Spring容器应运而生.Spring通过解析配置元数据来完成bean(就是被Spring管理的普通的java对象)的实例化,装配,依赖处理等待.简而言之就是用户提供配置元数据,Spring交付一个完备待用的对象

    配置元数据

    配置元数据描述了对象的依赖关系,实例化所需的信息,作用范围等等..主要途径有

    • XML
    • 注解
      这些信息被Spring读取后,对应每个对象生成一个BeanDefinition.

    一个典型的xml配置会是这样

    <beans>
        <bean id="foo" class="x.y.Foo">
            <constructor-arg ref="bar"/>
            <constructor-arg ref="baz"/>
        </bean>
    
        <bean id="bar" class="x.y.Bar"/>
    
        <bean id="baz" class="x.y.Baz"/>
    </beans>
    

    基于注解的配置会是这样

    @Bean
    class Foo{
      @Autowired
      private Bar bar;
      @Autowired
      private Baz baz;
    }
    

    个人倾向于注解形式,但是要认识要使用注解和xml本质上是相同的,使用注解更为方便是因为Spring的开发人员帮你把这些脏活累活做了

    总结

    Spring是javaEE事实上的标准,它的设计理念是轻量级,非侵入式,最小依赖.它的实现充分利用了面向对象的思想,也充分利用了各种设计模式,代码整洁优雅,值得花费时间去学习,最快最好的方式就是先看下它的官方文档,再去看源码(Spring的源码现在已经非常庞大了,自己摸索太过困难,最好有人带着,这里推荐下刘欣大佬的<从零开始造spring>,不需要完全理解,只是找着敲一遍就能收获很多了).

    这篇文章很久之前就想写了,一直拖着,今天突然有了灵感,哈哈,很舒服


    1. Spring官方文档
    2. IBM的事务注解详解
    3. 从零开始造spring github地址

    相关文章

      网友评论

        本文标题:Spring的个人理解

        本文链接:https://www.haomeiwen.com/subject/kswmvftx.html