学习java框架-J2EE体系-Spring-IOC-AOP-Bean-事务-

文章目录

整体架构

整体架构 = 表示层 + 业务层 + 持久层
最原始的 = JSP + Servlet + Javabean Java JDBC(实现数据库访问)
SSH = Structs + Spring +Hibernate
SSM = SpringMVC + Spring + Mybatis
MVC设计 = M模型、V视图、C控制器

Spring Boot相关项目设计框架:
SSM = SpringMVC + Spring Boot + Mybatis

J2EE

概述

J2EE是什么
J2EE = Java2平台企业版(Java 2 Platform Enterprise Edition),是Java技术企业级应用开发的工业标准;
提供基于组件的方式来设计、开发、组装和部署企业应用。

J2EE平台组成
Java2 平台有三个版本:J2SE(Java2平台标准版)、J2EE(Java2平台企业版)、J2ME(Java2平台Micro版)。
J2EE平台由 一套服务(Services)、应用程序接口(APIs)和协议构成。

J2EE应用程序的组件
J2EE应用程序是由组件构成的。
J2EE组件是一个封装了功能的软件单元,能够与相关的类和文件一起组成J2EE应用程序。

J2EE分层设计模型
使用分层的设计模型,分别是:客户层、Web层、业务层、企业信息系统层(EIS)。
客户层 = 运行在客户端上的组件;
Web层 = 运行在J2EE服务器上的组件;
业务层 = 运行在J2EE服务器上的组件;
企业信息系统层(EIS) = 运行在EIS服务器上的软件系统;

在这里插入图片描述

  • 客户层:
    Web浏览器——Web客户端,也就是我们日常生活中使用的网页,它通过标准的格式来显示从服务器端传递过来的页面,服务器传递给浏览器的时候格式为HTML、XML格式;
    Applet(小应用程序)——嵌套在浏览器中的一种轻量级客户端,当浏览器不能充分表现数据或者应用界面的时候,才会使用它,是web页面的替代手段,可以使用J2SE开发Applet。是小程序吗?
    应用程序客户端——相对于Applet而言,是较重量级的客户端,可以使用大量的服务和API,运行在客户机上,能提供强大而灵活易用的用户界面,如使用Swing或AWT创建图形化的用户界面。类似于qq的客户端?可以访问业务层的Bean 或者 打开HTTP连接与Web层上的Servlet之间的通信。

  • J2EE应用层服务器
    由组件和容器组成;
    组件 = 能够显示在屏幕上的各种图形化的实体;
    容器 = Component子类的任何一个对象;
    容器可以添加其他组件形成复合组件。

两大容器 = Web容器 + EJB容器(表示层 + 业务逻辑层)
Web容器 = 管理所有的Servlet等Web组件的运行;
EJB容器 = 支持EJB组件的事务处理和生命周期管理,以及Bean的查找和其他服务,支持J2EE多层架构的基础结构。

两大组件 = Web组件 + EJB组件
Web组件 = 与基于Web的客户端进行交互。有三类Web组件:Servlet、JSP、JavaBean。
EJB组件 = 包含三种不同类型的EJB:会话Bean、消息驱动Bean、实体Bean。

  • J2EE企业信息系统层:
    ERP、数据库、目录服务、其他遗留系统等。

SSM设计模型

在这里插入图片描述

传统web开发

存在的问题
层与层之间的耦合性严重,接口与具体的实现紧密耦合在一起;如果后期需要修改具体的实现则需要在修改对应的代码。
解决:程序中不需要手动new对象,通过第三方实现对象创建。

通用的事务功能、日志功能耦合在业务代码中;
解决:通过第三方提供需要的Bean对象的代理实现。

Spring概述

使用Spring框架的好处?

好处说明
轻量轻量的,基本的版本大约2MB。
控制反转IoC通过控制反转实现了松散耦合,对象们给出它们的依赖,而不是创建或查找依赖的对象们。
面向切面的编程(AOP)支持面向切面的编程,并且把应用业务逻辑和系统服务分开。
容器包含并管理应用中对象的生命周期和配置。
MVC框架Web框架是个精心设计的框架,是 Web框架的一个很好的替代品。
事务管理提供一个持续的事务管理接口,可以扩展到 上至本地事务 下至全局事务(JTA)。
异常处理提供方便的API把具体技术相关的异常(比如由JDBC,Hibernate or JDO抛出的)转化为一致的unchecked 异常

框架是什么?

概念
框架 = 一些类 和 接口的集合,通过类、接口协调来完成一系列的程序实现。

Spring概念?

概念
是为了解决企业级应用开发的复杂性而创建的,简化开发;
是分层的轻量级JavaEE框架;
以IOC和AOP为核心,使用基本的JavaBean来完成之前只能用EJB完成的工作。

EJB = Enterprise JavaBean,它是企业Java Beans。
理解 = 把编写的软件中要执行指定的任务的类,不放在客户端软件上,而是给他打包放到服务器上,并依靠RMI进行通信。

两个核心
IOC 和 AOP;
IOC = 控制反转,把创建对象的过程交给Spring进行管理;
AOP = 面向切面编程,不修改源代码进行功能增强。

BeanFactory接口

第三方创建Bean对象
  • 概念:
    BeanFactory就是第三方用于创建Bean的工厂。
  • 创建Bean的过程:
    在这里插入图片描述
  • 代码实例:
    代码整体架构图
    在这里插入图片描述
    接口以及实现类
// interface
package com.beanioc;
public interface UserService {
}

// interface implements
package com.Impl;
import com.beanioc.UserService;
public class UserServiceImpl implements UserService {
}

配置base.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">

<!--    配置UserService对象-->
    <bean id="userService" class="com.Impl.UserServiceImpl"></bean>

</beans>

测试代码;

package com.beanioc.test;

import com.beanioc.UserService;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;

public class BeanFactoryTest {
    public static void main(String[] args){
//        创建工厂对象
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//        创建一个读取器(xml文件)
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
//        读取配置文件给工厂
        reader.loadBeanDefinitions("base.xml");
//        根据id获取bean实例对象
        UserService userService = (UserService) beanFactory.getBean("userService");
        System.out.println(userService);
    }
}

04

DI依赖注入
  • 概念:
    将Bean1设置Bean2到自己内部的过程也交付给BeanFactory,需要通过配置文件实现。

  • 实现过程:
    要求:需要在UserServiceImpl中设置UserDao对象;
    定义UserDao接口及具体实现UserDaoImpl;
    修改UserServiceImpl实现类,加入setUserDao(UserDao userDao)方法接收UserDao对象;
    修改Base.xml文件的配置文件,在UserDaoImpl的中嵌入 配置注入;视频中 我理解是在UserServiceImpl中的配置。
    修改测试代码,获取UserService时,setUserDao方法就被框架调用执行了。

  • 具体代码:
    图示:
    在这里插入图片描述
    配置文件:

<?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">

xhj:核心内容是property属性。
<!--    配置UserService对象-->
    <bean id="userService" class="com.Impl.UserServiceImpl">
<!--        name表示的是userService中setxxx方法中xxx首字母小写的名字-->
<!--        ref 表示在容器中找Bean对象xxx-->
<!--        整体的含义是:从容器中找id名字为userDao的Bean对象设置到容器中id名字为WieuserService的Bean对象中名字为userDao的方法-->
        <property name="userDao" ref = "userDao"></property>
    </bean>

<!--    配置UserDao对象-->
    <bean id ="userDao" class="dao.impl.UserDaoImpl"></bean>
</beans>

新建的UserDao接口与其实现类UserDaoImpl

接口:
package dao;
public interface UserDao {
}
实现类:
package dao.impl;
import dao.UserDao;
public class UserDaoImpl implements UserDao {
}

UserServiceImpl修改:

package com.Impl;
import com.beanioc.UserService;
import dao.UserDao;
public class UserServiceImpl implements UserService {
    // BeanFactory 去调用该方法,从容器中获取userDao设置到此处
    public void setUserDao(UserDao userDao){
        System.out.println("BeanFactory 去调用该方法,获取userDao设置到此处"+userDao);
    }
}

05

继承关系

BeanFactory是核心接口,具体的实现是DafaultListableBeanFactory。

ApplicationContext接口

实现Spring容器

概念:
ApplicationContext被称为Springd的容器,内部封装了BeanFactory;
使用其作为进行开发时。xml配置文件通常名字较为ApplicationContext.xml;

测试代码:

package com.beanioc.test;

import com.beanioc.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ApplicationContextTest {
    public static void main(String[] args) {
//        通过Application接口的实现类 ClassPathXmlApplicationContext创建对象
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("base.xml");
//        通过ApplicationContext对象的getBean方法来实现 对象的创建;
        UserService userService = (UserService) applicationContext.getBean("userService");
        System.out.println(userService);
    }
}
继承

