SSM框架

    • 目录

1~5
1.Spring框架中的单例Bean是线程安全的吗?
2.什么是AOP?
3.项目中哪些场景可以使用AOP?
4.Spring中的事务是如何实现的?
5.Spring中事务失效的场景有哪些?
6~10
6.Spring Bean的生命周期
7.什么是Spring的循环依赖?
8.SpringMVC的执行流程
9.Spring IOC和DI
10.SpringBoot的自动配置原理(高频)
11~15
11.Spring、SpringMVC、SpringBoot常见注解有哪些?
12.Spring加载Bean有哪些方式?
13.Spring IOC的工作流程
14.Spring为什么流行?SpringBoot解决了哪些问题?
15.SpringBoot如何解决跨域问题?
16~20
16.Spring中@Component和@Bean的区别?
17.MyBatis的执行流程
18.MyBatis是否支持延迟加载?
19.MyBatis的一级、二级缓存?
20.MyBatis是如何解决SQL注入的?
21~25
21.MyBatis是如何进行分页的?
22.MyBatis中#{}和${}的区别是什么?

1~5

1.Spring框架中的单例Bean是线程安全的吗?

不是线程安全的

默认的情况下,Spring的Bean是单例的,即每个应用上下文中只有一个实例。在多线程环境下,多个线程可以同时访问和修改同一个实例,所以单例Bean是线程不安全的。

Spring的作用域(scope)

  • singleton:单例,默认作用域(线程不安全)
  • prototype:原型,每次创建一个新对象(线程安全)
  • request:请求,每次Http请求创建一个新对象,适用于WebApplicationContext环境下(线程安全)
  • session:会话,同一个会话共享一个实例,不同会话使用不用的实例(线程安全)
  • global-session:全局会话,所有会话共享一个实例(线程安全)

singleton和prototype是最常用的两个作用域。

如果要保证线程安全,可以将Bean的作用域改为prototype(原型)

session、request、global-session

session: Spring中的Session是线程安全的。在Web应用中,每个用户请求都会在独立的线程中处理,每个线程都有自己的Session对象。Spring框架使用容器来管理Session,这些容器是线程安全的,因此Session也是线程安全的。
request: 在Spring框架中,request对象是线程安全的,因为它是从Servlet API中继承的,Servlet API的设计者已经考虑到了线程安全问题,并确保了HttpServletRequest对象的线程安全。
global-session: 全局会话(Global Session)是Spring Session的一种存储策略,它允许跨多个HTTP请求和线程共享会话数据。全局会话通常将数据存储在一个共享的、线程安全的存储区中,例如Redis或数据库。
对于全局会话本身,它确实是线程安全的。这意味着在多个线程同时访问和修改全局会话数据时,不会出现数据竞争或不一致的问题。Spring Session使用了同步机制来确保全局会话的线程安全性

2.什么是AOP?

面向切面编程,可以把那些重复性的代码抽取出来作为公共模块复用,降低耦合(比如:作为日志,参数校验,spring的事务也是aop实现的)

Spring AOP的几个常用注解

  • @EnableAspectJAutoProxy 开启spring对注解aop的支持
  • @Aspect 配置切面
  • @Pointcut 切入点
  • @Before 前置通知:目标方法之前执行
  • @After 后置通知:目标方法之后执行(始终执行)
  • @AfterReturning 返回之后通知:执行方法结束之前执行(异常不执行)
  • @AfterThrowing 异常通知:出香异常后执行
  • @Around 环绕通知:环绕目标方法执行

3.项目中哪些场景可以使用AOP?

  • 参数校验
  • 日志
  • 缓存

4.Spring中的事务是如何实现的?

通过aop功能,对方法前后进行拦截,在执行方法之前开启事务,在执行目标之后根据执行情况进行提交或者回滚事务

5.Spring中事务失效的场景有哪些?

1.异常捕获处理的时候,自己处理了异常,没有抛出可能导致事务失效(解决:手动抛出异常)

2.方法不是public会导致的事务失效(解决改为public)
3.抛出检查异常的时候(解决:配置rollbackFor属性为Exception)

比如你的事务控制代码如下:

@Transactional
public void transactionTest() throws IOException{
    User user = new User();
    UserService.insert(user);
    throw new IOException();
}

如果@Transactional 没有特别指定,Spring 只会在遇到运行时异常RuntimeException或者error时进行回滚,而IOException等检查异常不会影响回滚。

public boolean rollbackOn(Throwable ex) {
	return (ex instanceof RuntimeException || ex instanceof Error);
}

