Spring IoC 全套资源!!(已完结,建议收藏观看)

Spring框架两大核心机制:

  1. IoC(控制反转)/DI(依赖注入)
  2. AOP(面向切面编程)

一. Spring概述

Spring 是一个企业级开发框架,是软件设计层面的框架,优势在于可以将应用程序进行分层,开发者可以自主选择组件。

MVC:Struts2, Spring MVC
ORMapping: Hibernate, MyBatis, Spring Data

为什么使用Spring(企业级项目特点)

  1. 大规模:用户数量多,数据规模大,功能模块众多
  2. 性能和安全要求高
  3. 业务复杂
  4. 灵活多变

Spring体系框架

Spring体系框架

Spring优点

  1. 低侵入式设计;
  2. 独立于各种应用服务器;
  3. 依赖注入特性组件关系透明化,降低了耦合度;
  4. 面向切面编程特性允许将通用任务进行集中式处理;
  5. 与第三方框架的良好整合

二. Spring IoC

1. 什么是控制反转

在传统的程序开发中,需要调用对象时, 通常由调用者来创建被调用者的实例,即对象是由调用者主动new出来的。但是在Spring 框架中创建对象的工作不在由调用者来完成,而是交给IoC容器来创建, 再推送给调用者,整个流程完成反转,所以是控制反转。(举例:传统的购物需要你去超市自己购买商品,而Ioc相当于你将一个购物袋放到门口,Spring会将你需要的商品主动放到你的购物袋中,你只需要打开门拿购物袋即可)。

2. 如何使用IoC

创建Maven 工程,在pom.xml中添加依赖

关于配置Maven的教程,这里有一个比较详细的博主写的,给大家推荐一下:Maven的安装与配置及IDEA配置(超详细图文讲解)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>Spring</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

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

        <!-- 简化实体类的开发 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>

注意!!!(特别重要!!!):

在这里你的jdk版本如果是8的话,Spring版本一定要在6以下,否则他会报下面的错误:
jdk版本与Spring版本不兼容
所以如果你使用的是jdk11(本人的jdk11版本也不相容6.0.12,最后换为jdk20可以了)以下版本,建议配置Spring6以下版本,或者提高自己jdk的版本。

创建实体类Student

package com.spring.entity;
import lombok.Data;
@Data
public class Student {
    private String name;
    private long id;
    private int age;
}

传统的开发方式,手动new Student

Student student = new Student();
student.setId(1);
student.setName("张三");
student.setAge(22);
System.out.println(student);

通过IoC创建对象, 在配置文件中添加需要管理的对象,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
">

    <bean id="student" class="com.spring.entity.Student">
        <property name="id" value="1"></property>
        <property name="name" value="zhangsan"></property>
        <property name="age" value="22"></property>
     </bean>
</beans>

从IoC中获取对象, 通过id获取

// 加载配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Student student = (Student) applicationContext.getBean("student");
System.out.println(student);

3. 配置文件

通过配置bean标签来完成对象的管理
  1. id: 对象名。
  2. class: 对象的模板类(所有交给IoC容器来的管理的类必须有无参构造函数,因为 Spring 底层是通过反射机制来创建对象,调用的是无参构造)
对象的成员变量通过property标签完成赋值
  1. name: 成员变量名。
  2. value: 成员变量值(基本数据类型,String 可以直接赋值, 如果是其他的引用类型,不能通过 value 赋值)
  3. ref: 将IoC中的另外一个bean赋给当前的成员变量(DI)
<bean id="student" class="com.spring.entity.Student">
   <property name="id" value="1"></property>
   <property name="name" value="zhangsan"></property>
   <property name="age" value="22"></property>
   <property name="address" ref="address"></property>
</bean>

<bean id="address" class="com.spring.entity.Address">
   <property name="id" value="12"></property>
   <property name="name" value="甘肃"></property>
</bean>

4. IoC底层原理

  1. 这里自己实现IoC的底层,首先定义在pom.xml中配置:
<dependency>
    <groupId>dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>你自己的dom4j版本</version>
</dependency>
  1. 创建以下目录:
    在这里插入图片描述
  2. 首先实现ApplicationContext接口:
public interface ApplicationContext {
    public Object getBean(String id);
}
  1. 接着实现ClassPathXmlApplicationContext类:
package com.spring.ioc;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class ClassPathXmlApplicationContext implements ApplicationContext{
    private Map<String, Object> ioc = new HashMap<>();
    public ClassPathXmlApplicationContext(String path) throws Exception {
        SAXReader reader = new SAXReader();

        // 将xml文件转为document对象然后进行读取
        Document document = reader.read("./src/main/resources/" + path);

        Element root = document.getRootElement();   // 这里拿到beans

        // 迭代器读取
        Iterator<Element> iterator = root.elementIterator();
        while(iterator.hasNext()){
            Element element = iterator.next();  // 拿到的是bean
            String id = element.attributeValue("id");
            String className = element.attributeValue("class");

            // 通过反射机制创建对象
            Class clazz = Class.forName(className);
            // 获取无参构造函数
            Constructor constructor = clazz.getConstructor();
            Object object = constructor.newInstance();
            ioc.put(id, object);
        }
    }
    @Override
    public Object getBean(String id) {
        return ioc.get(id);
    }
}

  1. 最后实现Test类:
import com.spring.entity.Student;

public class Test {
    public static void main(String[] args) throws Exception {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Student student = (Student) applicationContext.getBean("student");
        System.out.println(student);
    }
}

注意:这里Test中的 ApplicationContext 接口以及 ClassPathXmlApplicationContext 类一定是自己刚才写的,而不是 org.springframework.context 包中的。

  1. 运行结果比较:
    自己实现底层
    我们主要实现的是无参构造的结构,对于数据处理并没有进行实现,所以是没有数据的。
    org自带的库实现的

读取配置文件

通过反射机制实例化配置文件中所配置所有的bean。

  1. 以下是有参构造的底层实现
package com.spring.ioc;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class ClassPathXmlApplicationContext implements ApplicationContext{
    private Map<String, Object> ioc = new HashMap<>();
    public ClassPathXmlApplicationContext(String path) throws Exception {
        SAXReader reader = new SAXReader();

        // 将xml文件转为document对象然后进行读取
        Document document = reader.read("./src/main/resources/" + path);

        Element root = document.getRootElement();   // 这里拿到beans

        // 迭代器读取
        Iterator<Element> iterator = root.elementIterator();
        while(iterator.hasNext()){
            Element element = iterator.next();  // 拿到的是bean
            String id = element.attributeValue("id");
            String className = element.attributeValue("class");

            // 通过反射机制创建对象
            Class clazz = Class.forName(className);
            // 获取无参构造函数, 创建目标函数
            Constructor constructor = clazz.getConstructor();
            Object object = constructor.newInstance();
            // 给目标函数赋值
            Iterator<Element> beanIter = element.elementIterator();
            while(beanIter.hasNext()){
                Element property = beanIter.next();
                String name = property.attributeValue("name");
                String valueStr = property.attributeValue("value");
                String ref = property.attributeValue("ref");
                if(ref == null){
                    String methodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
                    Field field = clazz.getDeclaredField(name);
                    Method method = clazz.getDeclaredMethod(methodName, field.getType());
                    System.out.println(field.getType().getName());
                    //根据成员变量的数据类型将value进行转换
                    Object value = null;
                    if(field.getType().getName() == "long"){
                        value = Long.parseLong(valueStr);
                    }
                    if(field.getType().getName() == "java.lang.String"){
                        value = valueStr;
                    }
                    if(field.getType().getName() == "int"){
                        value = Integer.parseInt(valueStr);
                    }
                    method.invoke(object, value);
                }
                ioc.put(id, object);
            }
        }
    }
    @Override
    public Object getBean(String id) {
        return ioc.get(id);
    }
}

这里只需要改变`ClassPathXmlApplicationContext`类即可,其他写法与上面的无参构造相同。
  1. 运行结果
    在这里插入图片描述
    对于address是另一个bean,获取其中的信息与上述相同,只需要拿到信息之后添加到student之中即可,不做演示。

通过用时类来获取bean

        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Student student = (Student) applicationContext.getBean(Student.class);
        System.out.println(student);
这种方式存在一个问题, 配置文件中一个数据类型的对象只能由一个实例,即在bean中只能有一个class为Student:
    <bean id="student" class="com.spring.entity.Student">

如果定义多个:
在这里插入图片描述
程序在运行的时候就会发现有两个Student类的bean,就会出现错误:
在这里插入图片描述

通过有参构造bean

  1. 在实体类中创建对应的有参构造函数
package com.spring.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor // 生成有参构造
@NoArgsConstructor  // 生成无参构造
public class Student {
    private long id;
    private String name;
    private int age;
    private Address address;
}

  1. 配置文件(通过name)
<bean id="student3" class="com.spring.entity.Student">
    <constructor-arg name="id" value="3"/>
    <constructor-arg name="name" value="王五"/>
    <constructor-arg name="age" value="18"/>
    <constructor-arg name="address" ref="address"/>
</bean>
这里的name可以省略,但是前提是在赋值的顺序必须和实体类中的命名顺序相同,不然会出选错误
  1. 通过下标进行赋值(index)
