五分钟带你速通Spring IOC

2022年05月15日 阅读数:5
这篇文章主要向大家介绍五分钟带你速通Spring IOC,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。



Inversion of Control“控制反转”

下降代码之间的耦合度html

其中最多见的的方式叫作依赖注入简称DIjava

🎀什么是ioc
不是技术,而是一种设计思想。在Java开发中,IOC意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。把对象建立和对象之间的调用交给Spring管理。python

🎨可控制反转是什么意思呢?mysql

谁控制谁?
控制什么?
为什么是反转(有反转就应该有正转了),哪些方面反转了?什么又是在正转呢?web

  • 谁控制谁,控制什么:通常来讲,咱们直接在对象内部经过new进行建立对象,是程序主动去建立依赖对象;而IOC是有专门一个容器来建立这些对象,即由IOC容器来控制对象的建立;redis

    谁控制谁?是IOC容器控制了对象;spring

    控制什么?那就是主要控制了外部资源获取(不仅是对象包括好比文件等)sql

  • 为什么是反转,哪些方面反转了:有反转就有正转,传统应用程序是由咱们本身在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙建立及注入依赖对象;数据库

    为什么是反转?由于由容器帮咱们查找及注入依赖对象,对象只是被动的接受依赖对象,因此是反转express

    哪些方面反转了?依赖对象的获取被反转了。(主动变被动)

🧨使用IOC的目的:为了下降耦合度

1、IOC底层原理:

xml 工厂模式 反射

工厂模式,还是存在耦合度的,进一步解耦

让耦合度下降到最低----IOC

一、IOC过程

第一步:xml配置文件 ,配置建立的对象

<bean id="dao" class= "com.yer.UserDao"></bean>

第二步 有dao 类 和service类,建立工厂类

class UserFactory{
   
   
    public static UserDao  getDao(){
   
   
        String classValue = class属性值 //1.由xml解析
        Class clazz = Class.forName(classValue)//2.经过反射建立
        return (UserDao)clazz.newInstance();
        
    }
}

二、IOC接口

🍕 ioc思想基于ioc容器完成,ioc容器就是对象工厂

🍤Spring提供ioc容器实现两种方式(两个接口)

  • BeanFactory :Ioc容器基本实现,是Spring内部的使用接口,不提供开发人员进行使用

    *加载配置文件的时候不回去建立对象,在使用对象的时候才回去建立对象

  • AppplicationContext:BeanFactory的子接口,提供更多更强大功能,通常由开发人员使用

    *加载配置文件的时候就会把配置文件对象建立

在耗时耗资源的过程在服务器启动的时候就完成–verygood!

ApplicationContext接口有实现类:

ClassPathXmlApplicationContext
实际开发中都是读取类路径,
它是用于读取类路径下的配置文件

FileSystemXmlApplicationContext 它是用于读取系统文件目录中的配置文件

2、ioc容器 bean管理xml方式(建立对象和set注入)

🍳什么是bean管理?

指的是两个操做

建立对象
注入属性

🥩bean 管理操做有两种方式

  • 基于xml配置文件方式实现
  • 基于注解实现方式实现

3、ioc操做bean管理(基于xml方式)

🥨基于xml方式建立对象

<bean id="user" class="com.yer.spring5.User"></bean>

在spring配置文件中使用bean标签,标签里面能够添加对应属性,就能够实现对象的建立

在bean标签中有不少属性:

  • id:惟一标识

  • class:类全路径

建立对象的也是默认执行无参构造方法

🥠基于xml方式注入属性

DI依赖注入 ,就是注入属性
先建立对象 再注入属性

什么是DI?

DI—Dependency Injection“依赖注入”
是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中

依赖注入的目的并不是为软件系统带来更多功能,而是为了提高组件重用的频率,并为系统搭建一个灵活、可扩展的平台经过依赖注入机制,咱们只须要经过简单的配置,而无需任何代码就可指定目标须要的资源,完成自身的业务逻辑,而不须要关心具体的资源来自何处,由谁实现。

