上周梳理了一下spring、springboot、springmvc、springcloud之间的区别联系。没有系统性的学习过spring所以也只能了解个大概。正好找到了百知孙哥的一套Spring5教学课,听了一下第一章Spring的工厂,非常的清晰,所以,从本篇开始,深入、系统性的学习一下spring。

贴一下上周梳理的区别:

Spring、SpringMVC、SpringBoot、SpringCloud四者的区别关系:

  1. Spring 框架就像一个家族,有众多衍生产品例如 boot、security、jpa等等。但他们的基础都是Spring的ioc、aop等. ioc 提供了依赖注入的容器, aop解决了面向横切面编程,然后在此两者的基础上实现了其他延伸产品的高级功能;
  2. springMvc主要解决WEB开发的问题,是基于Servlet 的一个MVC框架,通过XML配置,统一开发前端视图和后端逻辑;
  3. 由于Spring的配置非常复杂,各种XML、JavaConfig、servlet处理起来比较繁琐,为了简化开发者的使用,从而创造性地推出了springBoot框架,默认优于配置,简化了springMvc的配置流程;但区别于springMvc的是,springBoot专注于单体微服务接口开发,和前端解耦,虽然springBoot也可以做成springMvc前后台一起开发,但是这就有点不符合springBoot框架的初衷了;
  4. 对于springCloud框架来说,它和springBoot一样,注重的是微服务的开发,但是springCloud更关注的是全局微服务接口的整合和管理,相当于管理多个springBoot框架的单体微服务;

所以,用最简练的语言概括就是:

Spring 是一个“引擎”;

Spring MVC 是基于Spring的一个 MVC 框架 ;(前后端写一块,通常是一个jsp里既有java又有js、html)

Spring Boot 是基于Spring4的条件注册的一套快速开发整合包。(与前端解耦)

Spring Cloud供多个Spring Boot框架中接口互相调用,解耦用的。(后端接口解耦)

一、引言

Spring是轻量级的EJB,那么EJB是个什么东西,至少我没接触过。可以理解为是包含众多设计模式的重量级框架,可以放在web中间件上运行,但是不同的中间件实现接口的方式又不一样,而且有些中间件不能使用EJB(比如Tomcat)。

所以,Spring是一个轻量级的JavaEE解决方案,整合众多优秀的设计模式。相较于EJB:

1. 对于运行环境是没有额外要求的
   开源 tomcat resion jetty 
   收费 weblogic  websphere 
2. 代码移植性高
   不需要实现额外接口

其中,Spring中应用最广泛的就是工厂模式(Spring的本质是工厂),目的是解耦。

二、工厂模式代码实例

以前在类中声明新对象时是通过硬编码的方式,比如:

UserService userService = new UserServiceImpl();
UserDao userDao = new UserDaoImpl();

遇到的问题:

   耦合:指定是代码间的强关联关系,一方的改变会影响到另一方
   问题:不利于代码维护
   简单:把接口的实现类,硬编码在程序中

比如我想调用UserService接口的另一个实现类UserServiceImplNew中的方法了,改成

UserService userService = new UserServiceImplNew();

的话需要编译代码才能生效,那么有没有一种模式可以实现通过修改配置文件,就可以达到切换对象的方式呢。工厂模式可以解决,一个简单工厂实现类:

package com.jin;

