Spring01 - 工厂篇

news2024/12/27 2:28:13

Spring入门(上)-工厂篇

文章目录

  • Spring入门(上)-工厂篇
    • 一:引言
      • 1:EJB存在的问题
      • 2:什么是Spring
      • 3:设计模式和反射工厂
    • 二:第一个spring程序
      • 1:环境搭建
      • 2:核心API - ApplicationContext
        • 2.1:核心API和两大实现类
        • 2.2:低级容器和高级容器
      • 3:程序开发和使用
        • 3.1:使用流程
        • 3.2:常见方法
        • 3.3:配置文件中的注意事项
      • 4:底层实现原理【简易版本】
      • 5:Spring和日志框架整合
    • 三:注入Injection
      • 1:注入概述
      • 2:set注入
        • 2.1:JDK内置类型
        • 2.2:自定义类型
        • 2.3:简化写法
      • 3:构造器注入
        • 3.1:使用方法
        • 3.2:构造方法重载问题
      • 4:注入总结
    • 四:控制反转(IOC)和依赖注入(DI)
      • 1:控制反转IOC
      • 2:依赖注入DI
    • 五:复杂对象的创建
      • 1:什么是复杂对象
      • 2:创建复杂对象的三种方式
        • 2.1:FactoryBean接口
        • 2.2:实例工厂
        • 2.3:静态工厂
    • 六:控制对象的创建次数
      • 1:如何控制对象的创建次数
        • 1.1:简单对象的创建次数
        • 1.2:复杂对象的创建次数
      • 2:为什么要控制对象的创建次数
    • 七:对象的生命周期
      • 1:创建阶段
      • 2:初始化阶段
      • 3:销毁阶段
      • 4:总结一下(面试重点)
    • 八:配置文件参数化
      • 1:配置文件参数化
      • 2:开发步骤
    • 九:自定义类型转换器
      • 1:类型转换器
      • 2:自定义类型转换器
    • 十:后置处理Bean
      • 1:BeanPostProcessor概述和原理
      • 2:开发步骤

一:引言

1:EJB存在的问题

运行环境苛刻 + 代码移植性性差 = 重量级框架

在这里插入图片描述

2:什么是Spring

spring = 轻量级的 + JavaEE解决方案的框架,没有引入新的技术,但是整合了众多的优秀设计模式

  • 所谓轻量级,是针对EJB而言:对于运行环境是没有要求的【开源tomcat, jetty等】,同时代码移植性高【 不用实现额外的移植接口】

  • 所谓JAVAEE的通用解决方案:是Spring的整合内容包含了JAVA分层的各个阶段:而不像struts2只整合Controller层,Mybatis只整合DAO层

在这里插入图片描述

3:设计模式和反射工厂

什么是设计模式

  • 狭义:GOF提出的23种设计模式:创建型 + 结构型 + 行为型
  • 广义:面向对象设计中,解决特定问题的经典代码

spring中常用的四个设计模式是:工厂模式【工厂 + 反射 = spring核心】,模板方法模式【可扩展性】,代理模式【AOP】,策略模式【子类的选择】

什么是工厂模式

通过工厂类创建对象,而不是通过显式的new()方式进行对象的创建,这样可以做到解耦合【解除代码之间的强关联关系】

因为一旦代码中出现强耦合,就会不利于代码的维护

UserService userSerivce = new UserServiceImpl(); // 强耦合

而如何解耦合呢,这时候就要想到对象的创建方式除了使用显式的new()创建还有什么方式了 -> 反射,所以,可以通过反射工厂来进行对象的创建实现解耦

public class MyBeanFactory {

