初学Spring不太容易理解的问题
-
Q: 控制反转(IoC)是什么?到底是谁的控制被反转了?
A: Ioc是一个容器,在Spring中,它会认为一切Java资源都是Java Bean,容器的目标就是管理这些Bean和它们之间的关系。所以在Spring IoC里面装载的各种Bean,也可以理角为Java的各种资源,包括Java Bean的创建、事件、行为等,它们由IoC容器管理。也就是说创建对象、处理对象之间的关系、对象的生命周期等等就不需要我们去管了,把这些工作都交由Spring IoC去做,我们需要做的只是告诉IoC我们需要什么IoC就会给你,本来由我们来控制的对象的创建活动转到了IoC身上,所以说控制被反转了。
-
Q: 依赖注入(DI)是什么?注入了什么?注到哪里了?
A: 依赖注入其实说白了就是给对象赋值。如果一个对象需要另一个对象协助时,无须在代码中创建被调用者,而是依赖外部的注入。比如我要拧螺丝,就需要依赖螺丝刀,依赖注入就是把被依赖的螺丝刀送到我手上来。依赖注入通常有两种:setter注入和构造器注入。简单点说setter注入就是setter是先通过无参构造器new一个空对象,再通过setter方法把值赋给这个空对象;而构造器注入就是根据相应的构造方法的参数传入对应的值来new一个对象。所以依赖注入就是将值(或对象)注入到目标对象中。
构造注入:
Screwdriver screwdriver = new Screwdriver(); //创建螺丝刀 Person person = new Person(screwdriver) //把螺丝刀给Person对象
setter注入:
Screwdriver screwdriver = new Screwdriver(); Person person = new Person(); person.setScrewdriver(screwdriver); //把螺丝刀给person对象
-
Q: 控制反转(IoC)和依赖注入(DI)的关系是什么?
A: 依赖注入是控制反转的一种实现方式。就是说虽然创建对象的工作归IoC容器管了,但容器还是得遵守学生守则的。想要创建对象就要通过构造方法,至于是在创建对象时将值传入(构造器注入)还是通过空构造方法创建对象后再通set方法传入(setter注入)那就是看你的喜好了。
-
Q: Spring中所说的Bean到底是什么,有什么用?
A: Spring容器就像一个大工厂,工厂根据人们所指定的方式(配置)生产产品(bean),这些产品可以实现某个具体的功能。当 然这些产品也并不是非要Spring工厂来生产不可,也可以由我们自己生产,就如我们想喝果汁,可以自己种各种疏果,然后购买榨汁机榨成果汁,最后根据自己的口味加点糖和冰。但其实我们只是想喝一杯果汁而己。在Spring中Bean就像是果汁,是一个有具体功能的对象,对于一些手动创建比较麻烦的对象,我们大可交给Spring去完成创建。
-
Q: 为什么要用Spring IoC容器来管理Bean?直接new一个不行吗?
A: 要知道在一个项目中一个对象往往不是孤立的,对象之间相互作用,相互协作,或者相互依赖。一个典型的相互依赖的例子是对象A中有属性B,要使用A的就需要先创建B的实例,而如果此时B又依赖C,C又依赖D呢?而在Spring中这些工作都是由Spring容器来完成的,使用时只要从Spring容器中“拿”对象A就行了。就像前面所说的喝果汁的故事,果汁对象需要糖对象和冰对象,而在榨出果汁之前还得先创建榨汁机对象,在这之前甚至需要疏果对象...在开发中程序员可能注意力都集中在如何去种植疏果了。
Juice juice = (Juice) context.getBean("juice");
-
Q: 面向切面是什么意思?切面是什么?
A: 在软件开发中,散布于应用中多处的功能被称为横切关注点(cross-cutting concern)。通常来讲,这些横切关注点从概念上是与应用的业务逻辑相分离的(但是往往会直接嵌入到应用的业务逻辑之中)。把这些横切关注点与业务逻辑相分离正是面向切面编程(AOP)所要解决的问题。比如在一个业务流程中,完成流程流程3之前需要完成一些操作(前置通知),完成流程3之后需要完成一些操作(后置通知),那么可以把之前之后需要做的操作封装成一个切面,只需在切点声明一个切面Spring就能自动执行这些操作。
比如,没有切面的代码是这样的//业务代码 service.process1(); service.process2(); processBeforeProcess3_1(); processBeforeProcess3_2(); processBeforeProcess3_3(); service.process3(); processAfterProcess3_1(); processAfterProcess3_2(); processAfterProcess3_3(); service.process4(); service.process5();
面向切面的代码
//定义一个切面 class Aspect{ //定义切点 @Pointcut("切点是service.process3()") public void pointCut() //service.process3()之前的操作 @Before public void before(){ processBeforeProcess3_1(); processBeforeProcess3_2(); processBeforeProcess3_3(); } //service.process3()之后的操作 @After public void after(){ processAfterProcess3_1(); processAfterProcess3_2(); processAfterProcess3_3(); } }
//业务代码 service.process1(); service.process2(); service.process3(); service.process4(); service.process5();
可以看到,面向切面方法的代码并没有变少,反而多定义了一个类,但是在业务代码中我们只需要关注真正的业务流程,而不会被其它杂事所干扰,
service.process3
甚至都不知道自己与service.process2()
和service.process4()
之间还执行了这么多操作。
一个典型的例子就是进行数据库操作,在进行真正的增删查改操作之前,需要加载驱动,然后获取一个连接,再定义一个SQL查询对象。数据库操作完成后还需要进行提交(如果需要用到事物的话),然后在catch
语句中写滚语句和写入日志,最后还得在finally
中关闭资源,关闭资源前甚至还要检查对象是否为null
。与真正进行数据库操作的其它步骤其实都可以放入before和after中,再声明需要此切面的切点即可。
- 感谢你赐予我前进的力量