本教程源码请访问:tutorial_demo
一、什么是Spring
Spring是分层的Java SE/EE 轻量级开源框架,以 IoC(Inverse Of Control:反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了表现层Spring MVC 和持久层Spring JDBC以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业应用开源框架。
二、Spring的优势
方便解耦,简化开发
通过Spring提供的IoC容器,可以将对象间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
AOP编程的支持
通过Spring的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付。
声明式事务的支持
可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。
方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。
方便集成各种优秀框架
Spring可以降低各种框架的使用难度,提供了对各种优秀框架(MyBatis、Hibernate、Hessian、 Quartz等)的直接支持。
降低JavaEE API的使用难度
Spring对JavaEE API(如 JDBC、 JavaMail、远程调用等)进行了封装,使这些 API 的使用难度大为降低。
三、程序的耦合
3.1、什么是程序的耦合
耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差( 降低耦合性,可以提高其独立性)。 耦合性存在于各个领域,而非软件设计中独有的,但是我们只讨论软件工程中的耦合。
在软件工程中, 耦合指的就是就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计应使类和构件之间的耦合最小。 软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。 **划分模块的一个准则就是高内聚低耦合。 **
我们在开发中,有些依赖关系是必须的,有些依赖关系可以通过优化代码来解除。
案例一:
/* 示例代码
* 账户的业务层实现类
*/
public class AccountServiceImpl impalements IAccountDao {
private IAccountDao accountDao = new AccountDaoImpl();
}
业务层调用持久层,并且此时业务层在依赖持久层的接口和实现类。如果此时没有持久层实现类,编译将不能通过。这种编译期依赖关系,应该在我们开发中杜绝。 我们需要优化代码解决 。
案例二:
public static void main(String[] args) throws Exception {
//1.注册驱动
//DriverManager.registerDriver(new com.mysql.jdbc.Driver());
Class.forName(driverName);//driverName是读取配置文件获取的字符串
//2.获取连接
//3.获取预处理 sql 语句对象
//4.获取结果集
//5.遍历结果集
}
早期我们的JDBC操作,注册驱动时,我们为什么不使用DriverManager的register方法,而是采用Class.forName的方式?我们的类依赖了数据库的具体驱动类(MySQL),如果这时候更换了数据库品牌(比如 Oracle),需要修改源码来重新数据库驱动。这显然不是我们想要的。
3.2、解决程序耦合的原则
当是我们讲解 jdbc 时,是通过反射来注册驱动的,代码如下:
Class.forName(driverName);//driverName是读取配置文件获取的字符串
耦合:程序间的依赖关系。包括类之间的依赖和方法间的依赖。
解耦:降低程序间的依赖关系。
实际开发中应该做到:编译期不依赖,运行时依赖。
解耦的思路:
- 使用反射来创建对象,而避免使用new关键字;
- 通过读取配置文件来获取要创建的对象全限定类名。
3.3、使用工厂模式解决程序耦合
在实际开发中,我们可以把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候,让一个类中的方法通过读取配置文件,把这些对象创建并进行存储。需要用的时候,直接拿来使用。这个读取配置文件,创建和获取三层对象的类就是工厂。具体实现请看下面的代码。
3.3.1、创建一个普通的Maven工程
pom.xml配置如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.codeaction</groupId>
<artifactId>factory</artifactId>
<version>1.0-SNAPSHOT</version>
</project>
3.3.2、创建各层实现类的配置文件bean.properties
accountService=org.codeaction.service.impl.AccountServiceImpl
accountDao=org.codeaction.dao.impl.AccountDaoImpl
3.3.3、创建工厂类
package org.codeaction.factory;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
/**
* 通过读取配置文件中配置的内容,反射创建对象
* 我的配置文件可以是xml也可以是properties
*/
public class BeanFactory {
//定义一个Properties对象
private static Properties props;
//定义一个Map,用于存放我们要创建的对象。我们把它称之为容器
private static Map<String, Object> beans;
//使用静态代码块为Properties对象赋值
static {
try {
//获取properties文件的流对象
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
//实例化对象
props = new Properties();
props.load(in);
//实例化容器
beans = new HashMap<String, Object>();
//取出配置文件中所有的Key
Enumeration<Object> keys = props.keys();
//遍历枚举
while (keys.hasMoreElements()) {
//取出每个Key
String key = keys.nextElement().toString();
//根据key获取value
String beanName = props.getProperty(key);
//反射创建对象
Object bean = Class.forName(beanName).newInstance();
//把key和value存入容器中
beans.put(key, bean);
}
} catch (IOException e) {
throw new ExceptionInInitializerError("初始化错误");
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 根据bean的名称获取对象
* @param beanName
* @return
*/
public static Object getBean(String beanName) {
return beans.get(beanName);
}
}
3.3.4、创建持久层接口及其实现类
持久层接口:
package org.codeaction.dao;
public interface IAccountDao {
void saveAccount();
}
持久层接口的实现类:
package org.codeaction.dao.impl;
import org.codeaction.dao.IAccountDao;
public class AccountDaoImpl implements IAccountDao {
//这里只是一个模拟,不要在意实现细节
@Override
public void saveAccount() {
System.out.println("账户保存成功");
}
}
3.3.5、创建业务层接口及其实现类
业务层接口:
package org.codeaction.service;
public interface IAccountService {
void saveAccount();
}
业务层接口的实现类:
package org.codeaction.service.impl;
import org.codeaction.dao.IAccountDao;
import org.codeaction.factory.BeanFactory;
import org.codeaction.service.IAccountService;
public class AccountServiceImpl implements IAccountService {
private IAccountDao accountDao = (IAccountDao) BeanFactory.getBean("accountDao");
@Override
public void saveAccount() {
accountDao.saveAccount();
}
}
3.3.6、创建视图层
package org.codeaction.ui;
import org.codeaction.factory.BeanFactory;
import org.codeaction.service.IAccountService;
public class AccountUI {
public static void main(String[] args) {
IAccountService accountService = (IAccountService) BeanFactory.getBean("accountService");
accountService.saveAccount();
}
}
3.3.7、Spring中解决程序的耦合
在Spring中使用IOC的方式解决程序的耦合,具体内容请见后面的教程。