SSM-----Spring
1.Spring 的两大核心是什么?谈一谈你对 IOC 的理解? 谈一谈你对 DI 的理解? 谈一谈你对 AOP的理解?(必会)
Spring 的两大核心是 IoC(Inversion of Control,控制反转) 和 AOP(Aspect-Oriented Programming,面向切面编程)。它们共同支撑了 Spring 框架 “简化开发、解耦代码” 的核心目标。
-
先说说 IoC,它本质是‘把对象的创建、依赖管理权从业务代码交给 Spring 容器’。具体来说,容器会先解析我们的配置(注解或 XML)生成 BeanDefinition(描述 Bean 的元信息,比如类型、依赖关系),再根据这个信息创建和管理 Bean。比如以前写 UserService,得手动 new UserDao;现在不用了,容器会按 BeanDefinition 自动创建 UserDao 实例,再关联到 UserService 里 —— 这不仅省了 new 的代码,更重要的是,如果 UserDao 换了实现类(比如从 JdbcDao 换成 MybatisDao),只需改配置,UserService 完全不用动,解耦效果很明显。而且容器还会管理 Bean 的生命周期,比如用 @PostConstruct 标注初始化逻辑,容器会在 Bean 创建后自动执行。
-
DI 是 IoC 的具体实现,就是‘容器创建对象时,自动把它依赖的东西注进去’。常用的构造器注入和 Setter 注入各有侧重:构造器注入适合‘必须依赖’(比如 Service 一定需要 Dao),能通过 final 关键字保证依赖不可变,还能避免循环依赖(容器会提前检测);Setter 注入更适合‘可选依赖’(比如给对象设置超时时间)。另外要注意,字段注入(直接 @Autowired 在字段上)虽然方便,但没法注入 final 字段,还可能隐藏循环依赖问题,所以我们团队一般禁用。
-
AOP 则是‘把日志、事务这些横切逻辑抽出来,和业务代码分开’。它的核心是通过动态代理在运行时织入逻辑:如果目标类有接口,用 JDK 动态代理(生成接口实现类作为代理);没接口就用 CGLIB(生成目标类的子类)。比如给 Service 加事务时,我们定义的 @Transactional 其实就是一个切面,里面的‘事务开启’是前置通知,‘提交 / 回滚’是后置通知,而‘哪些方法需要事务’是通过切入点表达式(比如 execution (* com.xxx.service…(…)))指定的。这样一来,业务方法里完全看不到事务代码,既减少重复,又方便统一修改(比如改事务隔离级别,只需改切面配置)。”
2.Spring 支持 bean 的作用域有几种吗? 每种作用域是什么样的?(必会)
Spring 支持多种 bean 作用域,主要分为通用环境(所有应用场景) 和Web 环境特有两类,每种作用域控制着 bean 实例的创建时机和生命周期:
- singleton(单例)
这是默认作用域,指在整个 Spring 容器中,该 bean 只创建一个实例,所有请求(比如依赖注入或 getBean ())都会拿到同一个对象。它的生命周期和容器一致 —— 容器启动时创建(可配置懒加载),容器销毁时才销毁。
适合无状态的 bean,比如 Service 层组件、工具类,因为单例共享实例,若有可变状态可能引发线程安全问题。 - prototype(原型)
每次请求该 bean 时(比如注入或获取),容器都会新建一个实例。容器只负责创建,不管理销毁,实例由开发者或 GC 处理。
适合有状态的 bean,比如用户的购物车对象,每个实例需要独立维护自己的数据。 - request(请求,Web 环境)
每个 HTTP 请求会对应一个新的 bean 实例,仅在当前请求范围内有效,请求处理完就销毁。
比如封装单次请求参数的对象,每次请求的数据互不干扰。 - session(会话,Web 环境)
每个用户会话(HTTP Session)共享一个 bean 实例,会话有效期内可用,会话失效(超时或退出)时销毁。
适合存储用户登录信息、会话级购物车等,比如用户在会话内操作的临时数据。 - application(应用,Web 环境)
整个 Web 应用(ServletContext)中只创建一个实例,所有用户共享,生命周期和应用一致(应用启动创建,关闭销毁)。
区别于 singleton:singleton 是 Spring 容器级单例(多容器可能多个),application 是应用级单例,比如全局系统配置参数。 - websocket(WebSocket 会话,Web 环境)
每个 WebSocket 连接对应一个独立实例,仅在连接存续期间有效,连接关闭后销毁。
适合实时通信场景,比如维护单个聊天连接的上下文信息。
实际开发中,singleton 和 prototype 最常用,Web 场景会根据需求选择 request 或 session 等作用域。
3.Spring 框架中都用到了哪些设计模式?(必会)
Spring 里用到很多经典设计模式,核心的有几种:
- 首先是工厂模式,比如 BeanFactory 和 ApplicationContext,负责创建管理 Bean,不用我们手动 new;
- 然后是单例模式,默认的 singleton Bean 就是靠它实现的,容器里只存一个实例;
- 还有代理模式,这是 AOP 的核心,JDK 动态代理(针对接口)和 CGLIB(针对无接口类)用来织入日志、事务这些增强逻辑;
- 另外像模板方法模式,比如 JdbcTemplate,定义了 JDBC 的流程,把 SQL 执行、结果映射这些可变部分留给我们;
- 还有观察者模式,比如 ApplicationEvent 和 ApplicationListener,用来做事件通信,比如容器初始化完成后触发缓存加载。这些模式本质上都是为了解耦和简化开发
4.Spring 事务的实现方式和实现原理(必会)
- 先说实现方式:
- 编程式:手动写代码控制事务,比如用 TransactionTemplate,把业务逻辑丢进它的 execute 回调里,它会自动管开启、提交,异常了就回滚。但这种侵入业务代码,实际开发里很少用,除非需要特别定制事务逻辑。
- 声明式:这是平时用得最多的,靠 @Transactional 注解,加在方法或类上就行。比如转账的 service 方法,加个 @Transactional,扣钱和加钱就会在一个事务里,任一失败都回滚,完全不用改业务代码。
- 原理的话,核心就三点:
- 一是靠事务管理器对接数据库,比如用 MyBatis 时,用 DataSourceTransactionManager,它底层是拿数据库连接,设 autoCommit=false 来控制事务;
- 二是AOP 动态代理,加了 @Transactional 的方法,Spring 会生成代理对象,在方法执行前开启事务,执行完正常就提交,抛了 RuntimeException(默认)就回滚;
- 三是靠事务定义控规则,比如注解里配 propagation=REQUIRED(默认),意思是如果当前有事务就加入,没有就新建,像调用多个 service 方法时,能保证在一个事务里。
实际项目里,基本都是用声明式,就注意下 @Transactional 的生效条件(比如非 private 方法、没抛检查异常)
5.Spring 的对象默认是单例的还是多例的? 单例 bean 存不存在线程安全问题呢?(必会)
-
Spring 中 bean 的默认作用域是 单例(singleton)—— 也就是说,默认情况下,Spring 容器中只会创建一个 bean 实例,所有请求都会共享这个实例。
-
关于单例 bean 的线程安全问题,要分情况看
- 如果是 无状态的 bean(比如大部分 Service 层组件、工具类,没有可修改的成员变量,或成员变量是常量 / 不可变对象),不存在线程安全问题。因为多线程访问时,仅调用方法,不会修改共享状态,比如一个只做查询的 Service 类。
- 如果是 有状态的 bean(存在可修改的成员变量,比如在 bean 中定义了 userName 这样的变量,且有方法会修改它),多线程并发访问并修改这些变量时,就可能出现线程安全问题(比如数据错乱)。
- 实际开发中,我们通常会避免在单例 bean 中定义可变状态,所以大部分场景下(比如 Service、Repository 层)单例 bean 是线程安全的。如果确实需要维护状态,可通过以下方式解决:
- 用 ThreadLocal 存储线程私有状态;
- 将有状态的部分抽离为 prototype 作用域的 bean;
- 对共享变量加锁(需谨慎,可能影响性能)。
简单说:单例是默认值,无状态则安全,有状态需额外处理。
6.Spring 的常用注解(必会)
- Bean 定义相关
@Component
:通用注解,标注类为 Spring 管理的 Bean(组件),适用于不确定具体层的类。
衍生注解(更语义化):@Service
:标注在业务逻辑层(Service) 类上,比如UserService,明确这是处理业务的 Bean。@Controller
:标注在Web 层(Controller) 类上,比如UserController,用于接收和处理请求。@Repository
:标注在数据访问层(Dao/Mapper) 类上,比如UserMapper,还能自动转换数据库操作异常。
- 依赖注入相关
@Autowired
:自动按类型注入依赖(默认要求依赖必须存在,可加required=false允许为 null)。比如 Service 层注入 Mapper:@Autowired private UserMapper userMapper。@Qualifier("beanName")
:配合@Autowired使用,当同一类型有多个 Bean 时,按名称指定注入哪个,比如- @Qualifier(“userServiceImpl”)。@Resource
:JDK 自带注解,默认按名称注入,找不到再按类型,比如@Resource(name = “userMapper”)。
- 配置类相关
@Configuration
:标注类为配置类(替代 XML 配置),类中用@Bean定义 Bean。@Bean
:标注在配置类的方法上,方法返回值会被注册为 Spring 的 Bean,比如定义数据源:@Bean public DataSource dataSource() { … }。
- AOP 相关
@Aspect
:标注类为切面类,用于定义横切逻辑(如日志、权限)。@Pointcut("execution(...)")
:定义切入点,指定哪些方法需要被增强,比如@Pointcut(“execution(* com.xxx.service..(…))”)表示匹配所有 Service 的方法。通知注解
:@Before(方法执行前)、@AfterReturning(方法正常返回后)、@Around(环绕方法执行,最常用),比如用@Around实现接口耗时统计。
- 事务相关
@Transactiona
l:标注在 Service 方法或类上,声明事务控制,比如转账方法加此注解,保证扣钱和加钱操作原子性。可配置传播行为(如propagation=REQUIRED)、隔离级别等。
- Spring MVC 相关
映射请求
:@RequestMapping(“/user”)(通用)、@GetMapping(“/{id}”)(GET 请求)、@PostMapping(POST 请求),用于 Controller 方法上,指定请求路径。接收参数
:@RequestParam(获取请求参数,如@RequestParam(“name”))、@PathVariable(获取 URL 路径参数,如@PathVariable(“id”))、@RequestBody(接收 JSON 请求体)。响应数据
:@ResponseBody(将返回值转为 JSON,Controller 类加@RestController可省略此类注解)。