将一个类声明为Bean的注解
-
@Component
:通用的注解,可标注任意类为Spring
组件。 -
@Respository
:对应Dao层/Mapper层 -
@Service
:对应业务层 -
@Controller
:对应控制层
@AutoWired 与 @Resource的区别
@Autowired
是 Spring 提供的注解,@Resource
是 JDK 提供的注解。Autowire
默认注入方式时byType,Resource
默认注入方式是byName- 当一个接口存在多个实现类的情况下,
@Autowired
和@Resource
都需要通过名称才能正确匹配到对应的 Bean。Autowired
可以通过@Qualifier
注解来显式指定名称,@Resource
可以通过name
属性来显式指定名称 @Autowired
支持在构造函数、方法、字段和参数上使用。@Resource
主要用于字段和方法上的注入,不支持在构造函数或参数上使用。
Bean的作用域
singleton : IoC 容器中只有唯一的 bean 实例。Spring 中的 bean 默认都是单例的,是对单例设计模式的应用。
prototype : 每次获取都会创建一个新的 bean 实例。也就是说,连续 getBean()
两次,得到的是不同的 Bean 实例。
request (仅 Web 应用可用): 每一次 HTTP 请求都会产生一个新的 bean(请求 bean),该 bean 仅在当前 HTTP request 内有效。
session (仅 Web 应用可用) : 每一次来自新 session 的 HTTP 请求都会产生一个新的 bean(会话 bean),该 bean 仅在当前 HTTP session 内有效。
application/global-session (仅 Web 应用可用):每个 Web 应用在启动时创建一个 Bean(应用 Bean),该 bean 仅在当前应用启动时间内有效。
websocket (仅 Web 应用可用):每一次 WebSocket 会话产生一个新的 bean。
解决Bean的线程安全问题
- 在Bean中尽量避免定义可变的成员变量
- 在类中定义一个
ThreadLocal
成员变量,将可变的成员变量保存在ThreadLocal
中
Bean的生命周期
- 从beanDefinitioon中获取bean配置信息
- 执行构造函数,实例化bean
- 依赖注入:装载bean的属性
- 处理aware接口
- BeanPostProcessor-before前置处理
- 初始化方法,实现了接口InitializingBean或者自定义了方法init-method
- BeanPostProcessor-after后置处理
- 启动容器:加载Bean
- 实例化Bean对象
- 依赖注⼊:装配Bean的属性
- 初始化Bean:执⾏aware接⼝⽅法、预初始化⽅法、初始化⽅法、后初始化⽅法
- 关闭容器:销毁Bean
IOC
IOC是指控制反转,Java开发中对象的创建和管理交给IOC容器,需要哪个对象,直接从容器里面去取,不需要手动new一个对象。
最常见的实现方式为依赖注入(DI)
Spring AOP
AOP是指面向切面编程,是将那些公共的逻辑或责任从业务代码中封装起来,形成一个个切面以减少重复代码,降低耦合度,通过动态代理实现
Spring事务的实现原理
- Spring事务的实现方式有两种:编程式事务和声明式事务。编程式事务是用户自己通过代码来控制事务,而声明式事务则是通过
@Transaction
注解来实现 - Spring事务是基于数据库事务和AOP机制实现的,是AOP的一个核心体现,当一个方法添加
@Transaction
注解后,spring会基于这个类生成一个代理对象,将这个代理对象作为bean,当使用这个代理对象的方法时,若有事务处理,会先关闭事务的自动提交,然后去执行具体的业务逻辑,执行过程中没有异常,则代理逻辑会直接提交,若出现异常,则会根据需要去进行回滚操作。 - Spring事务的隔离级别对应数据库的隔离级别。
- Spring事务的传播机制是基于数据库来做的,一个数据库连接一个事务,若传播机制要新开一个事务,实际上就是先建立一个数据库连接,然后执行sql语句。
事务失效的情况
spring事务的原理是AOP,进行了切面增强,失效的根本原因是这个AOP不起作用了。
- 数据库不支持事务
- 方法不是public的,
@Transactional
只能用在public
的方法上。 - 没有被
spring
管理 - 发生自调用,类里面使用this调用本类的方法(this通常省略),此时这个this对象不是代理类,而是userService本身。
- 异常被捕获
循环依赖
循环依赖:两个或两个以上的bean互相持有对方,最终形成闭环。
一级缓存+二级缓存解决普通对象的循环依赖
-
一级缓存:单例池,缓存已经经历了完整的生命周期,已经初始化完成的bean对象
-
二级缓存:缓存早期的bean对象,尚未装配属性(生命周期还没走完)
-
三级缓存:缓存的是ObjectFactory,表示对象工厂,用来创建某个对象的
Spring里面的设计模式
- 单例模式:Spring里面的Bean都是单例的,容器里只有一个bean实例
- 工厂设计模式 :BeanFactory、ApplicationContext创建对象
- 代理模式:AOP功能的实现
- 模板方法模式:jdbcTemplate操作数据库
- 适配器模式:AOP的增强或通知
SpringMVC执行流程
1、用户发送出请求到前端控制器DispatcherServlet,这是一个调度中心
2、DispatcherServlet收到请求调用HandlerMapping(处理器映射器)。
3、HandlerMapping找到具体的处理器(可查找xml配置或注解配置),生成处理器对象及处理器拦截器(如果有),再一起返回给DispatcherServlet。
4、DispatcherServlet调用HandlerAdapter(处理器适配器)。
5、HandlerAdapter经过适配调用具体的处理器(Handler/Controller)。
6、Controller执行完成返回ModelAndView对象。
7、HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet。
8、DispatcherServlet将ModelAndView传给ViewReslover(视图解析器)。
9、ViewReslover解析后返回具体View(视图)。
10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
11、DispatcherServlet响应用户。
前后端分离,没有视图这些,一般都是handler中使用Response直接结果返回
SpringBoot自动配置原理
-
在SpringBoot项目的启动类上有一个注解@SpringBootApplication,它对@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan三个注解进行了封装。
-
@EnableAutoConfiguration是实现自动配置的核心注解,它内部就是读取项目对应的jar包的classpath下的spring.factories文件中的所配置的类的全类名。在这些配置类中所定义的bean会根据条件注解
@ConditionalOnxx
所指定的条件来决定是否将其导入到容器中; -
条件判断会有@ConditionalOnClass这样的注解,判断是否有对应的class文件,有则加载该类,把这个配置类的bean放入容器。
Mybatis执行流程
- 读取mybatis-xml配置文件
- 构建会话工厂SqlSessionFactory,一个项目只要一个,由spring进行管理
- SqlSessionFactory创建SqlSession对象,包含执行sql语句的所有方法
- 操作数据库的接口,Executor执行器,动态生成SQL语句,同时负责查询缓存的维护
- Executor接口的执行方法中有一个MapperStatement类型的参数,封装了映射信息
- 输入参数映射
- 封装结果集
Mybatis的延迟加载
延迟加载:需要用到数据时才进行加载,不需要数据时就不加载
在Mybatis配置文件中,可以配置是否启用延迟加载
lazyLoadingEnabled=true|false
,默认是关闭的
延迟加载在底层主要使用的CGLIB动态代理完成的
第一是,使用CGLIB创建目标对象的代理对象,这里的目标对象就是开启了延迟加载的mapper
第二个是当调用目标方法时,进入拦截器invoke方法,发现目标方法是null值,再执行sql查询
第三个是获取数据以后,调用set方法设置属性值,再继续查询目标方法,就有值了
Mybatis的一级缓存和二级缓存
- 一级缓存又叫本地缓存,其存储作用域为 Session,当Session进行flush或close之后,该Session中的所有Cache就将清空,默认打开一级缓存
- 二级缓存是基于namespace和mapper的作用域起作用的,不依赖于sqlSession,在映射XML文件配置
<cache></cache>
,实现了sqlSession对象中数据的共享。
一级缓存是为了避免每次查询都去数据库中找,后续的sql在命中缓存的情况下直接去本地缓存读取,sqlSession级别。
二级缓存是跨sqlsession的,存在脏读问题,默认关闭
MyBatis查询的先后顺序:
二级缓存->一级缓存->数据库
#{}
与${}
的区别
#{}
会进行预编译处理,可以防止SQL注入,${}
没有预编译处理,直接拼接字符串,存在SQL注入问题,不安全。- 在进行预编译处理时,
#{变量}
会替换为?,同时对变量进行数据类型检测,若为字符类型,会自动加上‘’
。
sql 注入是将用户输入的数据拼接到代码中,并被当成 sql 语句执行。
如:
s=“ ” +“1 or 1=1”;
select * from user where id=‘%s’;
拼接后为:select * from user where id=’ ’ or 1=1;
可以通过该SQL恶意去获取user表里的所有信息,不安全
一般来说,更建议用#{}
,但是在一些变量为字段名、表名的情况下,则用${}
(因为#{}
会自动加引号,导致错误)