你真的了解Spring吗(全网最全Spring笔记中)

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 简单对象指的就是可以直接通过调用构造方法(new)创建出来的对象。 复杂对象指的就是不可以直接通过调用构造方法(new)创建出来的对象。比如JDBC的Connection对象、Mybatis的SqlSessionFactory对象。

1.JPG


五、Spring工厂


5.1、简单对象和复杂对象


5.1.1、简单对象


   简单对象指的就是可以直接通过调用构造方法(new)创建出来的对象。


5.1.2、复杂对象


   复杂对象指的就是不可以直接通过调用构造方法(new)创建出来的对象。比如JDBC的Connection对象、Mybatis的SqlSessionFactory对象。


5.2、Spring创建复杂对象的三种方式


5.2.1、FactoryBean


5.2.1.1、FactoryBean接口


   如果在applicationContext.xml配置文件中配置的class属性是FactoryBean接口的实现类,那么通过id属性获得的是这个类所创建的复杂对象(底层会调用重写的getObject()方法)。


public class MyFactoryBean implements FactoryBean<Connection> {
  // 用于书写创建复杂对象的代码,并且把复杂对象作为方法的返回值返回
  @Override
  public Connection getObject() throws Exception {
    Class.forName("com.mysql.jdbc.Driver");
    Connection connection = DriverManager.getConnection("jdbc:mysql:///javaweb?characterEncoding=utf-8&useSSL=false","root","123456");
    return connection;
  }
  // 返回所创建的复杂对象的Class对象
  @Override
  public Class<?> getObjectType() {
    return Connection.class;
  }
  // 配置是否是单例模式
  @Override
  public boolean isSingleton() {
    return false;
  }
复制代码
<bean id="factoryBean" class="com.test.MyFactoryBean">
复制代码
/**
  * 用于测试factoryBean
  */
  @Test
  public void testFactoryBean(){
    ClassPathXmlApplicationContext ctr = new ClassPathXmlApplicationContext("/applicationContext.xml");
    Connection conn  = (Connection) ctr.getBean("factoryBean");
    System.out.println(conn);
  }
复制代码


5.2.1.2、FactoryBean接口的细节


  1. 如果我不想获得创建的复杂对象(Connection),想获得普通的简单对象(FactoryBean),我们仅仅只需在getBean(id)的前面加一个&即可。


import java.sql.Connection;
import java.sql.DriverManager;
import org.springframework.beans.factory.FactoryBean;
/**
 * @Description
 * @Author XiaoLin
 * @Date 2021/2/24 19:47
 */
public class MyFactoryBean implements FactoryBean<Connection> {
  @Override
  public Connection getObject() throws Exception {
    Class.forName("com.mysql.jdbc.Driver");
    Connection connection = DriverManager
        .getConnection("jdbc:mysql:///javaweb?characterEncoding=utf-8&useSSL=false", "root",
            "1101121833");
    return connection;
  }
  @Override
  public Class<?> getObjectType() {
    return Connection.class;
  }
  @Override
  public boolean isSingleton() {
    return true;
  }
}
复制代码
<?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="factoryBean" class="MyFactoryBean">
  </bean>
</beans>
复制代码
import java.sql.Connection;
import org.junit.Test;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
 * @Description
 * @Author XiaoLin
 * @Date 2021/2/24 19:50
 */
public class MyFactoryBeanTest {
  /**
  * 用于测试复杂类型对象的创建
  */
  @Test
  public void testMyFactoryBeanTest(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/applicationContext.xml");
    MyFactoryBean connection = (MyFactoryBean)applicationContext.getBean("&factoryBean");// 获取普通的简单对象FactoryBean,不获取复杂的Connection对象
    System.out.println(connection);
  }
}
复制代码
  1. isSingleton()方法,如果返回值为true时,他只会创建一个对象,返回false时会创建多个对象,一般根据对象的特点来判断返回true(SqlSessionFactory)还是false(Connection)。


5.2.1.3、BeanFactory实现原理图


2.JPG


5.2.1.4、FactoryBean总结


