十一. BeanFactory 和 ApplicationContext 有什么区别?

特性 BeanFactory ApplicationContext
定位 Spring框架最底层的、最基本的核心接口。 BeanFactory 的子接口,提供了更多的企业级功能。
Bean的加载时机 延迟加载。只有在第一次使用getBean()时才会创建Bean。 默认立即加载。容器启动时就会创建并配置所有的单例Bean(非懒加载的)。
功能 功能相对基础,主要负责Bean的定义、创建和访问。 在BeanFactory基础上,额外提供了:
• 国际化的消息解析(MessageSource)。
• 资源访问(如文件、URL)。
• 事件发布(ApplicationEventPublisher)。
• AOP集成
• 更方便的与Web应用集成
使用场景 在资源受限的移动设备或Applet中,内存占用非常关键时。 绝大多数场景下的标准选择,如Java SE桌面应用、Web应用等。

总结: ApplicationContext 是 BeanFactory 的完整超集。除非有特殊的内存限制,否则总是应该使用 ApplicationContext


十二. 请解释Spring Bean的生命周期?

对于一个普通的Spring Bean,其生命周期可以概括为下图所示的完整流程:


十三.Spring Bean的作用域之间有什么区别?

作用域 描述 使用场景
singleton (默认)每个Spring IoC容器中只有一个Bean实例。 无状态的Bean,如Service、Dao等。
prototype 每次请求(注入或getBean())都会创建一个新的Bean实例。 有状态的Bean,如每个请求需要独立状态的对象。
request 每个HTTP请求创建一个新的Bean。 用于存储与一次请求相关的信息。
session 每个HTTP Session创建一个新的Bean。 用于存储与用户会话相关的信息,如用户登录信息。
application 每个ServletContext生命周期内一个Bean。 用于存储全局的、应用级别的信息。
websocket 每个WebSocket会话内一个Bean。 用于WebSocket通信。

十四. 使用Spring框架的好处是什么?

  1. 轻量级与非侵入式: Spring本身是轻量级的,并且其核心容器对业务对象几乎没有侵入性(不需要实现特定接口)。

  2. 控制反转(IoC)与依赖注入(DI): 这是Spring的核心。通过IoC容器管理对象的生命周期和依赖关系,实现了组件之间的解耦,使得代码更加灵活、可测试和可维护。

  3. 面向切面编程(AOP): 将横切关注点(如日志、事务、安全)与核心业务逻辑分离,提高了代码的模块化和可重用性。

  4. 强大的事务管理: 提供了一致性的事务管理抽象,支持声明式事务和编程式事务,极大地简化了数据库事务的处理。

  5. 便捷的集成能力: 像一个“粘合剂”,可以方便地集成其他优秀的框架和技术,如Hibernate、MyBatis、JMS、Quartz等。

  6. 强大的MVC框架: Spring MVC提供了一个功能强大、高度可配置的Web框架。

  7. 异常处理: 提供了一套一致的、未检查的异常处理哲学。

  8. 测试支持: 提供了专门的测试模块,支持单元测试和集成测试。


十五.Spring 中用到了那些设计模式?

  1. 工厂模式: BeanFactory 和 ApplicationContext 是工厂模式的典型实现,用于创建和管理Bean。

  2. 单例模式: Spring管理的Bean默认就是单例的。

  3. 代理模式: Spring AOP 的核心就是使用JDK动态代理或CGLIB字节码生成技术来创建代理对象。

  4. 模板方法模式: JdbcTemplateRestTemplate 等,它们定义了算法的骨架,而将一些步骤延迟到子类或由回调函数实现。

  5. 观察者模式: Spring的事件驱动模型(ApplicationEventApplicationListener)就是观察者模式的实现。

  6. 适配器模式: 在Spring MVC中,HandlerAdapter 使得多种类型的Controller(如@ControllerHttpRequestHandler)能够被统一处理。

  7. 策略模式: 在资源访问时,Resource 接口是策略接口,不同的实现类(ClassPathResourceUrlResource)是不同的策略。

  8. 装饰者模式: 在处理数据源等场景时,Spring经常使用装饰者模式来增强功能。


十六.Spring 如何保证 Controller 并发的安全?

Spring MVC的Controller默认是单例的,但它是线程安全的吗?

