Spring

spring概述:

Spring 是分层的 Java SE/EE 应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control:反转控制)

和 AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层 SpringMVC 和

持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多

著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业应用开源框架。

spring优势:

方便解耦,简化开发:通过 Spring 提供的 IoC 容器,可以将对象间的依赖关系交由 Spring 进行控制,避免硬编码所造成的过度程序耦合。

AOP 编程的支持:通过 Spring 的 AOP 功能,方便进行面向切面的编程

声明式事务的支持

方便程序的测试

方便集成各种优秀框架:提供了对各种优秀框架(Struts、Hibernate、Hessian、 Quartz等)的直接支持。

降低 JavaEE API 的使用难度:Spring 对 JavaEE API(如 JDBC、 JavaMail、远程调用等)进行了薄薄的封装层

Java 源码是经典学习范例:各种设计模式

程序的耦合:

耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调

用模块的方式以及通过界面传送数据的多少。模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关

系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差。

软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。 划分模块的一个准则就是高内聚低耦合。

耦合分类:

(1) 内容耦合。当一个模块直接修改或操作另一个模块的数据时,或一个模块不通过正常入口而转入另

一个模块时,这样的耦合被称为内容耦合。

(2) 公共耦合。两个或两个以上的模块共同引用一个全局数据项,这种耦合被称为公共耦合。在具有大

量公共耦合的结构中,确定究竟是哪个模块给全局变量赋了一个特定的值是十分困难的。

(3) 外部耦合 。一组模块都访问同一全局简单变量而不是同一全局数据结构,而且不是通过参数表传

递该全局变量的信息,则称之为外部耦合。

(4) 控制耦合 。一个模块通过接口向另一个模块传递一个控制信号,接受信号的模块根据信号值而进

行适当的动作,这种耦合被称为控制耦合。

(5) 标记耦合 。若一个模块 A 通过接口向两个模块 B 和 C 传递一个公共参数,那么称模块 B 和 C 之间

存在一个标记耦合。

(6) 数据耦合。模块之间通过参数来传递数据,那么被称为数据耦合。数据耦合是最低的一种耦合形

式,系统中一般都存在这种类型的耦合,因为为了完成一些有意义的功能,往往需要将某些模块的输出数据作为另

一些模块的输入数据。

(7) 非直接耦合 。两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的。

一个简单的例子解释依赖:

下面1、2同为创建数据库连接前的注册数据库驱动的操作。对于1,程序使用了new关键字,是编译器依赖,如果mysql的jar包不在,那么连编译都通不过。而2中,"com.mysql.jdbc.Driver"只是一个字符串,假如mysql的jar包不在,那么编译期是可以通过的,尽管有运行时异常(如果没有jar包的话)。

1、DriverManager.registerDriver(new com.mysql.jdbc.Driver());

2、Class.forName("com.mysql.jdbc.Driver");

我们实际开发中, 应该尽量做到:编译期不依赖,运行时才依赖。这就是所谓的解耦: 降低程序间的依赖关系。

解耦的思路:

第一步:使用反射来创建对象,而避免使用new关键字。

第二步:通过读取配置文件来获取要创建的对象全限定类名

使用工厂模式解耦dao和service

我们在开发中,有些依赖关系是必须的,有些依赖关系可以通过优化代码来解除的。比如下面的代码,业务层调用持久层,并且此时业务层在依赖持久层的接口和实现类。如果此时没有持久层实现类,编译将不能通过。 这种编译期依赖关系,应该在我们开发中杜绝。 我们需要优化代码解决。



public class AccountServiceImpl implements IAccountService {
	private IAccountDao accountDao = new AccountDaoImpl();
}



我们可以写一个Bean对象的工厂来代替编译期的new关键字:

先解释一下bean:

Bean:在计算机英语中,有可重用组件的含义。

JavaBean:用java语言编写的可重用组件。

javabean > 实体类

它就是创建我们的service和dao对象的。

我们在BeanFactory需要的操作:

第一个:需要一个配置文件来配置我们的service和dao配置的内容:唯一标识=全限定类名(key=value)

第二个:通过读取配置文件中配置的内容,反射创建对象

我的配置文件可以是xml也可以是properties



public class BeanFactory {
    //定义一个Properties对象
    private static Properties props;

    //定义一个Map,用于存放我们要创建的对象。我们把它称之为容器
    private static Map<String,Object> beans;

    //使用静态代码块为Properties对象赋值
    static {
        try {
            //实例化对象
            props = new Properties();
            //使用类加载器,获取properties文件的流对象
            InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
            props.load(in);
            //实例化容器
            beans = new HashMap<String,Object>();
            //取出配置文件中所有的Key
            Enumeration keys = props.keys();
            //遍历枚举
            while (keys.hasMoreElements()){
                //取出每个Key
                String key = keys.nextElement().toString();
                //根据key获取value
                String beanPath = props.getProperty(key);
                //反射创建对象
                Object value = Class.forName(beanPath).newInstance();
                //把key和value存入容器中
                beans.put(key,value);
            }
        }catch(Exception e){
            throw new ExceptionInInitializerError("初始化properties失败!");
        }
    }

    /**
     * 根据Bean的名称获取bean对象
     * @param beanName
     * @return
     * */
    public static Object getBean(String beanName){
        Object bean = null;
        try {
            String beanPath = props.getProperty(beanName);
//            System.out.println(beanPath);
            bean = Class.forName(beanPath).newInstance();//每次都会调用默认构造函数创建对象
        }catch (Exception e){
            e.printStackTrace();
        }
        return bean;
    }
}



这里我们在resource目录下写一个配置文件 bean.properties



accountService=cn.figo.service.impl.AccountServiceImpl
accountDao=cn.figo.dao.impl.AccountDaoImpl



这样工厂类通过读取bean配置文件,可以通过bean的名字得到bean的类路径,将二者存到一个容器map中,然后根据反射Class.forName(beanPath).newInstance(),在运行时创建一个对象。

这样在service中调用dao时,就可以通过BeanFactory来创建dao对象



public class AccountServiceImpl implements IAccountService {

//    private IAccountDao accountDao = new AccountDaoImpl();

    private IAccountDao accountDao = (IAccountDao)BeanFactory.getBean("accountDao");

//    private int i = 1;

    public void  saveAccount(){
        int i = 1;
        accountDao.saveAccount();
        System.out.println(i);
        i++;
    }
}



在需要service对象时,也可以通过同样的方式来获取对象:



/**
 * 模拟一个表现层,用于调用业务层
 */
public class Client {

    public static void main(String[] args) {
        //IAccountService as = new AccountServiceImpl();
        for(int i=0;i<5;i++) {
            IAccountService as = (IAccountService) BeanFactory.getBean("accountService");
            System.out.println(as);
            as.saveAccount();
        }

    }
}





springboot解耦 spring为什么可以解耦_创建对象


可以看出,我们这样创建的对象时多例的,每次创建service对象时,会调用代码中的


BeanFactory.getBean("accountService");


而getBean()方法中,又会使用代码


Class.forName(beanPath).newInstance();


每次创建对象时,都会调用默认构造函数创建对象,这样多例由于每次都会创建对象,效率低。因此我们需要考虑单例。

在运行时使用对象时,从beans中取就行了,这时beans中只有一个对象,因为在静态代码块中,类BeanFactory加载时就创建了一个service对象和一个dao对象,保存在了bean这个map中。


public static Object getBean(String beanName){
        return beans.get(beanName);
    }


这时就是单例的


springboot解耦 spring为什么可以解耦_创建对象_02