01_Spring概述及程序的耦合

本教程源码请访问: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是读取配置文件获取的字符串

耦合:程序间的依赖关系。包括类之间的依赖和方法间的依赖。

解耦:降低程序间的依赖关系。

实际开发中应该做到:编译期不依赖,运行时依赖。

解耦的思路

  1. 使用反射来创建对象,而避免使用new关键字;
  2. 通过读取配置文件来获取要创建的对象全限定类名。

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的方式解决程序的耦合,具体内容请见后面的教程。

上一篇:132-打印流概述和使用PrintStream


下一篇:132