写了很久!详细理解Spring和IoC

原创
09/16 09:00
阅读数 3K

点击蓝色“程序员的时光 ”关注我 ,标注“星标”,及时阅读最新技术文章

写在前面:

小伙伴儿们,大家好!今天我们开篇学习Spring,仔细解读核心技术

我是时光,后续还会持续更新,敬请期待!

思维导图:


1,Spring是什么

 Spring是一个轻量级开源框架,以 IOC(Inverse Of Control:控制反转)和 AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层 Spring MVC 和持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,最为典型的当属数据库事务的使用,还能整合开源世界众多著名的第三方框架和类库,应该说Spring框架已经融入到了Java EE的各个开发领域。

2,Spring的优势

2.1,简化开发

Spring 就是一个大工厂,可以将所有对象的创建和依赖关系的维护交给 Spring 容器管理,降低了组件之间的耦合性。

2.2,声明式事务的支持

只需要通过配置就可以完成对事物的管理,而无须手动编程, 提高开发效率和质量。

2.3,集成各种优秀框架

Spring接受各种开源框架,其内部提供了对各种优秀框架的直接支持。

2.4,AOP 编程的支持

Spring 提供面向切面编程,可以方便地实现对程序进行权限拦截和运行监控等功能。

3,Spring下载

Spring官网:

https://spring.io/

最新开发包及文档下载地址:

http://repo.springsource.org/libs-release-local/org/springframework/spring/

下载5.25版本即可

4,Spring环境搭建

4.1,新建普通Maven项目

建好之后命名Spring就可以了,看一下项目结构:

项目结构图

4.2,导入相关依赖

这里面包括Spring的核心包

 <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

4.3,我们新建一个HelloWorld类

在Java文件夹下新建类命名为com.java.test.HelloWorld;

package com.java.test;

public class HelloWorld {
    public void say(){
        System.out.println("Spring 你好!");
    }
}

然后我们就可以让Spring来管理这个类的实例,通过配置文件applicationContext.xml,Spring可以很轻松地做到。

4.4,Spring配置文件

我们在resources文件夹下新建一个配置文件applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd"
>


    <!--通过id来直接管理HelloWorld的实例对象-->
    <bean id="helloWorld" class="com.java.test.HelloWorld"></bean>
  
</beans>

4.5,通过Spring来获取类的对象

我们新建一个测试类命名为com.java.service.Test;

package com.java.service;

import com.java.test.HelloWorld;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        //加载配置文件applicationContext.xml
        ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
        //通过id获取bean,返回一个对象
        HelloWorld helloWorld=(HelloWorld)ac.getBean("helloWorld");
        //调用方法
        helloWorld.say();
    }

}

运行结果:

运行结果

5,深入理解IoC

IoC(控制反转:Inverse  of  Control),又称作依赖注入,是一种重要的面向对象编程的法则来削减计算机程序的耦合问题,也是轻量级的Spring框架的核心。

5.1,先来看几个关键问题

什么是Spring中的bean?

Spring里面的bean就类似是定义的一个组件,而这个组件的作用就是实现某个功能的,这里所定义的bean就相当于给了你一个更为简便的方法来调用这个组件去实现你要完成的功能。

什么是控制反转?

控制:是容器对bean进行创建、管理。控制bean的整个生命周期。

反转:我们是被动接受Spring容器创建的对象,而不是自己主动去控制。

什么是依赖注入?

DI—Dependency Injection,即依赖注入;

依赖:应用程序依赖IoC容器,在程序运行的时候,应用程序需要IoC容器来提供对象需要的外部资源;

注入:IoC容器注入应用程序的某个对象,注入了其所需要的的外部资源(对象、资源和数据等);

所以说控制反转是通过依赖注入实现的,其实它们是同一个概念的不同角度描述。通俗来说就是IoC是设计思想,DI是实现方式。

5.2,IoC实例讲解

我们来具体实现一下控制反转的设计思想;比方说业务场景是有一项工作需要A和B两个测试员来完成:

我们先建一个类A:

package com.java.service;

public class A {
    public void test(){
        System.out.println("A-测试员");
    }
}

然后工作类Work先让A来做测试:

package com.java.service;

public class Work {
    public void doTest(){
        //A做测试
        A a=new A();
        a.test();
    }
}

我们测试一下:

package com.java.test;

import com.java.service.Work;

public class Test {
    public static void main(String[] args) {
        Work work=new Work();
        work.doTest();
    }
}

结果是"A-测试员",没毛病。我们分析一下代码,这个人A和工作Work就是完全耦合在一起,耦合度高。