答案是:这取决于Controller的状态

  • 如果Controller是无状态的: 即Controller类中没有可变的成员变量(实例变量),那么它就是线程安全的。这是推荐的、绝大多数情况下的做法

    java

    @Controller // 线程安全:没有实例变量,所有操作都在方法栈帧内完成。
    public class MyController {
        @Autowired
        private MyService myService; // 这是一个依赖,本身也应该是单例且无状态的。
    
        @RequestMapping("/test")
        public String test() {
            myService.doSomething(); // 安全
            return "view";
        }
    }
  • 如果Controller是有状态的: 即Controller类中定义了可变的实例变量,那么它就不是线程安全的,因为多个线程会共享并修改这个变量。

    java

    @Controller // 非线程安全!
    public class UnsafeController {
        private int count = 0; // 可变实例变量
    
        @RequestMapping("/count")
        public String count() {
            count++; // 非原子操作,存在竞态条件
            return "view";
        }
    }

如何保证并发安全?

  1. 最佳实践:保持无状态: 不要在Controller中定义实例变量。所有需要的数据都通过方法参数传入,在方法内部处理。

  2. 如果需要状态,改变作用域: 将Controller的作用域改为 @Scope("prototype"),这样每次请求都会创建一个新的Controller实例。但这种方法会增加GC负担,一般不推荐。

  3. 使用ThreadLocal: 可以将一些需要在线程内共享的数据(如当前用户信息)存储在ThreadLocal中。但需要确保在使用后及时清理,防止内存泄漏。

  4. 避免在Controller中定义非静态、非final的字段


十七.在 Spring中如何注入一个java集合?

Spring可以通过XML和注解两种方式注入集合。

1. XML配置方式:
使用 <list><set><map><props> (对应Properties) 标签。

xml

<bean id="collectionBean" class="com.example.CollectionBean">
    <property name="myList">
        <list>
            <value>Apple</value>
            <ref bean="someOtherBean"/>
            <value>Banana</value>
        </list>
    </property>
    <property name="myMap">
        <map>
            <entry key="key1" value="value1"/>
            <entry key="key2" value-ref="someOtherBean"/>
        </map>
    </property>
    <property name="mySet">
        <set>
            <value>Value1</value>
            <value>Value2</value>
        </set>
    </property>
</bean>

2. 注解方式(结合Java配置类):
在配置类中使用 @Bean 方法返回集合。

java

@Configuration
public class AppConfig {
    @Bean
    public List<String> myList() {
        return Arrays.asList("A", "B", "C");
    }

    @Bean
    public MyService myService() {
        MyService service = new MyServiceImpl();
        service.setMyList(myList()); // 注入list
        return service;
    }
}

或者,在属性上使用 @Autowired 或 @Resource 来自动装配一个已定义的集合Bean。


十八. Spring支持的事务管理类型?

  1. 编程式事务管理: 通过编写代码(如使用TransactionTemplate或直接使用PlatformTransactionManager)来精确控制事务的边界。

  2. 声明式事务管理(最常用): 通过配置(XML或@Transactional注解)来定义事务规则,由Spring容器通过AOP在运行时自动管理事务。这是非侵入式的,推荐使用。


十九.Spring框架的事务管理有哪些优点?

  1. 提供一致的编程模型: 无论使用JDBC、Hibernate、JPA还是其他数据访问技术,都使用相同的事务管理API和配置方式。

  2. 支持声明式事务管理: 这是最大的优点,使事务管理与业务代码解耦,开发更简单。

  3. 比复杂的事务API(如JTA)更简单: Spring提供了一个简单而强大的抽象层。

  4. 与Spring数据访问抽象层完美集成: 与JdbcTemplate、HibernateTemplate等无缝协作。

  5. 支持多种事务传播行为: 可以精细地控制事务的边界和行为。


二十.Spring MVC的主要组件?

  1. DispatcherServlet: 前端控制器,是所有请求的入口,负责协调各个组件。

  2. HandlerMapping: 根据请求的URL,找到对应的处理器(Controller)。

  3. HandlerAdapter: 实际调用处理器方法,并处理参数绑定、方法调用、返回值处理等。

  4. Controller: 处理器,执行具体的业务逻辑。

  5. ViewResolver: 根据控制器返回的逻辑视图名,解析出实际的View对象。

  6. View: 负责渲染视图,将模型数据展示给用户(如JSP、Thymeleaf模板)。

  7. HandlerExceptionResolver: 处理请求过程中抛出的异常。

  8. MultipartResolver: 处理文件上传请求。


二十一. SpringMvc怎么和AJAX相互调用的?