理解DI的关键是:“谁依赖谁,为何须要依赖,谁注入谁,注入了什么”,那咱们来深刻分析一下:

  • 谁依赖于谁:固然是应用程序依赖于IOC容器

  • 为何须要依赖:应用程序须要IOC容器来提供对象须要的外部资源

  • 谁注入谁:很明显是IOC容器注入给应用程序 某个对象,应用程序依赖的对象(IOC容器把应用

    ​ 程序所依赖的对象注入进应用程序中)

  • 注入了什么:就是注入某个对象所须要的外部资源(包括对象、资源、常量数据)

🌭 第一种注入方式:使用set方法进行注入

(1)建立类,定义属性和对应的set方法

(2)在spring配置文件配置对象建立配置属性注入

<bean id="book" class="com.yer.spring5.Book">
    <property name="bname" value="java一学就会"></property>
    <property name="bname" value="mysql一学就会"></property>
</bean>

🍣 第二种注入方式:使用有参构造进行注入

(1)建立类:定义属性,建立属性对应有参构造方法

(2)在spring配置文件中进行配置

//有参数构造注入属性
<bean id="oders" class="com.yer.spring5.Orders">
        <constructor-arg name="oname" value="小可爱"></constructor-arg>
        <constructor-arg name="address" value="中国"></constructor-arg>
</bean>

🍚p名称空间注入(了解)

使用p名称空间注入能够简化基于xml配置方式

第一步:添加p名称空间在配置文件中

xmlns:p="http://www.springframework.org/schema/p"

第二步:进行属性注入,在bean标签里面进行操做

<bean id="book" calss="com.yer.spring.Book" p:bname="" p:bauthor=""></bean>

实体类中必须有set方法;
实体类中必须有无参构造器(默认存在);

4、IOC操做Bean管理(xml注入其余类型属性)

🧀字面量

(1)null值

<property name="address">
       <null/>
</property>

(2)属性值包含特殊符号

<property name="address">
     <value><![CDATA[<<北京>>]]></value>
</property>

5、IOC操做Bean管理(注入属性- 外部bean ,内部bean,级联)

一、经过service去调用dao —引入外部bean

public class UserService {
   
   
    
    
    //建立UserDao类型属性,生成set方法
    private UserDao userDao;
    
    public void setUserDao(UsesrDao userDao){
   
   
        this.userDao = userDao;
        
    }
    public void add(){
   
   
        System.out.println("sevice add......")
            //原始方式建立UserDao对象
            //UserDao userDao = new UserDaoImpl();
            //userDao.update();
    }
}

bean.xml

<bean id="userservice" class="com.yer.spring5.service.UserService">
    //注入userDao对象
    //name属性:类里面属性名称
    //ref属性:建立userDao对象bean标签id值 ---userDapImpl
    <property id="userDao" ref="userDapImpl"></property>

</bean>

<bean id="userDaoImpl" class="com.yer.spring5.service.UserDaoImpl">
</bean>

用ref 外部bean 注入进来


二、内部bean ------ 一对多 (部门 和 员工)

  <bean id="emp" class="com.yer.spring5.bean.Emp">
        <!-- 设置两个普通属性-->
      <property name="ename" value="西西"></property>
      <property name="eage" value="18"></property>
         <!-- 设置对象类型属性-->
      <property name="dept" >
          <bean id="dept" class="com.yer.spring5.Dept">
              <property name="dname" value="金融部"></property>
          </bean>
      </property>

  </bean>

property中写bean


三、 级联赋值

第一种

    <!--级联赋值-->
    <bean id="emp" class="com.yer.spring5.bean.Emp">
        <!--     设置两个普通属性-->
        <property name="ename" value="西西"></property>
        <property name="eage" value="18"></property>
        <!--级联赋值-->
        <property name="dept" ref="dept"></property>
    </bean>
    <bean id="dept" class="com.yer.spring5.Dept">
        <property name="dname" value="金融部"></property>
    </bean>

相比外部 有属性

    <!--级联赋值-->
    <bean id="emp" class="com.yer.spring5.bean.Emp">
        <!--     设置两个普通属性-->
        <property name="ename" value="西西"></property>
        <property name="eage" value="18"></property>
        <!--级联赋值-->
        <property name="dept" ref="dept"></property>
        <property name="dept.dname" value="技术部"></property>
    </bean>
    <bean id="dept" class="com.yer.spring5.Dept">
        <property name="dname" value="金融部"></property>
    </bean>
    <!--获得dept的dname属性 ,咱们要生成get方法 -->

