如何用比较快速的方法掌握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 特别地,请讲述单例和多例,并请结合具体例子说明在项目里的用法。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏开源优测

[接口测试 - 基础篇] 01 你应该了解的协议基础

概述 对于很多软件测试从业人员而言,可能并不具备协议相关基础,更别说去独立的构建协议抓包环境、分析协议报文,并在实际测试过程中结合业务进行接口级自动化测试。 下...

2894
来自专栏FreeBuf

对windows密码抓取神器mimikatz的逆向分析

mimikatz可谓获取windows明文密码神器,新版本更是加上了64位支持。用过一个小型获取明文密码程序,只有一个可执行文件ReadPSW.exe,通过逆向...

5538
来自专栏Albert陈凯

2018-05-17 架构师技能图谱,搞懂这些找工作无敌数据结构常用算法并发操作系统设计模式运维 & 统计 & 技术支持中间件网络数据库搜索引擎性能大数据安全常用开源框架分布式设计设计思想 & 开发模

8364
来自专栏我和未来有约会

AS一个新的框架Prana Framework

一个面向 Adobe Flex及ActionScript 3的控制反转(Inversion of Control,即IoC)应用框架。 控制反转(Invers...

2985
来自专栏JackieZheng

学习SpringMVC——国际化+上传+下载

  每个星期一道菜,这个星期也不例外~~~   一个软件,一个产品,都是一点点开发并完善起来的,功能越来越多,性能越来越强,用户体验越来越好……这每个指标的提高...

2536
来自专栏美团技术团队

日志级别动态调整——小工具解决大问题

背景 随着外卖业务的快速发展,业务复杂度不断增加,线上系统环境有任何细小波动,对整个外卖业务都可能产生巨大的影响,甚至形成灾难性的雪崩效应,造成巨大的经济损失。...

8235
来自专栏IT笔记

从构建分布式秒杀系统聊聊验证码

为了拦截大部分请求,秒杀案例前端引入了验证码。淘宝上很多人吐槽,等输入完秒杀活动结束了,对,结束了...... 当然了,验证码的真正作用是,有效拦截刷单操作,让...

1412
来自专栏开源优测

[接口测试 - 基础篇] 01 你应该了解的协议基础

概述 对于很多软件测试从业人员而言,可能并不具备协议相关基础,更别说去独立的构建协议抓包环境、分析协议报文,并在实际测试过程中结合业务进行接口级自动化测试。 下...

2946
来自专栏小特工作室

解决微信公众平台IP白名单

微信公众平台,作为自媒体的旗舰级产品,越来越多的人已经投入它的怀抱。正如它的广告词所说:再小的个体,也有品牌 好吧,闲话不多说,今天要说的是它的IP白名单机制。...

8376
来自专栏刘君君

Rest Notes-将REST应用于URI

1493

扫码关注云+社区