主要通过返回JSON或XML格式的数据来实现。

  1. 后端(Spring MVC Controller)

    • 使用 @RestController 注解,或者在有 @Controller 注解的类的方法上使用 @ResponseBody

    • 方法直接返回Java对象(如POJO、List、Map等)。

    • 配置消息转换器(如MappingJackson2HttpMessageConverter),Spring会自动使用Jackson库将返回的对象转换为JSON字符串。

    java

    @RestController
    public class UserController {
        @RequestMapping("/getUser")
        public User getUser(@RequestParam Long id) {
            User user = userService.findById(id);
            return user; // 自动转换为JSON
        }
    }
  2. 前端(AJAX调用)

    • 使用jQuery、Axios、Fetch API等发起AJAX请求。

    • 在成功回调函数中处理返回的JSON数据。

    javascript

    $.ajax({
        type: "GET",
        url: "/getUser",
        data: { id: 123 },
        success: function(user) {
            console.log("User name: " + user.name);
        }
    });

二十二.mybatis的缓存机制,一级,二级介绍一下?

MyBatis提供了两级缓存来提高查询性能。

特性 一级缓存 二级缓存
作用域 SqlSession 级别。 Mapper (Namespace) 级别。
默认开启 是。 否,需要在配置文件中显式开启(<setting name="cacheEnabled" value="true"/>)并在Mapper.xml中配置 <cache/>
共享性 同一个SqlSession内共享。 同一个namespace下的所有SqlSession共享。
失效时机 当执行 INSERT、UPDATE、DELETE 操作,或调用 sqlSession.clearCache(),或关闭SqlSession时,一级缓存会清空。 当执行同namespace下的增删改操作时,二级缓存会被清空。也可以通过配置设置刷新间隔等。
存储结构 内存中的一个简单HashMap。 可以配置使用不同的缓存实现,如Ehcache、Redis等。

工作顺序: 当执行一个查询时,MyBatis会先查找二级缓存,再查找一级缓存,如果都没有,最后才去查询数据库。

 

二十三.Mybatis的基本工作流程?

  1. 加载配置: MyBatis 从配置文件(mybatis-config.xml)和映射文件(*Mapper.xml)中加载全局配置信息和 SQL 映射信息,构建出 SqlSessionFactory 对象。

  2. 创建 SqlSession: 通过 SqlSessionFactory 创建一个 SqlSession 对象,它代表一次数据库会话。

  3. 获取 Mapper 接口代理: 通过 SqlSession 的 getMapper(Class<T> type) 方法,获取 Mapper 接口的动态代理对象。

  4. 执行 SQL: 调用 Mapper 接口的方法。MyBatis 底层会根据方法名找到对应的 SQL 语句,设置参数,然后执行 SQL。

  5. 操作数据库: SqlSession 将 SQL 操作委托给 Executor 执行器。Executor 通过 StatementHandler 操作 Statement 对象与数据库交互。

  6. 处理结果: Executor 通过 ResultSetHandler 将 JDBC 返回的 ResultSet 结果集转换为指定的 Java 对象(POJO、List、Map等)。

  7. 提交/回滚事务: 如果设置了手动提交,需要调用 sqlSession.commit()。如果发生异常,则回滚事务。

  8. 关闭 SqlSession: 最终,关闭 SqlSession,释放数据库连接等资源。

核心对象流程: SqlSessionFactory -> SqlSession -> Executor -> MappedStatement -> 数据库


二十四. 什么是MyBatis的接口绑定,有什么好处?

  • 定义: 接口绑定是指 MyBatis 允许我们只定义一个 Mapper 接口(通常是 Java 接口),而不需要编写其实现类。MyBatis 会使用 JDK 动态代理 在运行时为这个接口生成一个代理对象。

  • 绑定方式

    1. XML 配置绑定: 在 XML 映射文件中定义命名空间 (namespace) 为接口的全限定名,SQL 语句的 id 为接口的方法名。

    2. 注解绑定: 在接口的方法上直接使用 @Select@Update 等注解来编写 SQL。

  • 好处

    1. 代码更简洁: 无需编写繁琐的 DAO 实现类。

    2. 类型安全: 方法调用是类型安全的,如果方法名或参数不匹配,会在编译期就报错(相比起基于字符串的 SQL ID 调用)。

    3. IDE 支持好: 可以被 IDE 的自动补全、重构等功能很好地支持。

    4. 解耦: 将 Java 接口与 SQL 实现清晰地分离开。


二十五. MyBatis的编程步骤?

  1. 创建 SqlSessionFactory

    java

    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
  2. 通过 SqlSessionFactory 创建 SqlSession

    java

    SqlSession session = sqlSessionFactory.openSession();
  3. 通过 SqlSession 执行数据库操作(两种方式):

    • 旧方式(不推荐): 直接调用 session.selectOne()session.insert() 等方法,通过字符串指定 SQL ID。

    • 推荐方式: 获取 Mapper 接口的代理对象,然后调用其方法。

      java

      UserMapper userMapper = session.getMapper(UserMapper.class);
      User user = userMapper.selectUserById(1);
  4. 提交事务: session.commit(); (如果设置为手动提交)。

  5. 关闭会话: session.close();


