【笔记】Spring

Spring

1.简介

  1. 开源框架
  2. 简化企业级开发
  3. IOC+AOP容器

在这里插入图片描述

优点:

  1. 非侵入式
  2. 可以整合其他框架
  3. 可以管理对象、依赖注入
  4. 减少依赖,解耦

2.HelloWorld

  1. 目标: 使用 Spring 创建对象, 为属性赋值

  2. 创建 Student 类

  3. 创建 Spring 配置文件

    <!-- 使用bean元素定义一个由IOC容器创建的对象 -->
    <!-- class属性指定用于创建bean的全类名 -->
    <!-- id属性指定用于引用bean实例的标识 -->
    <bean id="student" class="com.atguigu.helloworld.bean.Student">
     <!-- 使用property子元素为bean的属性赋值 -->
     <property name="studentId" value="1001"/>
     <property name="stuName" value="Tom2015"/>
     <property name="age" value="20"/>
    </bean>
    
  4. 测试: 通过 Spring 的 IOC 容器创建 Student 类实例

    //1.创建IOC容器对象
    ApplicationContext iocContainer = new ClassPathXmlApplicationContext("helloworld.xml");
    //2.根据id值获取bean实例对象
    Student student = (Student) iocContainer.getBean("student");
    

3.IOC

1.IOC(Inversion of control)

控制反转,不用自己new对象,由框架去创建,核心是反射机制。

2.DI(Dependency Injection)

依赖注入,不需要开发人员手动为属性赋值,Spring通过配置文件或注解方式为属性方式

spring通过DI实现了IOC

3.IOC 容器在 Spring 中的实现

1.实现
  1. 在通过 IOC 容器读取 Bean 的实例之前, 需要先将 IOC 容器本身实例化。
  2. Spring 提供了 IOC 容器的两种实现方式
    1. BeanFactory: IOC 容器的基本实现, 是 Spring 内部的基础设施, 是面向Spring 本身的, 不是提供给开发人员使用的。
    2. ApplicationContext: BeanFactory 的子接口, 提供了更多高级特性。 面向Spring 的 使 用 者 , 几乎所有场合都使用 ApplicationContext 而 不 是 底 层 的BeanFactory
2.FactoryBean

Spring 中有两种类型的 bean, 一种是普通 bean, 另一种是工厂 bean, 即 FactoryBean。
工厂 bean 跟普通 bean 不同, 其返回的对象不是指定类的一个实例, 其返回的是该工厂 bean 的 getObject 方法所返回的对象。
工厂 bean 必须实现 org.springframework.beans.factory.FactoryBean 接口。

<bean id="product" class="com.atguigu.spring.bean.ProductFactory">
	<property name="productName" value="Mp3" />
</bean>
3.ApplicationContext 的主要实现类
  1. ClassPathXmlApplicationContext: 对应类路径下的 XML 格式的配置文件
  2. FileSystemXmlApplicationContext: 对应文件系统中的 XML 格式的配置文件
  3. 在初始化时就创建单例的 bean, 也可以通过配置的方式指定创建的 Bean 是多实例的。

4.bean

1.给 bean 的属性赋值 (实现DI的三种方式 )

  1. 构造器注入(Spring 自动匹配合适的构造器)

    <bean id="book" class="com.atguigu.spring.bean.Book" >
        <constructor-arg value= "10010"/>
        <constructor-arg value= "Book01"/>
    </bean >
    
  2. setter方法注入

在这里插入图片描述

  1. 接口注入

2.bean 属性可以使用的值

<bean class="com.atguigu.spring.bean.Book" id="bookNull" >
	<!--1.字面量:基本类型、字符串 -->
	<property name= "bookId" value ="2000"/>
    <property name= "categoryList">
        <!-- 2.List 集合 -->
        <list>
            <value> 历史</value >
            <value> 军事</value >
        </list>
    </property>
    <!-- 3.map -->
    <property name="bookMap">
		<map>
            <entry>
                <key>
                    <value>bookKey01</value>
                </key>
                <ref bean="book01"/>
            </entry>
        </map>
	</property>
</bean >

3.bean的作用域

1. singleton:单例、是所有bean的默认作用域,在Spring IOC容器中仅存一个bean实例
2. prototype:多例,每次调用getBean()都会返回一个bean实例
3. request:一次请求后会被回收
4. session:session过期后,bean会随之失效
5. global- session