   FactoryBean是Spring中用于创建复杂对象的一种方式 也是Spring原生提供的,后面框架整合会大量运用。


5.2.2、实例工厂


5.2.2.1、FactoryBean的弊端


   使用FactoryBean的话有Spring的侵入,实现了FactoryBean接口,一旦离开了Spring,整个类都无法使用。


5.2.2.2、实例工厂的使用


// 实例工厂
public class ConnectionFactory {
  public Connection getConnection(){
    Connection conn = null;
    try {
      Class.forName("com.mysql.jdbc.Driver");
      conn = DriverManager.getConnection("jdbc:mysql:///javaweb?characterEncoding=utf-8&useSSL=false","root","1101121833");
    } catch (ClassNotFoundException | SQLException e) {
      e.printStackTrace();
    }
   return conn;
  }
}
复制代码
<bean id="connFactory" class="com.factory.ConnectionFactory"/>
  <bean id="conn" factory-bean="connFactory" factory-method="getConnection"/>
复制代码


5.2.3、静态工厂


   前面我们学了实例工厂,由于实例工厂的getConnection()方法是实例方法,需要由对象来调用,所以需要先创建对象然后再通过对象来调用方法。


   而静态工厂由于getConnection()方法是静态方法,不需要由对象来调用,直接通过类进行调用。这就是实例工厂与静态工厂最大的区别。


public class ConnectionStaticBeanFactory {
  public static Connection getConnection(){
    Connection conn = null;
    try {
      Class.forName("com.mysql.jdbc.Driver");
      conn = DriverManager.getConnection("jdbc:mysql:///javaweb?characterEncoding=utf-8&useSSL=false","root","1101121833");
    } catch (ClassNotFoundException | SQLException e) {
      e.printStackTrace();
    }
    return conn;
  }
}
复制代码
<bean id="staticBeanFactory" class="com.factory.ConnectionStaticBeanFactory" factory-method="getConnection"/>
复制代码


5.3、创建对象的细节


5.3.1、控制简单对象的创建次数


   控制简单对象的创建次数我们只需要配置bean标签的scope属性值即可。他常用的有两个值:


  1. singleton:默认为单例模式,只会创建一个简单对象。
  2. prototype:每次都会创建一个新的对象。


<bean id="person" scope="singleton(prototype)"  class="com.doamin.Person"/>
复制代码


5.3.2、控制复杂对象的创建次数


  FactoryBean接口的isSingleton()方法的返回值来进行控制(如果没有isSingleton()方法,那么还是通过scope属性来进行控制):


  1. 返回true:只会创建一次。
  2. 返回false:每一次都会创建一个新的对象。


5.3.3、控制对象创建次数的原因


 可以被大家共享的对象(SqlSessionFactory、各种Dao、Service)可以只创建一次,不可以被大家共享的对象(Connection、SqlSession、Controller)可以创建多次,控制对象创建次数的最大好处是可以节省不必要的内存浪费。


5.4、对象的生命周期


   生命周期指的是一个对象的创建、存活、消亡的一个完整过程。由Spring来负责对象的创建、存活、销毁。了解生命周期,有利于我们使用好Spring为我们创建的对象。


   Spring帮我们创建的对象有三个阶段:


  1. 创建阶段
  2. 初始化阶段
  3. 销毁阶段


5.4.1、创建阶段


  1. 当 scope = "singleton" 时,Spring工厂创建的同时,对象会随之创建。如果我们不想在Spring工厂创建的同时创建,想在获取对象的时候创建,只需在配置文件的bean标签添加一个lazy-init = true即可。


  1. 当 scope = "prototype" 时,Spring工厂会在获取对象的同时创建对象。


5.4.2、初始化阶段