若是没有get方法会报红的(dept.name)

建立对象,get拿到属性才能赋值


6、IOC操做Bean管理(xml 注入集合属性)

🍱 注入数组类型属性

 <!--数组类型属性注入-->
        <property name="courses">
            <array>
                <value>java</value>
                <value>mysql</value>
            </array>
        </property>

🍔List集合

  <!--list类型属性注入-->
        <property name="list">
            <list>
                <value>三三</value>
                <value>四四</value>
            </list>
        </property>

🥞Map集合


        <!--map类型属性注入-->
        <property name="maps">
            <map>
                <entry key="JAVA" value="java-map"/>
                <entry key="myBatis" value="myBatis-map"/>
            </map>
        </property>

🌭set类型

        <!--set类型属性注入-->
        <property name="sets">
            <set>
                <value>mysql</value>
                <value>redis</value>
            </set>
        </property>

经过ref 标签的 bean 能够建立对象

集合中能够是一个个的对象

🧈把集合注入部分提取出来
1.在spring配置中引入命名空间util

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
     http://www.springframework.org/schema/util
     http://www.springframework.org/schema/util/spring-util-4.1.xsd">
</beans>

使用util:list

    <!--一、提取list集合类型属性注入-->
    <util:list id="bookList">
        <value>java</value>
        <value>python</value>
        <value>mysql</value>
    </util:list>
    
    <!--二、提取list集合类型属性注入-->
    <bean id="book" class="com.yer.spring5.Book">
        <property name="list" ref="bookList"/>
    </bean>

7、IOC 操做 Bean管理 (FactoryBean)

** spring中有两种bean ,一种普通bean,一种工厂bean**

🍍普通bean :在配置文件中定义的bean类型就是返回的类型
🥧工厂bean:在配置文件定义的bean类型和返回的类型能够不同

第一步,建立一个类,让这个类做为工厂bean,实现接口FactoryBean

第二步,实现接口,在实现的方法中定义返回的bean类型
(总结一下FactoryBean和BeanFactory 待更新…)

8、IOC 操做 Bean管理 (bean 做用域)

  • singleton(单实例):默认,每一个容器中只有一个bean的实例,单例的模式由BeanFactory自身来维护。

  • prototype(多实例):为每个bean请求提供一个实例。

  • request:为每个网络请求建立一个实例,在请求完成之后,bean会失效并被垃圾回收器回 收。

  • session:与request范围相似,确保每一个session中有一个bean的实例,在session过时后, bean会随之失效

  • global-session:全局做用域,global-session和Portlet应用相关。当你的应用部署在Portlet 容器中工做时,它包含不少portlet。若是你想要声明让全部的portlet共用全局的存储变量的话,那 么这全局变量须要存储在global-session中。全局做用域与Servlet中的session做用域效果相同。

🍛在spring中,能够设置建立的bean是单实例仍是多实例,如何设置单实例仍是多实例?

  • 在spring配置文件bean标签中,有属性(scope)用于设置单/多实例

  • scope属性值:
    默认值singleton 表示单实例对象
    prototype ,表示多实例对象

    <bean id= "book" class="com.yer.spring5-1.Book" scope="prototype">
        <property name="list" ref="bookList"></property>
    </bean>
    

🧁 singleton 和prototype的区别

  1. singleton单实例,prototype多实例

  2. 设置scope值是singleton的时候,加载spring配置文件的时候就会建立单实例对象为prototype时,是在调用getBean方法的时候建立多实例对象,而不是在加载spring配置文件时

9、IOC 操做 Bean管理 (bean 生命周期)

首先说一下Servlet的生命周期:实例化,初始init,接收请求service,销毁destroy;
Spring上下文中的Bean生命周期也相似
生命周期:从对象建立到对象销毁的过程

