zoukankan      html  css  js  c++  java
  • 如何用比较快速的方法掌握Spring的核心——依赖注入,Java web轻量级开发面试教程 读书笔记

          我们知道,Java方面的高级程序员一定得掌握Spring的技能,其中包括Spring 依赖注入(IOC),面向切面(AOP),和数据库的整合(比如和Hibernate整合或声明式事务等)以及Spring MVC架构。其中,Spring的依赖注入是重中之重,在面试时,面试官一定会问这方面的问题。

          根据我的培训和面试经验,这方面的知识点虽然不难(初学者估计最多3天就能看明白并调通程序),但要把其中的各混淆点(也就是面试点)讲清楚不容易,换句话说,初级程序员在学习Spring IOC这部分的知识时,或多或少会走些弯路,在通常情况下,会经过一次或多次面试的失败(就是交学费)后才能理顺这部分的知识体系。

          在本文里,将整理归纳 java web轻量级开发面试教程 里的相关知识点,一次性地把这部分的知识点讲全,可以这样说,大家看完这篇文章后,能尽量减少交学费的次数。

          以上是开场白,如下是正文。

    --------------------------------------------------------------------------------------------------------------------

            1 通过一个简单但易学的案例来了解依赖注入

            步骤一 开发提供服务的SayHello.java程序。        

     1 package com;
     2 public class SayHello {
     3     private HelloWorldSpring helloWorldSpring;
     4     public HelloWorldSpring getHelloWorldSpring() {
     5         return helloWorldSpring;
     6     }
     7     public void setHelloWorldSpring(HelloWorldSpring helloWorldSpring) {
     8         this.helloWorldSpring = helloWorldSpring;
     9     }
    10     public void sayHello(){
    11         System.out.println("Say Hello:" + helloWorldSpring.sayHello());
    12         }
    13     }

           第10行定义了一个sayHello的方法,在这方法里,调用了在第3行定义的helloWorldSpring对象,输出一串文字。

           这里有一个比较有意思的现象,虽然在第4行和第7行针对helloWorldSpring对象定义了get和set的方法,但在第11行使用helloWorldSpring对象之前,始终没有用new关键字初始化这个对象,那么按照以往的经验,会不会出现空指针异常呢?

            别着急,先看下HelloWorldSpring这个类里有没有特殊的动作。

            步骤二 定义HelloWorldSpring.java这个类。

    1 package com;
    2 public class HelloWorldSpring {
    3     private String sayContent;    
    4     public String sayHello() {
    5         System.out.println("HelloWorld Spring!");
    6         return "Hello World Spring";
    7     }
    8 }

            这里直接在第4行定义了sayHello的方法,也没看到特殊的代码。接下来看一下在SpringMain这个类里是如何调用的。

            步骤三 开发调用者SpringMain.java,请大家注意一下调用sayHello方法的方式。

            

     1 package com;
     2 import org.springframework.context.ApplicationContext;
     3 import org.springframework.context.support.ClassPathXmlApplicationContext;
     4 public class SpringMain {
     5     public static void main(String[] args) {
     6         ApplicationContext context = new ClassPathXmlApplicationContext(
     7                 "conf/applicationContext-*.xml");
     8 
     9     SayHello sayHello = (SayHello) context.getBean("SayHello");
    10     sayHello.sayHello();        
    11     }
    12 }

            关键代码在第9行和第10行,是通过一个getBean方法,获得了SayHello对象的一个实例,随后调用了其中的sayHello方法。在这之前,通过第6行的代码,装载了一个配置文件。

           戏法变到这里,大家看到了两个不可思议的地方。第一,在SayHello类里,始终没有初始化HelloWorldSpring对象,就直接用了。第二,在SpringMain类里,没有像往常那样用SayHello sayHello = new SayHello();  的方法初始化对象,而是通过getBean的方式来获得类的实例。

          其实,这里大家已经能看到“低耦合”的写法了。让我们最后看完Spring的配置文件再来综合体验IoC的好处。

          步骤四 编写Spring的配置文件applicationContext-service-api.xml,关键代码如下

          

    1 <bean id="HelloWorldSpring" class="com.HelloWorldSpring">
    2 </bean>
    3 <bean id="SayHello" class="com.SayHello" >
    4 <property name="helloWorldSpring" ref="HelloWorldSpring" />
    5 </bean>

            在第1行和第2行里,定义了一个Bean。在Spring里,一个Bean往往和一个类所对应,这里id是HelloWorldSpring的这个Bean是和com.HelloWorldSpring这个类所对应。

            而在第3行到第5行里,用id是SayHello这个Bean对应com.SayHello这个类,请大家注意第4行,用内置property这个方式,把HelloWorldSpring这个类内嵌到SayHello类里。

           2 IoC的特点,不用New就可以初始化类

           SpringMain.java的主要代码如下。

    1     ApplicationContext context = new ClassPathXmlApplicationContext("conf/applicationContext-*.xml");

    2     SayHello sayHello = (SayHello) context.getBean("SayHello");

    3     sayHello.sayHello();       

           运行SpringMain.java时,首先是把配置文件里定义的信息装载到context类里;接着在第2行里,通过context.getBean方法,根据配置文件的定义,获取ID为SayHello(即class是com.SayHello)这个类;随后在第3行里使用这个类的sayHello方法。从代码中大家可以看出,这里同样没有用到new,而是根据配置文件来初始化类。

           没有使用new,就意味着低耦合,具体而言,就是SayHello、HelloWorldSpring和SpringMain这三个类之间的耦合度很低。

          假设有三个团队在开发维护这三个类,如果用常规new方法来创建类,比如在SayHello类里用HelloWorldSpring helloWorldSpring = new HelloWOrldSpring();,那么一旦管理HelloWorldSpring类的团队要修改调用的接口,比如new的构造函数需要带参数,那么SayHello类的管理者就不得不受无妄之灾,也需要修改本身的代码。

          要知道在公司里,修改代码并且发布到生产环境,要经过很烦琐且很严格的审批流程,必须要经历代码审查、代码提交、测试人员测试、领导审批、最终发布以及发布后检查这些步骤。如果用刚才看到的通过配置文件装载类,在本地代码里没有new的这套开发方式,那么如果一个团队修改了代码,其他团队就有可能不必改动代码,这样就可以很大程度上避免不必要的工作。

          3 控制翻转和依赖注入

          控制翻转的英文名字叫IoC(Inversion of Control),依赖注入英文名叫DI(Dependency Injection),下面通过下表来看一下它们的概念。

           

    概念名

    含义

    表现形式

    控制翻转(IoC,控制反转)

    类之间的关系,不用代码控制,而是由Spring容器(也就是Spring的jar包)来控制。控制权由代码翻转到容器里,这叫控制翻转

    在初始化对象时,在代码里无须new,而是把类之间的关系写到配置文件里

    依赖注入(DI)

    在代码运行时,如果我们要在一个类里使用(也叫注入)另一个类,比如在上述的SayHello类里要初始化另外一个HelloWorldSpring类,那么这种注入就是依赖于配置文件的

    同样是把类之间的调用关系写到配置文件里,在运行时,会根据配置文件,把HelloWorldSpring这个类注入SayHello里

            通过上面的描述,能看到它们其实是从不同的角度讲述的同一件事情。依赖注入强调类的注入是由Spring容器在运行时完成,而控制反转强调类之间的关系是由Spring容器控制。

           从这两个名词可知, Spring给我们带来了一种全新的编程理念,即不用new也可以创建和使用对象。这种开发方式让我们能像搭积木一样组装不同的类,组装后的类之间的耦合度很低,一个类的修改可以不影响(或者影响度很小)其他的类,这样就可以避免一个小修改带来的一大串连锁反应。

           大家在了解Spring的时候,一定请理解“低耦合”这个好处,这本来是面向对象思想带给我们的好处,在Spring开发的过程中我们确实能感受到。

     

          4 读取配置文件的各种方式

           在Spring里,通常在配置文件中描述各类以及类之间的包含关系,在使用的时候,会先加载配置文件,Spring的内核会读取配置文件,随后动态地组装各类。

           通过下表来总结一下读取配置文件的各种方式,它们之间没有优劣之分,大家可以挑选个最适用的,具体来讲,没有特殊情况,就可以用ClassPathXmlApplicationContext。

     

    类名

    例子

    XmlBeanFactory

    Resource resource = new ClassPathResource("bean.xml"); 

    BeanFactory factory = new XmlBeanFactory(resource); 

    ClassPathXmlApplicationContext

    ApplicationContext factory=new ClassPathXmlApplicationContext("conf/appcontext.xml"); 

    用文件系统类来读取

    FileSystemXmlApplicationContext

    ApplicationContext factory=new FileSystemXmlApplicationContext("classpath:appcontext.xml"); 

                  5 单例和多例

            我们知道,Spring的容器会在程序运行时,根据配置文件自动地创建(或者叫实例化)具体的Java类(也叫class,或叫Bean)。在配置文件里,可以设置创建文件时是否用单例的方式,如果没有设置,则会自动用默认的单例的方式来创建文件。如果不想用单例,则可以通过如下两种语法来修改,它们是等价的。

            <bean id="SayHello" class="com.SayHello" singleton="false"> 或者

             <bean id="SayHello" class="com.SayHello" scope="prototype">

           在实际项目中,一般用单例模式来创建无状态的Bean,而对于有状态的Bean,一般不用这种模式。所谓无状态的Bean,是指没有能够标识它目前状态属性的Bean,比如共享单车,A用好以后,可以放入共享池(即放到马路边上),B可以继续使用。由于没有供某个特定的用户使用,所以也就不能保持某一用户的状态,所以叫无状态Bean。相反,如果针对个人的自行车,那么会有个状态来表明是个人的。

          讲到这里,请大家确认如下概念,并不是我们首先设置了singleton是false,所以Spring容器才用单例的方式,恰恰相反,根据实际的需求,待创建的类可以被其他多个类共享,因此我们才设置singleton是false。是先有需求再有具体的实现。

        这个知识点可以说是Spring面试的必考点,下面通过下表来对比一下两者的差别

         

    列别

    实际用例

    特点

    有状态Bean

    我们访问网站登录后都有自己的用户名和密码,系统可以用一个有状态的Bean来记录我们的访问信息,比如来源IP访问页面列表和访问时间等

    会为每次调用创建一个实例,一旦调用结束,比如用户离开了网站,则该Bean就会被销毁

    无状态Bean

    数据库连接的通用类,其他类可以用它来获取数据库连接并进行操作

    可以在缓冲池里只维护一个实例,无须创建和销毁操作,性能高,但是线程不安全

     

     

      6 论面试

            当年我追过一本小说,叫天择,里面有个故事情节,皇帝请主角吃饭,让主角点菜,主角点的不是龙肝凤胆,也不是山珍海味,是两个家常菜,炒青菜和蛋炒饭,如下我引用的是书中原话:

           天下万事万物,都是有一个从简单到复杂,又从复杂趋向于简单的过程,用道家的话来说,就是天下大道,以简驭繁,用佛家的话来说,就是看山是山,看山不是山,看山仍是山的三大境界。蛋炒饭和炒青菜这两样东西每个人都吃了不知道多少次,但是,正因为如此,能够在这平凡当中做出来令人难忘的伟大味道,这才是顶尖的高手的境界!

         学习Spring IOC这平凡的知识点,也会经历过上述”从简单到复杂“的过程,作者根据多年面试培训(甚至包括写书)的经验,从纷繁复杂的Spring IOC的诸多知识点中提炼出针对初级程序员有用的上述内容,不能说是顶尖高手,但至少也经过沉淀,对大家多少有些帮助,也一定能帮助大家少走些弯路(这也是本文申请加入首页的理由)。

          面试时也这样,面试官会在乎候选人掌握多少知识点(广度),更在乎对于知识点的深度,如果候选人能从IOC这种平凡的知识点里说出自己的高深体会,这样反而能更打动面试官。

            在这方面可以说出如下的要点:

          1 基本概念(谁都会说)

          2 结合项目说明怎么用IOC,以及IOC的好处(不用new就能用,低耦合),这大家可以结合本文里提到的案例说明

          3 一些外围的知识点,比如如何导入配置文件

          4 特别地,请讲述单例和多例,并请结合具体例子说明在项目里的用法。

     

     

  • 相关阅读:
    【LeetCode】085. Maximal Rectangle
    哈希查找
    堆排序
    归并排序
    希尔排序
    快速排序
    堆区和栈区,malloc和new的区别
    C++基础
    二分查找
    冒泡排序
  • 原文地址:https://www.cnblogs.com/JavaArchitect/p/7513803.html
Copyright © 2011-2022 走看看