• 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中,再声明需要此切面的切点即可。