4.bean的生命周期

1. 通过构造器或工厂方法创建bean实例
2. 为bean属性设置值,和对其他bean的引用
3. 调用bean的init()
4. 使用bean
5. 容器关闭时,销毁bean,调用destroy()

拓展方法管理生命周期@PostConstruct、@PreDestroy

5.自动装配

1.定义
  1. 手动装配: 以 value 或 ref 的方式明确指定属性值都是手动装配。
  2. 自动装配: 根据指定的装配规则, 不需要明确指定, Spring 自动将匹配的属性值注入bean 中。
2.装配模式
  1. 根据类型自动装配: 将类型匹配的 bean 作为属性注入到另一个 bean 中。 若 IOC 容器中有多个与目标 bean 类型一致的 bean, Spring 将无法判定哪个 bean 最合适该属性, 所以不能执行自动装配
  2. 根据名称自动装配: 必须将目标 bean 的名称和属性名设置的完全相同

6.spring的三种配置bean 方式

1.xml

applicationContext.xml,配置bean,加载配置文件,获取spring容器,获取对象

<beans> 
	<bean id="myService" class="com.somnus.services.MyServiceImpl"/> 
</beans>
2.基于 Java 配置的方式
@Configuration 
public class AppConfig{ 
	@Bean 
	public MyService myService() { 
		return new MyServiceImpl(); 
	} 
}
public static void main(String[] args) { 
	ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); 
	MyService myService = ctx.getBean(MyService.class); 
	myService.doStuff();
 }
3.注解
1.使用注解标识组件
//功能等效,语义不同,便于开发人员理解区分
@Component
@Controller
@Service
@Repository
2.扫描组件

只是配置了组件扫描,并不会创建类的对象,需要给类加注解

<context:component-scan base-package="com.atguigu.component"/>
3.组件装配
// 根据类型自动装配
@Autowired
// 根据名称自动装配
@Qualifier(" ")

// java自带的`@Resource`=@Autowired+@Qualifier(" ")
@Resource

5.SpEL (Spring Expression Language)

1.使用#{…}作为定界符

所有在大框号中的字符都将被认为是 SpEL 表达式。

<bean id="emp04" class="com.atguigu.parent.bean.Employee">
    <!--1.字面量:整数、小数、字符串、Boolean-->
    <property name="empId" value="1003"/>
    <property name="empName" value="Kate"/>
    <!--2.引用其他 bean-->
    <property name="detp" value="#{dept}"/>
    <!--3.引用其他 bean 的属性值作为自己某个属性的值-->
    <property name="deptName" value="#{dept.deptName}"/>
</bean>

<!-- 创建一个对象, 在SpEL表达式中调用这个对象的方法 -->
<bean id="salaryGenerator" class="com.atguigu.spel.bean.SalaryGenerator"/>
<bean id="employee" class="com.atguigu.spel.bean.Employee">
    <!-- 通过对象方法的返回值为属性赋值 -->
    <property name="salayOfYear" value="#{salaryGenerator.getSalaryOfYear(5000)}"/>
</bean>
2运算符
  1. 算术运算符: +、 -、 *、 /、 %、 ^
  2. 字符串连接: +
  3. 比较运算符: <、 >、 ==、 <=、 >=、 lt、 gt、 eq、 le、 ge
  4. 逻辑运算符: and, or, not, |
  5. 三目运算符: 判断条件?判断结果为 true 时的取值:判断结果为 false 时的取值
  6. 正则表达式: matches

6.AOP

Aspect-Oriented Programming,面向切面编程,是oop的补充,通过预编译方式和运行期动态代理实现

业务隔离,解耦,便于开发维护。
使用了设计模式中的代理模式

1.pom

    <dependency>
         <groupId>org.aspectj</groupId>
         <artifactId>aspectjweaver</artifactId>
         <version>1.9.5</version>
     </dependency>

2.说明

切面:@Aspect
切点:@Pointcut
通知注解:

// 连接点:JointPoint
@Before
@After
//方法抛出异常后执行
@AfterThrowing
// 方法返回结果后执行,不管是否异常
@AfterReturning
// 环绕方法执行,连接点:ProceedingJointPoint、是JointPoint的子接口
@Around