bean 生命周期

  1. 建立bean实例,经过构造器建立bean实例(无参数构造)
  2. 为bean中相关属性设置值,和对其余bean的引用(调用set方法)
  3. 调用bean的初始化方法(须要进行配置初始化的方法)
  4. bean可使用了(对象获取到,可使用)
  5. 当容器关闭的时候,调用bean的销毁方法(须要进行配置销毁的方法)

init-method 调初始化方法

destory 销毁方法

除了这五步还有两步 bean 的后置处理器

在3的初始化以前 和初始化以后

把bean的实例传递给bean后置处理器的方法

BeanPostProcessor 接口中有两个方法

在第三步以前 在初始化以前 postProcessBeforeInitialization

在第三步以后 初始化以后 postProcessAfterInitialization

手动销毁 调用销毁方法

生命周期七步

10、IOC 操做 Bean管理 (xml 自动装配)

什么是自动装配:根据指定装配规则
bean标签属性autowire,配置自动装配

<bean id="emp" class="ocom.yer.spring5-1.Emp" autowire="byType">
</bean>

autowire 属性一般用两个值

byType 根据属性类型

byName 根据属性名称

11、IOC 操做bean管理(外部属性文件)

  1. 直接配置数据库信息

    配置德鲁伊链接池

  2. 引入外部属性文件配置数据库
    在这里插入图片描述

  3. 建立外部属性文件 properties 文件
    把外部properties属性文件引入到spring配置文件中
    在这里插入图片描述

  4. 引入命名空间context

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
             http://www.springframework.org/schema/contexthttp://www.springframewor .org/schema/context/spring-context.xsd">

</bean>

在这里插入图片描述

12、IOC 操做bean管理(基于注解管理)

  1. 什么是注解

    (1) 注解是代码特殊标记,格式:@注解名称(属性名称=属性值,属性名称=属性值)

    (2)注解在什么地方用呢? 注解做用在: 类 属性 方法

    (3)使用注解目的:简化xml配置

  2. 在spring中针对bean管理 中 建立对象提供注解四个

    • @Component
    • @Service
    • Controller
    • Repository

    四个注解功能都同样,均可以用来建立bean实例

  3. 基于注解方式实现对象建立

    第一步:引入依赖 aop

    第二步:开启组件扫描

    第三步:建立类,在类上面添加建立对象注解

    ​ 注解中vaule属性值能够省略不写 默认是类名称的首字母小写 UserService-userService

    注解原理是与aop有关—重点

    开启组件扫描
    若是扫描多个包,使用逗号隔开
    要记得扫描包的上层目录

<context:component-scan base-package="con.yer"></context:component-scan>

首先加载配置文件 bean.xml

bean.xml 中只有一段代码,开启注解扫描

发现了相关注解@Component (可默认为类名首字母小写)

  1. 开启组件扫描细节配置

<!--示例1:
use-default-filters="flase"表示如今不是用默认filter,本身配置filter
context:include-filter,设置去扫描哪些内容-->
<context:component-scan base-package="con.yer" use-default-filters="flase">
    <context:include-filter type="annotation"
                            expression="org.springframework.stereotype.Controller"/>
</context:component-scan>



<!--示例2:扫描全部内容 context:exclude-filter:设置哪些内容不进行扫描-->
<context:component-scan base-package="con.yer" >
    <context:exclude-filter type="annotation"
              expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
  1. 基于注解方式实现属性注入

    (1)@Autowired:根据属性类型自动进行装配

    (2)@Qualifier:根据属性名称

    (3)@Resource: 能够根据类型,能够根据名称

    (4)@Value:注入普通类型属性

@Autowired :根据属性类型进行自动装配

第一步,把service和dao的对象进行建立,在service和dao类添加建立对象注解

package com.yer.dao;

public interface UserDao {
   
   
    void add();

}

package com.yer.dao;

import org.springframework.stereotype.Repository;

@Repository
public class UserDaoImpl implements UserDao{
   
   

    @Override
    public void add() {
   
   
        System.out.println("UserDaoImpl add...");

    }
}


package com.yer.sevice;

import com.yer.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

 
@Service
public class UserService {
   
   

    //定义dao类型属性
    //不须要添加set方法
    //添加注入属性注解
    @Autowired
    private UserDao userDao;