ApplicationContext内部维护的BeanFactory的具体实现也是DefaultListableBeanFactory;
在这里插入图片描述
选中ApplicationContext 键盘输入ctrl + h 可以找到ApplicationContext的继承体系;
当只导入spring时,继承体系如下,只有三个可以使用的实现类;

在这里插入图片描述
只在Spring基础环境下三个实现类功能描述:在这里插入图片描述

BeanFactory和ApplicationContext区别?

BeanFactory早期的接口,被称为Spring的Bean工厂;ApplicationContext是Spring的容器;
ApplicationContext除了有BeanFactory基础功能外,还有别功能(监听和国际化);BeanFactory的API更偏向底层、ApplicationContext的API主要是对那些底层API的封装;
BeanFactory创建Bean是在BeanFactory调用getBean时候创建的;而ApplicationContext是在加载xml文件时候就创建了;
BeanFactory是延迟加载,而ApplicationContext是立即加载。

两者关系:
BeanFactory中存在Bean的创建和功能;ApplicationContext继承了BeanFactory又维护BeanFactory的引用;同时存在继承与融合关系。

基于xml的Spring应用

Bean使用xml形式配置涉及属性

在这里插入图片描述
理解:

  1. 其中id是在配置过程中对于bean的标识,后期会将其转换成bean name,通过getBean获取对象的时候也是通过bean name获取的;
  2. autowire可以自动根据实现类中的set方法找到对应的Bean进行设置,不需要在对应的Bean中通过property进行设置;
  3. 通过getBean获取Bean对象,参数可以是:beanName和name别名;
  4. Bean的销毁和Bean销毁方法得调用是两回事。假如ApplicationContext对象没有了,它内部维护的Bean对象也没有了,但销毁方法却没有被调用,原因在于Spring没有指定到调用方法的步骤,容器就挂掉了;
  5. 在创建对象之后,想要指定初始化方法完成一些初始化操作,有两种形式:
    ①创建方法,然后在对应的bean配置中通过init-method配置
    ②通过实现InitializingBean接口,重写afterPropertiesSet方法实现初始化操作;
    整体执行顺序是:创建Bean对象、属性设置、afterPropertiesSet初始化、init-method初始化。
  6. constructor-org标签,所有在xml中配置需要参数都可以使用该标签进行配置;

具体解释:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Bean实例化的两种方式

Bean实例化的方式:构造方法、工厂方式;
在这里插入图片描述
构造方法实例化:

1 = 创建对象通过new的形式创建
类名 对象名 = new 类名();

2 = xml配置
<bean id="bean的标识" class="类的全限定类名">
<!-- 如果是有参构造方法,则需要传入参数-->
	<constructor-arg name="有参构造方法参数名字" value="具体的值"></constructor-arg>
</bean>

其中如果涉及有参构造方法,则需要在对应Bean配置文件中 添加constructor-arg标签
在这里插入图片描述

Bean工厂方式的实例化:三种方式
静态工厂方式、实例工厂方式、实现FactoryBean规范延迟Bean实例化。

静态工厂方式实例化Bean:通过fatory-method标签实现;

1 = 对象的创建采用类.静态方法的形式
类名 对象名 = 类名.静态方法();

2 = xml配置
<bean class="全限定类名" factory-method="静态方法名">
<!-- 如果是有参构造方法,则需要传入参数-->
	<constructor-arg name="方法中的参数名" value="具体的值"></constructor-arg>
</bean>

该方式的作用在于:
原始创建Bean在之前或之后想执行别的操作无法实现,但是通过工厂方式可以实现。→直接在定义的工厂类MyFactory类中的静态类中书写即可;
第三方jar包中的Bean需要交由Spring容器管理。→ 通过静态工厂方式实现。
在这里插入图片描述
实例化工厂方式实例化Bean:

1 = 通过对象.普通方法得到对象
类1 对象1 = new 类1()
类2 对象2 = 对象1.方法()

2 = xml配置
<bean id="bean的标识" class="全限定类名-工厂对象" >
<!-- 如果是有参构造方法,则需要传入参数-->
	<constructor-arg name="方法中的参数名" value="具体的值"></constructor-arg>
</bean>

<bean id="bean的标识" factory-bean="工厂对象的id" factory-method="工厂对象使用的方法名">
<!-- 如果是有参构造方法,则需要传入参数-->
	<constructor-arg name="方法中的参数名" value="具体的值"></constructor-arg>
</bean>

该方式创建的工厂类中包含普通方法,需要先创建工厂对象,然后在调用普通类;
配置:先配置工厂对象;在配置方法;
该方式的作用在于:有些第三方jar包创建Bean是某些对象的非静态方法产生的,需要将这些交给Spring容器管理,在需要采用实例化工厂的形式去配置。
在这里插入图片描述
静态工厂方式和实例工厂方式创建Bean对象,其中的方法是含有参数的,如何配置?
就在对应的bean标签中,添加constructor-arg标签添加name 和 value来对对应的name=参数名,value=具体值 得到。

实现FactoryBean规范延迟Bean实例化
在这里插入图片描述

Bean的注入方式

手动property、constructor-arg、list、set、map、props

在这里插入图片描述
在这里插入图片描述
通过Bean的set注入:
普通变量注入;
在这里插入图片描述
对象引用注入;
在这里插入图片描述
通过构造Bean的方法注入:
普通变量注入;
注入普通变量
对象引用注入;
在这里插入图片描述注入集合数据类型:如果注入的是List、set,则在property便签下的子标签需要使用list或set。在这里插入图片描述
如果List中的参数是Bean实例,配置方式有两种:

通过ref注入
<bean id="userService2" class="com.Impl.UserServiceImpl2">
	<property name="stringList">
		<list>
			<ref bean = "userDao1"></ref>
			<ref bean = "userDao2"></ref>
			<ref bean = "userDao3"></ref>
		</list>
	</property>
</bean>

<bean id="userDao1" class="dao.UserDaoImpl"></bean>
<bean id="userDao2" class="dao.UserDaoImpl"></bean>
<bean id="userDao3" class="dao.UserDaoImpl"></bean>

通过bean注入:
<bean id="userService2" class="com.Impl.UserServiceImpl2">
	<property name="stringList">
		<list>
			<bean class= "dao.UserDaoImpl"></bean>
			<bean class= "dao.UserDaoImpl"></bean>
			<bean class= "dao.UserDaoImpl"></bean>
		</list>
	</property>
</bean>

总结:
Bean注入有三种内容,分别是基本数据类型、对象引用、集合对象。

基本数据类型
<bean id="" class="">
-set方法引入
	<property name="" vlue=""> </property>
-构造方法引入
	<constructor-arg name="" vlue=""> </constructor-arg>
</bean>

对象引用
<bean id="" class="">
-set方法引入
	<property name="" ref=""><property>
-构造方法引入
	<constructor-arg name="" ref=""></constructor-arg>
</bean>

集合对象List
<bean id="xxx" class="">
	<property name="">
		<list>
			<value></value>
			<ref></ref>
		</list>
	</property>
</bean>

集合对象Set引入
<bean id="xxx" class="">
	<property name="">
		<set>
			<value></value>
			<ref></ref>
		</set>
	</property>
</bean>
集合对象Map引入
<bean id="xxx" class="">
	<property name="">
		<map>
			-基础数据引入
			<entry key="" value=""></entry>
			- 对象引用引入
			<entry key-ref="" value-ref=""></entry>
		</map>
	</property>
</bean>
集合对象properties引入 键值对
<bean id="xxx" class="">
	<property name="">
		<props>
			<prop key = "">value</prop>
		</props>
	</property>
</bean>
自动装配 autowire

可以实现将对象引用自动装配到对应的bean中;

<bean id="" class="" autowire="">
autowire参数两个:
byName = 通过setXxx与id=“xxx0”(或name=“xxx”)匹配,一致则自动装配
byType = 通过Bean类型从容器中匹配,但容易由于多个相同Bean类型,导致报错。
也即是多个bean的class都是相同的全限定类名。

spring的配置标签

标签分为:默认标签、自定义标签;
默认标签 = 不需要导入额外命名空间;
自定义标签 = 需要导入额外命名空间,通过前缀引用标签,例如context:property-placeholder/

在这里插入图片描述
在这里插入图片描述
spring的默认命名空间存在的标签有: beans、bean、import、alias
在这里插入图片描述
beans标签:
作用:作为根标签、嵌套在根标签中用于区分环境,通过profile属性切换开发环境。

根标签:
<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">
</beans>

区分环境:
<beans>
	-测试环境
	<beans profile="test"></beans>
	-开发环境
	<beans profile="dev"></beans>
</beans>

指定环境:

-代码指定
System.setProperty("spring.profiles.active","环境名")
-命令行指定
Dspring.profiles.active=环境名

import
导入其他配置文件;每个模块有需要有各自的配置文件,为了方便applicationContext导入配置文件,故将所有配置文件整合成一个文件。

<import resource="classpath:xxx"></import>

alias
给bean起别名。

