spring IOC基础概述

15 篇文章 0 订阅
5 篇文章 0 订阅

1.Spring概述

1.1什么是Spring

​ Spring 是一个开源框架,是为了解决企业应用程序开发复杂性而创建的(解耦)。

​ 框架的主要优势之一就是其分层架构,分层架构允许您选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。

1.2 Spring的优点

1.方便解耦,简化开发(基础重要功能)

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

  1. AOP编程的支持开闭原则

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

  1. 声明式事务的支持

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

  1. 方便程序的测试

​ 可以用非容器依赖的编程方式进行几乎所有的测试工作,在Spring里,测试不再是昂贵的操作,而是随手可做的事情。例如:Spring对Junit4支持,可以通过注解方便的测试Spring程序。

1.3 Spring的体系结构

tu_0

2.IOC

2.1工厂模式解耦

​ 耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差( 降低耦合性,可以提高其独立性)。 耦合性存在于各个领域,而非软件设计中独有的,但是我们只讨论软件工程中的耦合。
​ 在软件工程中, 耦合指的就是就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计
应使类和构件之间的耦合最小。 软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。 划分模块的一个准则就是高内聚,低耦合。
总结:
​ 耦合是影响软件复杂程度和设计质量的一个重要因素,在设计上我们应采用以下原则:

​ 如果模块间必须存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,尽量避免使用内容耦合。内聚与耦合内聚标志一个模块内各个元素彼此结合的紧密程度,它是信息隐蔽和局部化概念的自然扩展。内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事。它描述的是模块内的功能联系。耦合是软件结构中各模块之间相互连接的一种度量,耦合强弱取决于模块间接口的复杂程度、进入或访问一个模块的点以及通过接口的数据。 程序讲究的是低耦合,高内聚。就是同一个模块内的各个元素之间要高度紧密,但是各个模块之间的相互依存度却要不那么紧密。

​ 内聚和耦合是密切相关的,同其他模块存在高耦合的模块意味着低内聚,而高内聚的模块意味着该模块同其他
模块之间是低耦合。在进行软件设计时,应力争做到高内聚,低耦合。

2.2 什么是工厂

它的核心思想就是:
1、通过Bean工厂读取配置文件使用反射创建对象。
2、把创建出来的对象都存起来,当使用者需要对象的时候,不再自己创建对象,而是调用Bean工厂的方法从容器中获取对象
这里面要解释两个问题:
什么是工厂?
工厂就是负责给我们从容器中获取指定对象的类。这时候我们获取对象的方式发生了改变。
原来:我们在获取对象时,都是采用 new 的方式。 是主动的。

之前的方式:

image-20210723171038644

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

image-20210723171231227

3.Spring - IOC

3.1 创建好maven工程,并引入依赖:

<dependencies>
        <!--引入相关依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.8</version>
        </dependency>
        <!--druid的依赖  如果mysql是8.0及其以上版本,那么druid就要使用1.1.10-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>
        <!--mysql驱动,为本地mysql驱动-->
        <dependency>
            <groupId>com.yxinmiracle</groupId>
            <artifactId>mysqlUtils</artifactId>
            <version>8.0.16</version>
        </dependency>
        <!--dbutils的依赖-->
        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.7</version>
        </dependency>
    </dependencies>
	<!--防止运行出错-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.4.2</version>
                <configuration>
                    <skipTests>true</skipTests>
                </configuration>
            </plugin>
        </plugins>
    </build>

3.2 创建接口以及实现类

  • UserService接口:
package com.yxinmiracle.service;

/**
 * @version 1.0
 * @author: YxinMiracle
 * @date: 2021-07-23 00:15
 */

public interface UserService {
    void addUser();
}

  • UserServiceImpl实现类:
package com.yxinmiracle.service.impl;

import com.yxinmiracle.dao.UserDao;
import com.yxinmiracle.service.UserService;

/**
 * @version 1.0
 * @author: YxinMiracle
 * @date: 2021-07-23 00:16
 */

public class UserServiceImpl implements UserService {
    @Override
    public void addUser() {
        System.out.println("UserServiceImpl");
    }
}

  • AnotherUserService
package com.yxinmiracle.service.impl;

import com.yxinmiracle.service.UserService;

/**
 * @version 1.0
 * @author: YxinMiracle
 * @date: 2021-07-23 00:14
 */

public class AnotherUserService implements UserService {
    public AnotherUserService() {
        System.out.println("AnotherUserService对象已经创建");
    }

    public void initData(){
        System.out.println("执行对象创建的时候的初始化操作");
    }

    @Override
    public void addUser() {
        System.out.println("AnotherUserService");
    }
}

3.3 在类路径下创建spring的配置文件,命名为任意名字.xml(不能为中文)

​ 目的:为了实现解耦,不使用new的方式来获取实现类的对象