<bean id="student3" class="com.spring.entity.Student">
  	<constructor-arg index="0" value="3"/>
  	<constructor-arg index="2" value="18"/>
  	<constructor-arg index="1" value="王五"/>
  	<constructor-arg index="3" ref="address"/>
</bean>

给bean注入集合

    <bean id="student" class="com.spring.entity.Student">
        <property name="id" value="1"/>
        <property name="name" value="zhangsan"/>
        <property name="age" value="22"/>
        <property name="addresses">
            <list>
                <ref bean="address"/>
                <ref bean="address2"/>
            </list>
        </property>
     </bean>

    <bean id="address" class="com.spring.entity.Address">
        <property name="id" value="12"/>
        <property name="name" value="甘肃"/>
    </bean>

    <bean id="address2" class="com.spring.entity.Address">
        <property name="id" value="24"/>
        <property name="name" value="浙江"/>
    </bean>

5. scope 作用域

Spring 管理的备案时根据scope来生成的, 表示bean的作用域,共有四种,默认是singleton模式:
1. singleton: 单例, 表示通过 IoC 容器获取的 bean 是唯一的;
以下是演示:
在这里插入图片描述

将scope设置为singleton模式(不设置也可以,默认就是singleton):

在这里插入图片描述

以下是运行结果:
在这里插入图片描述
singleton模式就相当于 getBean方法在获取student2的时候,IoC中只有一个student2类,每一次IoC都会复制一份交给Bean方法,所以每个student2的地址是一样的,因此比较结果也是相同的。

2. prototype: 原型,表示通过 IoC 容器获取的 bean 是不同的;
  	以下是演示:

在这里插入图片描述
运行结果:
在这里插入图片描述

prototype模式就相当于 getBean方法在获取student2的时候,IoC中只有一个student2类,IoC会将这个类交给Bean方法,然后再获取的时候IoC会重新生成一个student2类,所以每个student2的地址是不一样的,因此结果也不相同。

3. request: 请求,表示再一次 HTTP 请求内有效;
4. session: 会话,表示在一个用户会话内有效。

request和session值适用于Web项目,大多数情况下,使用单例和原型较多。

prototype 模式当业务代码获取 IoC 容器中的 bean 时, Spring 才去调用无参构造创建对应的 bean 。
singleton 模式无论业务代码是否获取 IoC 容器中的 bean,Spring 在加载 spring.xml 时都会创建 bean。

6. Spring 的继承

与Java 的继承不同 ,Java 是类层面的继承, 子类可以继承父类的内部结构信息; Spring 是对象层面的继承, 子对象可以继承父对象的属性值。
    <bean id="student2" class="com.spring.entity.Student" scope="prototype">
        <property name="id" value="2"/>
        <property name="name" value="李四"/>
        <property name="addresses">
            <list>
                <ref bean="address"/>
                <ref bean="address2"/>
            </list>
        </property>
    </bean>

    <bean id="address" class="com.spring.entity.Address">
        <property name="id" value="12"/>
        <property name="name" value="甘肃"/>
    </bean>

    <bean id="address2" class="com.spring.entity.Address">
        <property name="id" value="24"/>
        <property name="name" value="浙江"/>
    </bean>
    
    <!--stu继承student2, 将student2中的所有值都继承, 修改子对象的属性值可以完成继承-->
    <bean id="stu" class="com.spring.entity.Student" parent="student2">
        <property name="id" value="5"/>
        <property name="name" value="王五"/>
    </bean>

    <bean id="user" class="com.spring.entity.User" parent="address"></bean>

Spring的继承关注点在于具体的对象,而不在于类,即不同的两个类的实例化对象可以完成继承,前提是子对象必须包含父对象的所有属性,同时可以在此基础上添加其他属性(属性的类型必须也相同)。

7. Spring的依赖

与继承相似, 依赖也是描述 bean 和 bean 之间的一种关系, 配置依赖之后,被依赖的 bean 一定先创建,在创建依赖的 bean ,A 依赖于 B , 先创建 B ,再创建 A 。

  1. 先在Student和User类中创建无参构造:
    public User(){
        System.out.println("创建了User");
    }
  1. 创建Spring-depend.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">

    <bean id="student" class="com.spring.entity.Student" depends-on="user"></bean>
    <bean id="user" class="com.spring.entity.User"></bean>
</beans>
  1. 创建 Test2.java 文件,获取Spring-depend.xml 文件:
import org.springframework.context.ApplicationContext;  // 一定要是spring自带的包,而不是前面自己写的包!!!
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test2 {
    public static void main(String[] args) throws Exception{
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Spring-depend.xml");
    }
}