<bean id="xxx" name="xxx1" class="com.beanioc.UserService"></bean>
<alias alias="xxx2" name="xxx"></alias>
则id为xxx的bean,有两个别名,即xxx1 xxx2

aliasmap中维护bean的别名。

spring自定义标签:
视频中描述在pom.xml中写入dependency被称为写入坐标,也即是jar包。
在这里插入图片描述

Spring的get方法

三种方式:
在这里插入图片描述
其中Class type需要通过反射得到class对象,即类.class 或者 对象.getClass 或者 Class.forName(“全限定类名”)

Spring配置非自定义的bean

druid配置

概念
将第三方jar包中的bean让spring管理 = spring配置非自定义的bean;
配置需要考虑:bean创建的形式是构造方式或工厂方式、是否需要注入依赖。
过程

1. 在pom.xml中引入druid依赖,有druid就得有mysql-connector-java
<dependencies>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.23</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.49</version>
    </dependency>
</dependenies>
3. 配置Druid数据源交由Spring管理;
就是在pom.xml文件中配置依赖,然后找到界面右侧maven刷新就可以得到jar包。
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!--        class全限定类名采用的是DruidDataSource类右键复制的-->
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/book"></property>
        <property name="username" value="root"></property>
        <property name="password" value="*****"></property>
     </bean>
4. 测试代码
public class ApplicationContextTest {
    public static void main(String[] args) {
//        通过Application接口的实现类 ClassPathXmlApplicationContext创建对象
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("base.xml");
        Object dataSource = applicationContext.getBean("druidDataSource");
        System.out.println(dataSource);
    }
}

注意
shift+shift 实现查看某个类的源码;
数据源要想使用需要四个基本信息:数据源驱动setDriverClassName、连接setUrl、用户名称setUsername、用户密码setPassword。
IDEA快捷键
视频中代码出现问题:
在这里插入图片描述

connection配置

概念
Connection产生是采用静态方法getconection创建的;

不交由spring管理,自己书写

- 加载驱动
Class.forName("com.mysql.jdbc.Driver");
- 创建connection对象
Connection connection = DeriverManager.getConnection("url","username","password");
分析:
两者都采用类.今天方法的形式,所以可以采用静态工厂方式构造;

交由spring管理,配置文件

<!--    配置connection-->
    <bean id="clazz" class="java.lang.Class" factory-method="forName">
<!--        因为 public static Class<?> forName(String className) 的参数名是className,所以这里填写name="className"-->
        <constructor-arg name="className" value="com.mysql.jdbc.Driver"/>
    </bean>
    <bean id ="connection " name ="connection" class="java.sql.DriverManager" factory-method="getConnection">
        <constructor-arg name="url" value="jdbc:mysql://127.0.0.1:3306/books"/>
        <constructor-arg name="user" value="root"/>
        <constructor-arg name="password" value="123456"/>
    </bean>

视频与实际操作不符合的部分
在这里插入图片描述

Date配置

xml文档配置:

    <bean id="simpleDateFormat" class="java.text.SimpleDateFormat">
        <constructor-arg name="pattern" value="yyyy-MM-dd HH:mm:ss"></constructor-arg>
    </bean>
    <bean id="date" factory-bean="simpleDateFormat" factory-method="parse">
        <constructor-arg name="source" value="2023-5-21 12:00:00"></constructor-arg>
    </bean>

测试文档:

public class ApplicationContextTest {
    public static void main(String[] args) {
//        通过Application接口的实现类 ClassPathXmlApplicationContext创建对象
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("base.xml");
		Object date = applicationContext.getBean("date");
        System.out.println(date);
        // 原始日期类型创建形式。
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        simpleDateFormat.parse("2023-5-21 12:00:00")
    }
}

理解:
在这里插入图片描述

MyBatis中的SqlSessionFactory配置

在pom.xml中配置依赖

<dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.49</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.5</version>
    </dependency>

不使用spring集中管理的代码文件:
在这里插入图片描述
base.xml配置文件

<bean id="in" class="org.apache.ibatis.io.Resources" factory-method="getResourceAsStream">
        <constructor-arg name="resource" value="mybatis-config.xml"></constructor-arg>
</bean>
<bean id ="builder" class ="org.apache.ibatis.session.SqlSessionFactoryBuilder"></bean>
<bean id= "sqlSessionFactory" factory-bean="builder" factory-method="build">
    <constructor-arg name="inputStream" ref="in"></constructor-arg>
</bean>

理解:
在这里插入图片描述

问题
原因在于mybatis-config.xml文件中多配置了mapper,所以出现报错。

idea=文件的刷新、清理和打包

Spring整合第三方框架

概念
整合第三方命名空间的方式:1 不需要第三方命名空间,比如Mybatis;2 需要第三方命名空间,比如Dubbo;
在这里插入图片描述

Mybatis不使用Spring整合使用

原始使用Mybatis查询
1 创建数据表的对象,类中需要有属性表示数据表中的元素。
2 定义UserMapper接口,里面有查询表格数据方法findAll,返回列表,列表元素是User对象。
3 配置UserMapper.xml文件,从参考连接中选择这个已映射的 SQL 语句 复制粘贴。修改参数为下图所示。
该文件需要存储在resources路径下,且层级路径与UserMapper一样的。
在这里插入图片描述
4 在mybatis-config.xml文件中配置UserMapper.xml文件。
在这里插入图片描述
5 测试代码书写:

package com.beanioc.test;

import Mapper.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import pojo.User;

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

public class MybatisTest {
    public static void main(String[] args) throws Exception {
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder builder  = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = builder.build(in);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> all = mapper.findAll();
        for (User user : all) {
            System.out.println(user);
        }
    }
}

Spring整合MyBatis

所需步骤
在这里插入图片描述

pom.xml文件相关坐标导入

    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>2.0.5</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.13.RELEASE</version>
    </dependency>

Mapper涉及文件

Mapper接口
package Mapper;

import pojo.User;

import java.util.List;

public interface UserMapper {
    List<User> findAll();
}

返回对象Userpackage pojo;

public class User {

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", nation='" + nation + '\'' +
                '}';
    }

    private Integer id;
    private String name;
    private String nation;

    public Integer getId() {
        return id;

    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNation() {
        return nation;
    }

    public void setNation(String nation) {
        this.nation = nation;
    }
}

Mapper.xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="Mapper.UserMapper">
    <select id="findAll" resultType="pojo.User">
        select * from author
    </select>
</mapper>
<!-- 研究已配置-->

配置SqlSessionFactoryBean和MapperScannerConfigurer

<!--    4/23 Spring整合Mybatis-->
    <!--数据源对象配置-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <!--        class全限定类名采用的是DruidDataSource类右键复制的-->
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/books"></property>
        <property name="username" value="root"></property>
        <property name="password" value="*****"></property>
    </bean>
    <!--    配置SqlSessionFactoryBean,将sqlSessionFactory存储到spring容器中-->
    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--        SqlSessionFactory底层是提供sqlSession的,而其在底层操作的时候需要connection,也就是数据源,需要一个数据源对象-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--    配置MapperScannerConfigurer,扫描指定的包,产生Mapper对象,存储在Spring容器中-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="Mapper"></property>
    </bean>
    <!--    配置UserService对象-->
    <bean id="userService11" class="com.Impl.UserServiceImpl11">
            <property name="userMapper" ref="userMapper"></property>
    </bean>

测试代码

//        4/23 Spring整合Mybatis 58
//        通过Application接口的实现类 ClassPathXmlApplicationContext创建对象
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("base.xml");
//        通过ApplicationContext对象的getBean方法来实现 对象的创建;
        UserService1 userService11 = (UserService1) applicationContext.getBean("userService11");
        userService11.show();

Spring整合MyBatis框架原理
在这里插入图片描述

在这里插入图片描述

Spring整合自定义标签context

需求:
数据源的信息配置往往采用键值对的形式,也就是properties文件实现;使用context命名空间的对应的标签。

过程:
1 在xml配置文件中配置命名空间;
2 案例书写数据源配置,数据源信息创建键值对文件;
3 xml配置数据源配置信息;
4 测试 其余代码设置均采用Spring整合MyBatis文件形式;

命名空间配置

<?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/context http://www.springframework.org/schema/context/spring-context.xsd
">

键值对文件

jdbc.Driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/books
jdbc.username=root
jdbc.password=*****

xml配置信息

<!--    加载properties文件-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <!--        class全限定类名采用的是DruidDataSource类右键复制的-->
        <property name="driverClassName" value="${jdbc.Driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

测试代码:

//        4/23 Spring整合Mybatis 58
//        通过Application接口的实现类 ClassPathXmlApplicationContext创建对象
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("base.xml");
//        通过ApplicationContext对象的getBean方法来实现 对象的创建;
        UserService1 userService11 = (UserService1) applicationContext.getBean("userService11");
        userService11.show();

Spring整合自定义框架context原理
63没有看,目前理解困难。

自定义命名空间总结

在这里插入图片描述
案例

