SSM

Spring学习笔记(一)——Spring介绍及工厂模式解耦

Nick · 3月31日 · 2020年本文7474字 · 阅读19分钟384

Spring概述

Spring是什么

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

为什么要学习Spring

  1. 方便解耦,简化开发:Spring就是一个大工厂,可以将所有对象的创建和依赖关系交给Spring来管理,避免硬编码所造成的过度程序耦合。
  2. AOP编程的支持:Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能。
  3. 声明式事务的支持:只需要通过配置就可以完成对事务的管理,而无需手动编程
  4. 方便程序的测试:Spring对Junit4支持,可以通过注解方便的测试Spring程序
  5. 方便集成各种优秀框架:Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、Mybatis、Quartz等)的直接支持
  6. 降低JavaEE API的使用难度:Spring对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低。

spring 的体系结构

在这里插入图片描述

Spring相关概念

程序的耦合:

耦合:程序间的依赖关系
* 类之间的依赖
* 方法间的依赖

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

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

解耦的思路:
1. 使用反射来创建对象,而避免使用new关键字。
2. 读取配置文件来获取来创建的对象的全限定类名

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

JavaBean:Java语言中可重复使用的组件

工厂模式和控制反转

工厂模式解耦:在实际开发中我们可以把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候,让一个类中的方法通过读取配置文件,把这些对象创建出来并存起来。在接下来的使用的时候,直接拿过来用就好了。那么,这个读取配置文件,创建和获取三层对象的类就是工厂。
当某一个接口删除后,编译不会报错,依然可以正常执行,但是会报异常“找不到文件”。

降低依赖关系可以用工厂模式来实现。工厂模式可以手写,但是这样开发效率很低,所以把工厂模式解耦都交给了Spring框架去做。

控制反转 IOC
控制反转把创建的对象的权力交给框架的重要特征,并非面向对象编程的专业术语,它包括依赖注入(DI)和依赖查找(DL)
作用:削减计算机程序的耦合(解除我们代码中的依赖关系)
在这里插入图片描述

手写工厂模式解耦案例

  1. IAccountDao
package dao;

/**
 * 账户的持久层接口
 */
public interface IAccountDao {

    /**
     * 模拟保存账户
     */
    void saveAccount();
}
  1. IAccountDaoImpl
package dao.impl;
import dao.IAccountDao;
/**
 * 账户的持久层实现类
 */
public class AccountDaoImpl implements IAccountDao {
    public  void saveAccount(){
        System.out.println("保存了账户");
    }
}
  1. BeanFactory(工厂方法)
package factory;

import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * 一个创建Bean对象的工厂
 *
 * Bean:在计算机英语中,有可重用组件的含义。
 * JavaBean:用java语言编写的可重用组件。
 *      javabean >  实体类
 *
 *   它就是创建我们的service和dao对象的。
 *
 *   第一个:需要一个配置文件来配置我们的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的名称获取对象
     * @param beanName
     * @return
     */
    public static Object getBean(String beanName){
        return beans.get(beanName);
    }

    /**
     * 根据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;
    }*/
}
  1. IAccountService
package service;
/**
 * 账户业务层的接口
 */
public interface IAccountService {

    /**
     * 模拟保存账户
     */
    void saveAccount();
}
  1. AccountServiceImpl
package service.impl;

import dao.IAccountDao;
import factory.BeanFactory;
import service.IAccountService;

/**
 * 账户的业务层实现类
 */
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++;
    }
}
  1. 配置文件 bean.properties
accountService=com.itheima.service.impl.AccountServiceImpl
accountDao=com.itheima.dao.impl.AccountDaoImpl
  1. 测试文件 Client
package ui;

import factory.BeanFactory;
import service.IAccountService;

/**
 * 模拟一个表现层,用于调用业务层
 */
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();
        }
    }
}

运行结果:
Spring学习笔记(一)——Spring介绍及工厂模式解耦-左眼会陪右眼哭の博客
通过手写工厂模式清楚的展现了上述图中的工厂代理模式,这种模式可以很有效的降低程序之间的依赖关系,sping就是采用了这种模式来达到解耦的效果。