6~10

6.Spring Bean的生命周期

  • 首先会通过一个非常重要的类,叫做BeanDefinition获取bean的定义信息,这里面就封装了bean的所有信息,比如,类的全路径,是否是延迟加载,是否是单例等等这些信息,在创建bean的时候:
  1. 第一步是调用构造函数实例化bean
  2. 第二步是bean的依赖注入,比如一些set方法注入,像平时开发用的@Autowire都是这一步完成
  3. 第三步是处理Aware接口,如果某一个bean实现了Aware接口就会重写方法执行
  4. 第四步是bean的后置处理器BeanPostProcessor,这个是前置处理器
  5. 第五步是初始化方法,比如实现了接口InitializingBean或者自定义了方法init-method标签或@PostContruct
  6. 第六步是执行了bean的后置处理器BeanPostProcessor,主要是对bean进行增强,有可能在这里产生代理对象
  7. 最后一步是销毁bean

7.什么是Spring的循环依赖?

  • 循环依赖:循环依赖其实就是循环引用,也就是两个或两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于A循环依赖在spring中是允许存在,spring框架依据三级缓存已经解决了大部分的循环依赖:

①一级缓存:单例池,缓存已经经历了完整的生命周期,已经初始化完成的

bean对象

②二级缓存:缓存早期的bean对象(生命周期还没走完)

③三级缓存:缓存的是ObjectFactory,表示对象工厂,用来创建某个对象的

那具体解决流程

  • 第一,先实例A对象,同时会创建ObjectFactory对象存入三级缓存singletonFactories
  • 第二,A在初始化的时候需要B对象,这个走B的创建的逻辑
  • 第三,B实例化完成,也会创建ObjectFactory对象存入三级缓存singletonFactories
  • 第四,B需要注入A,通过三级缓存中获取ObjectFactory来生成一个A的对象同时存入二级缓存,这个是有两种情况,一个是可能是A的普通对象,另外一个是A的代理对象,都可以让ObjectFactory来生产对应的对象,这也是三级缓存的关键
  • 第五,B通过从通过二级缓存earlySingletonObjects 获得到A的对象后可以正常注入,B创建成功,存入一级缓存singletonObjects
  • 第六,回到A对象初始化,因为B对象已经创建完成,则可以直接注入B,A创建成功存入一次缓存singletonObjects
  • 第七,二级缓存中的临时对象A清除

** 构造方法出现了循环依赖怎么解决?**

  • 由于bean的生命周期中构造函数是第一个执行的,spring框架并不能解决构造函数的的依赖注入,可以使用@Lazy懒加载,什么时候需要对象再进行bean对象的创建,如果@Lazy解决不了,可以直接把需要调用的方法抽出一个单独服务,再去注入

8.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直接结果返回

9.Spring IOC和DI

Spring IOC的全称叫控制反转也叫Inversion of Control

在传统的Java程序开发当中,只能通过new关键字来创建对象,这种方式会导致程序里面对象的依赖关系比较复杂,耦合度高。
IOC 的作用就是实现了对象的管理,也就是把设计好的对象交给IOC容器来控制,在需要用到的时候直接从容器当中去获取。
有了 IOC容器 来管理Bean以后,相当于把对象的创建和查找依赖对象的控制交给了容器,这种理念使得对象和对象之间是一种松耦合状态,极大的提升了程序的灵活性和功能的复用性。

DI

DI 表示依赖注入,也就是对于 IOC容器 中管理的Bean,如果Bean之间存在依赖关系,那么 IOC容器 需要自动去实现依赖注入的实例注入。 通常我们有三种方式去描述Bean和Bean之间的依赖关系:

  • 1.接口注入;
  • 2.setter注入;
  • 3.构造器注入

另外为了更加灵活的去实现Bean的实例的依赖注入,Spring还提供了@Rescource和@Autowired,它们可以分别根据Bean的name和Bean的类型来去实现依赖注入。

10.SpringBoot的自动配置原理(高频)

自动装配就是自动把第三方组件的Bean装载到IOC容器里,不需要开发人员再去写Bean相关的配置

在SpringBoot中只需要在引导类加上@SpringBootApplication就可以实现自动装配

@SpringBootApplication注解是一个复合注解,它包含了

  • @SpringBootConfiguration
  • @EnableAutoConfiguration
  • @ComponentScan