二十六. JDBC编程有哪些不足之处,MyBatis是如何解决这些问题的?

JDBC 不足 MyBatis 解决方案
1. 代码繁琐,大量重复样板代码 (创建连接、声明Statement、处理结果集、关闭资源) 自动化: 封装了底层操作,开发者只需关注 SQL 和映射。
2. 硬编码 (SQL、数据库连接参数写在代码中) 配置化: SQL 和配置写在 XML 或注解中,与代码解耦。
3. 手动设置参数和映射结果集 (需要手动将 ResultSet 字段一个个取出并设置到对象属性中) 自动映射: 通过反射或自定义 ResultMap,自动完成 POJO 的属性填充。
4. 数据库连接管理繁琐 集成连接池: 方便地与各种连接池(如Druid, HikariCP)集成,管理连接生命周期。
5. 功能单一 (不支持缓存、延迟加载等高级特性) 提供高级功能: 内置一级/二级缓存、延迟加载、插件机制等。

二十七. MyBatis的优缺点?

优点:

  1. SQL 灵活可控: 开发者可以编写和优化 SQL,特别适合复杂查询和性能要求高的场景。

  2. 学习成本低: 相对于 Hibernate 等全自动 ORM,更容易上手。

  3. 与 JDBC 相比代码量大大减少

  4. 很好的与各种数据库兼容(因为 SQL 是自己写的)。

  5. 提供了映射标签,支持对象与数据库的字段映射

  6. 提供了 XML 标签,支持编写动态 SQL

缺点:

  1. SQL 工作量大: 当字段多、关联表多时,编写 SQL 和映射文件是负担。

  2. 数据库依赖性高: 由于 SQL 是自己写的,数据库切换成本比 Hibernate 高。

  3. 半自动: 需要自己编写 SQL,不像全自动 ORM 那样完全屏蔽数据库差异。


二十八. 谈谈你对SpringMVC的理解?

Spring MVC 是一个基于 Java 的、实现了 MVC 设计模式的请求驱动类型的轻量级 Web 框架。它通过一套注解,让一个简单的 Java 类成为处理请求的控制器,而无须实现任何接口。它支持 RESTful 编程风格的请求。

核心思想: 通过 DispatcherServlet 这个“总调度员”,将复杂的 Web 应用请求处理流程标准化、组件化,使得各组件(控制器、视图解析器等)职责单一,松散耦合,易于扩展和测试。


二十九. 简述Mybatis的插件运行原理,以及如何编写一个插件?

  • 运行原理: MyBatis 的插件本质上是一种拦截器,使用 JDK 动态代理。它允许拦截并增强 MyBatis 核心组件的方法调用。
    MyBatis 有四大核心对象可以被拦截:

    • Executor (执行器)

    • ParameterHandler (参数处理器)

    • ResultSetHandler (结果集处理器)

    • StatementHandler (语句处理器)
      插件会为这些目标对象创建一个代理对象。当调用代理对象的方法时,会先执行插件中定义的拦截逻辑。

  • 编写步骤

    1. 实现 Interceptor 接口,并重写 intercept 方法(在此编写增强逻辑)。

    2. 使用 @Intercepts 和 @Signature 注解指定要拦截哪个对象的哪个方法。

    3. 在 mybatis-config.xml 中注册这个插件。

    示例(分页插件原理)

    java

    @Intercepts({
        @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
    })
    public class MyPlugin implements Interceptor {
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            System.out.println("在执行SQL之前做一些事情...");
            // 继续执行原方法
            Object result = invocation.proceed();
            System.out.println("在执行SQL之后做一些事情...");
            return result;
        }
    }

三十. Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?

  • 支持

  • 实现原理: 基于 动态代理

    1. 配置: 在全局配置中开启延迟加载 lazyLoadingEnabled=true

    2. 触发: 当查询主对象时,MyBatis 只执行主 SQL。对于关联对象(一对一、一对多),MyBatis 不会立即查询,而是返回一个代理对象

    3. 加载时机: 当程序第一次真正访问这个代理对象的某个方法(如 getter 方法)时,代理对象会触发一次新的 SQL 查询,去数据库加载真正的关联数据。

例如: 查询 Order 对象,它有一个 User 属性。默认只查 Order,当调用 order.getUser().getName() 时,才会去执行查询 User 的 SQL。


 

 

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