Spring七大模块与强大的IOC

Java学习之道

共 7003字,需浏览 15分钟

 · 2021-02-15

点击上方 Java学习之道,选择 设为星标

每天12点,干货准时奉上!

Spring 是什么

Spring是由「Rod Johnson」创建的一个轻量级的控制反转(IOC)面向切面编程(AOP)的开源优秀框架。

有以下几个优点:

1.贯彻控制反转(IOC)思想

2.贯彻面向切面编程(AOP)思想

3.贯彻依赖注入(DI)思想

4.支持事务的处理

5.表现出对框架整合的强大能力

使用Spring能够简化软件开发的复杂性,并降低程序的耦合度。具体如何降低会在后面详述。

Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。

同时Spring框架也是Spring Boot和Spring Cloud的基础。

Spring的七大组成部分

此部分先对Spring框架做一个简单的介绍,具体实现放到后文实现,以便于读者能更好的感悟Spring的强大。

Spring的七大组成模块如下:

2.1 核心容器(Spring Core)

Spring的核心包,即为Spring框架最基础的部分,支撑了整个Spring。core包提供了控制反转和依赖注入的特征实现。核心的思想是设计模式--工厂模式,并借此消除了程序对单例模式的依赖,并且展现出了Spring消除耦合的能力,便是「分离了程序的配置和依赖关系」

2.2 应用上下文模块(Spring Context)

对应UI和老式框架EJB和邮件的各种支持。执行Spring的IOC容器时所依赖的上下文对象,提供框架式的对象访问方法,也就是Spring启动时加载Spring所需要的数据结构并且填充数据,为运行准备好一个环境。

2.3 JDBC抽象和DAO模块(Spring DAO)

对应DAO层的支持,对多个持久化技术提供了集成支持,包括 Hibernate、 MyBatis、JPA、JDO;提供一个简化JDBC API操作的Spring JDBC框架。Spring面向DAO制定了一个通用的异常体系,屏蔽具体持久化技术的异常,使业务层和具体的持久化技术实现解耦。Spring提供了模板类简化各种持久化技术的使用。

2.4 对象/关系映射集成模块(Spring ORM)

全称为「Object Relational Mapping」,即对象关系映射,对数据库中的各种表映射成对象。开发人员就可以把对数据库的操作转化为对这些对象的操作。方便开发人员以面向对象的思想来实现对数据库的操作。

同时提供对各种框架的支持。

2.5 面向切面编程(Spring AOP)

AOP即为面向切面编程,是OOP面向对象编程的一种扩展补充。SpringAOP通过动态代理来实现AOP。

2.6 Spring的Web模块(Spring Web)

提供了远程调用和远程服务的支持,同时SpringWeb包含了Spring Web MVC。

2.7 Spring的MVC框架(Spring Web MVC)

对WEB和JSP等的各种支持

Spring有的人说是三大核心思想,但是控制反转其实也可以被成为依赖注入,所以说实际上,Spring框架是IOC和AOP思想的优秀产物。

IOC

IOC被成为控制反转,是一种很重要的思想,

下面我们通过一个案例来说明IOC的重要程度。

不使用IOC的思想来编写代码

/* DAO层的接口和实现 */
public interface UserDao {
    void getUser();
}

public class UserDaoImpl implements UserDao {
    @Override
    public void getUser() {
        System.out.println("dao层默认获取用户数据");
    }
}

public class UserDaoMySQLImpl implements UserDao {
    @Override
    public void getUser() {
        System.out.println("MySQL获取用户数据");
    }
}

public class UserDaoOracleImpl implements UserDao {
    @Override
    public void getUser() {
        System.out.println("Oracle获取用户数据");
    }
}
/* service层的接口和实现 */
public interface UserService {
    void getUser();
}

import com.javastudyway.dao.UserDaoImpl;
public class UserServiceImpl implements UserService {
    /* 如果是这样的编码,需求改变了之后,每次都要通过修改源码才能更换DAO层的实现 */
    private UserDao userDao = new UserDaoImpl();
    public void getUser(){
        userDao.getUser();
    }
}
/* 测试类 */
public class MyTest {
    public static void main(String[] args) {
        //用户实际调用业务层,dao层不需要接触
        UserService userService = new UserServiceImpl();
        userService.getUser();
        /*输出结果:
         * dao层默认获取用户数据           
         */

    }
}

如果需求改变需要使用其他的数据库来实现DAO层的查询用户数据,则每一次都要去修改ServiceImpl中的

「private UserDao userDao = new UserDaoImpl();」

这种编码方式显然不实际,所以这时候就来引出我们的IOC思想编码方式。

使用IOC的思想来编写

DAO层的代码不需要去改变,仅改动Service就能实现程序适应用户需求的改变。

import com.javastudyway.dao.UserDao;import com.javastudyway.dao.UserDaoImpl;
public class UserServiceImpl implements UserService {
    //不在通过new直接创建出Dao对象
    private UserDao userDao;
    //新增一个set方法,通过set方法来实现接口的切换
    public void setUserDao(UserDao userDao){
        this.userDao = userDao;
    }

    public void getUser(){
        userDao.getUser();
    }
}

有人会想了,不就是多了个setter吗?这很常见啊。

值得注意的是这个setter他成功实现了程序的灵活性,用户需要使用DAO层的哪个接口就通过setter传入哪个接口即可。

在程序的使用中,将接口的选择权移交到用户手中,此即为控制反转。

下面再通过代码来演示:

import com.javastudyway.dao.UserDao;
public class UserServiceImpl implements UserService {

    private UserDao userDao;

    /* 利用set进行动态实现值的注入 */
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void getUser() {
        userDao.getUser();
    }
}