在这里插入图片描述
在这里插入图片描述
haohaoannotation.xsd文件

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.itheima.com/haohao"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            targetNamespace="http://www.itheima.com/haohao">

    <xsd:element name="annotation-driven"></xsd:element>

</xsd:schema>

META-INF文件夹中的handlers和schemas文件

handlers文件内容
http\://www.itheima.com/haohao=com.handlers.HaohaoNamespaceHandler
#对应的是 命名空间字符串对应的命名空间处理器类对应的全限定类名

schema文件
http\://www.itheima.com/haohao/haohaoannotation.xsd=./com/itheima/config/haohao/haohaoannotation.xsd
# 配置schema映射地址字符串与当前文件的真实映射

命名空间处理器

package com.handlers;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

public class HaohaoNamespaceHandler extends NamespaceHandlerSupport {
    @Override
    public void init() {
        // 初始化 一个命名空间下有多个标签,会在init方法中为每一个标签注册一个标签解析器
        this.registerBeanDefinitionParser("annotation-driven", new HaohaoBeanDefinitionParser());
    }
}

标签解析器

package com.handlers;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;

public class HaohaoBeanDefinitionParser implements BeanDefinitionParser {
    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        // 注入一个BeanPostProcessor
        BeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setBeanClassName("com.beanioc.HaohaoBeanPostProcessor");
        parserContext.getRegistry().registerBeanDefinition("haohaoBeanPostProcessor", beanDefinition);
        return beanDefinition;
    }
}

标签解析器功能所需Bean后处理器对象

package com.beanioc;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class HaohaoBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("HaohaoBeanPostProcessor执行...");
        return bean;
    }
}

控制反转IOC

你对IOC的理解?

  • 概念:
    IOC = inversion of control 控制反转。也就是Bean创建权的反转。
    把创建对象的过程交给Spring进行管理,借助IOC容器实可以降低对象之间的耦合度;

  • 作用
    在于解耦,可以解除对象之间的耦合,让对象和对象之间完全没有联系,这样完成或修改一个对象时不需要考虑其他对象。

  • 出现原因:
    Java是面向对象的语言,应用程序需要通过一个个对象之间的关联和作用来完成功能。
    对象之间紧密的咬合形成的系统会具有较高的耦合度,存在一个对象出现问题,整个系统无法工作的情况。
    加入IOC容器,会使得对象之间的耦合度降低,修改一个对象不会对其他对象造成影响。

  • 原理理解:
    没有IOC容器的时候,对象A依赖对象B,A在运行到某一时刻的时候会去创建B的对象,这样A具有主动权,它控制了对象B的创建;
    引入IOC容器以后对象A和对象B之间就没有直接的联系,当对象A运行的时候由IOC容器创建B对象在适当的时候注入到对象A中,这样对象B的控制权就由A对象转移到了IOC容器。

依赖注入(DI)Dependency Injection

  • 概念:
    强调Bean之间的关系。

  • 场景:
    现在有两个Bean,其中Bean1中需要Bean2才可以达到想要的效果;
    原始做法,通过第三方创建出Bean1和Bean2,然后通过代码设置把Bean2设置给Bean1;
    现在做法,将Bean2设置给Bean1动作也交给第三方,但第三方创建Bean1时内部就创建了Bean2,当程序调用Bean1的时候内部就有Bean2的引用。

控制反转IOC 和 依赖注入(DI)关系?

关系控制反转依赖注入
名称IOC ( Inversion of Control)DI (Dependency injection)
概念将创建对象的权利交给Spring原始:对象A执行过程中需要创建对象B
现在:IOC容器将创建好的对象B注入到对象A中
描述角度不同调用者角度:
原始:对象A 需要对象B 直接通过new创建就好
现在:对象B的创建由IOC容器完成
控制反转 = 对于依赖对象的控制权 由调用者 转移到 IOC容器上
Spring容器角度:
spring容器负责将需要的对象注入到调用者的成员变量。

备注2022/8/17
注入依赖的本质 = 创建应用程序对象之间的协作关系的行为 = 装配(wiring)。

例子:创建的userServiceImpl类中,有UserDao类的对象

控制反转 =  
将创建UserDao对象的权利 从userServiceImpl对象手中 交给 IOC容器,强调的是一种思想。

依赖注入 = 
IOC容器将 userDao对象,注入到 userServiceImpl对象,强调的是一个过程和实现。

Spring常用的注入方式有哪些?

Spring通过依赖注入(DI)实现IOC(控制反转)。
常用的注入方式主要有:构造方法注入、setter注入、基于注解的注入。

注意
构造方法注入、setter注入都是通过Java的反射机制实现的。

参考

构造方法注入

  • 定义:
    类中必须提供构造方法,属性的set方法不需要;

  • 实现:
    在Spring-config.xml文件中配置;标签是constructor-arg
    前提:

package com.bean;
public class Car{
	private String brand;
	private float price;
	public Car(){}
	public Car(String brand,float price){
		this.brand = brand;
		this.price = price;
	}
}

①通过构造方法形参名字注入;

<bean id="car" class="com.bean.Car"> <!-- Car类的全限定类名 -->
	<constructor-arg name="brand" value = "奇瑞"></constructor-arg>
	<constructor-arg name="price" value = "10000"></constructor-arg>
</bean>

②通过构造方法形参的索引位置注入。

<bean id="car" class="com.bean.Car">
	<constructor-arg name="0" value = "奇瑞"></constructor-arg>
	<constructor-arg name="1" value = "10000"></constructor-arg>
</bean>

setter注入

  • 定义:
    通过set方法完成注入,类中必须给属性设定set方法;

  • 实现:
    在Spring-config.xml文件中配置;标签是property
    前提:

package com.bean;
public class Car{
	private String brand;
	private float price;
	public Car(){}
	public void setBrand(String brand){this.brand = brand;}
}

配置文件:

<bean id="car" class="com.bean.Car">
	<property name="brand" value = "奇瑞"></property>
</bean>

前两者的测试文件都是:

public static void main(String[] args){
	ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Spring-config.xml");
	Person person = (Person)applicationContext.getBean("car");
}

基于注释的注入

  • 定义:
    通过@Autowired注释方式,可以实现自动装配,只要在对应的属性上面添加该注解即可 = 在类创建的时候,在对应的属性前面加@Autowired。
    但@Autowired注解是按照byte类型来注入的。

  • 实现:
    类文件:

package com.bean;
public class Person{
	String name;//姓名
	@Autowired
	private Car car;
	public String getName(){return name;}
	public void setName(String name){this.name = name;}
	public void getCarInfo(){输出;}
}

Spring-config.xml文件

<context:component-scan base-package = "com.bean"></context:component-scan>
<!-- 表示在com.bean包下扫描Person类上面的@Autowired注解,将Spring容器中的car汽车对象注入到Person类的car属性中-->

个人理解Spring-config.xml文件应该是:不确定对不对?

<bean id="car" class="com.bean.Car"> <!-- Car类的全限定类名 -->
	<constructor-arg name="brand" value = "奇瑞"></constructor-arg>
	<constructor-arg name="price" value = "10000"></constructor-arg>
</bean>
<bean id="person" class="com.bean.Person"> <!-- Person类的全限定类名 -->
	<property name="brand" value = "奇瑞"></property> <!-- 通过setter注入 -->
	<context:component-scan base-package = "com.bean"></context:component-scan>
	<!-- 表示在com.bean包下扫描Person类上面的@Autowired注解,将Spring容器中的car汽车对象注入到Person类的car属性中-->
</bean>

IOC容器的初始化?

添加链接描述

Bean

Bean实例化基本流程

Bean实例化 = 从xml文件中的bean标签→进入容器中→再通过getBean得到Bean的过程。

图解:
在这里插入图片描述
在这里插入图片描述

断点执行:
bean标签封装:BeanDefinitionMap
在这里插入图片描述
SingletonObjects:
在这里插入图片描述

Spring的后处理器

概念:
Bean的创建过程是标签封装成BeanDefinition对象,存储在BeanDefinitionMap中,然后Spring遍历Map集合通过反射创建Bean对象存储在SingletonObjects中。
Spring后处理器 表示可以介入到Bean对象创建过程中,包括动态注册BeanDefiniton、修改BeanDefinitionMap、以及动态修改Bean。

实现方式:
BeanFactoryPostProcessor:Bean工厂后处理器,在BeanDefinitonMap填充之后,Bean实例化之前 执行;
BeanPostProcessor:Bean后处理器,Bean实例化之后,填充到单例池SingletonObjects之前 执行。

在这里插入图片描述
Bean实例化流程
在这里插入图片描述

BeanFactoryPostProcessor Bean工厂后处理器

BeanFactoryPostProcessor使用
1 创建类实现BeanFactoryPostProcessor接口,并重写其中方法

package com.beanioc;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        System.out.println("BeanDedifintion创建之后,Bean实例化对象之前调用");
    }
}

2 将bean配置到容器中

