Spring 知识点
Spring
官网下载地址:https://repo.spring.io/release/org/springframework/spring/
GitHub:https://github.com/spring-projects/spring-framework
1.1 简介
- 2002 年 首次推出Spring框架雏形:interface21.io
- Spring 框架以 interface21.io 为基础,于 2004 年 3 月 24 号 发布 1.0 版本
- Spring Framework 创建人:Rod Johnson 悉尼大学 音乐出身
- Spring 理念:使现有的技术更加容易使用
- Spring 是为了解决企业级应用开发的复杂性而创建的,简化开发。
SSH
- Struct2 + Spring + Hibernate
SSM
- SpringMVC + Spring + Mybatis
Maven Jar
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
1.2 概念
- Spring 是一个开源的免费的框架
- Spring 是一个轻量级的,非入侵式的框架
- 特性:控制反转(IOC)、面向切面编程(AOP)
- 支持事务处理,对框架整合的支持
总结:Spring 是一个轻量级的 控制反转和面向切面编程的框架
1.3 组成
Spring 框架指的是 Spring Farmework,它是很多模块的组合,使用这些模块可以很方便协助开发。这些模块是:
核心容器、数据访问、Web、AOP、工具、消息、测试模块。
Core Container 中的 Core 组件是 Spring 所有组件的核心;Beans 组件和 Context 组件是实现 IOC 和 依赖注入的基础;AOP组件用于实现面向切面编程。
六个特性:
- 核心技术:依赖注入(DI), AOP, 事件, 资源, i18n, 验证, 数据绑定, 类型转换, SpEL
- 测试:
- 数据访问:事务, DAO支持, JDBC, ORM, 编组XML
- Web支持:Spring MVC, Spring WebFlux Web 框架
- 集成:远程处理
- 语言:Kotlin, Groovy, 动态语言
- Spring Core:基础,可以说Spring 其他所有的功能都需要依赖于该类库。主要提供 IoC 依赖注入功能。
- Spring Aspects:该模块为与 AspectJ 集成提供支持
- Spring AOP:面向切面编程
- Spring JDBC:Java 数据库连接
- Spring JMS:Java 消息服务
- Spring ORM:用于支持 Hibernate 等 ORM 工具
- Spring Web:为创建 Web 应用程序提供支持
1.4 拓展
- Spring Framkwork
- Spring Boot
- 快速开发的脚手架
- 基于SpringBoot 可以快速的开发单个微服务
- 约定大于配置
- Spring Cloud
- SpringCloud是基于 SpringBoot 实现的
2.IOC 理论
2.1 IOC 本质
IOC: 对象由Spring来创建、管理、装配!
IOC 是一种编程思想,由主动的编程变成被动的接收
1.对象由谁创建?
- 对象由Spring 创建
2.对象的属性由谁设置?
- 对象的属性由Spring容器设置
这个过程就叫控制反转:
控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的
反转:程序本身不创建对象,而是被动的接收对象
依赖注入:利用Set方法来进行注入
beans.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
样例:
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id = "useDaoImpl" class="dao.UserDaoImpl" />
<bean id = "useMysqlDaoImpl" class="dao.UserMysqlDaoImpl" />
<!-- ref 引用Spring容器创建好的对象,value 具体的值 -->
<bean id="useServiceImpl" class="service.UserServiceImpl">
<property name="userDao" ref = "useMysqlDaoImpl" />
</bean>
</beans>
使用样例:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import pojo.User;
import service.UserServiceImpl;
public class BeanTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserServiceImpl userService = (UserServiceImpl) context.getBean("useServiceImpl");
userService.getUserById();
User user = (User) context.getBean("user");
System.out.println(user.getId());
}
}
2.2 IOC 创建对象的方式
1.使用无参构造创建对象。默认
2.有参构造创建对象
-
下标赋值
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg index="0" value="7500000"/> <constructor-arg index="1" value="42"/> </bean>
-
类型赋值(不建议使用)
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg type="int" value="7500000"/> <constructor-arg type="java.lang.String" value="42"/> </bean>
-
参数名
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg name="name" value="Vincent"/> </bean>
总结:在配置文件加载的时候,容器中的对象就已经初始化了!
3.Spring 配置
3.1别名
<!-- alias 别名,可以使用别名获取对象 -->
<alias name="user" alias="usershow" />
3.2 bean 的配置
<bean id="user" class="pojo.User">
<property name="id" value="1" />
</bean>
- id:bean 的唯一标识符,也就是对象名
- class:bean 对象 对应的权限定名(包名+类名)
- name:别名
3.3 import
import 一般用于团队开发,可以将多个配置文件导入合并为一个。
<import resource="classpath:****.xml" />
4.依赖注入 DI
4.1 构造器注入
转:2.2
4.2 Set 方式注入(重点)
依赖注入:本质是 Set 注入
- 依赖:Bean 对象的创建依赖于容器
- 注入:Bean 对象中的所有属性由容器注入
Set 注入样例
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="pojo.User">
<property name="id" value="1" />
</bean>
<bean id="stu" class="pojo.Student">
<!-- 普通属性注入 -->
<property name="name" value="Vincent" />
<!-- 对象注入 ref -->
<property name="user" ref="user" />
<!-- 数组 -->
<property name="books">
<array>
<value>遮天</value>
<value>完美世界</value>
<value>神墓</value>
</array>
</property>
<!-- List -->
<property name="hobbys">
<list>
<value>BALL</value>
<value>GAMES</value>
</list>
</property>
<!-- Map -->
<property name="card">
<map>
<entry key="cardno" value="1231412214"/>
</map>
</property>
<!-- Set -->
<property name="games">
<set>
<value>lol</value>
<value>cf</value>
</set>
</property>
<!-- 空值注入 / null 注入 -->
<property name="wife">
<null/>
</property>
<!-- Properties -->
<property name="info">
<props>
<prop key="1">1111</prop>
<prop key="2">2222</prop>
</props>
</property>
</bean>
</beans>
4.3 拓展方式注入
p 命名空间 和 c 命名空间
- 不可直接使用,需要导入 xml 配置
p 命名配置:
xmlns:p="http://www.springframework.org/schema/p"
# 使用
<bean name="p-namespace" class="com.example.ExampleBean"
p:email="someone@somewhere.com"/>
c 命名配置:
xmlns:c="http://www.springframework.org/schema/c"
<bean id="beanOne" class="x.y.ThingOne"
c:thingTwo-ref="beanTwo"
c:thingThree-ref="beanThree"
c:email="something@somewhere.com"/>
4.4 Bean 的作用域
- singleton: 唯一 bean 实例,Spring 中的 bean 默认都是 单例的。
- prototype: 每次请求都会创建一个新的 bean 实例
- request: 每一次Http请求都会产生一个新的bean, 该bean 仅在当前 Http request 有效
- session: 每一次Http请求都会产生一个新的bean, 该bean 仅在当前 Http session 有效
- global-session: 全局session作用域,仅仅在基于portlet的web应用中才有意义,Spring5已经没有了。
5.Bean 的自动装配
- 自动装配是 Spring 装配 Bean 依赖的一种方式
- Spring 会在上下文中自动寻找,并自动给bean装配属性
Spring 中三种装配方式
- 在 xml 中显示装配
- 在 java 代码中装配
- 隐式的装配
5.1 ByName 自动装配
- 会自动在Spring容器上下文中,查询与set属性相对于的 bean id
<bean name="p-namespace" class="com.example.ExampleBean" autowire="buName"/>
5.2 ByType 自动装配
- 会自动在Spring容器上下文中,查找与对象类型相对于的bean
<bean name="p-namespace" class="com.example.ExampleBean"autowire="buType"/>
小结:
- byName 需要保证所有bean 的 id 唯一,并且这个bean需要和自动注入的属性的set方法的值一致
- byType 需要保证所有 bean 唯一,并且这个bean需要和自动注入的属性的类型一致
5.3 使用注解自动装配
使用注解配置
- 导入约束:context
- 添加配置
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 开启自动注入配置 -->
<context:annotation-config/>
</beans>
@Autowired
- 在属性上使用即可, 也可以在SET上使用
- 可不用编写 SET 方法,
@Resource
小结:
@Autowired 和 @Resource 区别
- 都是用来自动装配,都可以放在属性字段上
- @Autowired 默认通过 byType 的方式实现,对象必须存在
- @Resource 默认通过 byName 的方式实现,如果找不到 name, 则自动通过 byType 方式找,都找不到就报错
- 执行顺序不同:
6. 使用注解开发
@Autowired
@Resource
@Component : 组件,说明bean 被Spring容器管理了
@Value : 属性注入
@Repository: dao
@Service:service
@Controller:controller
@Scope:作用域
小结:
@Component 、@Repository、@Service、@Controller、将类注册到Spring容器中,装配Bean
配置需要扫描的包:
<context:component-scan base-package="com.**.**"/>
7. Java 配置Spring
7.1 JavaConfig
@Configuration 代表这是一个配置类,相当于beans.xml,它本身是一个 @Component 组件
@ComponentScan 自动扫描包
@Import 导入类
@Bean
8.代理模式
为什么学习代理模式?
- Spring AOP 的底层
代理模式分类
- 静态代理
- 动态代理
8.1 静态代理
角色分析:
- 抽象角色:一般使用接口或者抽象类解决
- 真实角色:被代理的角色
- 代理角色:代理真是角色,代理真实角色后,
- 客户:访问代理角色的人
代理模式实现步骤:
-
接口
public interface Rent { public void rent(); }
-
真实角色
public class Host implements Rent{ @Override public void rent() { System.out.println("我要出租房子"); } }
-
代理角色
public class Proxy { private Host host; public Proxy() { } public Proxy(Host host) { this.host = host; } public void rent(){ host.rent(); } public void seeHouse(){ System.out.println("看房子"); } }
-
客户端访问代理角色
public class Client { public static void main(String[] args) { Host host = new Host(); Proxy proxy = new Proxy(host); proxy.rent(); } }
代理模式的好处
- 可以使真实角色的操作更加纯粹,不用关注一些公共的业务
- 公共也就交给代理角色,实现业务的分工
- 公共业务发生拓展的时候,方便集中管理
缺点:
- 一个真实角色就会产生一个代理角色,代码量会翻倍,开发效率变低
8.2 动态代理
- 动态代理和静态代理角色一样
- 动态代理的代理类是动态生成的,非直接写的
- 动态代理分为两大类:基于接口的动态代理、基于类的动态代理
- 基于接口:JDK 动态代理
- 基于类:cglib
- java字节码:javasist
Proxy & InvocationHandler
proxy 生成动态代理实例
InvocationHandler 调用处理程序并返回结果
InvocationHandler
动态代理的好处:
- 静态代理的他都有
- 一个动态代理类代理是一个接口,一般就是对应一个业务
- 一个动态代理可以代理多个类,只要是实现了一个接口
9.AOP
9.1 什么是AOP
AOP (Aspect Oriented Programming) : 面向切面编程
通过预编译方式和运行期动态代理实现程序功能统一维护的技术。AOP是 OOP 的延续,是Spring 框架中的一个重要内容,是函数式编程 的一种衍生范型。
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,并提供开发效率。
9.2 AOP在Spring中的作用
提供声明式事务,允许用户自定义切面
9.3Spring 实现AOP
导入 Maven 包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
方式一:Spring API 接口
<aop:config>
<!-- 切入点 expression:表达式 -->
<aop:pointcut id="pointcut" expression="execution(* pers.vincent.service.UserServiceImp.*(..))"/>
<aop:advisor advice-ref="log" pointcut-ref="pointcut" />
<aop:advisor advice-ref="afterlog" pointcut-ref="pointcut" />
</aop:config>
方式二:自定义类实现
10.整合Mybatis
步骤:
- 导入JAR包
- junit
- mybatis
- mysql 数据库连接
- spring 相关的
- aop
- spring-mybatis
- 编写配置文件
- 测试
spring-database.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- DataSoure: 使用Spring 的数据源替换 Mybatis 的配置 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/smbms?useUnicode=true&characterEncoding=UTF8"/>
<property name="username" value="root" />
<property name="password" value="root" />
</bean>
<!-- sqlsessionFactory -->
<bean id="sqlsessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configuration" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:pers/vincent/mapper/*.xml" />
</bean>
<!-- SqlSessionTemplate 就是 SqlSession -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!-- 只能使用构造器注入sqlsessionFactory, 因为它没有set方法 -->
<constructor-arg index="0" ref="sqlsessionFactory" />
</bean>
</beans>
- 编写数据源
- sqlSessionFactory
- sqlSessionTemplate
- 需要添加实现类
11.事务
11.1 事务的 ACID 原则
- 原子性:事务是最小执行单位
- 持久性:事务一旦提交,无论系统发生什么,结果都不会被影响,被持久化到存储器中
- 一致性
- 隔离性:多个业务可能操作一个资源,防止数据损坏
11.2 Spring 中的事务管理
- 声明式事务:AOP (推荐事务)
- 在配置文件中配置
- 基于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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- DataSoure: 使用Spring 的数据源替换 Mybatis 的配置 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/smbms?useUnicode=true&characterEncoding=UTF8"/>
<property name="username" value="root" />
<property name="password" value="root" />
</bean>
<!-- sqlsessionFactory -->
<bean id="sqlsessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configuration" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:pers/vincent/mapper/*.xml" />
</bean>
<!-- SqlSessionTemplate 就是 SqlSession -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!-- 只能使用构造器注入sqlsessionFactory, 因为它没有set方法 -->
<constructor-arg index="0" ref="sqlsessionFactory" />
</bean>
<!-- 配置声明式事务 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.">
</bean>
<!-- 结合 AOP 实现事务的织入 -->
<!-- 配置事务的通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 给哪些方法配置事务 -->
<!-- 配置事务的传播特性: propagation-->
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 配置事务切入 -->
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* pers.vincent.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>
</beans>
-
编程式事务:(不推荐使用)
- 在代码里硬编码
为什么需要事务?
- 如果不配置事务,可能存在数据提交不一致的情况
- 事务在开发过程中十分重要,涉及到数据的一致性和完整性
11.3 Spring 事务中的隔离级别
TransactionDefinition 接口定义了五个表示隔离级别的常量
-
TransactionDefinition.ISOLATION_DEFAULT: 使用后端数据库默认的隔离级别。
MySQL 默认采用的REPEATABLE_READ;Oracle 默认采用 READ_COMMITTED
-
TransactionDefinition.ISOLATION_READ_UNCOMMITTED: 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
-
TransactionDefinition.ISOLATION_READ_COMMITTED: 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍可能发生
-
TransactionDefinition.ISOLATION_REPEATABLE_READ: 对同一个字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以组织脏读和不可重复读,但幻读仍有可能发生。
-
TransactionDefinition.ISOLATION_SERIALIZABLE: 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读,不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
12. 相关问题
@RestController 和 @Controller
@RestController
-
相当于:@Controller + @ResponseBody
-
RESTful Web 服务
-
返回 JSON 和 XML 形式数据:对象数据直接以 JSON 和 XML 形式写入 HTTP 响应(Response)
-
对应 前后端分离
@Controller
- 单独使用不加 @ResponseBody 的话,一般使用再要返回一个视图的情况,传统的 SpringMVC 应用,前后端不分离
Spring IOC 和 Spring AOP
Spring IOC
- 控制反转:是一种思想,将原本在程序中手动创建对象的控制权交由 Spring 管理。
- IoC 容器是 Spring 用来实现 IoC 的载体,IoC 容器实际上就是 Map, Map 中存放的是各种对象。
- 将对象之间的相互依赖关系交给 IoC 容器来管理,并由 IoC 容器完成对象的注入。
- 初始化过程:
- 配置XML文件
- 读取Resource
- 解析 BeanDefinition
- 注册 BeanFactory
Spring AOP
- 面向切面编程:将业务模块需要共同调用的逻辑或责任(例如事务管理、日志管理、权限控制等)封装起来,便于减少系统的代码重复,降低模块间的耦合度,并有利于未来的可拓展性和可维护性
- 基于动态代理
Spring AOP 和 AspectJ AOP
- Spring AOP 是基于动态代理,属于运行时增强
- 以集成 AspectJ, AspectJ 算的上是 Java 生态系统中最完整的 AOP 框架
- AspectJ AOP 是基于字节码操作,属于编译时增强
Spring Bean
1.Spring 中的单例 bean 的线程安全问题了解吗?
主要是因为当多个线程操作同一个对象的时候,对这个对象的非静态成员变量的写操作会存在线程安全问题。
解决方法:
在类中定义一个 ThreadLocal 成员变量,将需要的可变成员变量保存在 ThreadLocal 中
@Component 和 @Bean 的区别是什么?
- 作用对象不同:@Component 注解作用于类,而@Bean注解作用于方法
- @Component 通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中;@Bean 注解通常是我们在标有该注解的方法中定义产生这个 bean, @Bean 告诉了Spring这是某个类的示例,当我们需要用他的时候还给我。
将一个类声明为 Spring 的 bean 的注解有哪些??
- @Autowired: 自动装配bean
- @Component: 可标注任意类为 Spring 组件
- @Service: 用于服务层
- @Resposity: 对应持久层即 Dao 层,主要用于数据库操作
- @Controller: 对应控制层
Spring Bean 的生命周期
Spring 框架中用到了哪些设计模式?
- 工厂设计模式:Spring 使用工厂模式通过 BeanFactory、ApplcationContext 创建 Bean 对象
- 代理设计模式:Spring AOP 功能实现
- 单例设计模式:Spring 中的Bean默认都是单例的
- 包装器设计模式:项目需连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。
- 观察者模式:Spring 事件驱动模型就是观察者模式很经典的应用
- 适配器模式:Spring AOP 的增加或通知使用到了适配器模式;SpringMVC 中的也是用到适配器模式 Controller
@Transactional(rollbackFor = Exception.class) 注解
Exception 分为 RuntimeException 和 非运营时异常。事务管理至关重要,即使出现异常情况,它也可以保证数据的一致性。
@Transactional 注解作用于类上时,该类的所有 public 方法都将具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。如果类和方法上都加了这个注解,那么这个类里面的方法抛出异常,就会回滚,数据库里面的数据也会回滚。
@Transactional 不配置 rollbackFor 属性,那么只会遇到 RuntimeException 时才会回滚;配置 rollbackFor = Exception.class 时,那么在非运行时异常时也会进行回滚。
相关文章:https://developer.ibm.com/zh/articles/j-master-spring-transactional-use/