Spring-01

1.自定义Spring工厂

1.1早期创建对象

在我们传统MVC项目中,后台代码一般分为:Controller层、Service层、Dao层,比如说:UserController雷、UserService接口、UserDao接口、UserServiceImpl实现类、UserDao实现类。
在Controller层、Service层,我们需要创建对应的实例来调用资源。
● Controller:
○ UserService userService = new UserServiceImpl();
● Service:
○ UserDao userDao = new UserDaoImpl();
在这里插入图片描述

上面这么写确实是可以实现功能的,但是代码的耦合性比较高,当我们删除UserServiceImpl类或则UserDaoImpl类,代码是编译不通过的,这就耦合性高。我们需要降低代码的耦合性,当删除对应的实现类,代码编译可以通过的,这就是代码耦合性低。但是删除对应的实现类是无法运行项目的。
代码的耦合性只能降低,不能消除。

1.2自定义Spring工厂

为了降低代码的耦合性,我们可以通过自定义一个工厂,将创建对象的任务交给工厂,在项目运行的时候,将对应的Service、Dao层的实体类的对象在工厂中创建,当我们使用的时候,不需要自己创建,只需要从工厂中取出就可以了。

可以在创建工厂的时候创建对象(单例模型,每次取出的对象地址相同,当对象实体类不涉及到变化的成员变量是可以的,相对多例节省资源);
也可以在我们使用对象的时候,再去创建对象(多例模型,每次取出的对象地址是不同的,当对象实体涉及到变化的成员变量,不能使用同一个对象,每次取出对象都需要是不同的对象)。

Spring可以代替我们自创建的工厂完成上述功能。

1.2.1 factory.properties文件

factory.properties文件用来存储实体类的key和 对应的全限定类名,需要保证 key的唯一性。
我们可以根据实体类的key获取到对应的全限定类名,通过反射创建对象。

userDao=com.sofwin.dao.impl.UserDaoImpl
userService=com.sofwin.service.impl.UserServiceImpl

1.2.2 FactoryBean工厂

FactoryBean工厂需要加载factory.properties文件,将对应文件中的key-value存储到:List<Map<String,String>> javabeans ,也可以创建List<Map<String,Object>>来存储对应的key-对象集合。

提供两者获取对象的方法,单例模式和多例模式。