<!--    BeanFactoryPostProcessor入门-->
    <bean class="com.beanioc.MyBeanFactoryPostProcessor"></bean>

作用1:修改BeanDefinition

package com.beanioc;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("BeanDedifintion创建之后,Bean实例化对象之前调用");
//        修改某个BeanDefinition
        BeanDefinition userService = beanFactory.getBeanDefinition("userService");
        userService.setBeanClassName("dao.impl.UserDaoImpl");
    }
}

前后结果为:
在这里插入图片描述
作用2:动态注册Bean 封装BeanDefinition

1 创建新的接口PersonDao 以及其实现类
// 接口
package dao;

public interface PersonDao {
}

//实现类
package dao.impl;

import dao.PersonDao;

public class personDaoImpl implements PersonDao {
}
2 在实现BeanFactoryPostProcessor的实现类中的postProcessorBeanFactory中书写
package com.beanioc;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("BeanDedifintion创建之后,Bean实例化对象之前调用");
//        修改某个BeanDefinition
        BeanDefinition userService = beanFactory.getBeanDefinition("userService");
        userService.setBeanClassName("dao.impl.UserDaoImpl");

        // 动态注册Bean BeanDefinition 扔到BeanDefinitionMap中
//        注册BeanDefinition 它是一个接口 需要通过它的实现来得到 setBeanClassName方法
        BeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setBeanClassName("dao.impl.personDaoImpl");
//        beanFactory本质是:DefaultListableBeanFactory
//        beanFactory强制转换为DefaultListableFactory
        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;
        defaultListableBeanFactory.registerBeanDefinition("PersonDao", beanDefinition);
    }
}

BeanDefinitionRegistryPostProcessor
它是BeanFactoryPostProcessor的子接口;内部的方法postProcessBeanDefinitionRegistry参数是BeanDefinitionRegistry,该对象有 registerBeanDefinition方法实现注册Bean,得到BeanDefinition。

1= BeanDefinitionRegistryPostProcessor接口的实现类
package com.beanioc;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;

public class MyBeanDefinitionPostProcessor implements BeanDefinitionRegistryPostProcessor {


    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        System.out.println("MyBeanDefinitionRegistryPostProcessor的postProcessorBeanDefinitionRegistry方法");
//        向容器当中注册BeanDefinition
        BeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setBeanClassName("dao.impl.personDaoImpl");
        beanDefinitionRegistry.registerBeanDefinition("personDao", beanDefinition);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        System.out.println("MyBeanDefinitionRegistryPostProcessor的postProcessBeanFactory方法");
    }
}
2 = 配置文件
<!--    BeanDefinitionRegistryPostProcessor-->
    <bean class="com.beanioc.MyBeanDefinitionPostProcessor"></bean>

在这里插入图片描述

加入BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor的Bean创建过程
在这里插入图片描述

案例:Spring的BeanFactoryPostProcessor扩展点完成自定义注解扫描

要求

在这里插入图片描述
具体实现过程:

1 创建类配置自定义标签@Component
2 配置自定义标签Component注解文件
3 添加扫描文件BaseClassScanUtils
4 设置实现BeanDefinitionRegistryPostProcessor接口的实现类

1 otherBean类

ackage com.otherBean;
import com.anno.MyComponent;
@MyComponent("otherBean")
// 字符串中的内容是字符串bean的名字
public class otherBean {
}

2 Component注解文件

package com.anno;

// 配置MyComponent注解文件
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//确定在哪个上面使用,ElementType.TYPE在类上使用
@Target(ElementType.TYPE)
//注解的存活范围 RetentionPolicy.RUNTIME 运行期间可见
@Retention(RetentionPolicy.RUNTIME)
public @interface MyComponent {
    String value();
}

3 扫描文件BaseClassScanUtils

package com.utils;

import com.anno.MyComponent;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;

import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class BaseClassScanUtils {

    //设置资源规则
    private static final String RESOURCE_PATTERN = "/**/*.class";

    public static Map<String, Class> scanMyComponentAnnotation(String basePackage) {

        //创建容器存储使用了指定注解的Bean字节码对象
        Map<String, Class> annotationClassMap = new HashMap<String, Class>();

        //spring工具类,可以获取指定路径下的全部类
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        try {
            String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                    ClassUtils.convertClassNameToResourcePath(basePackage) + RESOURCE_PATTERN;
            Resource[] resources = resourcePatternResolver.getResources(pattern);
            //MetadataReader 的工厂类
            MetadataReaderFactory refractory = new CachingMetadataReaderFactory(resourcePatternResolver);
            for (Resource resource : resources) {
                //用于读取类信息
                MetadataReader reader = refractory.getMetadataReader(resource);
                //扫描到的class
                String classname = reader.getClassMetadata().getClassName();
                Class<?> clazz = Class.forName(classname);
                //判断是否属于指定的注解类型
                if(clazz.isAnnotationPresent(MyComponent.class)){
                    //获得注解对象
                    MyComponent annotation = clazz.getAnnotation(MyComponent.class);
                    //获得属value属性值
                    String beanName = annotation.value();
                    //判断是否为""
                    if(beanName!=null&&!beanName.equals("")){
                        //存储到Map中去
                        annotationClassMap.put(beanName,clazz);
                        continue;
                    }

                    //如果没有为"",那就把当前类的类名作为beanName
                    annotationClassMap.put(clazz.getSimpleName(),clazz);

                }
            }
        } catch (Exception exception) {
        }

        return annotationClassMap;
    }

    public static void main(String[] args) {
        Map<String, Class> stringClassMap = scanMyComponentAnnotation("com.itheima");
        System.out.println(stringClassMap);
    }
}

4 实现BeanDefinitionRegistryPostProcessor接口实现类

package com.beanioc;
import com.utils.BaseClassScanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;

import java.util.Map;

public class MyComponentBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        // 通过扫描工具去扫描指定包以及其子包下的所有子类,收集使用@MyComponent的注解的类
//         其中string表示@注解括号中的名字,Class是获取的类的全限定名
        Map<String, Class> myComponentAnnotation = BaseClassScanUtils.scanMyComponentAnnotation("com.otherBean");
        // 遍历Map,组装BeanDefinition进行注册 Lamda表达式
        myComponentAnnotation.forEach((beanName,clazz)->{
            // 获取beanClassName 也就是类的全限定类名
            String beanClassName = clazz.getName();
            // 创建BeanDefinition
            BeanDefinition beanDefinition = new RootBeanDefinition();
            // 注册BeanDefinition
            beanDefinition.setBeanClassName(beanClassName);
            // 写入BeanDefinitionMap中
            beanDefinitionRegistry.registerBeanDefinition(beanName, beanDefinition);

        });
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }
}
BeanPostProcessor Bean后处理器

概念:
BeanPostProcessor是对Bean对象的操作,不是对Bean定义的操作。
是Bean被实例化之后,存储SingletonObjects单例池之后前,中间会经过Bean的初始化过程。例如属性的填充、初始方法init的执行等。

步骤:
创建类,实现BeanPostProcessor接口

package com.beanioc;

import dao.impl.UserDaoImpl;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBeanPostProcessor implements BeanPostProcessor {
    // BeanPostProcessor接口中的方法是静态方法 不需要必须重写;
//     通过BeanPostProcessor接口实现类 来实现bean对象的设置
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    // 判断是否是UserDaoImpl对象,是的话 设置其属性Name的值
        if(bean instanceof UserDaoImpl){
            UserDaoImpl userDao = (UserDaoImpl) bean;
            userDao.setName("123");
        }
        System.out.println(beanName + ":postProcessBeforeInitialization");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName+":postProcessBeforeInitialization");
        return bean;
    }
}

xml文件配置

<!--    Bean后处理器 BeanPostProcessor配置-->
    <bean class="com.beanioc.MyBeanPostProcessor"></bean>

执行顺序
Bean实例化、初始化init、继承InitializingBean接口的afterPropertiesSet方法、以及postProcessBeforeInitialization和postProcessAfterInitialization的执行顺序

结果:在这里插入图片描述
多个文件之间关系:
在这里插入图片描述

代码:
Dao接口的实现类

package dao.impl;

import dao.UserDao;
import org.springframework.beans.factory.InitializingBean;

public class UserDaoImpl implements UserDao, InitializingBean {
//    设置一个参数
    private String name;

//     如果需要值,则一般情况会需要在base中通过property配置;但也可以通过BeanPostProcessor实现类 来实现属性的输入‘
    public void setName(String name) {
        this.name = name;
    }

    public UserDaoImpl() {
        System.out.println("Dao对象的创建");
    }

//    init初始化方法
    public void init(){
        System.out.println("init方法执行");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet方法执行");
    }
}

xml配置文件:

<!--    配置UserDao对象-->
    <bean id ="userDao" class="dao.impl.UserDaoImpl" init-method="init"></bean>

BeanPostProcessor实现类:

package com.beanioc;