<?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"><!--
    我们要将,需要创建的类都配置到配置文件中
    id 就是对象唯一标识
    class就是他的全限名
    scope表示对象的使用范围:
        1:singleton 单例,默认值
        2:prototype 多例 (有可变的成员变量的时候才使用多例)
    lazy-init 标识创建对象的过程是否懒加载,默认是false
        不懒加载,核心容器创建的时候,就将对象创建出来 !使用空间换时间
    init-method 表示对象创建的时候,需要调用的方法,需要在对象创建之后立即执行的代码就写在这个方法里
    destroy-method 表示对象销毁的时候需要调用的方法
-->
    <bean id="UserServiceImpl" class="com.yxinmiracle.service.impl.UserServiceImpl"></bean>
    <bean id="AnotherUserService" class="com.yxinmiracle.service.impl.AnotherUserService" init-method="initData"></bean>
</beans>

​ 此时我们就可以在代码中通过id来获取UserServiceImpl这个实现类的对象去调用这个实现类中的addUser方法

3.4 测试类代码:

    @Test
    public void test01(){
        // 创建UserService的实现类对象,执行addUser方法
        // 1. 创建spring的核心容器对象,制定要加载的配置文件,ClassPathXmlApplicationContext去加载类路径下的xml配置文件
        // spring在容器创建的时候就已经创建了对象
        ApplicationContext act = new ClassPathXmlApplicationContext("spring.xml");
        // 2. 从核心容器中根据id取出配置文件
        UserService userService = (UserService) act.getBean("AnotherUserService");
        /*
        * 实现解耦
        * */
        userService.addUser();
    }

此时要是我们将AnotherUserService这个实现类删除的话,整个项目的代码也不会报错,实现了解耦。spring默认创建的对象是单例的,可以通过debug来测试一下,此外还可以观察一下init-method 有无起作用:

image-20210723174924720

当在加载核心容器的时候,也就是第62行代码,就将ApplicationContext中的构造函数以及initData都执行了。

image-20210723175135838

最后打印为true,也就证明了spring核心容器创建出的对象默认是单例的。

3.5 配置文件的详解(bean标签)

    id 就是对象唯一标识
    class就是他的全限名
    scope表示对象的使用范围:
        1:singleton 单例,默认值
        2:prototype 多例 (有可变的成员变量的时候才使用多例)
    lazy-init 标识创建对象的过程是否懒加载,默认是false
        不懒加载,核心容器创建的时候,就将对象创建出来 !使用空间换时间
    init-method 表示对象创建的时候,需要调用的方法,需要在对象创建之后立即执行的代码就写在这个方法里
    destroy-method 表示对象销毁的时候需要调用的方法

使用spring使用IOC:

1. 由spring的核心容器去加载spring的配置文件,从而创建出使用者想要的所有对象,存储到文档中
2. 在调用核心容器的方法,传入对象的id,从而获取对象,这样就可以实现解耦

spring的IOC的使用步骤:

 1. 引入依赖
 2. 编写配置文件,要创建对象的类就对应一个bean标签
 3. 在要使用对象的地方,创建核心容器制定要加载的配置文件,然后调用核心容器的getBean(id)获取对象
 ! 可以解耦,但是不仅仅是为了解耦,就是把对象交给spring管理

spring核心容器中的对象什么时候创建,什么时候销毁

1. 如果是单例:
     1.1 懒加载:在调用核心容器的getBean()方法的时候创建
     1.2 不懒加载: 在核心容器穿件的时候就创建对象
2. 如果是多例:(不可能在核心容器创建的时候就创建出来,因为是多例,不可能只有一个)
     都是在调用核心容器的getBean()方法的时候创建,可以使用debug的方法进行尝试
 对象的销毁:
   1. 如果对象是在核心容器里面的(单例) ,只有在容器close的时候,对象才会销毁,但是这个方法只有在接口的实现类中才有
   2. 如果对象不是在核心容器里面的 (多例), 那么对象会在gc线程使用垃圾回收算法判定为垃圾后,就会被回收了

4. Spring工厂详解

4.1 ApplicationContext接口的三种实现类

  • ClassPathXmlApplicationContext:它是从类的根路径下加载配置文件
  • FileSystemXmlApplicationContext:它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
  • AnnotationConfigApplicationContext:当我们使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。

ApplicationContext的类图:

image-20210723102415411

可以清楚的看见他继承了六个接口,其中最重要的是:

image-20210723102605604

其中有三个实现类较为重要,分别是:

image-20210723102800613

  • ClassPathXmlApplicationContext是加载类路径下的配置文件
  • FileSystemXmlApplicationContext从构造函数中可以看出是加载电脑中磁盘中的配置文件

image-20210723103155652

  • AnnotationConfigApplicationContext 我们使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。

从图中可以看出ApplicationContext是继承与BeanFactory的,那么他们两者有什么区别?

从类图中可以查出BeanFactory有一个直接实现类,为:XmlBeanFactory

image-20210723103536981