如果不加depend-on属性,IoC默认是从上到下创建:
在这里插入图片描述
加上depend-on属性之后,student 依赖于 user 类,创建的时候先创建 user ,再创建 student :
在这里插入图片描述
因为前面教大家手动实现过IoC的底层原理,但是并没有实现依赖,在这里的 Test2.java 中使用的时候一定要使用 Spring 包自带的,不能导自己写的,别问我怎么知道的,问就是自己导错了,导成自己的包了,查了半天!!!

8. Spring 的 P 命名空间

p 命名空间是对 IoC / DI 的简化操作,使用 p 命名空间可以更加方便地完成 bean 的配置以及 bean 的依赖注入。

<?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:p="http://www.springframework.org/schema/p"
       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">
    <bean id="student" class="com.spring.entity.Student" p:id="1" p:name="张三" p:age="18" p:address-ref="address"></bean>
    <!--p:name 就相当于 <property name="id" value="1"/>-->
    <bean id="address" class="com.spring.entity.Address" p:id="2" p:name="和谐路"></bean>
</beans>

9. Spring 的工厂方法

IoC通过工厂模式创建bean的方式有两种:

  1. 静态工厂方法:
  2. 实例工厂方法:

静态工厂方法:

  1. 创建实体类:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Car {
    private long id;
    private String name;
}
  1. 创建静态工厂方法:
import com.spring.entity.Car;

import java.util.HashMap;
import java.util.Map;

public class StaticCarFactory {
    private static Map<Long, Car> carMap;
    static{
        carMap = new HashMap<Long, Car>();
        carMap.put(1L, new Car(1L, "宝马"));
        carMap.put(2L, new Car(2L, "奔驰"));
    }
    public static Car getCar(long id){
        return carMap.get(id);
    }
}
  1. 配置文件:
<?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:p="http://www.springframework.org/schema/p"
       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">
    <!-- 配置静态工厂创建 Car -->
    <bean id="car" class="com.spring.factory.StaticCarFactory" factory-method="getCar">
        <constructor-arg value="2"/>
    </bean>
</beans>
  1. 调用:
public class Test4 {
    public static void main(String[] args){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-factory.xml");
        Car car = (Car)applicationContext.getBean("car");
        System.out.println(car);
    }
}

实例工厂方法:

  1. 创建实体类(同上):
  2. 创建实例工厂方法:
import com.spring.entity.Car;
import java.util.HashMap;
import java.util.Map;

public class InstanceCarFactory {
    private static Map<Long, Car> carMap;
    // 在无参构造创建map集合
    public InstanceCarFactory(){
        carMap = new HashMap<Long, Car>();
        carMap.put(1L, new Car(1L,"宝马"));
        carMap.put(2L, new Car(2L,"奔驰"));
    }
    public Car getCar(Long id){
        return carMap.get(id);
    }
}
  1. 配置文件:
<!-- 配置实例工厂 bean -->
<bean id="carFactory" class="com.spring.factory.InstanceCarFactory"></bean>
<!-- 配置实例工厂创建 Car -->
<bean id="car2" factory-bean="carFactory" factory-method="getCar">
    <constructor-arg value="2"/>
</bean>
  1. 调用(同上):

10. IoC 自动装载(Autowire)

IoC 负责创建对象, DI 负责完成对象的依赖注入, 通过配置 property 标签的 ref 属性来完成, 同时 Spring 提供了另外一种更加简便的依赖注入方式:自动装载(不需要手动配置 property ,IoC 容器会自动选择 bean 完成注入。)
自动装在有两种方式:

  1. byName: 通过属性名自动装载;
  2. byType: 通过属性的数据类型自动装载。

byName:

  1. 创建实体类:
@Data
public class Person {
    private int id;
    private String name;
    private Car car;
}

  1. 配置文件:
    在这里插入图片描述

原理: 在配置 person 文件中使用了 autowire = “byName” 的方法,因为 person 具有 id ,name ,和 car 三个属性,其中 id 和 name 属性我们通过 property 实现了手动配置,剩余一个 car ,所以 IoC 会在自己的容器中去寻找一个 id 为 car 的 bean 标签,找到之后会将其自动装载在 person 中,如果未找到,则显示 null ;

byType:

  1. 创建实体类(同上):
  2. 配置文件:
    在这里插入图片描述

原理: 在配置 person 文件中使用了 autowire = “byName” 的方法,因为 person 具有 id ,name ,和 car 三个属性,其中 id 和 name 属性我们通过 property 实现了手动配置,剩余一个 car ,所以 IoC 会在自己的容器中去寻找一个 class为 Car 的 bean 标签(但是只能有一个 Car 类型,不然会抛出异常),找到之后会将其自动装载在 person 中,如果未找到,则显示 null ;

IoC 已完结,后续会更新 AOP 。感兴趣的话点个关注吧!!

  • 10
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值