import dao.impl.UserDaoImpl;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBeanPostProcessor implements BeanPostProcessor {
    // BeanPostProcessor接口中的方法是静态方法 不需要必须重写;
//     通过BeanPostProcessor接口实现类 来实现bean对象的设置
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if(bean instanceof UserDaoImpl){
            UserDaoImpl userDao = (UserDaoImpl) bean;
            userDao.setName("123");
        }
        System.out.println(beanName + ":postProcessBeforeInitialization");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName+":postProcessBeforeInitialization");
        return bean;
    }
}
Bean方法进行执行时间日志增强

要求:
Bean的方法执行之前和之后控制台打印当前时间。

文件之间关系:
在这里插入图片描述
代码
UserDao接口及实现类

接口:
package dao;

public interface UserDao {
    void show();
}

实现类:
import dao.UserDao;
import org.springframework.beans.factory.InitializingBean;

public class UserDaoImpl implements UserDao {
    @Override
    public void show() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("show........");
    }
  }
  

继承BeanPostProcessor接口的实现类

package com.beanioc;

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

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Date;

// 创建类实现BeanPostProcessor接口
public class DateBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//        使用动态代理模式对目标bean进行增强,返回proxy对象,进而存储到单例池中SingletonObjects中
        Object beanproxy = Proxy.newProxyInstance(
                bean.getClass().getClassLoader(),
                bean.getClass().getInterfaces(),
//                这里是InvocationHandler 对象
                (proxy, method, args) -> {
//                  1   输出开始时间
                    System.out.println("方法" + method.getName() + "-开始时间" + new Date());
//                    2 执行方法
                    Object result = method.invoke(bean, args);
//                    3 结束时间
                    System.out.println("方法" + method.getName() + "-结束时间" + new Date());
                    return result;
                }
        );
        return beanproxy;
    }
}

配置文件:

<!--    配置UserDao对象-->
    <bean id ="userDao" class="dao.impl.UserDaoImpl" ></bean>
<!--    Bean后处理器 BeanPostProcessor Date日期 前后处理案例-->
    <bean class="com.beanioc.DateBeanPostProcessor"></bean>

Bean的生命周期

概念
Bean通过反射实例化对象之后,形成一个完整的Bean对象,最终存储在单例池中的过程。
在这里插入图片描述

初始化阶段:Bean实例的属性填充

每一个Bean标签注入的属性都不会被存储在propertyValues中。
属性填充的层级索引

属性注入过程

普通属性:String、int或者存储基本数据类型,采用set方法的反射;
单向对象引用属性:通过容器调用getBean获取 再通过set方法反射设置;
双向对象引用属性:采用循环引用实现;

对于单向对象引用属性来说,关键点在于看容器bean、属性bean以及属性注入三者的时机。
先创建容器bean、在创建属性bean、再执行属性注入;
双向对象引用属性:采用三级缓存解决该内容
三级缓存存储的是:完整的Bean实例和半成品Bean实例。

在这里插入图片描述

过程图
在这里插入图片描述

传统java bean
传统的Java应用中,bean的生命周期很简单,使用Java关键字 new 进行Bean 的实例化,然后该Bean 就能够使用了。一旦bean不再被使用,则由Java自动进行垃圾回收。

循环引用:三级缓存

概念:是什么
在这里插入图片描述
循环引用-三级缓存对应存储内容理解
在这里插入图片描述
具体过程:

1 在一级缓存中找,看是否有Service对象,没有就实例化对象,进入2
2 将生成实例化对象Service的工厂存储到三级缓存中 进入3
3 完成Service对象的初始化过程,发现需要属性填充userDao 进入4
4 在三个缓存集合中找是否存在userDao对象 发现没有 进入5
5 实例化对象userDao,进入6
6 将生成实例化对象userDao的工厂存储到三级缓存中,进入7 
7 初始化对象Dao 属性填充userService,进入8
8 查看三个缓存集合中是否有userService对象,发现有,进入9
9 将三级缓存中生成userService对象的工厂直接赋值,再将其userService存储到二级缓存中

循环引用图解
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
三级缓存源码解释
解释
没看懂。

初始化阶段:Aware接口注入

概念
当我们在创建对象的时候 想要获取对象的属性,比如beanName、applicationContext、beanFactory、servletContext对象等,都需要实现对应的接口通过其内部方法实现对象的返回。
具体
在这里插入图片描述
注意
在实现ServletContextAware接口的时候,如果实现类代码爆红,则需要在pom.xml文件中进行配置。
javax.servlet-api和javax.servlet.jsp-api

    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>javax.servlet.jsp-api</artifactId>
      <version>2.3.3</version>
    </dependency>

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

Bean的理解?

  • 定义:
    是一个由Spring IOC容器实例化、组装和管理的对象;
    并且构成了应用程序的主干;

  • 理解:
    Bean是一个对象,可以有一个或多个;
    Bean由Spring IOC容器管理;
    Bean是应用程序的主干,也就是程序由Bean组成;

  • 规律:
    凡是子类及带有方法或属性的类都要加上注册Bean到Spring IOC的注解 = Bean注解中的注册Bean。
    @Bean 用在方法上,告诉Spring容器,可以从下面这个方法拿到一个Bean = Spring容器会自动完成对@Bean对象的实例化。

Bean的注解?

创建Bean
即把想要实例化的对象转化成一个Bean,放在IOC容器中,等到需要的时候,配合@Autowired 或 @Resource使用。
即@Component、@Service、@Controller、@Repository。

注解说明
@Component通用的注解,将当前类对象存入IOC容器中
@Controller对应 Spring MVC 控制层,主要用户接受用户请求并调用 Service 层返回数据给前端页面
@ Service对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层
@Repository对应持久层即 Dao 层,主要用于数据库相关操作

修改创建对象的注入数据的注释
@Autowired = 可以自动按照类型注入数据到bean中,按照byte类型注入;

IOC容器中有唯一的bean对象与要被注入的变量类型匹配,则正常执行;
IOC容器中没有对应的bean对象类型和要被注入的变量类型匹配,则会抛出异常;
IOC容器中多个bean对象和要被注入的变量类型匹配,则根据id匹配,也一样,则根据变量的名称匹配,还一样,抛出异常;

@Resource = 可以指定注入的bean id,单独使用,不需要和@Autowired一起使用,按照byName类型注入;
@Qualifier = 在@Autowired的基础之上,添加value属性值,指定注入bean id(需要和@Autowired一起)
@Value = 用于注入基本数据类型和String数据类型;

用于改变创建对象作用范围的注解
@Scope = 指定Bean的作用范围;

生命周期相关的注解
@PreDestroy = 销毁方法的注解
@PostConstruct = 初始化方法的注解

新注解
@Configuration = 用于指定当前类是一个 Spring 配置类,当创建容器时 会从该类上加载注解;
@ComponentScan = 用于指定 spring 在初始化容器时要扫描的包;
@Bean = 该注解只能写在方法上,表明使用此方法创建一个对象,并且放入 spring 容器;
@PropertySource = 用于加载 .properties 文件中的配置 ;
@Import = 为两个没有关系的配置类建立关系链接;

@Component 和 @Bean区别?

@Component 注解作用于类;@Bean注解作用于方法。
@Component通常是通过类路径扫描来自动侦测以及自动装配到 Spring 容器中;@Bean 注解表示在我们标有该注解的方法中定义产生这个 bean;
@Bean 注解比 @Component 注解的自定义性更强,而且很多地方我们只能通过 @Bean 注解来注册 bean

Bean的作用域(注解scope)?

概念
Bean的作用域可以通过scope属性来指定。

具体值
singleton:默认值。当IOC容器一创建就会创建Bean的实例,而且是单例的,每次得到同一个。
prototype:原型的。当IOC容器创建时不会立即实例化该Bean,每次调用getBean方法时再实例化该Bean,而且每次调用都会返回一个新的实例。
request:每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境。
session:同一个HTTP Session 共享一个Bean,不同的 HTTP Session 使用不同的Bean。该作用域仅适用于WebApplicationContext环境。

Spring中的单例Bean的线程安全问题?

概念
单例Bean 存在线程安全问题。
单例Bean的线程安全问题:
无状态Bean = 线程中的操作不会对Bean的成员执行查询以外的操作,故此单例Bean是线程安全的;
有状态Bean = 即有实例变量的对象,可以保存数据,是非线程安全的。

Spring默认的Bean是单例的,也没有进行封装处理。

现象
当多个线程操作同一个对象的时候,对这个对象的非静态成员变量的写操作会存在线程安全问题。

解决

  • 使用ThreadLocal,把变量变成线程私有的
  • synchronized、lock、CAS,保证互斥访问临界区
  • 把Bean从单例(singleton)设置成原型(protopyte、多例)

备注2022/8/18
Spring中Bean被定义为两种模式:prototype(多例)和singleton(单例)。
通过属性值 scope设置。
prototype(多例):对这个Bean的每次请求都会创建一个新的Bean实例,类似于new。多个变量指向不同的对象。
singleton(单例):只有一个共享的实例存在,所有对这个bean的请求都会返回这个唯一的实例。多个变量指向同一个对象。