4.2 BeanFactory 和 ApplicationContext 的区别

  • ApplicationContext (现在)

    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    
  • XmlBeanFactory是老版本使用的工厂,目前已经被废弃

    BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
    

两者的区别:

  • ApplicationContext加载方式是框架启动时就开始创建所有单例的bean,存到了容器里面

    • 非懒加载: 在核心容器创建的时候,创建出所有的bean对象,存到核心容器中
    • 懒加载: 第一次调用getBean()的时候,创建出bean对象,存到核心容器中
  • BeanFactory 默认是懒加载

4.3 创建bean的三种方式

4.3.1 无参构造方式

image-20210723105319736

4.3.2静态工厂

getDataSource方法为:

 /**
     * 获取数据源
     * @return
     */
public static DataSource getDataSource(){
    return dataSource;
}

可以通过类名直接调用,这个就是静态工厂的方式:

<bean id="dataSource" class="com.yxinmiracle.utils.DruidUtil" factory-method="getDataSource"></bean>
4.3.3实例工厂

现在将getDataSource改为:

 /**
     * 获取数据源
     * @return
     */
public DataSource getDataSource(){
    return dataSource;
}

不是静态方法就意味着我们需要先创建出DruidUtil类的对象,才能使用这个方法,也就是采用实例工厂的方式:

<!-- 实例工厂 -->
<bean id="DruidUtil2" class="com.yxinmiracle.utils.DruidUtil2"></bean>
<bean id="dataSource" factory-bean="DruidUtil2" factory-method="getDataSource"></bean>

5 注入依赖

5.1 注入依赖的两种方式:

  1. 构造方法方式注入
  2. set方法方式的注入
方式一:采用构造函数的方式进行注入,使用constructor-arg
    name属性就是构造函数的参数名
    ref属性就是要注入的对象的id
方式二:采用set方法的方式进行注入,使用property标签
    name属性就是要赋值的变量名(具体来说是setxxxx函数的xxx)
    ref就是要注入的对象id
方式一:采用构造函数的方式进行注入,使用constructor-arg
    name属性就是构造函数的参数名
    ref属性就是要注入的对象的id
方式二:采用set方法的方式进行注入,使用property标签
    name属性就是要赋值的变量名(具体来说是setxxxx函数的xxx)
    ref就是要注入的对象id

5.2 构造方式注入

如何用spring将userDao赋值

package com.yxinmiracle.service.impl;

import com.yxinmiracle.dao.UserDao;
import com.yxinmiracle.service.UserService;

/**
 * @version 1.0
 * @author: YxinMiracle
 * @date: 2021-07-23 00:16
 *
 * 1. 构造方法方式注入
 * 2. set方法方式的注入
 */

public class UserServiceImpl implements UserService {
    private UserDao userDao; // 如何使用spring核心对象给他赋值

    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void addUser() {
        userDao.addUser();
    }
}

配置文件中这样填写:

    <bean id="userService" class="com.yxinmiracle.service.impl.UserServiceImpl">
        <constructor-arg name="userDao" ref="userDao"></constructor-arg>
    </bean>

5.3 set方法进行注入

如何用set方法将userDao进行赋值

package com.yxinmiracle.service.impl;

import com.yxinmiracle.dao.UserDao;
import com.yxinmiracle.service.UserService;

/**
 * @version 1.0
 * @author: YxinMiracle
 * @date: 2021-07-23 00:16
 *
 * 1. 构造方法方式注入
 * 2. set方法方式的注入
 */

public class UserServiceImpl implements UserService {
    private UserDao userDao; // 如何使用spring核心对象给他赋值

    public UserServiceImpl() {
    }
    
    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void addUser() {
        userDao.addUser();
    }
}

配置文件中这样填写:

    <bean id="userService" class="com.yxinmiracle.service.impl.UserServiceImpl">
        <property name="userDao" ref="userDao"></property>
    </bean>
    <bean id="userDao" class="com.yxinmiracle.service.impl.UserDaoImpl"></bean>

5.4 注入数组类型

<bean id="userDao" class="com.yxinmiracle.dao.impl.UserDaoImpl">
    <!--注入数组 name为java代码中的数组名-->
    <property name="stringArray">
        <array>
            <value>hello1</value>
            <value>hello2</value>
            <value>hello3</value>
            <value>hello4</value>
        </array>
     </property>
</bean>

5.5 注入Map类型

<bean id="userDao" class="com.yxinmiracle.dao.impl.UserDaoImpl"">
   <!--注入map-->
    <property name="map">
        <map>
            <entry key="username" value="aobama"></entry>
            <entry key="pwd" value="123456"></entry>
            <entry key="address" value="召唤师峡谷"></entry>
        </map>
    </property>
</bean>

5.6 注入简单类型

<bean id="userDao" class="com.yxinmiracle.dao.impl.UserDaoImpl">
    <!--set方法注入-->
    <property name="age" value="28"></property>
</bean>
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值