spring 的 IOC 解决程序耦合

准备 spring 的开发包

官网:http://spring.io/
下载地址
http://repo.springsource.org/libs-release-local/org/springframework/spring

基本概念

ApplicationContext的三个常用实现类:
ClassPathXmlApplicationContext:它可以加载类路径下的配置文件,要求配置文件必须在类路径下。不在的话,加载不了。(更常用)
FileSystemXmlApplicationContext:它可以加载磁盘任意路径下的配置文件(必须有访问权限)

AnnotationConfigApplicationContext:它是用于读取注解创建容器的。

核心容器的两个接口引发出的问题:
ApplicationContext: 单例对象适用 采用此接口
它在构建核心容器时,创建对象采取的策略是采用立即加载的方式。也就是说,只要一读取完配置文件马上就创建配置文件中配置的对象。
BeanFactory: 多例对象使用
它在构建核心容器时,创建对象采取的策略是采用延迟加载的方式。也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象。
在这里插入图片描述

什么是单例和多例
单例:创建对象时在不同的地方获取的Bean对象是同一个
多例:创建对象时在不同的地方获取的Bean对象是不同的对象
在这里插入图片描述

创建bean对象的三种方式

<!--创建Bean的三种方式 -->
    <!-- 第一种方式:使用默认构造函数创建。
            在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时。
            采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数,则对象无法创建。
    -->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>


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


    <!-- 第三种方式:使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)
    -->
    <bean id="accountService" class="com.itheima.factory.StaticFactory" factory-method="getAccountService"></bean>

bean的作用范围调整

bean标签的scope属性:
作用:用于指定bean的作用范围
取值: 常用的就是单例的和多例的
* singleton:单例的(默认值)
* prototype:多例的
* request:作用于web应用的请求范围
* session:作用于web应用的会话范围
* global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session
<bean id=”accountService” class=”com.itheima.service.impl.AccountServiceImpl”scope=”singleton” init-method=”init” destroy-method=”destroy”></bean>

bean对象的生命周期

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

基于 XML 的配置IOC 耦合

  1. 导入jar包依赖
 <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
 </dependencies>
  1. :在类的根路径下创建一个任意名称的 xml 文件(不能是中文)
<?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">

    <!--把对象的创建交给spring来管理-->
    <!-- 配置 service -->
     <bean id="userService" class="service.impl.UserServiceImpl" scope="singleton"
          init-method="init" destroy-method="destroy"></bean>
    <!-- 配置 dao -->
    <bean id="userDao" class="dao.impl.AccountDaoImpl"></bean>
</beans>
  1. 写Service接口,和接口实现类方法
    UserService
package service;/*
 *Created by tao on 2020-03-29.
 */

public interface UserService {
    /**
     * 模拟保存账户
     */
    void saveAccount();
}

UserServiceImpl

package service.impl;/*
 *Created by tao on 2020-03-29.
 */

import service.UserService;

public class UserServiceImpl implements UserService {

    public UserServiceImpl() {
        System.out.println("对象创建了");
    }

    public void saveAccount() {
        System.out.println("service中的saveAccount方法执行了。。。");
    }

    public void init() {
        System.out.println("对象初始化了。。。");
    }

    public void destroy() {
        System.out.println("对象销毁了。。。");
    }
}
  1. 测试配置是否成功
package test;/*
 *Created by tao on 2020-03-29.
 */

import org.springframework.context.support.ClassPathXmlApplicationContext;
import service.UserService;

public class Client {
    public static void main(String[] args) {
        //1.获取核心容器对象
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.根据id获取Bean对象
        UserService us1 = (UserService) ac.getBean("userService");
        UserService us2 = (UserService) ac.getBean("userService");
        System.out.println(us1);
        System.out.println(us1 == us2);
        us1.saveAccount();
    }

}

运行结果
Spring学习笔记(一)——Spring介绍及工厂模式解耦-左眼会陪右眼哭の博客
使用spring框架基于xml配置,很容易的就实现了上述很复杂的工厂模式解耦。

0 条回应
在线人数:1人
隐藏