ApplicationContext通常的实现有哪些?

概念:
ApplicationContext,是Spring中的核心接口和容器,允许容器通过应用程序上下文环境创建、获取、管理Bean。
在构建容器的时候,创建对象采用的策略是立即加载的方式,即只要一读取完配置文件就立即创建配置文件中配置的对象。

BeanFactory采用的是延迟加载的方式,什么时候根据id获取对象了,什么时候才真正地创建对象。

实现类说明
ClassPath XmlApplicationContext可以加载类路径下的配置文件,要求配置文件必须在类路径之下。
FileSystem XmlApplicationContext可以加载磁盘中任意路径下的配置文件,要求具有访问权限。
AnnotationConfig ApplicationContext用于读取注解创建容器。

AOP

解释下什么是AOP?

概念
AOP(Aspect-Oriented Programming)面向切面编程。功能的纵向抽取,实现方式主要是Proxy代理。
是指将那些与业务无关,却被多个业务模块所共同调用逻辑或责任(例如事务处理、日志管理、权限控制等),将其封装起来,便于减少系统的重复代码,降低模块间的耦合度,提升系统的可维护性。

出现原因
对OOP(Object-Oriented Programming)面向对象编程的补充和完善。

  • 解释:
    OOP 引入封装、继承和多态性等概念来建立一种对象层次结构,用来模拟公共行为的一个集合。
    OOP 允许定义从上到下的关系,但并不适合定义从左到右的关系。
    例如日志功能,日志代码往往水平地散布在所有对象层次中,而与它所散布到对象的核心功能毫无关系。
    这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在 OOP 设计中,它导致了大量代码的重复,而不利于各个模块的重用。

组成
使用“横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。
业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。
横切关注点的特点是:它们经常发生在核心关注点的多处,而各处都基本相似。比如:权限认证、日志、事务处理。

作用
面向切面编程(AOP)提供另外一种角度来思考程序结构,通过这种方式弥补了面向对象编程(OOP)的不足。
重点在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来,降低业务逻辑的耦合性,提高程序的可重用性和可维护性。

AOP的实现有哪几种方式?

两种方式:静态代理(AspectJ AOP)、动态代理(Spring AOP)(基于接口的JDK代理 、 基于继承的CGLIB动态代理)。
具体使用哪种方式生成由 AopProxyFactory 根据 AdvisedSupport 对象的配置来决定。默认的策略是如果目标类是接口,则使用 JDK 动态代理技术,否则使用 CGlib 来生成代理。

AspectJ AOP
使用静态代理方式,也称为编译时增强;
AOP框架会在编译阶段生成AOP代理类,并将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。

Spring AOP动态代理形式
AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。

JDK动态代理
通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口InvocationHandler。
JDK动态代理的核心是InvocationHandler接口和Proxy类。
Spring默认的动态代理方式就是JDK动态代理。
如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。

CGLIB(Code Generation Library)
概念:是一个代码生成的类库,可以在运行时动态的生成某个类的子类。
CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理。

怎么实现Spring AOP动态代理?

JDK动态代理

CGLIB代理

AOP的基本概念?

AOP出现原因:缓解OOP面向对象编程存在的不足。
于是将业务处理分成核心关注点和横切关注点;
核心关注点 = 业务的主要处理内容;
横切关注点 = 与业务处理无关,但是被多个业务模块所调用的逻辑或方法,将其封装起来,降低模块间的耦合度,提升了代码的复用性和提高系统的可维护性。

被代理类Target→连接点join point→切入点 point cut→通知Advice→切面Aspect→织入weaning→代理Proxy

  • 目标(Target):
    指代理的目标对象;
  • 连接点(Join point):
    指那些可以被切面插入的时机,在Spring中,指可以被动态代理拦截目标类的方法。
  • 切入点(Point cut):
    指要对哪些join point进行切入,即被拦截的点 = 实际切面的切入点;
  • 通知(Advice):
    指Point Cut做的事情,即对切入点增强的内容。
  • 切面(Aspect):
    切入点和通知的结合。point cut + advice
  • 植入(Weaving):
    指把增强代码应用到目标上,生成代理对象的过程。
  • 代理(Proxy):
    指生成的代理对象;

各基本概念之间的关系
在这里插入图片描述

常见的通知类型?

概念
Advice = 通知,增强或增加的功能,即切面的什么、干什么;

具体通知类型

  • 前置通知(Before advice)
    在某连接点(JoinPoint)之前执行的通知,但这个通知不能阻止连接点前的执行。
    ApplicationContext 中在 < aop:aspect > 里面使用 < aop:before > 元素进行声明;
<aop:before method = "目标方法名" pointcut-ref="切入点名称"> 
  • 环绕通知(Around advice)
    包围一个连接点joinpoint的通知,类似 Web 中 Servlet规范中的 Filter 的 doFilter 方法。
    可以在目标方法执行的前后完成自定义的行为,也可以选择不执行。
    ApplicationContext 中在 < aop:aspect > 里面使用 < aop:around > 元素进行声明。
<aop:pointcut id="名字" expression = "execution()">
<!-- 在环绕通知中必须手动的调用目标方法,否则目标方法不会执行。
这个手动调用是通过ProceedingJoinPoint来实现的,
可以在环绕通知中接收一个此类型的形参,spring容器会自动将该对象传入,这个参数必须处在环绕通知的第一个形参位置。-->
<aop:around method = "目标方法名" pointcut-ref="切入点名字">
  • 后置通知(After advice)
    当目标方法成功执行之后执行的通知(不论是正常返回还是异常退出)。
    ApplicationContext 中在 < aop:aspect > 里面使用 < aop:after-returning > 元素进行声明。
<aop:after-returning method = "目标方法名" pointcut-ref="切入点名字">
  • 抛出异常后通知(After throwing advice)
    在方法抛出异常退出时执行的通知。
    ApplicationContext 中在 < aop:aspect > 里面使用 < aop:after-throwing > 元素进行声明。
<aop:after-throwing method = "目标方法名" pointcut-ref="切入点名字">
配置传入的JoinPoint获取目标对象、目标方法相关信息 处于参数列表的第一位,其余参数:throwing 异常通知接收到目标方法抛出的异常对象
  • 返回后通知(After return advice )
    在某连接点正常完成后执行的通知,不包括抛出异常的情况。
    与后置通知不同,后置通知在方法正常返回之后执行的通知;如果没有正常返回(抛出异常),则后置通知不执行,执行异常通知after-throwing;返回后通知,无论目标方法调用执行后是否正常执行完成都会完成该通知
    ApplicationContext 中在 < aop:aspect > 里面使用 < after-returning > 元素进行声明。
<aop:after method = "目标方法名" pointcut-ref="切入点名字">

事务处理

你对Spring中的事务的理解?

概念
事务是逻辑上的一组操作,要么都执行,要么都不执行;

目的
事务会把数据库从一种一致状态转换为另一种一致状态;

本质
Spring的事务是数据库对事务的支持,事务能否生效 取决于 数据库引擎是否支持事务;
例如:数据库MySQL默认支持事务的是innodb引擎,如果把数据库引擎变成myisam,则程序也不能再支持事务;

特性
原子性(Atomicity)(事务是一组逻辑上的操作,要么全部执行,要么全部不执行);
一致性(consistency)(事务执行前后,数据库的完整性没有被破坏);
隔离性(isolation)(并发访问,数据库允许多个并发事务同时对其数据进行读写和修改,可以防止多个事务并发执行时交叉执行导致数据不一致的情况);
持久性(durability)(一旦事务提交,对数据的修改将是永久的)。

Spring的事务管理?

  • 概念:
    按照给定的事务规则来执行提交或者回滚操作;
    Spring并不直接管理事务,而是提供了多种事务管理器;通过该接口,为各个平台JDBC、hibernate、JPA提供了对应的事务管理器;

  • 接口:
    PlatformTransactionManager:平台事务管理器,Spring事务策略的核心;
    TransactionDefinition:事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则);
    TransactionStatus:事务运行状态。

  • 三者之间的关系:
    PlatformTransactionManager = 事务上层的管理者;
    其余两个接口是事务的描述。
    PlatformTransactionManager 会根据 TransactionDefinition 的定义(事务超时时间、隔离级别、传播行为等)来进行事务管理;TransactionStatus提供了一些方法来获取事务相应的状态(是否新事务、是否可以回滚等)。

Spring中事务的隔离级别?

TransactionDefinition接口中定义了五个表示隔离级别的常量:

隔离级别说明
TransactionDefinition.lSOLATION_DEFAULT使用后端数据库默认的隔离级别,
MySQL默认采用的REPEATABLE_READ隔离级别(可重复读),
Oracle 默认采用的READ_COMMITTED隔离级别(读已提交)
TransactionDefinition.ISOLATION_READ_UNCOMMITTED最低的隔离级别
允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读;
TransactionDefinition.ISOLATION_READ_COMMITTED允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生;
TransactionDefinition.lSOLATION_REPEATABLE_READ对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生;
TransactionDefinition.ISOLATION_SERIALIZABLE最高的隔离级别,完全服从ACID的隔离级别。
所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,该级别可以防止脏读、不可重复读以及幻读。
但是这将严重影响程序的性能,通常情况下也不会用到该级别。

Spring中的事务传播行为?

作用
为了解决业务层方法之间互相调用的事务问题。
当一个事务方法(当前方法B)被另一个事务方法(调用者A)调用时,该方法B对另一个事务方法A的态度

在TransactionDefinition定义的传播行为常量

支持当前事务说明
TransactionDefinition.Propagation_required当前事务方法B 必须在事务中运行,事务可以是调用者A的 或者 自己新开启的事务
调用者是否有事务调用者A有事务——当前方法B加入调用者A事务中
调用者A没有事务——当前方法B自己新开启一个事务运行
调用者调用完成,抛出业务异常,如何回滚调用者A抛出异常——方法B和调用者A一起回滚
调用者A抛出异常且已执行的SQL不回滚——当前方法B不回滚
当前方法发生异常,调用者捕获当前方法的业务异常,如何回滚调用者A提交事务抛出异常——调用者A和当前方法B在同一事务中,一起回滚 (不允许存在)
调用者以非事务形式运行——当前方法B回滚
当前方法出现异常,调用者不捕获当前方法异常,怎么回滚调用者抛出当前方法异常——当前方法和调用者在同一个事务中一起回滚
调用者非事务形式运行,抛出当前方法异常,已执行的SQL不回滚——当前方法的事务回滚
TransactionDefinition.Propagation_supports当前方法B 不必在事务中运行
调用者是否有事务调用者A有事务——当前方法B加入调用者A事务中
调用者A没有事务——当前方法B以非事务的形式运行
调用者调用完成,抛出业务异常,如何回滚调用者A抛出异常——当前方法B和调用者A一起回滚
调用者A抛出异常且已执行的SQL不回滚——当前方法B已执行的SQL不回滚
当前方法发生异常,调用者捕获当前方法的业务异常,如何回滚调用者提交事务抛出异常——调用者和方法B一起回滚
调用者A以非事务的形式运行——当前方法已执行的SQL不回滚
当前方法出现异常,调用者不捕获当前方法异常,怎么回滚调用者抛出当前方法异常——调用者和当前方法一起回滚
调用者抛出当前方法异常且已执行的SQL不回滚——当前方法已执行的SQL不回滚
TransactionDefinition.Propagation_mandatory当前事务方法B 必须在调用者事务中运行
调用者是否有是否调用者A有事务——将事务方法B加入调用者A事务中
调用者A没有事务——当前方法B抛出异常
调用者调用完成,抛出业务异常,如何回滚调用者抛出异常——当前方法B和调用者A一起回滚
调用者调用当前方法抛出异常(不允许该场景发生)
当前方法发生异常,调用者捕获当前方法的业务异常,如何回滚调用者提交事务发生异常——调用者和当前方法B一起回滚(不允许存在)
调用者调用当前方法时抛出异常——调用者已运行的SQL不回滚
当前方法出现异常,调用者不捕获当前方法异常,怎么回滚调用者抛出当前方法异常——调用者和当前方法一起回滚
调用者调用方法抛出异常且已执行的SQL不回滚(不允许该场景产生)
非事务形式非事务形式就是设置了自动提交,一个方法中有多个操作,每个操作都会在不同的事务中完成,不会保证他们的原子性。
不支持当前事务说明
TransactionDefinition.Propagation_required_new当前方法B必须在新事务中运行
调用者否是有事务调用者A有事务需要挂起——当前方法B自己新开一个事务运行
调用者A没有事务——当前方法B自己开启一个新事务运行
调用者调用完成,抛出业务异常,如何回滚调用者A抛出异常且回滚——当前方法B不回滚
调用者A抛出异常且已执行的SQL不回滚——当前B不回滚
当前方法发生异常,调用者捕获当前方法的业务异常,如何回滚调用者A以事务的形式正常执行——当前方法B抛出异常且回滚
调用者A非事务形式运行——当前方法B抛出异常且回滚
当前方法出现异常,调用者不捕获当前方法异常,怎么回滚调用者抛出异常且回滚——当前方法抛出异常且回滚
调用者抛出异常且已执行的SQL不回滚——当前方法抛出异常且回滚
TransactionDefinition.Propagation_not_supported当前方法B 不支持在事务中运行
调用者是否有事务调用者A有事务方法执行期间挂起——当前方法B以非事务的形式运行
调用者A没有事务——当前方法B以非事务形式运行
调用者调用完成,抛出业务异常,如何回滚调用者A抛出异常且回滚——当前方法B已执行的SQL不回滚
调用者A抛出异常且已执行SQL不回滚——当前方法B已执行的SQL不回滚
当前方法发生异常,调用者捕获当前方法的业务异常,如何回滚调用者A正常执行——当前方法B抛出异常且已执行的SQL不回滚
调用者A正常执行——当前方法B抛出异常且已执行的SQL不回滚
当前方法出现异常,调用者不捕获当前方法异常,怎么回滚调用者抛出异常且回滚——当前方法抛出异常且已执行的SQL不回滚
调用者抛出异常且已执行的SQL不回滚
当前方法抛出异常且已执行的SQL不回滚
TransactionDefinition.Propagation_never调用者必须以非事务的形式运行
调用者是否有事务调用者A有事务——抛出异常
调用者A没有事务——当前方法B以非事务的形式运行
调用者调用完成,抛出业务异常,如何回滚调用者调用当前方法抛出异常(不允许存在该场景)
调用者A抛出异常且已执行的SQL不回滚——当前方法B已执行的SQL不回滚
当前方法发生异常,调用者捕获当前方法的业务异常,如何回滚调用者A抛出异常 该场景不存在
调用者A以非事务的形式正常执行——当前方法B已执行的SQL不回滚
当前方法出现异常,调用者不捕获当前方法异常,怎么回滚调用者调用当前方法抛出异常(不允许存在该场景)
调用者抛出异常且已执行的SQL不回滚——当前方法抛出异常且已执行的SQL不回滚
其他情况说明
TransactionDefinition.Propagation_nested当前方法B 必须在自己的事务中运行
调用者是否有事务调用者A有事务——当前方法B以“嵌套事务”的形式加入到调用者A的事务中
调用者A没有事务——当前方法B自己新开启一个事务运行
调用者调用完成,抛出业务异常,如何回滚调用者A抛出异常——调用者和当前方法一起回滚
调用者抛出异常且已执行的SQL语句不回滚——当前方法B不回滚
当前方法发生异常,调用者捕获当前方法的业务异常,如何回滚调用者A正常运行——当前方法B以嵌套事务回滚
调用者以非事务形式运行——当前方法的事务回滚
当前方法出现异常,调用者不捕获当前方法异常,怎么回滚调用者抛出异常且回滚——当前方法嵌套事务回滚
调用者抛出当前方法异常且已执行的SQL不回滚

Spring框架中用到了哪些设计模式?

设计模式说明
工厂设计模式Spring使用工厂模式通过BeanFactory、ApplicationContext 创建bean对象
代理设计模式Spring AOP功能的实现
单例设计模式:Spring中的Bean默认都是单例
模板方法模式Spring中jdbcTemplate、hibernateTemplate等以Template结尾的对数据库操作的类
观察者模式Spring 事件驱动模型就是观察者模式很经典的一个应用
适配器模式Spring AOP的增强或通知(Advice)的AdvisorAdapter、SpringMVC 中也是用到了HandlerAdapter适配Controller
装饰器模式Spring中含有Wrapper和含有Decorator的类
策略模式资源访问Resource接口

备注2022/8/19
待看
观察者模式:
Spring 中的 Event 和 Listener。前者spring 事件:ApplicationEvent,该抽象类继承了EventObject 类,JDK 建议所有的事件都应该继承自 EventObject。后者spring 事件监听器:ApplicationListener,该接口继承了 EventListener 接口,JDK 建议所有的事件监听器都应该继承EventListener 。

Spring如何解决循环依赖问题?

  • 概念:
    多个Bean之间相互依赖,形成了一个闭环。
    默认的单例模式Bean中,属性相互引用的场景。
    循环依赖是Spring容器注入时候出现的问题。

  • 解决方式:
    Spring中单例Bean的三级缓存;
    第一级缓存:Map<String, Obiect>(也叫单例池)singletonObjects:存放已经经历了完整生命周期的Bean对象;
    第二级缓存: Map<String, Obiect> earlySingletonObjects,存放早期暴露出来的Bean对象,Bean的生命周期未结束(属性还未填充完整);
    第三级缓存: Map<String, ObiectFactory<?>> singletonFactories,存放可以生成Bean的工厂;

参考

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值