Spring aop就是一个同心圆,要执行的方法为圆心,最外层的order最小。从最外层按照AOP1、AOP2的顺序依次执行doAround方法,doBefore方法。然后执行method方法,最后按照AOP2、AOP1的顺序依次执行doAfter、doAfterReturn方法。也就是说对多个AOP来说,先before的,一定后after。

如果我们要在同一个方法事务提交后执行自己的AOP,那么把事务的AOP order设置为2,自己的AOP order设置为1,然后在doAfterReturn里边处理自己的业务逻辑。
在这里插入图片描述

3.例子

package com.dyn.basepro.frame.aspect;

import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 *@description: 请求log打印
 *@author: dyn
 *@create: 2020-04-08 21:05
 */
@Aspect
@Configuration
// 切面优先级:值越小,优先级越高
@Order(0)
@Slf4j
public class RequestInfoLogAspect {

    @Pointcut("execution(* com.dyn.*.*.controller.*Controller.*(..))")
    public void excudeService() {}

    @Around("excudeService()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest request = sra.getRequest();

        String method = request.getMethod();
        String uri = request.getRequestURI();
        String paramString = null;
        try {
            paramString = JSONUtil.toJsonPrettyStr(pjp.getArgs());
        } catch (Exception e) {
        }
        String jpclass = pjp.getTarget().getClass().toString();
        String jpmethod = pjp.getSignature().getName();
        log.info("Controller接收请求,URI:{},method:{},params:{},处理类为:{},处理方法为:{}", uri, method, paramString, jpclass, jpmethod);
        Object result = pjp.proceed();
        log.info("Controller接收结束,Controller:{}方法{}的返回值是:{}", jpclass, jpmethod, JSONUtil.toJsonPrettyStr(result));
        return result;
    }

}

7.事务

spring源码阅读–@Transactional实现原理

1.Spring事务管理机制

Spring事务管理高层抽象主要包括3个接口,Spring的事务主要是由他们共同完成的:

  • PlatformTransactionManager:事务管理器—主要用于平台相关事务的管理
  • TransactionDefinition: 事务定义信息(隔离、传播、超时、只读)—通过配置如何进行事务管理。
  • TransactionStatus:事务具体运行状态—事务管理过程中,每个时间点事务的状态信息。
1.PlatformTransactionManager:事务管理器

该接口提供三个方法:

  • commit:提交事务
  • rollback:回滚事务
  • getTransaction:获取事务状态

DataSourceTransactionManager是事务管理器的主要实现,针对JdbcTemplate、MyBatis 事务控制 ,使用Connection(连接)进行事务控制 :

  • 开启事务 connection.setAutoCommit(false);
  • 提交事务 connection.commit();
  • 回滚事务 connection.rollback();
2.TransactionDefinition:事务定义信息
1.事务隔离ACID特性
  • 原子性(Atomicity):原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
  • 一致性(Consistency):要么都成功,要么都失败。
  • 隔离性(Isolation):并发执行的各个事务之间不能互相干扰。
  • 持久性(Durability):事务一旦提交,它对数据库中的数据的改变就应该是永久性的。
2.数据库事务并发问题

假设现在有两个事务: Transaction01 和 Transaction02 并发执行。

1.脏读

一个事务读取了另一个事务改写但还未提交的数据,如果这些数据被回滚,则读到的数据是无效的。

①Transaction01 将某条记录的 AGE 值从 20 修改为 30。
②Transaction02 读取了 Transaction01 更新后的值: 30。
③Transaction01 回滚, AGE 值恢复到了 20。
④Transaction02 读取到的 30 就是一个无效的值。

2.不可重复读

在同一事务中,多次读取同一数据返回的结果有所不同。换句话说就是,后续读取可以读到另一事务已提交的更新数据。相反,“可重复读”在同一事务中多次读取数据时,能够保证所读数据一样,也就是,后续读取不能读到另一事务已提交的更新数据。

①Transaction01 读取了 AGE 值为 20。
②Transaction02 将 AGE 值修改为 30。
③Transaction01 再次读取 AGE 值为 30, 和第一次读取不一致。

3.幻读

一个事务读取了几行记录后,另一个事务插入一些记录,幻读就发生了。再后来的查询中,第一个事务就会发现有些原来没有的记录。