如果这个时候让B测试员来完成工作,那么代码的改动就很大,破坏完整性,需要在工作Work类里面重新new对象并且调用方法。

于是乎,我们来看看Spring中有没有更简单的方式吧!

我们先来写一个接口,让A和B都实现这个接口:

public interface Jiekou {
    public void test();
}

我们在上述的Work类中把接口的set()方法写一下:

public class Work {
    private Jiekou jiekou;

    public void setJiekou(Jiekou jiekou) {
        this.jiekou = jiekou;
    }

    public void doTest(){
        jiekou.test();
    }
}

然后就是测试类中的修改:

public class Test {
    public static void main(String[] args) {
        //加载配置文件applicationContext.xml
        ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
        //通过id获取bean,返回一个对象
        Work work=(Work)ac.getBean("work");
        //调用方法
        work.doTest();
    }
}

其中重要的是applicationContext.xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd"
>


    <bean id="a" class="com.java.service.A"></bean>

    <bean id="b" class="com.java.service.B"></bean>

    <bean id="work" class="com.java.service.Work">
        <property name="jiekou" ref="b"></property>
    </bean>
  
</beans>

我们这里只需要在ref中修改参数值即可决定是A测试员还是B测试员才参与(这里的文件详解我们下面再说)!非常地方便!

5.3,IoC配置文件详解

我们看上面的配置文件,第一段是一些命名空间及其规范的介绍;第二段就是bean的属性赋值等;

  • id:对象的唯一标识;
  • class:bean的完全限定名称,从包名称到类名称;
  • property:给属性赋值, name的名称取决于 set()方法后面的参数, ref引用具体的值;

5.4,IoC容器的设计使用

我们来看一下这两行代码:

//加载配置文件applicationContext.xml
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
//通过id获取bean,返回一个对象
Work work=(Work)ac.getBean("work");

要知道我们并没有new这个work对象,是Spring帮我们创建了这个对象,那又是如何创建的呢?

  • Spring IoC容器设计主要是基于 BeanFactoryApplicationContext两个接口,其中后者是前者的子接口之一,也就是说 BeanFactory是IoC容器所定义的最底层接口。
  • 在第一行中,我们用 ApplicationContext的子类 ClassPathXmlApplicationContextclass path中加载配置文件;
  • 在第二行中,可以获取到具体的bean了,这里用的是 getBean()方法来获取对象,点进去发现是在 BeanFactory.class里面定义的;这样就可以获取到Spring容器为我们创建的对象了。
  • getBean的多个方法是用于获取给Spring IoC容器的Bean。可以去看看 BeanFactory的源码。

5.5,Spring IoC的初始化流程

关于这个初始化流程,我们需要注意的是,Bean的定义和初始化在Spring IoC容器中是两大步骤,它是先定义,然后才是初始化和依赖注入的。Bean的定义分为3步:

  1. Resource定位,这步是Spring IoC容器根据开发者的配置,寻找用户定义的bean资源,在Spring开发中,通过XML或者注解方式都可以,而定位的内容也就是bean资源是由用户提供的。
  2. BeanDefinition的载入,这个时候只是将 Resource定位到的信息保存到 BeanDefinition中,此时并不会创建Bean的实例。
  3. BeanDefinition的注册,这里就是将 BeanDefinition的信息发布到Spring IoC容器中,此时仍旧没有对应的Bean的实例创建。

做完这三步之后,我们要对Bean进行初始化,在Spring Bean的配置选项中,还有一个lazy-init属性,其含义是是否初始化Bean,在默认情况下,她的默认值是false,也就是Spring IoC默认为自动初始化Bean,不需要我们手动操作。如果将其设置为true,那么只有当我们使用Spring IoC容器的getBean方法获取它时,它才会进行Bean的初始化,完成依赖注入。

小结

我们再来总结一下Spring创建对象的过程:

首先通过ApplicationContext这个IoC容器的入口,用它的子类ClassPathXmlApplicationContext从class path中加载配置文件,读取数据,再用getBean()方法获取具体的Bean实例。Spring框架会为我们创建对象,交给IoC容器管理,这个过程就降低了耦合度。

好了,这期就分享到这里了,下期继续来聊聊Bean的作用域,生命周期等以及依赖注入的几种方式,敬请期待!


好了,今天就先分享到这里了,下期继续给大家带来Spring后续内容!更多干货、优质文章,欢迎关注我的原创技术公众号~



本文分享自微信公众号 - 程序员的时光(gh_9211ec727426)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
打赏
0
1 收藏
分享
加载中
更多评论
打赏
0 评论
1 收藏
0
分享
在线直播报名
返回顶部
顶部