上周梳理了一下spring、springboot、springmvc、springcloud之间的区别联系。没有系统性的学习过spring所以也只能了解个大概。正好找到了百知孙哥的一套Spring5教学课,听了一下第一章Spring的工厂,非常的清晰,所以,从本篇开始,深入、系统性的学习一下spring。
贴一下上周梳理的区别:
Spring、SpringMVC、SpringBoot、SpringCloud四者的区别关系:
- Spring 框架就像一个家族,有众多衍生产品例如 boot、security、jpa等等。但他们的基础都是Spring的ioc、aop等. ioc 提供了依赖注入的容器, aop解决了面向横切面编程,然后在此两者的基础上实现了其他延伸产品的高级功能;
- springMvc主要解决WEB开发的问题,是基于Servlet 的一个MVC框架,通过XML配置,统一开发前端视图和后端逻辑;
- 由于Spring的配置非常复杂,各种XML、JavaConfig、servlet处理起来比较繁琐,为了简化开发者的使用,从而创造性地推出了springBoot框架,默认优于配置,简化了springMvc的配置流程;但区别于springMvc的是,springBoot专注于单体微服务接口开发,和前端解耦,虽然springBoot也可以做成springMvc前后台一起开发,但是这就有点不符合springBoot框架的初衷了;
- 对于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);
}
通用工厂(解决代码冗余)的设计,传参(把配置文件的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目录下即可。
Spring的核心API是ApplicationContext:
作用:Spring提供的ApplicationContext这个工厂,用于对象的创建
好处:解耦合
ApplicationContext接口类型
接口:屏蔽实现的差异
非web环境 : ClassPathXmlApplicationContext (main junit)
web环境 : XmlWebApplicationContext
关键步骤就两步,创建对象(这一步又可以拆为几步,看下图的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工厂底层实现原理:
可见第二步在xml中操作就是去通过反射创建对象(具体咋操作的后续学习再了解)。
注意:Spring工厂创建的对象,叫做bean或者组件(componet)
Spring工厂还有许多可以操作的api和需要注意的细节,在老师的电子书里有,我就不贴了,建议支持正版~
或者直接去Spring官网看也行;https://spring.io/。