①Transaction01 读取了 STUDENT 表中的一部分数据。
②Transaction02 向 STUDENT 表中插入了新的行。
③Transaction01 读取了 STUDENT 表时, 多出了一些行。

3.事务隔离级别

default:使用后端数据库默认的隔离级别(spring中的的选择项)

事务隔离级别描述脏读不可重复读幻读MySQLOracle
read_uncommitted
读未提交
允许你读取还未提交的改变了的数据×
read_committed
读已提交
允许在并发事务已经提交后读取×√(默认)
repeatable_read
可重复读
对相同字段的多次读取是一致的,除非数据被事务本身改变××√(默认)×
serializable
串行
完全服从ACID的隔离级别,确保不发生脏、幻、不可重复读。
这在所有的隔离级别中是最慢的
它是典型的通过完全锁定在事务中涉及的数据表来完成的。
×××
Mysql可重复读实现原理分析

InnoDB 里面每个事务有一个唯一的事务 ID,叫作 transaction id。它是在事务开始的时候向 InnoDB 的事务系统申请的,是按申请顺序严格递增的。

一个事务只需要在启动的时候,找到所有已经提交的事务 ID 的最大值,记为 up_limit_id;

而每行数据也都是有多个版本的。每次事务更新数据的时候,都会生成一个新的数据版本,并且把 transaction id 赋值给这个数据版本的事务 ID,记为 row trx_id。
img

假设事务A id为100,B为101,C为102。现在已经提交了的事务id 为99,最大数据版本为90,那么对于事务A我只认不大于up_limit_id的数据版本,只认90的数据,其他事务修改数据产生更大的数据版本我不认。看下图

img

Q2将回滚到自己要的数据版本90实现了一致性读。

彩蛋

Q1的查询结果是啥,如果按照上述的规则Q1所在的事务的up_limit_id为99,那么它也只认数据版本为90的数据,这么认为就错了,判断可见性有俩个规则,上述为其中一个,另一个为“自己修改的”,所以Q1最终查询结果为3.

4.事务的传播行为PropagationBehavior
  • 7种传播行为

    • PROPAGATION_REQUIRED-----支持当前事务,如果不存在 就新建一个
    • PROPAGATION_SUPPORTS-----支持当前事务,如果不存在,就不使用事务
    • PROPAGATION_MANDATORY----支持当前事务,如果不存在,抛出异常
    • PROPAGATION_REQUIRES_NEW-----如果有事务存在,挂起当前事务,创建一个新的事务
    • PROPAGATION_NOT_SUPPORTED------以非事务方式运行,如果有事务存在,挂起当前事务
    • PROPAGATION_NEVER-----以非事务方式运行,如果有事务存在,抛出异常
    • PROPAGATION_NESTED-----如果当前事务存在,则嵌套事务执行
      只对DataSourceTransactionManager 起效
  • 三大类传播行为

    • PROPAGATION_REQUIRED(默认值)、PROPAGATION_SUPPORTS、PROPAGATION_MANDATORY
      支持当前事务, A调用B,如果A事务存在,B和A处于同一个事务 。
      事务默认传播行为 REQUIRED。最常用的。
    • PROPAGATION_REQUIRES_NEW、PROPAGATION_NOT_SUPPORTED、PROPAGATION_NEVER
      不会支持原来的事务 ,A调用B, 如果A事务存在, B肯定不会和A处于同一个事务。
      常用的事务传播行为:PROPAGATION_REQUIRES_NEW
    • PROPAGATION_NESTED
      嵌套事务 ,只对DataSourceTransactionManager有效 ,底层使用JDBC的SavePoint机制,允许在同一个事务设置保存点,回滚保存点
  • 常用的三种传播行为

    • PROPAGATION_REQUIRED(默认值)
      支持当前事务, A调用B,如果A事务存在,B和A处于同一个事务
    • PROPAGATION_REQUIRES_NEW
      不会支持原来的事务 ,A调用B, 如果A事务存在, B肯定不会和A处于同一个事务。
    • PROPAGATION_NESTED
      嵌套事务 ,只对DataSourceTransactionManager有效 ,
      底层使用JDBC的SavePoint机制,允许在同一个事务设置保存点,回滚保存点
    Connection conn = null;
    try {
        conn.setAutoCommit(false);
        Statement stmt = conn.createStatement();
    	stmt.executeUpdate("update person set name='888' where id=1");
    	Savepoint savepoint = conn.setSavepoint();
        try{   
            conn.createStatement().executeUpdate("update person set name='222' where sid=2");
        }catch(Exception ex){
            conn.rollback(savepoint);    
         }
        stmt.executeUpdate("delete from person where id=9");
        conn.commit();
        stmt.close();
    } catch (Exception e) {
         conn.rollback();
    }finally{
        try {
            if(null!=conn && !conn.isClosed()) conn.close();
        } catch (SQLException e) { e.printStackTrace(); }
    }
    
