简介
# 前言
感谢黑马程序员
# Spring概述
首先我们来看一张图片
这就是我们将要学习的SSM框架的基本架子,我们可以看到,由SpringMVC去处理视图层,Mybatis去处理持久化层,而Spring干什么呢?其实什么也不做,它不属于我们熟知的三层架构的任何一层,但其实是SSM中的核心。
Spring是什么
Spring,是Java全栈轻量级开发框架,现如今成为了最多的JavaEE企业应用开源框架
Spring官网:https://spring.io (opens new window)
Spring的核心
Spring的核心就两件事:IOC控制反转和AOP面向切面编程,基于这两个特性,可以大大简化我们编写JavaEE应用,实现快速开发
IOC控制反转是什么
- 控制反转,就是将创建对象的活交给工厂区创建
打个比方,我现在要买个房子
以前的做法是:到处打听谁要买房,然后和具体的人去沟通协商,然后两家签订合同
现在的做法是:让中介去干这个活,我到最后只管签合同
AOP面向切面编程是什么
- 面向切面编程,就是通过预编译和运行期动态代理的方式实现功能的维护
打个比方,比如现在我的程序从上到下已经全部开发完成了,现在有个需求说我要在哪个过程上加上一个新功能
这个时候我只需要在那个过程范围内添加一个新的功能即可
形象化来说,我们开发过程是从上到下竖向进行的,面向切面编程就是在这基础上横向添加
Spring的优势
- 解耦
- AOP
- 声明式事务支持
- 方便测试
- 可以集成各种框架
- 降低API使用难度
- Spring源码是学习的范例
Spring的体系结构
# 问题分析:如何将程序简化
# 以前的使用案例
package com.howling.Before.Dao;
/**
* 持久层接口
*/
public interface AccountDao {
void saveAccount();
}
package com.howling.Before.Dao;
/**
* 持久层
*/
public class AccountDaoImpl implements AccountDao {
public void saveAccount() {
System.out.println("持久化层接口");
}
}
package com.howling.Before.Service;
/**
* 业务层接口
*/
public interface AccountService {
void saveAccount();
}
package com.howling.Before.Service;
import com.howling.Before.Dao.AccountDao;
import com.howling.Before.Dao.AccountDaoImpl;
/**
* 业务层
*/
public class AccountServiceImpl implements AccountService{
private AccountDao accountDao = new AccountDaoImpl();
public void saveAccount() {
System.out.println("业务层调用持久化层");
accountDao.saveAccount();
}
}
package com.howling.Before;
import com.howling.Before.Service.AccountService;
import com.howling.Before.Service.AccountServiceImpl;
public class Before {
public static void main(String[] args) {
AccountService accountService = new AccountServiceImpl();
accountService.saveAccount();
}
}
# 以前做法的缺点分析
以前的做法耦合太严重,既然说到耦合,就说一下程序的耦合
耦合:程序之间的依赖关系,其中包括类和类之间的依赖关系,程序和程序之间的依赖关系
我们要做到解耦合,应该做到的程度是:编译器中看不到依赖,但是在运行中能够依赖。
# 使用工厂和配置文件来简化
# 工厂模式概述
工厂模式,属于23种设计模式中的一种,属于比较常使用的设计模式之一
工厂模式,顾名思义。
假如我们之前都是手工制品,现在我们要上流水线了。
说到工厂模式在提一嘴JavaBean,Bean这个词在英语单词中有豆子的意思,也有着可重用组件的含义。
之前我们一直说创建一个实体类,一个标准的实体类可以叫做JavaBean,其实JavaBean的含义是包含实体类的。
JavaBean>实体类
,JavaBean是可重用组件的一部分
# 准备工作
1、编写持久化层和业务层
package com.howling.FactoryDecoupl.Dao;
/**
* 持久层接口
*/
public interface AccountDao {
void saveAccount();
}
package com.howling.FactoryDecoupl.Dao;
/**
* 持久化层
*/
public class AccountDaoImpl implements AccountDao {
public void saveAccount() {
System.out.println("持久化层");
}
}
package com.howling.FactoryDecoupl.service;
/**
* 业务层接口
*/
public interface AccountService {
void saveAccount();
}
package com.howling.FactoryDecoupl.service;
/**
* 业务层
*/
public class AccountServiceImpl implements AccountService{
public void saveAccount() {
System.out.println("业务层");
}
}
因为要使用工厂来创建,所以业务层没有调用持久化层,表现层根本没写
2、通过反射来创建对象,从而避免使用new关键字
- bean.properties
## 在resources下面创建bean.prpperties
accountDao=com.howling.FactoryDecoupl.Dao.AccountDaoImpl
accountService=com.howling.FactoryDecoupl.service.AccountServiceImpl
# 使用工厂
创建Bean工厂
package com.howling.factory;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* 静态工厂
*/
public class BeanFactory {
private static Properties properties;
static {
try {
properties = new Properties();
InputStream inputStream = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
properties.load(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 工厂模式创建bean工厂
*
* @param beanName bean的名称
* @return bean
*/
public static Object getBean(String beanName) {
Object bean = null;
try {
String property = properties.getProperty(beanName);
bean = Class.forName(property).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return bean;
}
/**
* 创建一个对应类型T的对象
*
* @param beanName bean的名称
* @param tClass 类型参数
* @param <T> 类型
* @return 对象
*/
public static <T> T getBean(String beanName, Class<T> tClass) {
T bean = null;
try {
String property = properties.getProperty(beanName);
bean = (T) Class.forName(property).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return bean;
}
}
package com.howling;
public class People {
public void eat() {
System.out.println("吃饭");
}
}
people=com.howling.People
现在我们可以来进行配置文件和工厂的测试了
# 工厂模式的问题
现在的工厂模式是不完美的
- 多例:工厂每次启动都会产生不同的实例,每次调用的实例均不相同,这样会导致内存大幅消耗
思路:
为了解决这个问题,我们引入单例模式,单例模式也是设计模式中的一种
1、我们使用单例模式对工厂进行改造,这样工厂每次返回的实例都是一个
2、之前我们每次创建一个新的对象都是使用Class.forName(beanName).newInstance();
来进行创建的,假如我们要返回一个实例,那么这个语句只能执行一次
3、语句执行一次创建对象之后,我们将初始化的值存储起来以作备用,以后就不用创建,直接返回即可
# 单例工厂
package com.howling.factory;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class SingleBeanFactory {
private static Properties properties;
private static Map<String, Object> factories = new HashMap<>();
static {
try {
properties = new Properties();
InputStream inputStream = SingleBeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
properties.load(inputStream);
// 获得所有的Key
Enumeration<Object> enumeration = properties.keys();
// 根据key进行遍历,将所有的东西装载进工厂中
while (enumeration.hasMoreElements()) {
String key = enumeration.nextElement().toString();
String property = properties.getProperty(key);
Object o = Class.forName(property).newInstance();
factories.put(key, o);
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获得bean对象
*
* @param name bean对象的名称
* @return bean对象
*/
public static Object getBean(String name) {
Object o = null;
try {
o = factories.get(name);
} catch (Exception e) {
e.printStackTrace();
}
return o;
}
/**
* 获得bean对象
*
* @param name bean对象的名称
* @param tClass bean对象类型
* @param <T>
* @return bean对象
*/
public static <T> T getBean(String name, Class<T> tClass) {
T o = null;
try {
o = (T) factories.get(name);
} catch (Exception e) {
e.printStackTrace();
}
return o;
}
}
# 懒加载静态单例工厂
我们可以看到,在上面的过程中,单例工厂是没有办法进行按需加载的,也就是说它必须要一次性加载完成,即使配置文件中存放有很多的类,所以我们必须要对他进行简化,让它懒加载
package com.howling.factory;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class LazySingleBeanFactory {
private static Properties properties;
private static final Map<String, Object> factories = new HashMap<>();
static {
try {
properties = new Properties();
InputStream inputStream = LazySingleBeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
properties.load(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static Object getBean(String name) {
Object o;
if ((o = factories.get(name)) != null) {
return o;
}
try {
String value = properties.getProperty(name);
o = Class.forName(value).newInstance();
factories.put(name, o);
return o;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
return null;
}
public static <T> T getBean(String name, Class<T> tClass) {
T o;
if ((o = (T) factories.get(name)) != null) {
return o;
}
try {
String value = properties.getProperty(name);
o = (T) Class.forName(value).newInstance();
factories.put(name, o);
return o;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
return null;
}
}