  Spring工厂在创建完对象后,会调用对象的初始化方法,完成对应的初始化操作。

 初始化方法是由程序员根据需求提供初始化方法,由Spring工厂调用,最终完成初始化操作。他有两种调用的方式:


  1. 实现InitializingBean接口(有Spring侵入的问题)。
  2. 提供一个普通方法并修改配置文件。


5.4.2.1、InitializingBean接口


// 这个就是初始化方法,做一些初始化操作,Spring会进行调用 
@Override
  public void afterPropertiesSet() throws Exception {
    // 初始化操作
  }
复制代码


5.4.2.2、提供普通方法


   由于实现InitializingBean接口存在Spring侵入的问题,所以Spring提供了另一个方法给我们进行初始化操作,那就是提供一个普通的方法,然后去配置文件中增加init-method="方法名"熟悉的配置即可。


public void init(){
    System.out.println("我是初始化方法");
  }
复制代码
<bean id="product" class="com.domain.Product" init-method="init"/>
复制代码


5.4.2.3、注意


   如果一个对象既实现了InitializingBean接口同时又提供了普通的初始化方法,那么两个初始化方法都会执行,先执行的是InitializingBean接口的方法,再执行普通的初始化方法。


  在执行初始化操作之前,会先进行属性的注入,注入在前,初始化在后。


   初始化需要做的操作一般是数据库、IO、网络操作。


5.4.3、销毁阶段


   在工厂关闭之前,Spring会在销毁对象前,会调用对象的销毁方法,完成销毁操作。

  销毁方法是程序员根据需求定义销毁方法,由Spring工厂调用销毁方法,完成销毁操作。他也有两种方法:


  1. 实现DisposableBean接口。
  2. 定义普通的销毁方法在配置文件中配置。


5.4.3.1、实现DisposableBean接口


public class Product implements InitializingBean, DisposableBean {
      @Override
  public void destroy() throws Exception {
    System.out.println("销毁操作,资源释放");
  }
}
复制代码


5.4.3.2、定义普通方法


public class Product implements InitializingBean, DisposableBean { 
  public void MyDestory(){
      System.out.println("自己定义的销毁方法");
    }
}
复制代码
<bean id="product" class="com.domain.Product" destroy-method="MyDestory"/>
复制代码


5.4.3.3、注意


  1. 销毁方法的操作只适用于scope="singleton"。
  2. 销毁操作主要指的是一些资源的释放操作。


5.5、Spring整合配置文件


   一般来说像数据库的一些配置信息我们都不会直接写在代码里面,会将他们抽取出来成一个配置文件,再利用Spring进行注入。我们只需要加入一个标签即可完成。


<!--告诉Spring你的db.properties在哪里-->  
<context:property-placeholder location="classpath:/db.properties"/>
<!--用$(db.properties中的key)来进行取值-->
<bean id="conn" class="com.factory.BeanFactory">
  <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>
复制代码


六、自定义类型转换器


6.1、类型转换器


   我们写在Spring配置文件中赋值的值都是String类型的,但是我们的实体类是Interger类型的值,按照语法来说,String类型的值是不可以直接赋值给Integer类型的,但是为什么能直接赋值呢?


  因为Spring内部帮我们进行了自动的类型转换,Spring通过类型转换器将配置文件中字符串类型的数据,转换成了对象中成员变量对应类型的数据,从而完成注入。


3.JPG


6.2、自定义类型转换器


6.2.1、问题引入


@Data
@AllArgsConstructor
@NoArgsConstructor
public class People {
  private String name;
  private Date birthday;
}
复制代码
<bean id="people" class="com.domain.People">
    <property name="name" value="XiaoLin"/>
    <property name="birthday" value="2021-2-6"/>
  </bean>
复制代码


