Spring的基础及复习上(IOC篇)

IOC篇

一、 IOC
1、概念

Spring 通过一个配置文件描述 Bean 及 Bean 之间的依赖关系,利用 Java 语言的反射功能实例化Bean 并建立 Bean 之间的依赖关系。 Spring 的 IoC 容器在完成这些底层工作的基础上,还提供了 Bean 实例缓存、生命周期管理、Bean 实例代理、事件发布、资源装载等高级服务。

2、IOC的实现流程

Spring 启动时的流程:

  • 读取应用程序提供的 Bean 配置信息,
  • 并在 Spring 容器中生成一份相应的 Bean 配置注册表,然后根据这张注册表实例化 Bean,
  • 装配好 Bean 之间的依赖关系,
  • 为上层应用提供准备就绪的运行环境。其中 Bean 缓存池为 HashMap 实现
    在这里插入图片描述
3、IOC 容器实现

BeanFactory 是 Spring 框架的基础设施,面向 Spring 本身;ApplicationContext 面向使用Spring 框架的开发者,几乎所有的应用场合我们都直接使用 ApplicationContext 而非底层的 BeanFactory。

  • BeanFactory 是 Spring 框架的基础设施**,面向 Spring 本身
    在这里插入图片描述

  • ApplicationContext 面向开发应用

ApplicationContext 由 BeanFactory 派 生 而 来 , 提 供 了 更 多 面 向 实 际 应 用 的 功 能 。ApplicationContext 继承了 HierarchicalBeanFactory 和 ListableBeanFactory 接口,在基础上,还通过多个其他的接口扩展了 BeanFactory 的功能:
在这里插入图片描述

  • WebApplication 体系架构

WebApplicationContext 是专门为 Web 应用准备的,它允许从相对于 Web 根目录的路径中装载配置文件完成初始化工作。 从 WebApplicationContext 中可以获得ServletContext 的引用,整个 Web 应用上下文对象将作为属性放置到ServletContext 中,以便 Web 应用环境可以访问 Spring 应用上下文。
在这里插入图片描述

4、Spring Bean 作用域(bean标签的属性设置)

Spring 3 中为 Bean 定义了 5 中作用域,分别singleton(单例)prototype(原型)requestsessionglobal session,5 种作用域说明如下:
singleton:单例模式(多线程下不安全):

  1. singleton:单例模式,Spring IoC 容器中只会存在一个共享的 Bean 实例,无论有多少个Bean 引用它,始终指向同一对象。该模式在多线程下是不安全的。Singleton 作用域是Spring 中的缺省作用域,也可以显示的将 Bean 定义为 singleton 模式,配置为:
<bean id="userDao" class="com.ioc.UserDaoImpl" scope="singleton"/>

prototype:原型模式每次使用时创建
2. prototype:原型模式,每次通过 Spring 容器获取 prototype 定义的 bean 时,容器都将创建一个新的 Bean 实例,每个Bean 实例都有自己的属性和状态,而 singleton 全局只有一个对象。根据经验,对有状态的bean使用prototype作用域,而对无状态的bean使用singleton
作用域。
Request:一次 request 一个实例
3. request:在一次 Http 请求中,容器会返回该 Bean 的同一实例。而对不同的 Http 请求则会产生新的 Bean,而且该 bean 仅在当前 Http Request 内有效,当前 Http 请求结束,该 bean实例也将会被销毁。

<bean id="loginAction" class="com.cnblogs.Login" scope="request"/>

session
4. session:在一次 Http Session 中,容器会返回该 Bean 的同一实例。而对不同的 Session 请求则会创建新的实例,该 bean 实例仅在当前 Session 内有效。同 Http 请求相同,每一次session 请求创建新的实例,而不同的实例之间不共享属性,且实例仅在自己的 session 请求
内有效,请求结束,则实例将被销毁。

<bean id="userPreference" class="com.ioc.UserPreference" scope="session"/>

global Session
5. global Session:在一个全局的 Http Session 中,容器会返回该 Bean 的同一个实例,仅在使用 portlet context 时有效

5、Spring Bean 生命周期

