Spring八股
Spring IOC
是spring的两大重要特性,IOC就是控制反转、注入依赖么。他就是Spring将我们的类通过注解的方式,将他的创建和生命周期都放到Spring容器中统一管理,这样我们其他的类如果需要调用某个类就可以直接到Spring的容器工厂中去找。spring也提供了很多注解能让我们之间注册bean到容器中,以及注入使用的注解。
什么是Spring
Spring是Java开发框架,通过可扩展的模型来开发Java应用程序,Spring的核心包括依赖注入和控制反转、面向切面编程、声明式事务、MVC框架等;Spring框架的核心是Spring容器,它负责管理应用程序中的对象,容器使用依赖注入来自动注入依赖项。
依赖注入和控制反转:将对象的创建和管理反转到Spring容器当中,容器根据配置和文件来帮助创建对象和维护依赖关系,通过依赖注入实现,当在容器中创建对象A的时候,发现标注依赖了B,就会将B创建出来塞到A里面。使得代码解耦,并且更容易进行单元测试和集成测试
面向切面编程:可以将通用的横切关注点(日志记录,事务管理)从代码中分离出来,提高了应用程序的可维护性和扩展性
MVC框架:使得我们可以轻松的构建一个WEB应用
依赖注入的方式
Spring支持三种依赖注入方式:
-
构造函数注入:通过在构造函数中声明需要注入的依赖项,容器创建对象时会自动将依赖项注入到构造函数中
- 优点:保证依赖不可变、保证对象初始化完成、适合必需依赖
- 缺点:参数较多时构造函数臃肿
-
Setter注入:通过在类中声明Setter方法,并在方法中传入需要注入的依赖项,对象创建的时候会调用方法注入依赖项
- 优点:灵活、可选依赖、可以随时修改
- 缺点:无法保证对象初始化完成
-
字段注入:通过在类中通过注解标明需要注入的依赖项的字段,容器在创建后自动注入依赖项
- 优点:代码简洁
- 缺点:无法设置final、不推荐使用、容易NPE
推荐使用构造函数注入,对于可选依赖可以使用Setter注入。
Spring AOP
AOP就是IOC的一个扩展,他是面向切面编程,其实就是一个动态代理,通常情况他的作用就是能实现我们非业务的代码,不去影响实际的业务,其实就是在不动已经完成的业务代码,想要在加上非业务的代码的时候会用到AOP思想,比如要看日志,算接口时间,事务管理等。
比如有一个user类,我想在它的add方法前后增加日志,通过AOP,Spring并不会入侵代码,而是创建一个和user类一模一样的代理对象,以后调用的其实是这个代理对象,代理对象会先执行日志任务再调用真正的user类中的add方法。Spring 会根据目标对象的类型,自动选一种代理方式:第一种是JDK动态代理,目标对象有接口时用,比如UserService实现了IUserService接口,Spring 就用 JDK 自带的工具生成一个实现了IUserService接口的代理类。
这个代理类里会持有真正的UserService对象,当你调用addUser()时,代理类会先跑日志代码,再调用UserService的addUser()。
第二个是CGLIB动态代理,目标对象无接口的时候,Spring 就用 CGLIB 生成一个继承UserService的子类当代理,子类会重写addUser()方法,在重写的方法里先执行日志逻辑,再调用父类(真正的UserService)的addUser()。
循环注入和循环依赖
循环依赖是指两个或多个Bean之间相互依赖,形成一个环形依赖。Spring通过三级缓存来解决循环依赖问题:
- 一级缓存(singletonObjects):存放完全初始化好的单例Bean
- 二级缓存(earlySingletonObjects):用于存储实例化完成但初始化未完成的Bean
- 三级缓存(singletonFactories):存储的工厂对象,用于生成Bean的代理对象或原始对象
注意:Spring只能解决单例Bean的Setter注入的循环依赖,构造函数注入的循环依赖会直接抛出异常。
Bean的生命周期
IOC的加载其实就是一个Bean的创建么,Bean从创建到销毁就是它的生命周期,首先是类实例化,它是通过反射去找类的构造函数实例化的。再属性赋值,解析自动装配的,一般用@autowired或者@resource,解决了循环依赖的问题。接着是初始化,初始化的时候调用初始化生命周期回调,这个时候就可以使用该类了,最后容器关闭的时候,调用销毁生命周期回调,将Bean进行一个销毁处理。
Spring 的自动装配过程如何识别要装配的类?会扫描全部类吗?
Spring 识别要装配的类主要通过 组件扫描(Component Scan),而非扫描项目中的全部类,具体逻辑如下:他会扫描spring用注解标记的一些类,将标记的类注册为 Bean。开发者需在配置中指定扫描的包路径(如@ComponentScan("com.example")),Spring 仅扫描该路径下的类,不会扫描所有类,以此提升效率。
Spring Boot 自动配置原理?
- 注解驱动:
@SpringBootApplication包含@EnableAutoConfiguration,触发自动配置; - SPI 机制:Spring 扫描
META-INF/spring.factories,加载所有AutoConfiguration类; - 条件装配:通过
@Conditional(如@ConditionalOnClass、@ConditionalOnBean)判断环境是否满足,满足则自动创建 Bean。
@Component和@Bean
首先在用途方面和使用方法不同,Component注解是标识普通的类,spring会通过Componentscan扫描将类注册到spring IOC容器中。@bean一般是在配置类中的方法,声明和手动配置一个bean对象。Component注解是spring自动创建和初始化的,bean注解可以手动控制bean的创建和配置过程
@autowired和@resource
@Autowired 是 Spring 框架提供的注解,默认按类型(byType)注入,若存在多个同类型 bean,需配合 @Qualifier 指定名称;而 @Resource 是 JDK 提供的注解,默认按名称(byName)注入,名称匹配失败时会 fallback 到按类型匹配。Resource支持属性配置可以配置name type等
Spring中用到的设计模式
Spring框架中使用了多种设计模式:
- 工厂模式:BeanFactory和ApplicationContext是工厂模式的实现,用于创建和管理Bean对象
- 单例模式:Spring Bean默认是单例的,通过Spring容器保证唯一性
- 代理模式:AOP的核心就是代理模式,通过JDK动态代理或CGLIB代理实现
- 模板方法模式:JdbcTemplate、RestTemplate等使用了模板方法模式,将不变的部分封装在父类,可变的部分留给子类实现
- 观察者模式:ApplicationEvent和ApplicationListener实现了事件监听机制
- 适配器模式:HandlerAdapter等使用了适配器模式,将不同的接口适配成统一的接口
Bean的作用域
Spring Bean支持多种作用域:
- singleton(单例):默认作用域,整个容器中只有一个Bean实例
- prototype(原型):每次获取Bean时都创建一个新的实例
- request:每个HTTP请求创建一个Bean实例,仅在Web应用中有效
- session:每个HTTP Session创建一个Bean实例,仅在Web应用中有效
- application:ServletContext生命周期,仅在Web应用中有效
默认使用singleton作用域,对于有状态的Bean应该使用prototype作用域。
对Spring的AOP理解
AOP(Aspect-Oriented Programming)面向切面编程是一种编程思想,它将通用的横切关注点(如日志、事务、权限等)从业务逻辑中分离出来。
AOP的核心概念
- 切面(Aspect):横切关注点的模块化,比如日志切面、事务切面
- 连接点(Join Point):程序执行的某个特定位置,比如方法调用、异常处理
- 切点(Pointcut):匹配连接点的表达式,定义在哪里应用通知
- 通知(Advice):在切点执行的动作,比如前置通知、后置通知、环绕通知
- 织入(Weaving):将切面应用到目标对象并创建代理对象的过程
- 目标对象(Target Object):被通知的对象
AOP的实现方式
Spring AOP通过动态代理实现:
- JDK动态代理:目标对象实现了接口,使用JDK动态代理
- CGLIB代理:目标对象没有实现接口,使用CGLIB生成子类代理
Spring框架中的单例Bean是线程安全的吗
Spring框架中的单例Bean本身不是线程安全的,但Spring容器通过依赖注入来管理Bean,对于无状态的Bean(比如Service、DAO),它们是线程安全的。对于有状态的Bean,Spring提供了以下解决方案:
- 使用ThreadLocal:将状态存储在ThreadLocal中,每个线程都有自己独立的副本
- 使用原型作用域:将Bean的作用域设置为prototype,每次获取都创建新实例
- 使用同步锁:通过synchronized或Lock保证线程安全,但会影响性能
- 使用并发容器:比如ConcurrentHashMap、CopyOnWriteArrayList等
建议尽量设计无状态的Bean,避免线程安全问题。
对Spring事务的理解
Spring事务是基于AOP实现的声明式事务管理,它将事务管理从业务代码中分离出来,通过注解或XML配置的方式管理事务。
Spring事务的特性
- ACID特性:原子性、一致性、隔离性、持久性
- 事务管理器:DataSourceTransactionManager、JtaTransactionManager等
- 事务传播:定义了多个事务方法相互调用时的行为
- 事务隔离级别:READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ、SERIALIZABLE
@Transactional注解
@Transactional可以标注在类或方法上,用于开启事务管理。常用的属性包括:
- propagation:事务传播行为
- isolation:事务隔离级别
- timeout:事务超时时间
- readOnly:是否只读事务
- rollbackFor:指定哪些异常回滚事务
- noRollbackFor:指定哪些异常不回滚事务
Spring事务什么情况下会失效
Spring事务在某些情况下会失效,需要注意:
- 方法不是public:@Transactional只能作用在public方法上,private、protected方法会失效
- 方法内部调用:同一个类中,一个事务方法调用另一个事务方法,内部调用的方法事务会失效
- 解决:将方法拆分到不同的Bean中,或者通过注入自身调用代理对象的方法
- 异常处理不当:默认只回滚RuntimeException和Error,如果抛出CheckedException且没有指定rollbackFor,事务不会回滚
- 解决:指定@Transactional(rollbackFor = Exception.class)
- 数据库引擎不支持事务:比如MySQL的MyISAM引擎不支持事务
- 未被Spring管理:如果类没有被Spring容器管理(比如通过new创建的对象),事务会失效
- 解决:确保类是通过Spring容器获取的
事务的传播机制
事务的传播机制定义了多个事务方法相互调用时的行为,Spring定义了7种传播行为:
- REQUIRED(默认):如果当前存在事务,则加入该事务;如果没有事务,则创建一个新事务
- SUPPORTS:如果当前存在事务,则加入该事务;如果没有事务,则以非事务方式执行
- MANDATORY:如果当前存在事务,则加入该事务;如果没有事务,则抛出异常
- REQUIRES_NEW:创建一个新事务,如果当前存在事务,则挂起当前事务
- NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,则挂起当前事务
- NEVER:以非事务方式执行,如果当前存在事务,则抛出异常
- NESTED:如果当前存在事务,则在嵌套事务中执行;如果没有事务,则创建一个新事务
Spring MVC的执行流程
Spring MVC的执行流程如下:
- 用户发送请求:用户通过浏览器发送HTTP请求到服务器
- DispatcherServlet接收请求:前端控制器DispatcherServlet接收请求,它是Spring MVC的核心
- HandlerMapping映射处理器:DispatcherServlet调用HandlerMapping,根据请求URL找到对应的Handler(Controller方法)
- HandlerAdapter执行处理器:DispatcherServlet调用HandlerAdapter,执行Controller方法
- Controller处理业务逻辑:Controller调用Service层处理业务逻辑,返回ModelAndView
- ViewResolver解析视图:DispatcherServlet调用ViewResolver,根据视图名称解析出具体的View
- 渲染视图:View将Model数据渲染到页面(比如JSP、Thymeleaf等)
- 返回响应:将渲染好的页面返回给用户
1 | 用户请求 → DispatcherServlet → HandlerMapping → HandlerAdapter → Controller |
Spring Boot自动配置
为什么使用Spring Boot
Spring Boot简化了Spring应用的初始搭建和开发过程,提供了开箱即用的默认配置:
- 简化配置:自动配置Spring应用,减少XML配置
- 内嵌服务器:内嵌Tomcat、Jetty等服务器,无需部署WAR包
- 自动依赖管理:通过starter简化依赖配置
- 生产就绪:提供监控、健康检查等生产级特性
- 快速开发:提高开发效率,快速搭建微服务应用
Spring Boot自动装配原理
Spring Boot自动装配的核心是:
- 注解驱动:@SpringBootApplication包含@EnableAutoConfiguration,触发自动配置
- SPI机制:Spring扫描META-INF/spring.factories,加载所有AutoConfiguration类
- 条件装配:通过@Conditional(如@ConditionalOnClass、@ConditionalOnBean)判断环境是否满足,满足则自动创建Bean
- 配置绑定:通过@ConfigurationProperties将配置文件中的属性绑定到Bean
Spring Boot常见注解
- @SpringBootApplication:启动类注解,包含@Configuration、@EnableAutoConfiguration、@ComponentScan
- @RestController:组合了@Controller和@ResponseBody,用于RESTful接口
- @RequestMapping:映射HTTP请求到Controller方法
- @GetMapping、@PostMapping:GET、POST请求的快捷映射
- @Autowired:自动注入依赖
- @Component:标识一个组件,被Spring容器管理
- @Configuration:标识配置类
- @Value:注入配置文件中的属性
Spring Boot开启事务
Spring Boot默认已经开启事务管理,只需在Service层方法上添加@Transactional注解即可:
1 |
|
过滤器和拦截器
过滤器(Filter):Servlet规范的一部分,可以拦截请求和响应,主要用于字符编码、跨域处理等
拦截器(Interceptor):Spring MVC特有的,可以访问Controller和Model,主要用于权限控制、日志记录等
区别:
- 拦截器基于Java的反射机制,过滤器基于函数回调
- 拦截器不依赖Servlet容器,过滤器依赖Servlet容器
- 拦截器只能对Controller请求起作用,过滤器可以对几乎所有请求起作用
- 拦截器可以访问IOC容器中的Bean,过滤器不能
Spring Boot和Spring MVC的区别
- Spring MVC:是Spring框架的一个模块,提供Web MVC功能
- Spring Boot:是基于Spring的快速开发框架,默认集成了Spring MVC
Spring Boot简化了Spring MVC的配置,提供了自动配置和starter依赖。
跨域问题
跨域是浏览器同源策略限制,Spring Boot解决跨域的方式:
- @CrossOrigin注解:在Controller或方法上添加@CrossOrigin注解
- 全局配置:实现WebMvcConfigurer接口,添加CorsMapping
- Nginx反向代理:通过Nginx配置反向代理解决跨域
MyBatis
#{}和${}的区别
- #{}:预编译SQL,使用占位符,可以防止SQL注入,传入的是参数值
- ${}:字符串拼接,直接替换SQL片段,不能防止SQL注入,传入的是字段名或表名
推荐使用#{},只有在动态表名或字段名时才使用${}。
MyBatis动态SQL
MyBatis提供了多种动态SQL标签:
- if:条件判断
- choose/when/otherwise:多条件选择
- trim/where/set:处理SQL拼接问题
- foreach:循环遍历集合
SSM
Spring + Spring MVC + MyBatis 三个框架的组合,是 Java 企业级开发中经典的 Web 应用架构。这三个框架各司其职,共同构成了一个完整的分层开发体系:
- Spring:负责全局管理(IoC 容器、AOP 切面)和组件协调;
- Spring MVC:作为表现层框架,处理 HTTP 请求和响应;
- MyBatis:作为数据访问层框架,简化数据库操作。