5.该接口主要提供的方法:
  • getIsolationLevel:隔离级别获取
  • getPropagationBehavior:传播行为获取
  • getTimeout:获取超时时间(事务的有效期)
  • isReadOnly:是否只读(保存、更新、删除—对数据进行操作-变成可读写的,查询-设置这个属性为true,只能读不能写),事务管理器能够根据这个返回值进行优化。
3.TransactionStatus:事务具体运行状态
  • flush()----------------给hibernate使用,底层发出sql的
  • hasSavepoint()-------判断是否有保留点
  • isCompleted()--------判断事务是否结束
  • isNewTransaction()—判断当前事务是否是新开的一个事务。
  • isRollbackOnly()------判断事务是否只能回滚
  • setRollbackOnly()-----设置事务是否回滚

2.事务管理两种方式

1.编程式的事务管理

通过TransactionTemplate手动管理事务
在实际应用中很少使用,因要修改原来的代码,加入事务管理代码 (侵入性 )

2.XML或注解配置
Spring的声明式事务是通过AOP实现的(环绕通知)、开发中经常使用(码侵入性最小)–推荐使用!
1.xml配置
1.service层
public class UserServiceImpl implements UserService {

    @Override
    public void change(String outName, String intoName, Double money) {
    }
}
2.applicationContext.xml
<!-- 2.配置事务通知 -->
	<!-- 人为设定id=“txAdvice” -->
	<!-- ransaction-manager 默认值就是 "transactionManager" 国际通用名就是transactionManager,该处可以不写 -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<!-- 配置属性的定义 -->
		<tx:attributes>
			<!-- 配置具体方法的相关事务属性
				isolation="DEFAULT"                事务的隔离级别,默认是按数据库的隔离级别来
				propagation="REQUIRED"             事务的传播行为,默认是同一个事务
				timeout="-1"                       事务的超时时间,默认值使用数据库的超时时间。
				read-only="false"                  事务是否只读,默认可读写。
				rollback-for                       遇到哪些异常就回滚,其他的都不回滚
				no-rollback-for                    遇到哪些异常不回滚,其他的都回滚。和上面互斥的
			 -->
			<tx:method name="change" isolation="DEFAULT" propagation="REQUIRED" timeout="-1" read-only="false"/>
			
			<!-- 支持通配符   	要求service中 方法名字必须符合下面的规则  -->
			<!-- read-only="false"          只可读,默认值false(可读可写)   -->
			<tx:method name="save*"/>
			<tx:method name="update*"/>
			<tx:method name="delete*"/>
			<tx:method name="find*" read-only="true"/>
		</tx:attributes>
	</tx:advice>
2.注解配置
// 1.@Transactional 会对该类中的所有方法自动加上事务
@Transactional
@Service(value = "userService")
public class UserServiceImpl implements UserService {
}

//2.启动类添加@EnableTransactionManagement

3.xml和注解的选择

  • XML方式,集中式维护,统一放置到applicationContext.xml文件中,缺点在于配置文件中的内容太多。
  • 使用@Transactional注解进行事务管理,配置太分散,使用XML进行事务管理,属性集中配置,便于管理和维护

8.常用注解

9.Spring中都用到了哪些设计模式

  1. 代理模式:AOP
  2. 单例模式:Spring管理的bean模式都是单例的
  3. 工厂模式:BeanFactory用来创建对象的实例
  4. 模板方法:RestTemplate(Http请求工具)

10.开发中主要用到了Spring什么技术

  1. IOC容器管理bean
  2. AOP实现日志打印
  3. 异常处理,@ControllerAdvice、@ExceptionHandler
  4. 事务@Transactional
  5. 文件解析:MultipartResolver
  6. 整合其他框架:@Slf4j
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值