public class MyTest {
    public static void main(String[] args) {
        //用户实际调用业务层,dao层不需要接触
        UserService userService = new UserServiceImpl();
        /*将接口强转成实现类,并且调用setter传入dao层实现 */
        ((UserServiceImpl)userService).
                setUserDao(new UserDaoMySQLImpl());
        userService.getUser();
    }
}

如上,如果用户需要使用mysql的dao层实现就传入一个MySQL的dao层实现,要调用默认就传入一个默认的dao层实现即可。

如果用户的需求可能会改变我们的代码,选择只通过一个Setter就能让我们不用整个程序去修改代码,程序的维修代价瞬间降低了下来。这就是IOC的强大之处。

控制反转,控制权从程序员手上转移到了用户的手上。这种思想从本质上优化了程序,程序员不再需要手动去管理对象,系统的偶尔度也大大降低了。这也是Spring的底层基石思想。

Spring的简单使用

讲完基本的Spring IOC容器后,我们来用代码体验以下Spring的强大之处。

前文说过,Spring是一个容器,那么容器内部肯定就会存放对象,那么Spring的对象是如何来的呢?下面我们通过案例来演示。

maven依赖的导入

maven导入Spring依赖技巧:

直接导入webmvc的依赖即可,因为webmvc又依赖于spring所需要的个大模块的包

仅通过一个依赖就处理好了Spring所需要的依赖

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-webmvcartifactId>
    <version>5.2.0.RELEASEversion>
dependency>

元数据

Spring IOC容器负责实例化,配置和组装Bean。容器通过读取配置元数据获取有关要实例化,配置和组装哪些对象的指令。Spring官方文档中给出三种使用元数据的方式:

  • 配置元数据以XML
  • Java注解 -- Spring2.5之后开始引入的配置元数据的技术
  • Java代码表示 -- Spring3.0开始提供的技术

最传统的配置就是通过 XML 文件来配置元数据。规范的一个XML配置文件的名称为:applicationContext.xml,但其实这个文件就是一个配置多个bean的xml文件。

以下是Spring官方文档中给出的配置元数据的xml最基本格式:


<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
        https://www.springframework.org/schema/beans/spring-beans.xsd"
>


    <bean id="..." class="...">  
        
    bean>

    <bean id="..." class="...">
        
    bean>

    

beans>

我们可以在beans中配置多个bean,通过id来区分不同的bean,并在后面的class属性来指向不同的POJO。容器启动时会以此为我们每一个类创建一个单例的对象。

我们接下来通过POJO类的定义和实例化容器来获取对象来阐述容器和对象的使用。

/* POJO类 */
public class Hello {
    //类中仅定义一个字段,str,重写toString并增加getter和setter
    private String str;

    @Override
    public String toString() {
        return "Hello{" +
                "str='" + str + '\'' +
                '}';
    }

    public String getStr() {
        return str;
    }

    public void setStr(String str) {
        this.str = str;
    }
}

接下来,为这个POJO来定义一个配置元数据。


<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
        https://www.springframework.org/schema/beans/spring-beans.xsd"
>


    
    <bean id="hello" class="com.javastudyway.pojo.Hello">
        <property name="str" value="Spring" />
    bean>

beans>

在 bean 这个标签的class属性中导向POJO类的全路径,IOC容器就会去寻找该类并创建一个单例对象,在实例化容器后我们就可以通过 id 的值来获取到这个对象。在 bean 中我们加入一个 property 的单闭合标签来为该对象赋值,name这个属性即类中的字段,value为该字段的值。

贴士:在编写application.xml时,IDEA会在上方提示:Application context not configured for this file

直接在IDEA中点击Create new application context...然后让IDEA自动为我们配置该文件的上下文对象即可。

至此,我们的bean和元数据的配置就完成了。接下来我们在测试类中演示「容器的实例化」「容器对象的获取」

import com.javastudyway.pojo.Hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
    public static void main(String[] args) {
        //获取Spring的上下文对象,也就是实例化IOC容器
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //对象现在都在Spring中管理,要使用直接在里面取,getBean()方法,传入的参数即为容器中bean的id
        Hello hello1 = (Hello) context.getBean("hello");
        Hello hello2 = (Hello) context.getBean("hello");
        System.out.println(hello1);
        System.out.println(hello1 == hello2);
        //~:输出结果为:Hello{str='Spring'}
        //              true
        //将刚刚在applicationContext.xml中配置的value赋值给了相应的name
    }
}

从这个例子中,我们可以就可以了解到Spring容器的对象创建方式,流程就是,在程序启动时,Spring容器通过我们配置的元数据,在applicationContext.xml文件中去寻找各个bean,然后把我们配置的各个值注入到相应的字段中,然后返回一个单例对象。

该对象的对象名对应bean中配置的id,对象的类型对应bean中配置的class。

实例化容器:

Spring实例化容器通过 -- 接口ApplicationContext来获取,借助ClassPathXmlApplicationContext这个实现类来读取外部资源文件。提供给ApplicationContext构造函数的一个或多个位置路径是资源字符串,可让容器从各种外部资源(例如本地文件系统,Java等)中加载配置元数据CLASSPATH。

这里有细心的人会发现这个POJO与前面讲到IOC的POJO有点类似了,都是仅仅定义一个属性字段但不赋值,然后借助setter去实现注入,Spring容器产生对象也是这种方式。所以说,Spring的基石思想是IOC。

总结

现在开始,我们已经不用再去程序中改动了,得益于IOC,现在要实现不同的操作,我们仅需要在 XML 配置文件中进行修改。Spring的IOC其实也就是 「Spring来搞定对象的创建,管理,装配」

-- END --

 | 更多精彩文章 -



《Java学习-进阶手册》

众号后台回复「手册」获取资料

加我微信,交个朋友
长按/扫码添加↑↑↑

浏览 10
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报