  我们运行代码之后发现报错了,说String类型的值不可以转化为Date类型的值,说明Spring内部没有这个转换器。


Caused by: java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'java.util.Date' for property 'birthday': no matching editors or conversion strategy found
  at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:262)
  at org.springframework.beans.AbstractNestablePropertyAccessor.convertIfNecessary(AbstractNestablePropertyAccessor.java:585)
  ... 39 more
复制代码

 

Spring内部没有提供特定类型转换器时,而程序员在应用的过程中又需要使用,所以需要程序员自己定义类型转换器。


6.2.2、代码实现


 自定义类型转换器我们分为两步实现:


  1. 实现Converter<转换前的类型, 转换后的类型>接口,并且重写里面的方法。
  2. 在配置文件中进行转换器的注册


public class MyConverter implements Converter<String, Date> {// 他有两个泛型,一个是转换前的类型,另一个是转换后的类型
  /*
  convert方法的作用是将String->Date
  parm:source代表的是配置文件中需要转换的内容
  return:把转换好的值作为返回值,Spring会自动为属性赋值
   */
  @Override
  public Date convert(String source) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    Date parse = null;
    try {
      parse = sdf.parse(source);
    } catch (ParseException e) {
      e.printStackTrace();
    }
    return parse;
  }
}
复制代码
<!--  类型转换器的注册,告诉Spring我们所创建的MyConverter类是类型转换器类,
      Spring提供了一个类ConversionServiceFactoryBean来完成类型转换器的注册-->
  <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters" >
      <set>
<!--        注册类型转换器-->
        <ref bean="myConvert"/>
      </set>
    </property>
  </bean>
复制代码


6.2.3、注意细节


  1. 创建ConversionServiceFactoryBean标签的id必须为conversionService,不然不生效。
  2. 其实Spring已经内置了日期类型的转换器,但是他只支持以/作为分隔符的字符串的格式:2021/2/6,不支持其他的格式,如果你的字符串格式已经是这种,就无需再写自定义类型转换器。


七、BeanPostProcessor

7.1、概述


   BeanPostProcessor称为后置处理Bean,他的作用是对Spring工厂所创建的对象进行二次加工,他是AOP的底层实现,他本质上是一个接口。


7.2、BeanPostProcessor分析


   他是一个接口,要实现他的两个方法:


  1. Object postProcessBeforeInitialization(Object bean,String beanName):他的作用是在Spring创建完对象后,在进行初始化方法之前,
  2. 通过参数获取到Spring创建好的对象,执行postProcessBeforeInitialization方法进行加工,最终通过返回值返回这个加工好的对象给Spring。
  3. Object postProcessAfterInitialization(Object bean,Stirng beanName):Spring执行完对象的初始化操作之后,运行postProcessAfterInitialization方法进行加工,通过参数获取Spring创建好的对象,最终通过返回值返回给Spring。

 

在日常的开发中,我们很少去处理Spring的初始化操作,所以没有必要区分前后,所以一般只需要实现其中一个方法即可,且BeanPostProcessor会对Spring工厂中的所有对象进行加工。


4.JPG


7.3、代码实现


@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
  private Integer id;
  private String name;
}
复制代码
public class  MyBeanPoster implements BeanPostProcessor  {
  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName)
      throws BeansException {
    return bean;
  }
  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    // 要进行类型判断,如果不是Student类型的话直接返回,不然会报类型转换错误,因为Spring会把工厂中的所有对象进行加工处理
    if (bean instanceof Student){
      Student student = (Student) bean;
      student.setName("lisi");
    }
    return bean;
  }
}
复制代码
<bean id="student" class="com.beanpost.Student">
    <property name="id" value="10"/>
    <property name="name" value="zs"/>
  </bean>
<!--注册后置Bean-->
  <bean id="myBeanProcessor" class="com.beanpost.MyBeanPoster"/>