@SpringBootConfiguration 注解标记启动类为配置类
@ComponentScan 注解找到启动时需要自动装配的类并注入到spring的bean中
@EnableAutoConfiguration会从classpath路径下搜索所有的META-INF/spring.factories配置文件,把里面EnableAutoConfiguration的key对应的配置加载到Spring容器里。

11~15

11.Spring、SpringMVC、SpringBoot常见注解有哪些?

Spring 的常见注解有哪些?

  • 第一类是:声明bean,有@Component、@Service、@Repository、@Controller
  • 第二类是:依赖注入相关的,有@Autowired、@Qualifier、@Resourse
  • 第三类是:设置作用域 @Scope
  • 第四类是:spring配置相关的,比如@Configuration,@ComponentScan 和@Bean
  • 第五类是:跟aop相关做增强的注解 @Aspect,@Before,@After,@Around,@Pointcut

SpringMVC常见的注解有哪些?

  • 有@RequestMapping:用于映射请求路径;@RequestBody:注解实现接收http请求的json数据,将json转换为java对象;@RequestParam:指定请求参数的名称;@PathViriable:从请求路径下中获取请求参数(/user/{id}),传递给方法的形式参数;@ResponseBody:注解实现将controller方法返回对象转化为json对象响应给客户端。@RequestHeader:获取指定的请求头数据,还有像@PostMapping、@GetMapping这些。

** Springboot常见注解有哪些?**

  • Spring Boot的核心注解是@SpringBootApplication , 他由几个注解组成 :@SpringBootConfiguration: 组合了- @Configuration注解,实现配置文件的功

能;@EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项@ComponentScan:Spring组件扫描

12.Spring加载Bean有哪些方式?

1.使用@Component以及它的衍生注解(比如@Service、@Repository、@Controller)来将Java类标记为Bean,Spring会自动扫描并加载这些插入的类
2.通过XML配置文件中的< bean>元素定义Bean,然后Spring容器获取并解析配置文件,完成Bean的加载
3.通过配置类的方式,在Java类中增加@Configuration注解,在指定方法上增加@Bean注解实现

13.Spring IOC的工作流程

IOC是什么?

IOC就是控制反转,它的核心思想是把对象的管理权限交给容器。
应用程序如果需要使用到某个对象实例,直接从IOC容器中去获取就行,这样设计的好处是降低了程序里面对象与对象之间的耦合性。
使得程序的整个体系结构变得更加灵活。

Spring Bean的声明方式

Spring里面很多方式去定义Bean,比如XML里面的< bean>标签、@Service、@Component、@Repository、@Configuration配置类中的@Bean注解等等。
Spring在启动的时候,会去解析这些Bean然后保存到IOC容器里面。

IOC的工作流程

Spring IOC的工作流程大致可以分为三个阶段。
第一个阶段,就是IOC容器的初始化
这个阶段主要是根据程序中定义的XML或者注解等Bean的声明方式
通过解析和加载后生成BeanDefinition,然后把BeanDefinition注册到IOC容器。
通过注解或者xml声明的bean都会解析得到一个BeanDefinition实体,实体中包含这个bean中定义的基本属性。
最后把这个BeanDefinition保存到一个Map集合里面,从而完成了IOC的初始化。
IoC容器的作用就是对这些注册的Bean的定义信息进行处理和维护,它IoC容器控制反转的核心。
第二个阶段,完成Bean初始化及依赖注入
然后进入到第二个阶段,这个阶段会做两个事情
通过反射针对没有设置lazy-init属性的单例bean进行初始化。
完成Bean的依赖注入。
第三个阶段,Bean的使用
通常我们会通过@Autowired或者BeanFactory.getBean()从IOC容器中获取指定的bean实例。
另外,针对设置layy-init属性以及非单例bean的实例化,是在每次获取bean对象的时候,调用bean的初始化方法来完成实例化的,并且Spring IOC容器不会去管理这些Bean。

14.Spring为什么流行?SpringBoot解决了哪些问题?

Spring通过IOC和AOP这两个核心属性,解耦了业务对象之间的依赖关系实现了高内聚、低耦合,让开发者专注于业务开发。
SpringBoot拥有自动配置的能力,简化了Spring的配置,降低了Spring配置的复杂性。

15.SpringBoot如何解决跨域问题?

什么是跨域?

跨域 指的是浏览器在执行网页中的JavaScript代码的时候,由于浏览器同源策略的一个限制,只能访问同源的资源而不能访问其它源的资源。

比如这样的,就会出现跨域问题

SpringBoot如何解决跨域问题?