/**
 * @author jinyunlong
 * @date 2021/11/15 17:49
 * @profession ICBC锅炉房保安
 */
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class BeanFactory {
    private static Properties env = new Properties();

    static {
        try {
            //第一步 获得IO输入流
            InputStream inputStream = BeanFactory.class.getResourceAsStream("/applicationContext.properties");
            //第二步 文件内容 封装 Properties集合中 key = userService value = com.jin.UserServiceImpl
            env.load(inputStream);

            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }


    /*
        对象的创建方式:
           1. 直接调用构造方法 创建对象  UserService userService = new UserServiceImpl();
           2. 通过反射的形式 创建对象 解耦合
               Class clazz = Class.forName("com.jin.UserServiceImpl");
               UserService userService = (UserService)clazz.newInstance();
     */

    public static UserService getUserService() {

        UserService userService = null;
        try {
                                         //com.jin.UserServiceImpl
            Class clazz = Class.forName(env.getProperty("userService"));
            userService = (UserService) clazz.newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        return userService;

    }


    public static UserDao getUserDAO(){

        UserDao userDao = null;
        try {
            Class clazz = Class.forName(env.getProperty("userDao"));
            userDao = (UserDao) clazz.newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        return userDao;

    }



    /*
     key 小配置文件中的key [userDAO,userService]
     */
//    public static Object getBean(String key) {
//        Object ret = null;
//        try {
//            Class clazz = Class.forName(env.getProperty(key));
//            ret = clazz.newInstance();
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//        return ret;
//    }
}

如上面代码的注释,两种在工厂中创建对象的方式,显然第一种直接调用构造方法创建不好,就是用工厂类统一管理return的new对象了,换汤不换药。

第二种通过反射创建对象的方式,加上前一步去读取配置文件从而加载到反射参数中,可以完美实现解耦。配置文件内容为key value格式:

userService = com.jin.UserServiceImpl

userDao = com.jin.UserDaoImpl

看代码,比较好理解,比如我现在不想创建UserServiceImpl对象了,创建UserServiceImplNew对象只需要去修改配置文件中的userService对应的value即可,这也是我们写项目为什么写这么多配置文件参数原因了吧,方便工厂管理😂,测试代码如下:

    @org.junit.Test
    public void test(){
//        UserService userService = new UserServiceImpl();
        UserService userService = (UserService) BeanFactory.getUserService();
//        UserService userService = (UserService) BeanFactory.getBean("userService");
        User user = new User();
        user.setName("大雄");
        user.setPasswd("123456");
        userService.getUser(user);
    }

1.PNG

通用工厂(解决代码冗余)的设计,传参(把配置文件的key传入)即可:

    /*
     key 小配置文件中的key [userDAO,userService]
     */
//    public static Object getBean(String key) {
//        Object ret = null;
//        try {
//            Class clazz = Class.forName(env.getProperty(key));
//            ret = clazz.newInstance();
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//        return ret;
//    }

通用工厂使用方式:

1. 定义类型 (类)
2. 通过配置文件的配置告知工厂(applicationContext.properties)
   key = value
3. 通过工厂获得类的对象
   Object ret = BeanFactory.getBean("key")

总结:上面的工厂模式代码实例是新建了一个类,然后通过读取配置文件,再反射去创建对象。其实Spring的本质就是工厂(ApplicationContext (applicationContext.xml)),接下来去看Spring中工厂对对象的管理创建。

三、Spring工厂

首先,引依赖

<!-- https://mvnrepository.com/artifact/org.springframework/spring-
context -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.1.4.RELEASE</version>
</dependency>

对应上面的properties的配置文件,Spring工厂的配置文件格式是xml的,命名随意,建议:applicationContext.xml,位置就建议放在resource目录下即可。

2.PNG

Spring的核心API是ApplicationContext:

作用:Spring提供的ApplicationContext这个工厂,用于对象的创建
好处:解耦合

ApplicationContext接口类型
接口:屏蔽实现的差异
非web环境 : ClassPathXmlApplicationContext (main junit)
web环境  :  XmlWebApplicationContext

3.PNG

关键步骤就两步,创建对象(这一步又可以拆为几步,看下图的Spring工厂底层原理),和获取对象:

    @org.junit.Test
    public void test2(){
        //1 获得Spring的工厂
        ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
        //2 通过工厂类获得对象
        Person person = (Person) ctx.getBean("person");
        System.out.println("person = " + person);
    }

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属性 名字(唯一)-->
    <!--    class属性 配置全限定名-->
    <bean id="person" class="com.jin.Person"/>

</beans>

Spring工厂底层实现原理:

4.PNG

可见第二步在xml中操作就是去通过反射创建对象(具体咋操作的后续学习再了解)。

注意:Spring工厂创建的对象,叫做bean或者组件(componet)

Spring工厂还有许多可以操作的api和需要注意的细节,在老师的电子书里有,我就不贴了,建议支持正版~

或者直接去Spring官网看也行;https://spring.io/


标题:Spring(1)
作者:jyl
地址:http://jinyunlong.xyz/articles/2021/11/16/1637072481127.html