实例化

  1. 实例化一个 Bean,也就是我们常说的 new。
    IOC 依赖注入
  2. 按照 Spring 上下文对实例化的 Bean 进行配置,也就是 IOC 注入。
    setBeanName 实现
  3. 如果这个 Bean 已经实现了 BeanNameAware 接口,会调用它实现的setBeanName(String)方法,此处传递的就是 Spring 配置文件中 Bean 的 id 值
    BeanFactoryAware 实现
  4. 如果这个 Bean 已经实现了 BeanFactoryAware 接口,会调用它实现的 setBeanFactory,
    setBeanFactory(BeanFactory)传递的是 Spring 工厂自身(可以用这个方式来获取其它 Bean,只需在 Spring 配置文件中配置一个普通的 Bean 就可以)。
    ApplicationContextAware 实现
  5. 如果这个 Bean 已经实现了 ApplicationContextAware 接口,会调用
    setApplicationContext(ApplicationContext)方法,传入 Spring 上下文(同样这个方式也
    可以实现步骤 4 的内容,但比 4 更好,因为 ApplicationContext 是 BeanFactory 的子接
    口,有更多的实现方法)
    postProcessBeforeInitialization 接口实现-初始化预处理
  6. 如果这个 Bean 关联了 BeanPostProcessor 接口,将会调用
    postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor 经常被用
    作是 Bean 内容的更改,并且由于这个是在 Bean 初始化结束时调用那个的方法,也可以被应
    用于内存或缓存技术。
    init-method
  7. 如果 Bean 在 Spring 配置文件中配置了 init-method 属性会自动调用其配置的初始化方法。
    postProcessAfterInitialization
  8. 如果这个 Bean 关联了 BeanPostProcessor 接口,将会调用
    postProcessAfterInitialization(Object obj, String s)方法。
    注:以上工作完成以后就可以应用这个 Bean 了,那这个 Bean 是一个 Singleton 的,所以一
    般情况下我们调用同一个 id 的 Bean 会是在内容地址相同的实例,当然在 Spring 配置文件中
    也可以配置非 Singleton。
    Destroy 过期自动清理阶段
  9. 当Bean不再需要时,会经过清理阶段,如果 Bean 实现了 DisposableBean 这个接口,会调用那个其实现的 destroy()方法;
    destroy-method 自配置清理
  10. 最后,如果这个 Bean 的 Spring 配置中配置了 destroy-method 属性,会自动调用其配置的销毁方法。
  11. bean 标签有两个重要的属性(init-method 和 destroy-method)。用它们你可以自己定制初始化和注销方法。它们也有相应的注解(@PostConstruct 和@PreDestroy)。
    <bean id="" class="" init-method="初始化方法" destroy-method="销毁方法">在这里插入图片描述
二、搭建一个spring的工程

spring中的ioc的配置有两种方式,一种是xml配置,一种是ioc注解配置
1、构建spring项目

  • pom.xml配置
<?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>com.jp</groupId>
    <artifactId>spring-review01</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

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

        <!-- https://mvnrepository.com/artifact/junit/junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>

    </dependencies>

</project>
  • 添加spring的配置文件(基于xml配置ioc)
    作用 :其主要作用是用于指导Spring工厂进行Bean(类实例)生产、注入及Bean实例的分发的核心组成部分,在resource目录下创建spring-config.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="beanId" class="com.jp.bean.Product"></bean>
</beans>

配置文件中的根元素为bean,其中bean有两个重要的属性id和class,id表示组件的默认名称,class表示组件的类型

  • 创建Pojo类
package com.jp.bean;

/**
 * @program: redis-review
 * @description: POJO类即简单的java对象
 * @author: CoderPengJiang
 * @create: 2020-04-24 00:00
 **/
public class Product {
    private int id;
    private String name;
    public Product() {
    }
    public Product(int id, String name) {
        System.out.println("invoke method -- Product(int id, String name)");
        this.id = id;
        this.name = name;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        System.out.println("invoke method -- setId");
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        System.out.println("invoke method -- setName");
        this.name = name;
    }
}

  • 测试
public class TestIocCaseStart {
    ApplicationContext ctx;
    @Test
    public void testCase() {
        ctx=new ClassPathXmlApplicationContext("SpringConfig.xml");
        Product product = (Product) ctx.getBean("beanId");
        System.out.println("ApplicationContext.getBean()="+product);
    }
}

结果如下:
在这里插入图片描述
分析原理:
上面是如何获取配置的Bean对象的?这里用到了ApplicationContext容器,该接口的实现类主要有以下两种
Bean的生命周期由spring进行管理
在这里插入图片描述
ClassPathXmlApplicationContext:
它是从类的根路径下加载配置文件 推荐使用这种
FileSystemXmlApplicationContext:
它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
AnnotationConfigApplicationContext:
当我们使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。
在这里插入图片描述
BeanFactory 才是 Spring 容器中的顶层接口。
ApplicationContext 是它的子接口。
BeanFactory 和 ApplicationContext 的区别:
创建对象的时间点不一样。
ApplicationContext:只要一读取配置文件,默认情况下就会创建对象。
BeanFactory:什么时候什么时候创建对象。

(2)Bean的定义(spring依赖注入四种方式)

Bean的定义也就是类实例的定义:

1. 构造器注入

