• 08 Spring框架的概念、优势、体系结构、耦合性和IOC(反转控制)


    1.spring介绍

      Spring 是分层的 Java SE/EE 应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control: 反转控制)AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层 Spring MVC 和持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业应用开源框架。

    2.Spring的优势

    (1)方便解耦,简化开发

      通过 Spring 提供的 IoC 容器,可以将对象间的依赖关系交由 Spring 进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可 以更专注于上层的应用。

    (2)AOP 编程的支持

      通过 Spring 的 AOP 功能,方便进行面向切面的编程,许多不容易用传统 OOP 实现的功能可以通过 AOP 轻松应付。

    (3)声明式事务的支持

      可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理, 提高开发效率和质量。

    (4)方便程序的测试

      可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。

    (5)方便集成各种优秀框架

      Spring 可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz 等)的直接支持。

    (6)降低 JavaEE API 的使用难度

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

    (7)Java 源码是经典学习范例

    Spring 的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对 Java 设计模式灵活运用以及对 Java 技术的高深造诣。它的源代码无意是 Java 技术的最佳实践的范例。

    3.Spring的体系结构

    4.耦合性介绍

      耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差( 降低耦合性,可以提高其独立性)。耦合性存在于各个领域,而非软件设计中独有的,但是我们只讨论软件工程中的耦合。

      在软件工程中,耦合指的就是就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计应使类和构件之间的耦合最小。软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个准则就是高内聚低耦合

      耦合性有如下分类:

      (1)内容耦合。当一个模块直接修改或操作另一个模块的数据时,或一个模块不通过正常入口而转入另一个模块时,这样的耦合被称为内容耦合。内容耦合是最高程度的耦合,应该避免使用之。

      (2)公共耦合。两个或两个以上的模块共同引用一个全局数据项,这种耦合被称为公共耦合。在具有大量公共耦合的结构中,确定究竟是哪个模块给全局变量赋了一个特定的值是十分困难的。

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

      (4)  控制耦合 。一个模块通过接口向另一个模块传递一个控制信号,接受信号的模块根据信号值而进行适当的动作,这种耦合被称为控制耦合。

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

      (6)  数据耦合。模块之间通过参数来传递数据,那么被称为数据耦合。数据耦合是最低的一种耦合形式,系统中一般都存在这种类型的耦合,因为为了完成一些有意义的功能,往往需要将某些模块的输出数据作为另一些模块的输入数据。

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

      总之,耦合是影响软件复杂程度和设计质量的一个重要因素,在设计上我们应采用以下原则:如果模块间必须存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,尽量避免使用内容耦合。

    4.程序的耦合
    耦合:程序间的依赖关系
      包括:
        类之间的依赖
        方法间的依赖
    解耦:降低程序间的依赖关系
    实际开发中:应该做到:编译期不依赖,运行时才依赖。
    解耦的思路
      第一步:使用反射来创建对象,而避免使用new关键字。
      第二步:通过读取配置文件来获取要创建的对象全限定类名

    5.IOC原理

    Inverse Of Control: 反转控制

    原来:我们在获取对象时,都是采用 new 的方式。是主动的。

    现在:我们获取对象时,同时跟工厂要,有工厂为我们查找或者创建对象。是被动的。

    这种被动接收的方式获取对象的思想就是控制反转,它是 spring 框架的核心之一。

    明确 ioc 的作用:削减计算机程序的耦合(解除我们代码中的依赖关系)。

    5.spring基于XML的IOC环境搭建和入门

    spring5 版本是用 jdk8编写的,所以要求我们的 jdk 版本是 8 及以上。同时 tomcat 的版本要求 8.5及以上。

    (1)创建一个maven的纯java工程,在pom文件中,添加spring依赖

    <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.0.2.RELEASE</version>
            </dependency>
        </dependencies>

    (2)创建业务层接口和实现类

    <1>业务层接口

    package service;
    
    /**
     * 账户业务层的接口
     */
    public interface IAccountService {
    
        /**
         * 模拟保存账户
         */
        void saveAccount();
    }

    <2>实现类

    package service.impl;
    import dao.IAccountDao;
    import dao.impl.AccountDaoImpl;
    import service.IAccountService;
    
    /**
     * 账户的业务层实现类
     */
    public class AccountServiceImpl implements IAccountService {
    
        private IAccountDao accountDao = new AccountDaoImpl();
    
        public void  saveAccount(){
            accountDao.saveAccount();
        }
    }

    (3)创建持久层接口和实现类

    <1>持久层接口

    package dao;
    
    /**
     * 账户的持久层接口
     */
    public interface IAccountDao {
    
        /**
         * 模拟保存账户
         */
        void saveAccount();
    }

    <2>实现类

    package dao.impl;
    
    import dao.IAccountDao;
    
    /**
     * 账户的持久层实现类
     */
    public class AccountDaoImpl implements IAccountDao {
    
        public  void saveAccount(){
    
            System.out.println("保存了账户");
        }
    }

    (4)创建bean.xml,让 spring 管理资源,在配置文件中配置 service 和 dao

    <!--把对象的创建交给spring来管理-->
    spring对bean的管理细节
      1.创建bean的三种方式
      2.bean对象的作用范围
      3.bean对象的生命周期

    (1)创建Bean的两种方式
      第一种方式:使用默认构造函数创建。
        在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时。
        采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数,则对象无法创建。注意:这里说的没有默认构造函数,是指构造函数被修改,而默认的构造函数又没有。

    下面的代码是没有默认的构造函数的:

    package service.impl;
    import dao.IAccountDao;
    import dao.impl.AccountDaoImpl;
    import service.IAccountService;
    
    /**
     * 账户的业务层实现类
     */
    public class AccountServiceImpl implements IAccountService {//带参数的构造函数
        public AccountServiceImpl(String name) {
        }
    
        public void  saveAccount(){
            //accountDao.saveAccount();
            System.out.println("AccountServiceImpl中的saveAccount方法执行了");
        }
    
    }

    下面的代码是有构造函数的。

    package service.impl;
    import dao.IAccountDao;
    import dao.impl.AccountDaoImpl;
    import service.IAccountService;
    
    /**
     * 账户的业务层实现类
     */
    public class AccountServiceImpl implements IAccountService {
    //默认的构造函数
        public AccountServiceImpl() {
        }
    
        //带参数的构造函数
        public AccountServiceImpl(String name) {
        }
    
        public void  saveAccount(){
            //accountDao.saveAccount();
            System.out.println("AccountServiceImpl中的saveAccount方法执行了");
        }
    
    }

    注意:以下的代码是有默认的构造方法的

    package service.impl;
    import dao.IAccountDao;
    import dao.impl.AccountDaoImpl;
    import service.IAccountService;
    
    /**
     * 账户的业务层实现类
     */
    public class AccountServiceImpl implements IAccountService {
    
    
        public void  saveAccount(){
            //accountDao.saveAccount();
            System.out.println("AccountServiceImpl中的saveAccount方法执行了");
        }
    
    }

    使用默认构造函数创建的配置方式:

    <bean id="accountDao" class="dao.impl.AccountDaoImpl"></bean>

      第二种方式: 使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)

    InstanceFactory.java这个类模拟一个工厂类,创建jar包找那个的某个类的对象。

    package factory;
    import service.IAccountService;
    import service.impl.AccountServiceImpl;
    
    /**
     * 模拟一个工厂类(该类可能是存在于jar包中的,我们无法通过修改源码的方式来提供默认构造函数)
     */
    public class InstanceFactory {
    
        public IAccountService getAccountService(){
            return new AccountServiceImpl();
        }
    }

     使用普通工厂中的方法创建对象的配置方法:

    <!--第二种方式: 使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)-->
       <bean id="instanceFactory" class="factory.InstanceFactory"></bean>
       <bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>

    (2)bean的作用范围调整
    bean标签的scope属性:
      作用:用于指定bean的作用范围
      取值: 常用的就是单例的和多例的
        singleton:单例的(默认值)
        prototype:多例的
        request:作用于web应用的请求范围
        session:作用于web应用的会话范围
        global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session

    <bean id="accountService" class="service.impl.AccountServiceImpl" scope="prototype"></bean>

    测试效果:

    package ui;
    import dao.IAccountDao;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import service.IAccountService;
    import service.impl.AccountServiceImpl;
    
    /**
     * 模拟一个表现层,用于调用业务层
     */
    public class Client {
    
        /**
         * 获取spring的Ioc核心容器,并根据id获取对象
         *
         * ApplicationContext的三个常用实现类:
         *      ClassPathXmlApplicationContext:它可以加载类路径下的配置文件,要求配置文件必须在类路径下。不在的话,加载不了。(更常用)
         *      FileSystemXmlApplicationContext:它可以加载磁盘任意路径下的配置文件(必须有访问权限)
         *
         *      AnnotationConfigApplicationContext:它是用于读取注解创建容器的,是明天的内容。
         *
         * 核心容器的两个接口引发出的问题:
         *  ApplicationContext:     单例对象适用              采用此接口
         *      它在构建核心容器时,创建对象采取的策略是采用立即加载的方式。也就是说,只要一读取完配置文件马上就创建配置文件中配置的对象。
         *
         *  BeanFactory:            多例对象使用
         *      它在构建核心容器时,创建对象采取的策略是采用延迟加载的方式。也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象。
         * @param args
         */
        public static void main(String[] args) {
            //1.获取核心容器对象
            ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    
            //2.根据id获取Bean对象,注意id是在bean.xml文件配置的
            IAccountService as1  = (IAccountService)ac.getBean("accountService");
            IAccountService as2  = (IAccountService)ac.getBean("accountService");
            System.out.println(as1==as2);
            
        }
    }

    控制台输出:

    (3)bean对象的生命周期
    单例对象
      出生:当容器创建时对象出生
      活着:只要容器还在,对象一直活着
      死亡:容器销毁,对象消亡
    总结:单例对象的生命周期和容器相同
    多例对象
      出生:当我们使用对象时spring框架为我们创建
      活着:对象只要是在使用过程中就一直活着。
      死亡:当对象长时间不用,且没有别的对象引用时,由Java的垃圾回收器回收

    <?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">
    
        <!--把对象的创建交给sping来管理-->
        <bean id="accountService" class="service.impl.AccountServiceImpl"></bean>
        <bean id="accountDao" class="dao.impl.AccountDaoImpl"></bean>
    
    </beans>

    (5)表现层

    package ui;
    import dao.IAccountDao;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import service.IAccountService;
    import service.impl.AccountServiceImpl;
    
    /**
     * 模拟一个表现层,用于调用业务层
     */
    public class Client {
    
        /**
         * 获取spring的Ioc核心容器,并根据id获取对象
         *
         * ApplicationContext的三个常用实现类:
         *      ClassPathXmlApplicationContext:它可以加载类路径下的配置文件,要求配置文件必须在类路径下。不在的话,加载不了。(更常用)
         *      FileSystemXmlApplicationContext:它可以加载磁盘任意路径下的配置文件(必须有访问权限)
         *
         *      AnnotationConfigApplicationContext:它是用于读取注解创建容器的,是明天的内容。
         *
         * 核心容器的两个接口引发出的问题:
         *  ApplicationContext:     单例对象适用              采用此接口
         *      它在构建核心容器时,创建对象采取的策略是采用立即加载的方式。也就是说,只要一读取完配置文件马上就创建配置文件中配置的对象。
         *
         *  BeanFactory:            多例对象使用
         *      它在构建核心容器时,创建对象采取的策略是采用延迟加载的方式。也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象。
         * @param args
         */
        public static void main(String[] args) {
            //1.获取核心容器对象
            ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    //        ApplicationContext ac = new FileSystemXmlApplicationContext("C:\Users\zhy\Desktop\bean.xml");
            //2.根据id获取Bean对象,注意id是在bean.xml文件配置的
            IAccountService as  = (IAccountService)ac.getBean("accountService");
            IAccountDao adao = ac.getBean("accountDao", IAccountDao.class);
    
            System.out.println(as);
            System.out.println(adao);
            as.saveAccount();
        }
    }

    控制台输出:

  • 相关阅读:
    【插件开发】—— 10 JFace开发详解
    百度地图POI数据爬取,突破百度地图API爬取数目“400条“的限制11。
    Python3中遇到UnicodeEncodeError: 'ascii' codec can't encode characters in ordinal not in range(128)
    Python 3.X 要使用urllib.request 来抓取网络资源。转
    python创建目录保存文件
    Python返回数组(List)长度的方法
    python中for、while循环、if嵌套的使用
    (转)python3 urllib.request.urlopen() 错误UnicodeEncodeError: 'ascii' codec can't encode characters
    python 之 string() 模块
    (转)Python3异常-AttributeError: module 'sys' has no attribute 'setdefaultencoding
  • 原文地址:https://www.cnblogs.com/luckyplj/p/11314158.html
Copyright © 2020-2023  润新知