    /**
     * 创建person对象
     * <p>
     *     对象的创建方式:
     *     (1)显式创建:Person person = new Person()
     *     (2) 隐式创建:反射 -> class.forName(全限定类型名) + newInstance()
     * </p>
     * @return person对象实例
     */
    public static Person getPersonInstance() {
        
        Person person = null;
        
        try {
            Class clazz = Class.forName("com.cui.commonboot.suns_spring.entity.Person");
            person = (Person) clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        return person;
    }
}

但是上述方式显然还是有两个问题:

(1)如果有多个类要进行工厂创建,是不是要写很多个这样的getXXXInstance()?

(2)这里面的Class.forName("com.cui.commonboot.suns_spring.entity.Person");并没有实现解耦,如果要改对应的类还是要到代码里去改

针对问题一:可以发现所有的类的工厂创建流程都是一样的,所以可以使用一个通用的方式进行创建【返回Object】

针对问题二:可以使用外部配置文件进行加载,然后使用java.util.Properties获取外部配置文件信息,这样就可以做到解耦了

// ================== 通用反射工厂 =================
package com.cui.commonboot.suns_spring.config;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * <p>
 * 功能描述:my bean factory
 * </p>
 *
 * @author cui haida
 * @date 2024/01/06/9:32
 */
public class MyBeanFactory {
    // 加载配置
    // 将properties配置文件中的内容读取到Properties中
    private static Properties env = null;
    // 因为io比较耗时,所以放到静态代码块中
    static {
        try {
            // 加载配置文件
            InputStream in = MyBeanFactory.class.getResourceAsStream("/applicationContext.properties");
            // 装载到env中
            env.load(in);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * get bean from factory
     * @param name bean name
     * @return bean instance
     */
    public static Object getBean(String name) {
        Object bean = null;
        try {
            // 获取指定的类
            String property = env.getProperty(name);
            Class clazz = Class.forName(property);
            bean = clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bean;
    }
}
# ==================== applicationContext.properties ===============
person=com.cui.commonboot.suns_spring.entity.Person

二:第一个spring程序

1:环境搭建

maven依赖

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

配置文件

Spring对于配置文件的放置位置和命名没有任何的要求,一般放在resource下

日后在应用spring框架时,需要进行配置文件的路径的设置

在这里插入图片描述

2:核心API - ApplicationContext

2.1:核心API和两大实现类

作用是:Spring提供的这个工厂可以用于对象的创建

好处:解耦合

特点:是一个接口,这样可以完美的屏蔽实现的差异

  • web环境:XmlWebApplicationContext
  • 非web环境:ClassPathXmlApplicationContext(main, Junit)

在这里插入图片描述

ApplicationContext是一个重量级资源【工厂的对象会占用大量的内存空间】

所以spring不会频繁的创建对象,一个应用只会创建一个工厂对象避免占用大量内存空间

同时ApplicationContext内部含有大量的独占锁,所以是线程安全的,支持多线程并发访问

2.2:低级容器和高级容器

在这里插入图片描述

在 Spring 中,有两种 IoC 容器:BeanFactoryApplicationContext。(ApplicationContext是BeanFactory的子类)

在这里插入图片描述

  • BeanFactory:Spring 实例化、配置和管理对象的最基本接口。

在这里插入图片描述

public interface BeanFactory {
    /**
     * 用来引用一个实例,或把它和工厂产生的Bean区分开
     * 如果一个FactoryBean的名字为a,那么,&a会得到那个Factory
     */
    String FACTORY_BEAN_PREFIX = "&";
    /*
     * 四个不同形式的getBean方法,获取实例
     */
    Object getBean(String name) throws BeansException;
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;
    <T> T getBean(Class<T> requiredType) throws BeansException;
    Object getBean(String name, Object... args) throws BeansException;
    
 	// 根据名称判断bean是否存在
    boolean containsBean(String name);
	// 是否为单实例Bean
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
	// 是否为原型(多实例)
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
	// 名称、类型是否匹配
    boolean isTypeMatch(String name, Class<?> targetType) throws NoSuchBeanDefinitionException;
	// 获取类型
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;
	// 根据实例的名字获取实例的别名
    String[] getAliases(String name);
}
  • ApplicationContext以更加面向框架的风格增强了BeanFactory,并提供了一些适用于企业应用程序的特性。
    • 默认初始化所有的Singleton,也可以通过配置取消预初始化。

    • 继承MessageSource,因此支持国际化。

    • 资源访问,比如访问URL和文件。

    • 事件传播特性,即支持aop特性。

    • 同时加载多个配置文件。

    • 以声明式方式启动并创建Spring容器。

3:程序开发和使用

3.1:使用流程

只有三个步骤:创建类型 -> 配置文件的配置【xxx.xml】-> 通过工厂类【ApplicationContext的两个实现类】,获得对象

<?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属性:配置全限定类名
    -->
    <bean id="person" class="org.example.entity.Person"/>

</beans>
@org.junit.Test
    public void test() {
    // 通过工厂读取配置文件,因为这里是非web环境,所以是ClassPathXmlApplicationContext实现类工厂
    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/bean.xml");
    // 通过工厂加载bean后获取对象实例,参数是配置文件中的id值
    Person person = (Person) ctx.getBean("person");

    System.out.println("person is: " + person);
}
3.2:常见方法
// getBean1: 通过工厂加载bean后获取对象实例,参数是配置文件中的id值
Person person = (Person) ctx.getBean("person");
// getBean2: 可以即指定id, 又指定返回值类型,这样可以不用强转
Person person0 = ctx.getBean("person", Person.class);
// getBean3:当前的配置文件中,如果只有一个类型是person的,可以只使用class作为参数,不指定id
Person person1 = ctx.getBean(Person.class);

// 获取所有的bean的id值列表
String[] allBeanNames = ctx.getBeanDefinitionNames();
for (String beanName : allBeanNames) {
    System.out.println(beanName);
}

// 获取指定类型的所有的bean的id值列表
String[] pointTypeAllBeanNames = ctx.getBeanNamesForType(Person.class);
for (String beanName : pointTypeAllBeanNames) {
    System.out.println(beanName);
}

// 判断是否存在指定id值的bean, name属性不生效
boolean flag = ctx.containsBeanDefinition("person");
System.out.println("flag is: " + flag);

// 判断是否存在指定id值的bean, name属性也生效
boolean flagWithName = ctx.containsBean("p");
System.out.println("flag is: " + flagWithName);
3.3:配置文件中的注意事项

只配置class属性

<bean class="org.example.entity.Person"></bean>

如果这个bean主需要使用一次,那么就可以省略id值,反之如果bean会使用多次或者会被其他bean引用需要设置id值

name属性:别名,可重复【逗号分割多个别名】

<bean id="person" name="p, p1, p2" class="org.example.entity.Person"/>

别名可以指定多个,但是id属性只能指定一个【你可以有多个小名,但是只能有一个大名】

ctx.containsBeanDefinition(String beanName)传入别名的时候识别不了,ctx.containsBean(name)可以识别

4:底层实现原理【简易版本】

这个是简易版本,不代表底层具体实现逻辑

在这里插入图片描述

🎉 spring工厂可以调用对象的私有构造方法创建对象

🎉 理论上程序的所有的对象都会交给spring工厂创建,但是实际上实体对象一般不会交给Spring管理创建,因为要操作数据库,所以一般会由持久层框架进行创建

5:Spring和日志框架整合

spring和日志框架进行整合,可以十分方便的在控制台输出运行过程中的重要的信息

<!-- spring整合日志 -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
<!-- 日志门面:将默认的logback和log4j2干掉,进而使得spring5可以整合支持log4j -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.25</version>
</dependency>

然后引入log4j.properties

# 配置根
log4j.rootLogger = debug, console

# 输出到控制台的配置
log4j.appender.console=org.apache.log4j.ConsoleAppender 
log4j.appender.console.Target=System.out
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

在这里插入图片描述

三:注入Injection

1:注入概述

所谓注入就是通过spring工厂和配置文件对创建的对象的成员变量赋值的操作

而常规的赋值操作是通过硬编码的方式,存在耦合

// 常规的变量赋值方法是通过set方法
Person person = (Person) ctx.getBean("person");
person.setId(1);
person.setName("cui");

而spring提供的注入方式可以消除这种硬编码,避免耦合

<bean id="person" name="p, p1, p2" class="org.example.entity.Person">
    <property name="id">
        <value>2</value>
    </property>
    <property name="name">
        <value>cui</value>
    </property>
</bean>

在这里插入图片描述

2:set注入

针对于不同的类型的成员变量,在<property>标签中需要嵌套其他的标签

在这里插入图片描述

2.1:JDK内置类型

8种基本类型 + String

  • <value>

  • <property name="id">
        <value>2</value>
    </property>
    <property name="name">
        <value>cui</value>
    </property>
    

数组

  • <list> - <value> , 里面也可以是其他类型的

  • <property name="emails">
        <list>
            <value>111111@qq.com</value>
            <value>123456@123.com</value>
        </list>
    </property>
    

set集合

  • 无序性,唯一性

  • <set> - <value>/<ref>.....

  • <property name="emails">
        <set>
            <value>111111@qq.com</value>
            <value>123456@123.com</value>
        </set>
    </property>
    

list集合

  • 有序性,可重复
  • <list> - <value>/<ref>.....

map

  • <map> - <entry> - <key> & <value>

  • <property name="emails">
        <map>
            <entry>
                <key>张三</key>
                <value>111111@qq.com</value>
            </entry>
            <entry>
                <key>李四</key>
                <value>111111@qq.com</value>
            </entry>
        </map>
    </property>
    

properties

  • 是一种特殊的map[key = string, value = string]

  • <property name="p">
        <props>
            <prop key="张三">value1</prop>
            <prop key="李四">value2</prop>
        </props>
    </property>
    
2.2:自定义类型

第一种方式

为成员变量提供get/set方法

配置文件中进行注入赋值【bean】

<bean id="userService" class="org.example.service.impl.UserServiceImpl">
    <property name="userDao">
        <bean class="org.example.Dao.impl.UserDaoImpl"/>
    </property>
</bean>

但是这种方式会导致:配置文件代码冗余 & 被注入的对象会被多次创建,导致大大的浪费内存资源

第二种方式

<bean id="userDao" class="org.example.Dao.impl.UserDaoImpl"></bean>

<bean id="userService" class="org.example.service.impl.UserServiceImpl">
    <property name="userDao">
        <ref bean="userDao"/>
    </property>
</bean>
2.3:简化写法

基于属性的简化

<bean id="student" class="springProject.Student">
    <!-- 通过property标签给各个成员变量赋值。name:成员变量名。value:数值类型的值。ref:引用类型的值。 -->
    <property name="stuNo" value="2"></property>
    <property name="userDao" ref="userDao"></property>
</bean>

p方式简化

需要引入p命名空间xmlns:p="http://www.springframework.org/schema/p"

然后在bean标签内使用p:成员变量名这种方式来赋值

<!-- 
简单类型:p:属性名="属性值"
引用类型(除了String外):p:属性名-ref="引用的id"
注意多个 p赋值的时候 要有空格。 -->
<bean id="student" class="springProject.Student" p:userDao-ref="userDao" p:stuNo="123"></bean>

3:构造器注入

3.1:使用方法

Spring调用构造方法,通过配置文件,为成员变量赋值

  • 使用这种方式的前提是需要bean类提供有参构造的方法
  • 如果不写index,将使用默认顺序
<bean id="student" class="springProject.Student">
    <!-- index:构造方法中成员变量的顺序,从0开始。value、ref的含义同property。 -->
    <constructor-arg value="3" index="0" ></constructor-arg>
    <constructor-arg value="2" index="2" ></constructor-arg>
    <constructor-arg value="zs" index="1"></constructor-arg>
</bean>
3.2:构造方法重载问题
  • 参数个数不同的时候,可以通过控制constructor-arg标签个数进行区分
  • 参数个数相同的时候,可以指定type属性,然后根据type属性进行区分
<bean id="person2" class="org.example.entity.Person">
    <constructor-arg type="int" value="15"/>
</bean>

<bean id="person3" class="org.example.entity.Person">
    <constructor-arg value="haha"/>
</bean>

4:注入总结

在这里插入图片描述

在应用中大多数用的都是set注入,因为构造注入有重载问题需要注意和解决,同时Spring底层大量应用了set注入

四:控制反转(IOC)和依赖注入(DI)

1:控制反转IOC

  • IOC是Spring核心特性之一,另一个核心特性是AOP
  • IOC(Inverse of Controll) -> 解耦合,底层是工厂设计模式
  • IoC 意味着将你设计好的对象交给容器控制

谁控制谁,控制什么

传统 JavaSE 程序设计,我们直接在对象内部通过 new 进行创建对象,是程序主动去创建依赖对象;

而 IoC 是有专门一个容器来创建这些对象,即由 IoC 容器来控制对象的创建;

  • 谁控制谁?当然是 IoC 容器控制了对象
  • 控制什么?那就是主要控制了外部资源获取(对于成员变量赋值的控制权,不只是对象包括比如文件等)。

为何是反转,哪些方面反转了

传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;

  • 为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;
  • 哪些方面反转了?依赖对象的获取被反转了

在这里插入图片描述

2:依赖注入DI

DI(Dependency Injection) -> 依赖注入是 IoC 的最常见形式。

注入:通过Spring的工厂和配置文件,为对象(bean,组件)的成员变量进行复制

依赖注入:当一个类需要另一个类的时候,就意味着依赖,依赖出现依赖,就可以将另一个类作为本类的成员变量,最终通过Spring配置文件进行注入

在这里插入图片描述

五:复杂对象的创建

1:什么是复杂对象

所谓复杂对象,就是指不能直接通过new方法创建的对象

在这里插入图片描述

2:创建复杂对象的三种方式

2.1:FactoryBean接口

实现FactoryBean接口,然后Spring配置文件配置即可完成

在后续学习Spring整合其他框架的时候,底层会大量的运用到这个接口

FactoryBean介绍

FactroyBean接口共有三个需要实现的方法来辅助我们创建复杂的对象,三个方法分别是:

public interface FactoryBean<T> {
    
    // 用于书写创建复杂对象的代码,并将复杂对象作为方法的返回值返回
    @Nullable
    T getObject() throws Exception;

    // 用于返回所创建的复杂的对象的class对象
    @Nullable
    Class<?> getObjectType();

    // 如果该对象只需要创建一次,将返回true[单例为true]
    // 多例为false
    // 默认是单例
    default boolean isSingleton() {
        return true;
    }
}

在这里插入图片描述

<bean id="conn" class="com.xxx.xxx.ConnectionFactoryBean"></bean>

FactoryBean获取对象

虽然上面的Spring配置文件中创建复杂对象的配置方法和简单对象的配置方法一致,但是本质却有很大的不同

在这里插入图片描述

其他细节

  1. 如果就想获得FactoryBean类型的对象, 可以使用getBean("&conn")【&标记】,此时获得的就是ConnectionFactoryBean对象
  2. DI的体会和运用
<bean id="conn" class="com.xxx.xxx.ConnectionFactoryBean">
    <!-- 作为属性,DI -->
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/cui?useSSL=false"/>
    <property name="userName" value="root"/>
    <property name="password" value="314159"/>
</bean>

实现原理【思想层面】

接口 + 反射,什么都能做 -> FactoryBean的核心原理就是接口回调

在这里插入图片描述

  1. 根据conn获得<bean>标签相关信息,并判断后面指定的全限定类名的类是不是属于FactoryBean的子类?
    1. 如果是:进行下一步
    2. 如果不是:说明是简单对象,直接底层new()
  2. 发现是子类,就要实现FactoryBean的getObject()方法,这个方法返回的内容就是ctx.getBean(xxx)返回的内容
2.2:实例工厂

在有些老的项目中,有一些已经写好的复杂对象的创建方法没有整合到Spring容器中,此时就需要实例工厂和Spring的整合了

// 已经写好的复杂对象的创建方法,没有实现FactoryBean接口
public class ConnectionFactory {
    // 数据库连接
    public Connection getConnection() {
        Connection conn = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverMannger.getConnection("jdbc:mysql://localhost:3306/cui?useSSL=false", "root", "314259");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

此时就可以通过配置文件将其加入到Spring容器的控制范围

<bean id="connFactory" class="com.xxx.xxx.ConnectionFactory"></bean>

<!-- factory-bean目的是注册connFactory为FactoryBean结构  -->
<!-- factory-method指明对标getObject的是那个方法  -->
<bean id="conn" factory-bean="connFactory" factory-method="getConnection"></bean>
2.3:静态工厂

静态工厂和实例工厂的唯一不同之处就是创建复杂对象的那个方法是静态的

// 已经写好的复杂对象的创建方法,没有实现FactoryBean接口
public class ConnectionFactory {
    // 静态的
    public static Connection getConnection() {
        Connection conn = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverMannger.getConnection("jdbc:mysql://localhost:3306/cui?useSSL=false", "root", "314259");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
<!-- 类名.静态方法名() -->
<bean id="conn" class="com.xxx.xxx.ConnectionFactory" factory-method="getConnection"></bean>

六:控制对象的创建次数

1:如何控制对象的创建次数

1.1:简单对象的创建次数
<bean id="person" scope="singleton|prototype" class="xxx.Person"></bean>

singleton:只会创建一次简单对象【默认就是单例的】

prototype:每一次都会创建新的对象

1.2:复杂对象的创建次数

在FactoryBean中的isSingleton()方法中指定

public class xxxFactoryBean implements FactoryBean {
    // 如果该对象只需要创建一次,将返回true[单例为true, 默认]
    // 多例为false默认是单例
    boolean isSingleton() {
        return true;
    }
}

2:为什么要控制对象的创建次数

因为要节省不必要的内存浪费

  • 创建一次的对象常见的有:DAO,Service,Controller,SqlSessionFactory
  • 每次都要新创建的对象常见的有:Connection,SqlSession,Session,Struts2 Action

七:对象的生命周期

对象的生命周期是指一个对象创建,使用到销毁的完整过程,熟悉对象的生命周期有利于更好的使用Spring管理创建对象

在这里插入图片描述

1:创建阶段

Spring工厂创建对象的时机取决于对象的创建次数:

  • 如果是单例的,将在Spring工厂创建的同时,创建对象;
  • 如果是多例的,将在获取对象的同时创建对象(ctx.getBean(xxx)

🎉 如果想要在获取对象的时候创建单例对象,需要在<bean>标签中加入lazy-init="true"属性

2:初始化阶段

Spring工厂在创建完成对象[完成依赖注入]之后,调用对象的初始化方法,完成对应的初始化操作:

  • 初始化方法的提供:由程序员根据需求提供初始化方法,最终完成初始化操作
  • 初始化方法的调用:由Spring工厂进行调用

定义初始化方法有两种途径:

第一种方法是实现InitializingBean这个接口

这个接口提供了一个afterPropertiesSet()方法,这个方法允许属性赋值之后做一些额外操作

这种方式耦合了Spring框架,需要实现InitializingBean接口

public class Product implements InitializingBean {

    public Product() {
        System.out.println("this is no args structure");
    }

    /**
     * 在这里可以做一些初始化操作 spring 会调用
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("Product.after properties set");
    }
}

第二种方法是配置文件中配置普通方法

public void myInit() {
    // 自己根据自己的需求,随便写初始化方法逻辑
}
<bean id="product" class="xxx.Product" init-method="myInit"></bean>

🎉 如果既实现了InitializingBean接口,又指定了init-method方法那么接口的顺序 > init-method方法的顺序

🎉 先进行注入才会执行初始化[先DI,再init]

🎉 初始化操作的主要是数据库,IO,网络流…

3:销毁阶段

Spring在调用ctx.close()之后便会进行对象的销毁,而对象的销毁也是有两种方式

第一种方式是通过实现DisposableBean接口中的destory()方法

public class Product implements InitializingBean, DisposableBean {

    public Product() {
        System.out.println("this is no args structure");
    }

    /**
     * 在这里可以做一些初始化操作 spring 会调用
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("Product.after properties set");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("this is destroy method");
    }
}

第二种方法是配置文件中配置普通方法为destroy-method属性

public myDestroy() throws Exception {
    // 自定义销毁方法
}
<bean id="product" class="xxx.Product" init-method="xxx" destory-method="myDestroy"></bean>

🎉 销毁方法只适用于单例模式的bean

🎉 所谓的销毁操作就是资源的释放操作io.close();connection.close()

4:总结一下(面试重点)

Spring Bean 生命周期简单概括为4个阶段:

  1. 实例化,创建一个Bean对象
  2. 填充属性,为属性赋值
  3. 初始化
    1. 如果实现了xxxAware接口,通过不同类型的Aware接口拿到Spring容器的资源
    2. 如果实现了BeanPostProcessor接口,则会回调该接口的postProcessBeforeInitialzationpostProcessAfterInitialization方法
    3. 如果配置了init-method方法,则会执行init-method配置的方法
  4. 销毁
    1. 容器关闭后,如果Bean实现了DisposableBean接口,则会回调该接口的destroy方法
    2. 如果配置了destroy-method方法,则会执行destroy-method配置的方法

在这里插入图片描述

八:配置文件参数化

1:配置文件参数化

所谓配置文件参数化就是:将Spring配置文件中需要经常修改的字符串信息转移到一个更小的配置文件中 -> 利于维护性

在这里插入图片描述

2:开发步骤

  1. 提供一个小的配置文件(.properties) -> 名字随便,放置位置随便
# 假设叫做db.properties,位于resource根路径下
jdbc.driverName = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/cui?useSSL=false
jdbc.username = root
jdbc.password = 314159
  1. Spring配置文件和小的配置文件进行整合
<context:property-placeholder location="classpath:/db.propertied"/>
  1. 通过${key}获取对应的值

在这里插入图片描述

九:自定义类型转换器

1:类型转换器

所谓类型转换器就是Spring通过类型转换器将配置文件中的字符串类型的数据转换成为对象中对应类型的数据,进而完成注入工作

在这里插入图片描述

2:自定义类型转换器

当Spring内部没有提供特定类型的转换器的时候【例如String -> Date】,就需要在开发过程中自定义类型转换器

而如果需要自己实现类型转换器,就要实现Convert接口,然后在Spring配置文件中进行注册

在这里插入图片描述

package org.example.config;

import org.springframework.core.convert.converter.Converter;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * <p>
 * 功能描述:自定义类型转换器
 * Converter<原始类型, 自定义类型>
 * </p>
 *
 * @author cui haida
 * @date 2024/01/06/17:21
 */
public class MyDateConverter implements Converter<String, Date> {

    // converter方法作用:String -> Date
    // SimpleDateFormat sdf = new SimpleDateFormat(format);
    // sdf.parse(String) --> Date

    @Override
    public Date convert(String s) {
        Date date = null;
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            date = sdf.parse(s);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return date;
    }
}

告知Spring框架,我们创建的是一个类型转换器,所以要在Spring中注册

<bean id="myDateConverter" class="xxx.MyDateConverter"></bean>

同时还要在Spring中声明用于注册类型转换器的服务类

<!-- 创建MyDateConverter对象 -->
<bean id="myDateConverter" class="org.example.config.MyDateConverter"/>

<!-- 用于注册类型转换器 -->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <!-- converters是一个set类型的 -->
        <set>
            <ref bean="myDateConverter"/> <!-- 注册刚才创建的MyDateConverter对象 -->
        </set>
    </property>
</bean>

🎉 Spring支持一种日期的类型转换器:2024/1/6这种格式的,其他格式的都不支持,需要自定义

十:后置处理Bean

1:BeanPostProcessor概述和原理

BeanPostProcessor的作用就是对Spring工厂所创建的对象进行再加工操作,AOP的底层原理大量的就是用的这个

在这里插入图片描述

再看一遍生命周期这张图,回顾下BeanPostProcessor所处的位置

在这里插入图片描述

而对于BeanPostProcessor规定的接口中有两个要实现的方法

public interface BeanPostProcessor {
    
    
    /**
     * 作用:Spring创建完成对象,并进行注入之后【完成属性赋值和容器赋值】,可以运行Before方法进行加工
     * 获得Spring创建好的对象,通过方法的参数
     * 最终通过获取返回值交给Spring框架
     */
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    /**
     * 作用:Spring执行完成对象的初始化之后【init-method or InitalzingBean中的setAfterProperties】,可以运行After方法进行加工
     * 获得Spring创建好的对象,通过方法的参数
     * 最终通过获取返回值交给Spring框架
     */
    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

🎉 因为在实战中很少去处理Spring的初始化操作,所以没有必要去区分Before和After,此时二者只需要实现一个即可

⚠️ 一定要返回bean对象在before方法中

2:开发步骤

1:类要实现BeanPostProcessor接口

package org.example.beanpost;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.lang.Nullable;

/**
 * <p>
 * 功能描述:
 * </p>
 *
 * @author cui haida
 * @date 2024/01/07/8:19
 */
public class MyBeanPostProcessor implements BeanPostProcessor {

    /**
     * 前置方法
     * @param bean bean
     * @param beanName bean id
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    /**
     * 后置方法
     * @param bean bean
     * @param beanName bean id
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Category category = (Category) bean;
        category.setName("zhangsan");
        return category;
    }
}

2:Spring的配置文件中进行配置

<bean id="c" class="org.example.beanpost.Category">
    <property name="id" value="10"/>
    <property name="name" value="cui"/>
</bean>

<bean id="myBeanPostProcessor" class="org.example.beanpost.MyBeanPostProcessor"/>

因为BeanPostProcessor会对工厂中所有的bean进行监控,所以在重写前置方法或者后置方法的时候可能要做特殊的处理

/**
 * 后置方法
 * @param bean bean
 * @param beanName bean id
 */
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    if (bean instanceof Category) { // or beanName.equals("xxxbeanId")
        Category category = (Category) bean;
        category.setName("zhangsan");
    }
    return bean;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2266128.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

攻防世界 unserialize3

开启场景 题目为unserialize3&#xff0c;这个单词在php中代表反序列化&#xff0c;代码 __wakeup 也是php反序列化中常见的魔术方法&#xff0c;所以这个题基本就是和反序列化有关的题目。根据代码提示&#xff0c;编写一个Exploit运行&#xff0c;将对象xctf的信息序列化 得到…

汽车免拆诊断案例 | 2011 款奔驰 S400L HYBRID 车发动机故障灯异常点亮

故障现象 一辆2011款奔驰 S400L HYBRID 车&#xff0c;搭载272 974发动机和126 V高压电网系统&#xff0c;累计行驶里程约为29万km。车主反映&#xff0c;行驶中发动机故障灯异常点亮。 故障诊断 接车后试车&#xff0c;组合仪表上的发动机故障灯长亮&#xff1b;用故障检测…

GitLab安装及使用

目录 一、安装 1.创建一个目录用来放rpm包 2.检查防火墙状态 3.安装下载好的rpm包 4.修改配置文件 5.重新加载配置 6.查看版本 7.查看服务器状态 8.重启服务器 9.输网址 二、GitLab的使用 1.创建空白项目 2.配置ssh 首先生成公钥&#xff1a; 查看公钥 把上面的…

Electron 学习笔记

目录 一、安装和启动electron 1. 官网链接 2. 根据文档在控制台输入 3. 打包必填 4. 安装electron开发依赖 5. 在开发的情况下打开应用 6. 修改main为main.js&#xff0c;然后创建main.js 7.启动 二、启动一个窗口 1. main.js 2. index.html 3. 隐藏菜单栏 三、其他…

网络管理-期末项目(附源码)

环境&#xff1a;网络管理 主机资源监控系统项目搭建 &#xff08;保姆级教程 建议点赞 收藏&#xff09;_搭建网络版信息管理系统-CSDN博客 效果图 下面3个文件的项目目录(python3.8.8的虚拟环境) D:\py_siqintu\myproject5\Scripts\mytest.py D:\py_siqintu\myproject5\Sc…

62.基于SpringBoot + Vue实现的前后端分离-驾校预约学习系统(项目+论文)

项目介绍 伴随着信息技术与互联网技术的不断发展&#xff0c;人们进到了一个新的信息化时代&#xff0c;传统管理技术性没法高效率、容易地管理信息内容。为了实现时代的发展必须&#xff0c;提升管理高效率&#xff0c;各种各样管理管理体系应时而生&#xff0c;各个领域陆续进…

MySQL用表组织数据

用表组织数据 文章目录 用表组织数据一.四种完整性约束二.数值类型2-1三.数值类型2-2四.字符串.日期类型五.设置1.设置主键2.设置标识列3.设置非空4.设置默认值 六.主外键建立后注意事项 一.四种完整性约束 1.域完整性 列 域完整性约束方法:限制数据类型,检查约束,外键约束,默…

iOS开发代码块-OC版

iOS开发代码块-OC版 资源分享资源使用详情Xcode自带代码块自定义代码块 资源分享 自提&#xff1a; 通过网盘分享的文件&#xff1a;CodeSnippets 2.zip 链接: https://pan.baidu.com/s/1Yh8q9PbyeNpuYpasG4IiVg?pwddn1i 提取码: dn1i Xcode中的代码片段默认放在下面的目录中…

基于微信小程序的校园访客登记系统

基于微信小程序的校园访客登记系统 功能列表 用户端功能 注册与登录 &#xff1a;支持用户通过手机号短信验证码注册和登录。个人资料管理 &#xff1a;允许用户编辑和更新个人信息及其密码。站内信消息通知&#xff1a;通知公告。来访预约&#xff1a;提交来访预约支持车牌…

mac启ssh服务用于快速文件传输

x.1 在mac上启SSH服务 方法一&#xff1a;图形交互界面启ssh&#xff08;推荐&#xff09; 通过sharing - advanced - remote login来启动ssh&#xff1b;&#xff08;中文版mac应该是 “系统设置 → 通用 → 共享”里打开“远程登录”来启动&#xff09; 查看自己的用户名和…

Magnet: 基于推送的大规模数据处理Shuffle服务

本文翻译自&#xff1a;《Magnet: Push-based Shuffle Service for Large-scale Data Processing》 摘要 在过去的十年中&#xff0c;Apache Spark 已成为大规模数据处理的流行计算引擎。与其他基于 MapReduce 计算范式的计算引擎一样&#xff0c;随机Shuffle操作&#xff08;即…

面试真题:Integer(128)引发的思考

引言 在 Java 编程语言中&#xff0c;数据类型的使用至关重要。作为一种静态类型语言&#xff0c;Java 提供了丰富的基本数据类型和对应的包装类。其中&#xff0c;Integer 类是 int 类型的包装类&#xff0c;承载着更复杂的功能&#xff0c;如缓存、装箱和拆箱等。掌握 Integ…

Windows脚本清理C盘缓存

方法一&#xff1a;使用power文件.ps1的文件 脚本功能 清理临时文件夹&#xff1a; 当前用户的临时文件夹&#xff08;%Temp%&#xff09;。系统临时文件夹&#xff08;C:\Windows\Temp&#xff09;。 清理 Windows 更新缓存&#xff1a; 删除 Windows 更新下载缓存&#xff0…

Type-c接口

6P Type C 接口座&#xff1a; 仅支持充电 16P 与 12P Type C 接口座&#xff1a; 支持数据传输 Type-c引脚&#xff1a; SUB1,SUB2为辅助通讯引脚&#xff0c;主要用在音视频信号传输中&#xff0c;很多DIY都用不到 CC1、CC2引脚用于连接检测&#xff0c;一般可以不用连接&am…

基于python语音启动电脑应用程序

osk模型进行输入语音转换 txt字典导航程序路径 pyttsx3引擎进行语音打印输出 关键词程序路径 import os import json import queue import sounddevice as sd from vosk import Model, KaldiRecognizer import subprocess import time import pyttsx3 import threading# 初始…

互联网视频云平台EasyDSS无人机推流直播技术如何助力野生动植物保护工作?

在当今社会&#xff0c;随着科技的飞速发展&#xff0c;无人机技术已经广泛应用于各个领域&#xff0c;为我们的生活带来了诸多便利。而在动植物保护工作中&#xff0c;无人机的应用更是为这一领域注入了新的活力。EasyDSS&#xff0c;作为一款集视频处理、分发、存储于一体的综…

垃圾分割数据集labelme格式659张1类别

数据集格式&#xff1a;labelme格式(不包含mask文件&#xff0c;仅仅包含jpg图片和对应的json文件) 图片数量(jpg文件个数)&#xff1a;659 标注数量(json文件个数)&#xff1a;659 标注类别数&#xff1a;1 标注类别名称:["garbage"] 每个类别标注的框数&#…

记我的Springboot2.6.4从集成swagger到springdoc的坎坷路~

项目背景 主要依赖及jdk信息&#xff1a; Springboot&#xff1a;2.6.4 Jdk: 1.8 最近新搭建了一套管理系统&#xff0c;前端部分没有公司的前端团队&#xff0c;自己在github上找了一个star较多使用相对也简单的框架。在这个管理系统搭建好上线之后&#xff0c;给组内的小伙…

NNDL 作业11 LSTM

习题6-4 推导LSTM网络中参数的梯度&#xff0c; 并分析其避免梯度消失的效果 先来推个实例&#xff1a; 看式子中间&#xff0c;上半部分并未有连乘项&#xff0c;而下半部分有到的连乘项&#xff0c;从这可以看出&#xff0c;LSTM能缓解梯度消失&#xff0c;梯度爆炸只是不易…

uniapp使用live-pusher实现模拟人脸识别效果

需求&#xff1a; 1、前端实现模拟用户人脸识别&#xff0c;识别成功后抓取视频流或认证的一张静态图给服务端。 2、服务端调用第三方活体认证接口&#xff0c;验证前端传递的人脸是否存在&#xff0c;把认证结果反馈给前端。 3、前端根据服务端返回的状态&#xff0c;显示在…