1.通过@CrossOrigin(origins = "http://localhost:8080" )注解指定允许哪些origins允许跨域
2.使用WebMvcConfiguration接口重写addCorsMappings()来配置允许跨域的请求源。

16~20

16.Spring中@Component和@Bean的区别?

1.@Component: 作用于类上,告知Spring,为这个类创建Bean。
2.@Bean:主要作用于方法上,告知Spring,这个方法会返回一个对象,且要注册在Spring的上下文中。 通常方法体中包含产生Bean的逻辑。

17.MyBatis的执行流程

  • ①读取MyBatis配置文件:mybatis-config.xml加载运行环境和映射文件
  • ②构造会话工厂SqlSessionFactory,一个项目只需要一个,单例的,一般由spring进行管理
  • ③会话工厂创建SqlSession对象,这里面就含了执行SQL语句的所有方法
  • ④操作数据库的接口,Executor执行器,同时负责查询缓存的维护
  • ⑤Executor接口的执行方法中有一个MappedStatement类型的参数,封装了映射信息
  • ⑥输入参数映射
  • ⑦输出结果映射

18.MyBatis是否支持延迟加载(延迟加载就是按需加载,查询数据的时候需要用到的时候再加载)?

  • 支持,但是默认情况下是没有开启的
  • 延迟加载的意思是:就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。
  • 可以再yml配置文件里加上mybatis: configuration: lazyLoadingEnabled: true来启动MyBatis的延迟加载

延迟加载的底层原理

  • 延迟加载在底层主要使用的CGLIB动态代理完成的
  1. 第一是,使用CGLIB创建目标对象的代理对象,这里的目标对象就是开启了延迟加载的mapper
  2. 第二个是当调用目标方法时,进入拦截器invoke方法,发现目标方法是null值,再执行sql查询
  3. 第三个是获取数据以后,调用set方法设置属性值,再继续查询目标方法,就有值了

19.MyBatis的一级、二级缓存?

  • mybatis的一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当Session进行flush或close之后,该Session中的所有Cache就将清空,默认打开一级缓存
  • 关于二级缓存需要单独开启,二级缓存是基于namespace和mapper的作用域起作用的,不是依赖于SQLsession,默认也是采用 PerpetualCache,HashMap 存储。如果想要开启二级缓存需要在全局配置文件和映射文件中开启配置才行。

20.MyBatis是如何解决SQL注入的?

MyBatis采用预编译来防止SQL注入

预编译(Prepared Statements):

在SQL执行前,会先将上面的SQL发送给数据库进行编译,#{}接收的参数会替换成占位符“?”;执行时,直接使用编译好的SQL,把占位符“?”替换成参数就可以了。所有的用户输入都被视为参数,而不是SQL的一部分 ,有效的防止了SQL注入。

例如,在MyBatis的XML映射文件中,可以使用#{param}来表示参数,MyBatis会将这些参数进行正确的转义和处理,确保安全性

<select id="getUserById" parameterType="int" resultType="User">
    SELECT * FROM users WHERE id = #{userId}
</select>

在这个例子中,#{userId}是一个参数,它会被安全地插入到SQL语句中,而不会导致SQL注入问题。

总的来说,MyBatis通过预编译语句和参数化查询保证了SQL的安全性,确保用户输入不会破坏SQL的结构或导致SQL注入攻击。

21~25

21.MyBatis是如何进行分页的?

MyBatis分页的三种方式

  • 直接在select语句上增加数据库提供的分页关键字limit(比如 limit 0,5 表示从索引0开始获取5条数据,第一个参数是索引位置,第二个参数是需要获取的数据条数),然后在前端传递当前页和每页展示的数据条数即可
  • 基于MyBatis里面的Interceptor拦截器,在select语句执行之前动态拼接分页关键字(例如分页助手PageHelper内部就帮我们实现了Intercetor拦截器)
  • 使用MyBatis提供的RowBounds对象,实现内存级别的分页

22.MyBatis中#{}和${}的区别是什么?

  • #{}是预编译处理,${}是字符串替换(可以拼接SQL,会导致SQL注入问题,存在安全隐患)。
  • MyBatis在处理#{}时,会将SQL中的#{}替换为?号,使用PreparedStatement(预编译)的set方法来赋值;MyBatis在处理 $ { } 时,就是把 ${ } 替换成变量的值。
  • 使用 #{}可以防止SQL注入,提高系统安全性;${}可以拼接SQL,会导致SQL注入问题,存在安全隐患,
end
SpringBoot
MyBatis
Spring
SpringMVC

评论区

暂无评论