<bean id="beanHasConstructorArg" class="com.javadevmap.bean.Product">
        <constructor-arg name="id" value="1001"></constructor-arg>
        <constructor-arg name="name" value="java dev map"></constructor-arg>
    </bean>
    @Test
    public void testConstructor() {
        ctx=new ClassPathXmlApplicationContext("SpringConfig.xml");
        Product product = (Product) ctx.getBean("beanHasConstructorArg");
        System.out.println("构造器定义bean="+product);
    }

测试结果在这里插入图片描述

2. setter注入:通过setter方法进行注入

<!--    setter方法注入-->
    <bean name="beanProperty" class="com.jp.bean.Product">
        <property name="id" value="1002"></property>
        <property name="name" value="java dev map"></property>
    </bean>

在这里插入图片描述
3. 静态工厂注入
静态工厂顾名思义,就是通过调用静态工厂的方法来获取自己需要的对象,为了让 spring 管理所有对象,我们不能直接通过"工程类.静态方法()"来获取对象,而是依然通过 spring 注入的形式获取,具体的示例代码如下:
建立product的静态工厂类

public class ProductFactory {
    public static Product createProduct(){
        Product product = new Product();
        product.setName("静态工厂方法注入");
        product.setId(1);
        return product;
    }
}

将静态工厂注入到spring容器中,其中的class为静态工程类,method为静态工程类的方法

  <!-- 静态工厂方法注入 -->
    <bean id="product" class="com.jp.bean.factoryMethod.ProductFactory" factory-method="createProduct">
    </bean>

测试结果:
在这里插入图片描述
4、实例工厂注入
实例工厂的意思是获取对象实例的方法不是静态的,所以你需要首先 new 工厂类,再调用普通的
bean包下的user类

package com.jp.bean;

public class User {
	private int id;
	private String username;
	private String password;
	
	public User(){
		System.out.println("user对象创建成功");
	}
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
}

dao层下的接口和实现类

public interface UserDAO {
	void insert(User user);
	
	void test();
}

public class UserDAOIMP implements UserDAO {

	public UserDAOIMP(){
		System.out.println("UserDAOIMP对象被创建");
	}
	
	@Override
	public void insert(User user) {
		System.out.println("UserDAOIMP的insert(user)方法被调用");
	}

	@Override
	public void test() {
		System.out.println("UserDAOIMP的test()方法被调用");
		
	}

}

service层下的类

public class UserAction {
	private UserDAO userDAOIMP;
	private User user;
	
	public void setUserDAOIMP(UserDAO userDAOIMP) {
		this.userDAOIMP = userDAOIMP;
	}

	public void setUser(User user) {
		this.user = user;
	}

	public void save() {
		// System.out.println("保存user对象");
		// userDAOIMP.test();
		userDAOIMP.insert(user);
	}
}

实例工厂类

public class DAOFactory {
	public UserDAO getUserDAOIMP(){
		return new UserDAOIMP();
	}
}	

bean的配置

<!--    工厂实例注入-->
    <bean name="userAction" class="com.jp.service.UserAction">
        <property name="user" ref="uservo"></property>
        <property name="userDAOIMP" ref="userimp"></property>
    </bean>

    <bean name="daoFactory" class="com.jp.factory.DAOFactory"></bean>

    <bean name="userimp" factory-bean="daoFactory" factory-method="getUserDAOIMP"></bean>

    <bean name="uservo" class="com.jp.bean.User"></bean>

结果:
在这里插入图片描述
(2)5 种不同方式的自动装配
Spring 装配包括手动装配和自动装配,手动装配是有基于 xml 装配、构造方法、setter 方法等自动装配有五种自动装配的方式,可以用来指导 Spring 容器用自动装配方式来进行依赖入。

  1. no:默认的方式是不进行自动装配,通过显式设置 ref 属性来进行装配。
  2. byName:通过参数名自动装配,Spring 容器在配置文件中发现 bean 的 autowire 属性被设
    置成 byname,之后容器试图匹配、装配和该 bean 的属性具有相同名字的 bean。
  3. byType:通过参数类型自动装配,Spring 容器在配置文件中发现 bean 的 autowire 属性被
    设置成 byType,之后容器试图匹配、装配和该 bean 的属性具有相同类型的 bean。如果有多
    个 bean 符合条件,则抛出错误。
  4. constructor:这个方式类似于 byType, 但是要提供给构造器参数,如果没有确定的带参数
    的构造器参数类型,将会抛出异常。
  5. autodetect:首先尝试使用 constructor 来自动装配,如果无法工作,则使用 byType 方式。
发布了9 篇原创文章 · 获赞 9 · 访问量 828

猜你喜欢

转载自blog.csdn.net/qq_36908783/article/details/105721776