    public  void add(){
   
   
        System.out.println("UserService add....");
        userDao.add();
    }

}

<context:conponent-scan base-package="com.yer"></context:conponent-scan>

@Qualifier :根据名称注入

@Qualifier 的使用是与@Autowired一块儿使用

@Resource 根据类型注入

@Resource(name="userDaoImpl22")
    private UserDao userDao;

    public  void add(){
   
   
        System.out.println("UserService add....");
        userDao.add();
    }

}
@Repository(value="userDaoImpl22")
public class UserDaoImpl implements UserDao{
   
   

    @Override
    public void add() {
   
   
        System.out.println("UserDaoImpl add");

    }
}

@Resource是 javax.annotation.Resource包下的

@Value( value="月月")

private String name;

  1. 彻底注解开发

(1)建立配置类,代替xml配置文件

package com.yer.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * @author Darling
 * @create 2022-02-28-16:58
 */
@Configuration //做为配置类,代替xml文件
@ComponentScan(basePackages = {
   
   "com.yer"})
public class SpringConfig {
   
   


}

package com.yer.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * @author Darling
 * @create 2022-02-28-16:58
 */
@Configuration //做为配置类,代替xml文件
@ComponentScan(basePackages = {
   
   "com.yer"})//扫描包
public class SpringConfig {
   
   


}

(2)编写测试类

package com.yer.testdemo;

import com.yer.config.SpringConfig;
import com.yer.sevice.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.testng.annotations.Test;

/**
 * @author Darling
 * @create 2022-02-28-16:20
 */
public class TestSpring5 {
   
   
    @Test
    public void testService(){
   
   
        ApplicationContext context =
//                new ClassPathXmlApplicationContext("bean.xml");
         new AnnotationConfigApplicationContext(SpringConfig.class);
        UserService userService =   
            context.getBean("userService",UserService.class);
        System.out.println(userService);
        userService.add();
    }
}

SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder”.
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder”.
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

报错了

https://blog.csdn.net/weixin_39548940/article/details/100015174

官网给出的解决思路以下:
This error is reported when the org.slf4j.impl.StaticLoggerBinder class could not be loaded into memory. This happens when no appropriate SLF4J binding could be found on the class path. Placing one (and only one) of slf4j-nop.jar, slf4j-simple.jar, slf4j-log4j12.jar, slf4j-jdk14.jar or logback-classic.jar on the class path should solve the problem.

翻译成汉语以下(我是直接英汉互译翻译过来的):
此错误在组织slf4j.inf.strestcoperbinder类没法装入内存时报告。当在类路径上找不到合适的slf4j绑定时,就会发生这种状况。slf4j-nop.jar放置一个(且只有一个), slf4j-simple.jar, slf4j-log4j12.jar, slf4j-jdk14.jar 或 logback-classic.jar 的类路径应该解决这个问题。

解决方案:
在Maven工程的pom文件中,新增一个上述的包文件之一的依赖配置,项目就能够正常编译运行了。

<dependency>  
  <groupId>org.slf4j</groupId> 
  <artifactId>slf4j-nop</artifactId> 
  <version>1.7.2</version> 
</dependency>

SLF4J binding could be found on the class path. Placing one (and only one) of slf4j-nop.jar, slf4j-simple.jar, slf4j-log4j12.jar, slf4j-jdk14.jar or logback-classic.jar on the class path should solve the problem.

翻译成汉语以下(我是直接英汉互译翻译过来的):
此错误在组织slf4j.inf.strestcoperbinder类没法装入内存时报告。当在类路径上找不到合适的slf4j绑定时,就会发生这种状况。slf4j-nop.jar放置一个(且只有一个), slf4j-simple.jar, slf4j-log4j12.jar, slf4j-jdk14.jar 或 logback-classic.jar 的类路径应该解决这个问题。

解决方案:
在Maven工程的pom文件中,新增一个上述的包文件之一的依赖配置,项目就能够正常编译运行了。

<dependency>  
  <groupId>org.slf4j</groupId> 
  <artifactId>slf4j-nop</artifactId> 
  <version>1.7.2</version> 
</dependency>

上一篇: 多线程初体验
下一篇: nacos技术分享