public class FactoryUtil {
    private static List<Map<String,String>> javabeans
        =new ArrayList<Map<String, String>>();
    private static List<Map<String,Object>> objects 
        = new ArrayList<Map<String, Object>>();
    /**静态代码块的作用: 1.读取factory.properties文件;
                        2.将文件的key-value对应关系存储到:List<Map<String,String>> javabeans中;
                        3.创建对应实体类的对象,存储到:List<Map<String,Object>> objects中*/
    static {
        InputStream resourceAsStream = 
            FactoryUtil.class.getClassLoader().getResourceAsStream("factory.properties");
        Properties properties=new Properties();
        try {
            properties.load(resourceAsStream);
            Enumeration<?> enumeration = properties.propertyNames();

            while(enumeration.hasMoreElements()){
                String enumKey =(String) enumeration.nextElement();
                Map map =new HashMap();
                map.put(enumKey,properties.get(enumKey));
                System.out.println("加载到工厂的map:"+enumKey+" : "+""+ properties.get(enumKey));
                javabeans.add(map);
                try {
                    /**
                     * 在Factory一创建的时候,就将 factory.properties中的对象实例化到集合objects中,
                     * 当通过单例模型获取对象的时候,可以直接从集合中拿对应的对象
                     * */
                    Map object = new HashMap();
                    object.put(enumKey,Class.forName(String.valueOf(properties.get(enumKey))).newInstance());
                    objects.add(object);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

1.2.3单例模式

单例模式每次获取到的对象是同一个,同样的地址。仅适用于:对象的实体类中不存在变化的成员变量,线程不安全。
对于自定义工厂实现单例模式,就是在工厂创建的时候,创建实体类对象,需要的时候再返回地址。

    /**
     * 单例模式获取javaBean对象,每次获取到的对象是一个,同样的地址
     * @return
     */
    public static Object getSingleObject(String key) throws Exception {
        Object result =null;
        for(Map<String,Object> object:objects){
            if(object.containsKey(key)){
                result = object.get(key);
            }
        }
        if(result==null){
            throw new Exception("Factory工厂中不存 :"+key+"对应的全限定类名");
        }else{
            System.out.println("获取单例对象:"+result);
        }
        return  result;
    }

1.2.4多例模式

多例模式每次获取到的对象是不同的,线程安全,可以用在对象实体类中存在变化的成员变量当中,相对单例资源消耗。

对于自定义工厂实现单例模式,就是在工厂创建的时候,不会创建实体类对象,而是在 需要的时候再创建对象的对象,每次获取到的对象的地址是不同的。

    /**
     * 多例模式获取javaBean对象,每次获取到对象是不同的,不同的地址
     * @param key
     * @return
     */
    public static Object getMultiObject(String key) throws Exception {
        Object result =null;
        for(Map<String,String>  javabean:javabeans){
            if(javabean.containsKey(key)){
                result=Class.forName(javabean.get(key).toString()).newInstance();
            }
        }
        if(result==null){
            throw new Exception("Factory工厂中不存:"+key+"对应的全限定类名");
        }else{
            System.out.println("获取多例对象:"+result);
        }
        return result;
    }

1.2.5测试演示

在这里插入图片描述

1.3工厂模式与IOC

在实际开发中我们可以把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候,让一个类中的方法通过读取配置文件,把这些对象创建出来并存起来。在接下来的使用的时候,直接拿过来用就好了。

那么,这个读取配置文件,创建和获取三层对象的类就是工厂。

在这里插入图片描述

控制反转(Inversion Of Control,IOC):把创建对象的权力交给框架(工厂),从工厂中拿对象,不在是自己通过new的方式创建对象。控制反转包括:依赖注入(DI)和依赖查找

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

2.Spring

2.1 Spring的概念和作用

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

使用Spring可以降低代码的耦合性:我们之前通过new创建对象,现在创建对象交给Spring,我们需要的时候从Spring容器中取出就可以。创建对象的权限由之前的我们决定交给了Spring决定,这就是控制反转(IOC)

创建了对象之后,对于对象的 成员变量,我们可以依赖注入(DI)进行赋值

在当前类中需要用到其他类对象,由spring为我们提供,我们需要在配置文件中说明。依赖关系的维护就是依赖注入。
Spring的IOC主要是为了降低耦合。

2.2Spring的体系结构

在这里插入图片描述

2.3使用Spring开发

创建一个maven项目,

2.3.1导入依赖

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

2.3.2创建Service类

package com.Eheart.service;

public class UserService {
}

2.3.3配置文件bean.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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--id是唯一的,通过id从容器中取出对象(根据全限定类名反射创建对象)-->
    <bean id="userService" class="com.Eheart.service.UserService"></bean>
</beans>

2.3.4测试

public class test {

    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        UserService userService = ac.getBean("userService", UserService.class);
        System.out.println(userService);
    }
}

在这里插入图片描述

2.4Spring的接口体系

在这里插入图片描述

2.4.1ApplicationContext的三个实现类

在这里插入图片描述

● ClassPathXmlApplicationContext:加载类路径下的配置文件,resource目录下。(更常用)
● FileSystemXmlApplicationContext:加载磁盘任意路径下的文件。
● AnnotationConfigApplicationContext:用于注解创建容器的类。

ApplicationContext classAc = 
    new ClassPathXmlApplicationContext("bean.xml");
ApplicationContext fileAc = 
    new FileSystemXmlApplicationContext("E:\\App\\ideaIU-2019.2.4.win\\workspacesub\\spring01\\src\\main\\resources\\bean.xml");

2.4.2BeanFactory和ApplicationContext的区别

● ApplicationContext:它在构建核心容器的时候,会立即加载立即创建对象(一读取完配置文件会创建配置的对象)加载bean的时候可以通过scope属性进行设置单例多例模式。
● BeanFactory:它在构建容器的时候,采用延迟加载的方式创建对象(什么时候根据id获取对象了,什么时候才真正的创建对象)

在这里插入图片描述

2.4.3三种创建bean的方式

● 基于构造函数的创建方式
● 基于工厂的普通方法创建bean对象
● 基于工厂的静态方法创建对象

在这里插入图片描述

2.4.4bean的作用范围

● 基于构造函数的创建方式的bean默认是单例模式,每次取出的bean是同样的地址
● 可以通过scope设置bean的作用范围:
○ singleton:单例模式(默认的)
○ prototype:多例模式
○ request:作用于web应用的请求范围
○ session:作用于web应用的会话范围
○ globalSession:全局会话范围,作用于集群环境的会话范围(全局会话范围),当不是集群环境的时候,它就是session
○ application:
○ websocket:

在这里插入图片描述

2.4.5bean的生命周期

  1. 单例对象
    a. 出生:当容器创建,单例对象也被创建
    b. 活着:只有容器存在,对象一直活着
    c. 消亡:容器销毁,对象消亡
    d. 总结:单例对象的生命周期和容器的生命周期相同。
  2. 多例对象
    a. 出生:容器创建的时候,不会创建多例对象。只有当调用到对象的时候,才会创建对象
    b. 活着:多例对象使用过程中就会一直活着
    c. 消亡:Spring不会回收,而是交给java。当对象长时间不用,且没有别的对象引用,由java的GC垃圾回收机制回收。调用close()方法无法销毁对象。

在这里插入图片描述
在这里插入图片描述

  1. 问题:由ApplicationContext ac =new ClassPathXmlApplicationContext(“bean.xml”); 创建的对象在Bean的生命周期中无法调用close()方法

在这里插入图片描述
解决措施:将对象看出子对象:
ClassPathXmlApplicationContext ac =new ClassPathXmlApplicationContext(“bean.xml”);

2.5DI依赖注入(Dependency Injection)

IOC的作用:降低程序间的耦合(依赖关系),之前通过new方式创建对象的方式被Spring的IOC代替,我们将对象的创建交给Spring,需要对象的时候从Spring容器中取对象。
DI:通过IOC创建的对象,对于对象的属性值,可能是没有值的。我们需要通过DI(依赖注入)来给对象的属性值赋值。

2.5.1 DI注入的数据类型

● 基本数据类型和String类型,可以使用value属性注入
● 其他bean类型(在配置文件或则注解中配置过bean),需要使用ref属性注入值
● 复杂类型/集合类型
○ 用于给List结构集合注入的标签:
■ list、array、set
○ 用于给Map结构集合注入的标签:
■ map、props
○ 结构相同,标签可以互换

2.5.2DI注入的方式

使用构造函数注入
○ 使用的标签:constructor-arg
○ 标签出现的位置:bean标签的内部
○ 标签中的属性:
■ type:指定注入数据的数据类型,该数据也是构造函数中的某个或则某些参数的类型
■ index:用于指定要注入的数据,通过构造函数中给指定位置的参数赋值。索引从0开始
■ name:用于给构造函数中指定名称的参数赋值
■ value:用于给基本数据类型和String类型赋值
■ ref:用于指定其他bean类型的数据,它就是在Spring的IOC容器中出现的bean对象。
○ 优势:在获取bean对象的时候,注入时数据是必然的操作。
○ 劣势:当我们在创建对象的时候,如果不需要这些数据,也是必须提供的。

注意:Spring使用构造函数依赖注入Date类型出现的问题:
Could not convert argument value of type [java.lang.String] to required type [java.util.Date]: Failed to convert value of type ‘java.lang.String’ to required type ‘java.util.Date’;

在这里插入图片描述
错误的原因:Date类型无法通过value进行赋值,value仅可以赋值:基本类型和String类

    <bean id="accountService" class="com.sofwin.service.impl.AccountServiceImpl" scope="singleton">
        <constructor-arg type="java.lang.Integer"  name="id" index="0" value="99"></constructor-arg>
        <constructor-arg type="java.lang.String" name="appName" index="1" value="stringvalue"></constructor-arg>
<!--    错误的写法:Date类型无法通过value进行赋值,value仅可以赋值:基本类型和String<constructor-arg type="java.util.Date" name="createDate" index="2"  value="2022-05-21"></constructor-arg>
-->
        <constructor-arg type="java.util.Date" index="2" name="createDate" ref="now"></constructor-arg>
    </bean>
    <bean id="now" class="java.util.Date"></bean>

使用set方法提供
○ 涉及的标签:property
○ 出现的位置:bean标签的内部
○ 标签的属性:
■ name:用于指定注入的时所谓用的set方法
■ value:用于提供基本类型和String类型的数据
■ ref:用于指定其他bean类型数据,它就是在spring的IOC容器中出现过的bean对象
○ 优势:创建对象的时候没有明确的限定,可以直接使用默认构造函数
○ 如果需要保证某个成员必须有值,则set方法无法保证一定注入;

在这里插入图片描述

2.5.3DI注入的数据类型

● 基本数据类型和String类型:可以直接通过value注入
● 其他bean对象类型:需要通过ref引用其他的bean对象
● 复杂数据类型:List、array、Set;map、properties

在这里插入图片描述

在这里插入图片描述

3、创建Spring工程中遇到的问题:

  1. Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanInitializationException: Could not load properties; nested exception is java.util.InvalidPropertiesFormatException: org.xml.sax.SAXParseException; lineNumber: 11; columnNumber: 77; 文档根元素 “beans” 必须匹配 DOCTYPE 根 “null”。
    答:在bean.xml(application.xml)文件中存在重复加载的文件
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值