相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
3天前
|
Java Spring
【编程笔记】在 Spring 项目中使用 RestTemplate 发送网络请求
【编程笔记】在 Spring 项目中使用 RestTemplate 发送网络请求
99 0
|
3天前
|
缓存 监控 Java
【Spring系列笔记】AOP
面向切面编程就是面向特定方法编程。通过将横切关注点(cross-cutting concerns)从主要业务逻辑中分离出来,提供一种更好的代码模块化和可维护性。 横切关注点指的是在应用程序中横跨多个模块或层的功能,例如日志记录、事务管理、安全性、缓存、异常处理等。
25 0
|
3天前
|
存储 缓存 Java
【Spring系列笔记】依赖注入,循环依赖以及三级缓存
依赖注入: 是指通过外部配置,将依赖关系注入到对象中。依赖注入有四种主要方式:构造器注入、setter方法注入、接口注入以及注解注入。其中注解注入在开发中最为常见,因为其使用便捷以及可维护性强;构造器注入为官方推荐,可注入不可变对象以及解决循环依赖问题。本文基于依赖注入方式引出循环依赖以及三层缓存的底层原理,以及代码的实现方式。
24 0
|
3天前
|
前端开发 Java 数据格式
【Spring系列笔记】定义Bean的方式
在Spring Boot应用程序中,定义Bean是非常常见的操作,它是构建应用程序的基础。Spring Boot提供了多种方式来定义Bean,每种方式都有其适用的场景和优势。
32 2
|
3天前
|
Java Spring 容器
【Spring系列笔记】IOC与DI
IoC 和 DI 是面向对象编程中的两个相关概念,它们主要用于解决程序中的依赖管理和解耦问题。 控制反转是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入和依赖查找。
34 2
|
3天前
|
SQL Java 数据库连接
(数据库链接池)spring内容复习7月16日笔记
(数据库链接池)spring内容复习7月16日笔记
16 0
|
3天前
|
安全 Java 数据库连接
啃完这些Spring知识点,我竟吊打了阿里面试官(附面经+笔记)
对于开发同学来说,Spring 框架熟悉又陌生。 熟悉:开发过程中无时无刻不在使用 Spring 的知识点;陌生:对于基本理论知识疏于整理与记忆。导致很多同学面试时对于 Spring 相关的题目知其答案,但表达不够完整准确。
|
3天前
|
机器学习/深度学习 安全 Java
硬核!阿里2023版Spring全家桶进阶笔记流出,堪称Java跳槽神器
最近小伙伴在我后台留言是这样的: 现在就这光景,不比以前,会个CRUD就有人要,即使大部分公司依然只需要做CRUD的事情......现在去面试,只会CRUD还要被吐槽: 面试造火箭,工作拧螺丝,就是现在互联网最真实的写照。很多程序员都是死磕八股文,以应对面试。这种情况无可厚非,但其实最重要的还是技术基础和深度学习。真正能用上的能有多少,不是看现在,还有未来!所以,以技术立命,我们能做的也就只有不断提升自己,去适应市场环境,提高自身技术水平!但这可不是一件简单的事情,虽然也可以自学,但站在巨人的肩膀上学习才是能让程序员事半功倍的最优道路。
硬核!阿里2023版Spring全家桶进阶笔记流出,堪称Java跳槽神器
|
3天前
|
前端开发 Java 开发者
阿里内部热捧“Spring全线笔记”,不止是全家桶,太完整了
对于每一位Java开发人员来说,提起Spring定是不陌生的,实际上自Spring框架诞生以来,就备受开发者的青睐,基本上现在的互联网公司都要使用到Spring框架。Spring框架中又包含了SpringMVC、SpringBoot、SpringCloud等,被开发者称呼为Spring全家桶。
|
3天前
|
存储 SpringCloudAlibaba Java
手把手教你,从零开始搭建Spring Cloud Alibaba!这份笔记太牛了
Spring Cloud Alibaba 是阿里巴巴提供的微服务开发一站式解决方案,是阿里巴巴开源中间件与 Spring Cloud 体系的融合。