# Spring

# 01. @Value 注解的作用是什么?

参考答案

基于 @Value 的注解可以读取 properties 配置文件,使用如下:

# 02. Spring 通知类型有哪些?

参考答案

Spring 通知类型总共有 5 种:前置通知、环绕通知、后置通知、异常通知、最终通知。

  • 前置通知(Before advice):在目标方法执行之前执行的通知。在某连接点( join point )之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)。

  • 环绕通知(Around Advice):在目标方法执行之前和之后都可以执行额外代码的通知,也可以选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。

  • 后置通知(After (finally) advice):目标方法执行之后(某连接点退出的时候)执行的通知(不论是正常返回还是异常退出)。

  • 异常后通知(After throwing advice):在方法抛出异常退出时执行的通知。

  • 最终通知(After returning advice):在某连接点(join point)正常完成后执行的通知,例如,一个方法没有抛出任何异常,正常返回。

# 03. 怎么理解 Spring 中的 IOC 容器?

参考答案

Spring IOC 就是把创建对象的权利交给框架去控制,而不需要人为的去创建,这样就实现了可插拔式的接口编程,有效地降低代码的耦合度,降低了扩展和维护的成本。

比如,去某地旅游不再用自己亲自为订购 A 酒店还是 B 酒店而发愁了,只需要把住店的需求告诉给某个托管平台,这个托管平台就会帮你订购一个既便宜又舒适的酒店,而这个帮你订购酒店的行为就可以称之为控制反转。

# 04. 怎么理解 Spring 中的依赖注入?

参考答案

依赖注入是指组件之间的依赖关系由容器在运行期决定,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

# 05. IoC 和 DI 有什么关系?

参考答案

IoC 是 Spring 中一个极为重要的概念,提供了对象管理的功能,从而省去了人为创建麻烦,而 DI 正是实现 IoC 的方法和手段。

# 06. @Component 和 @Bean 有什么区别?

参考答案

答:它们的作用对象不同:

  • @Component 作用于类;

  • @Bean 注解作用于方法。

@Component 通常是通过类路径扫描来自动侦测和装配对象到 Spring 容器中,比如 @ComponentScan 注解就是定义扫描路径中的类装配到 Spring 的 Bean 容器中;

@Bean 注解是告诉 Spring 这是某个类的实例,当我需要用它的时把它给我,@Bean 注解比 @Component 注解自定义性更强,很多地方我们只能通过 @Bean 注解来注册 Bean,比如当我们引用第三方库中的类需要装配到 Spring容器时,则只能通过 @Bean 来实现,比如以下示例,只能通过 @Bean 注解来实现:

public class WireThirdLibClass {
    @Bean
    public ThirdLibClass getThirdLibClass() {
        return new ThirdLibClass();
    }
}

# 07. Spring 中 bean 的作用域有几种类型?

参考答案

Spring 中 bean 的作用域有四种类型,如下列表:

  • 单例(Singleton):整个应用程序,只创建 bean 的一个实例;
  • 原型(Prototype):每次注入都会创建一个新的 bean 实例;
  • 会话(Session):每个会话创建一个 bean 实例,只在 Web 系统中有效;
  • 请求(Request):每个请求创建一个 bean 实例,只在 Web 系统中有效。

Spring 默认的是单例模式。

# 08. 什么是 Spring 的内部 bean?

参考答案

当一个 bean 仅被用作另一个 bean 的属性时,它能被声明为一个内部 bean,为了定义 inner Bean,在 Spring 的基于 XML 的配置元数据中,可以在 <property/><constructor-arg/> 元素内使用 <bean/> 元素,内部 bean 通常是匿名的,它们的 Scope 一般是 prototype 。

# 09. Spring 注入方式有哪些?

参考答案

Spring 的注入方式包含以下五种:

  • setter 注入
  • 构造方法注入
  • 注解注入
  • 静态工厂注入
  • 实例工厂注入

其中最常用的是前三种,官方推荐使用的是注解注入,相对使用更简单,维护成本更低,更直观。

# 10. 在 Spring 中如何操作数据库?

参考答案

在 Spring 中操作数据库,可以使用 Spring 提供的 JdbcTemplate 对象,JdbcTemplate 类提供了很多便利的方法,比如把数据库数据转变成基本数据类型或对象,执行自定义的 SQL 语句,提供了自定义的数据错误处理等,JdbcTemplate 使用示例如下:

@Autowired
private JdbcTemplate jdbcTemplate;

// 新增
@GetMapping("save")
public String save() {
    String sql = "INSERT INTO USER (USER_NAME,PASS_WORD) VALUES ('laowang','123')";
    int rows = jdbcTemplate.update(sql);
    return "执行成功,影响" + rows + "行";
}

// 删除
@GetMapping("del")
public String del(int id) {
    int rows= jdbcTemplate.update("DELETE FROM  USER  WHERE ID = ?",id);
    return "执行成功,影响" + rows + "行";
}

// 查询
@GetMapping("getMapById")
public Map getMapById(Integer id) {
    String sql = "SELECT * FROM USER WHERE ID = ?";
    Map map= jdbcTemplate.queryForMap(sql,id);
    return map;
}

# 11. Spring 的 JdbcTemplate 对象和 JDBC 有什么区别?

参考答案

Spring 的 JdbcTemplate 是对 JDBC API 的封装,提供更多的功能和更便利的操作,比如 JdbcTemplate 拥有:

  • JdbcTemplate 是线程安全的;
  • 实例化操作比较简单,仅需要传递 DataSource;
  • 自动完成资源的创建和释放工作;
  • 创建一次 JdbcTemplate,到处可用,避免重复开发。

# 12. Spring 有几种实现事务的方式?

参考答案

Spring 实现事务有两种方式:编程式事务和声明式事务。

编程式事务,使用 TransactionTemplate 或 PlatformTransactionManager 实现,示例代码如下:

private final TransactionTemplate transactionTemplate;

public void add(User user) throws Exception{
    // Spring编码式事务,回调机制
    transactionTemplate.execute(new TransactionCallback<Object>() {
        @Override
        public Object doInTransaction(TransactionStatus status) {
            try {
                userMapper.insertSelective(user);
            } catch (Exception e) {
                // 异常,设置为回滚
                status.setRollbackOnly();
                throw e;
            }
            return null;
        }
    });
}

如果有异常,调用 status.setRollbackOnly() 回滚事务,否则正常执行 doInTransaction() 方法,正常提交事务。

如果事务控制的方法不需要返回值,就可以使用 TransactionCallbackWithoutResult(TransactionCallback 接口的抽象实现类)示例代码如下:

public void add(User user) throws Exception {
    // Spring编码式事务,回调机制
    transactionTemplate.execute(new TransactionCallbackWithoutResult() {
        @Override
        protected void doInTransactionWithoutResult(TransactionStatus status) {
            try {
                userMapper.insertSelective(user);
            } catch (Exception e) {
            // 异常,设置为回滚
            status.setRollbackOnly();
                throw e;
            }
        }
    });
}

声明式事务,底层是建立在 Spring AOP 的基础上,在方式执行前后进行拦截,并在目标方法开始执行前创建新事务或加入一个已存在事务,最后在目标方法执行完后根据情况提交或者回滚事务。

声明式事务的优点:不需要编程,减少了代码的耦合,在配置文件中配置并在目标方法上添加 @Transactional 注解来实现,示例代码如下:

@Transactional
public void save() {
    User user = new User("laowang");
    userMapper.insertSelective(user);
    if (true) {
        throw new RuntimeException("异常");
    }
}

抛出异常,事务会自动回滚,如果方法正常执行,则会自动提交事务。

# 13. Spring 事务隔离级别有哪些?

参考答案

Spring 的注入方式包含以下五种:

  • ISOLATION_DEFAULT:用底层数据库的设置隔离级别,数据库设置的是什么我就用什么;
  • ISOLATIONREADUNCOMMITTED:未提交读,最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读);
  • ISOLATIONREADCOMMITTED:提交读,一个事务提交后才能被其他事务读取到(会造成幻读、不可重复读),SQL server 的默认级别;
  • ISOLATIONREPEATABLEREAD:可重复读,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(会造成幻读),MySQL 的默认级别;
  • ISOLATION_SERIALIZABLE:序列化,代价最高最可靠的隔离级别,该隔离级别能防止脏读、不可重复读、幻读。

默认值为 ISOLATION_DEFAULT 遵循数据库的事务隔离级别设置。

# 14. Spring 声明式事务无效可能的原因有哪些?

参考答案

可能的原因如下:

  • MySQL 使用的是 MyISAM 引擎,而 MyISAM 是不支持事务的;
  • @Transactional 使用在非 public 方法上,@Transactional 注解只能支持 public 级别,其他类型声明的事务不会生效;
  • @Transactional 在同一个类中无事务方法 A() 内部调用有事务方法 B(),那么此时 B() 事物不会生效。

# 15. Spring 中的 AOP 的底层实现原理是什么?

参考答案

Spring AOP 的底层实现原理就是动态代理。

Spring AOP 的动态代理有两种实现方式,对于接口使用的是 JDK 自带的动态代理来实现的,而对比非接口使用的是 CGLib 来实现的。

# 16. Spring 中的 Bean 是线程安全的吗?

参考答案

Spring 中的 Bean 默认是单例模式,Spring 框架并没有对单例 Bean 进行多线程的封装处理,因此默认的情况 Bean 并非是安全的,最简单保证 Bean 安全的举措就是设置 Bean 的作用域为 Prototype(原型)模式,这样每次请求都会新建一个 Bean 。

# 17. 说一下 Spring 中 Bean 的生命周期?

参考答案

Spring 中 Bean 的生命周期如下:

  1. 实例化 Bean:对于 BeanFactory 容器,当客户向容器请求一个尚未初始化的 Bean 时,或初始化 Bean 的时候需要注入另一个尚未初始化的依赖时,容器就会调用 createBean 进行实例化。对于 ApplicationContext 容器,当容器启动结束后,通过获取 BeanDefinition 对象中的信息,实例化所有的 Bean;

  2. 设置对象属性(依赖注入):实例化后的对象被封装在 BeanWrapper 对象中,紧接着 Spring 根据 BeanDefinition 中的信息以及通过 BeanWrapper 提供的设置属性的接口完成依赖注入;

  3. 处理 Aware 接口:Spring 会检测该对象是否实现了 xxxAware 接口,并将相关的 xxxAware 实例注入给 Bean:

    • 如果这个 Bean 已经实现了 BeanNameAware 接口,会调用它实现的 setBeanName(String BeanId) 方法,此处传递的就是 Spring 配置文件中 Bean 的 id 值;
    • 如果这个 Bean 已经实现了 BeanFactoryAware 接口,会调用它实现的 setBeanFactory() 方法,传递的是 Spring 工厂自身;
    • 如果这个 Bean 已经实现了 ApplicationContextAware 接口,会调用 setApplicationContext(ApplicationContext) 方法,传入 Spring 上下文;
  4. BeanPostProcessor:如果想对 Bean 进行一些自定义的处理,那么可以让 Bean 实现了 BeanPostProcessor 接口,那将会调用 postProcessBeforeInitialization(Object obj, String s) 方法;

  5. InitializingBean 与 init-method:如果 Bean 在 Spring 配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法;

  6. 如果这个 Bean 实现了 BeanPostProcessor 接口,将会调用 postProcessAfterInitialization(Object obj, String s) 方法;由于这个方法是在 Bean 初始化结束时调用的,因而可以被应用于内存或缓存技术;

    以上几个步骤完成后,Bean 就已经被正确创建了,之后就可以使用这个 Bean 了。

  7. DisposableBean:当 Bean 不再需要时,会经过清理阶段,如果 Bean 实现了 DisposableBean 这个接口,会调用其实现的 destroy() 方法;

  8. destroy-method:最后,如果这个 Bean 的 Spring 配置中配置了 destroy-method 属性,会自动调用其配置的销毁方法。

# 18. Spring 有哪些优点?

参考答案

Spring 优点如下:

  • 开源免费的热门框架,稳定性高、解决问题成本低;

  • 方便集成各种优秀的框架;

  • 降低了代码耦合性,通过 Spring 提供的 IoC 容器,我们可以将对象之间的依赖关系交由 Spring 进行控制,避免硬编码所造成的过度程序耦合;

  • 方便程序测试,在 Spring 里,测试变得非常简单,例如:Spring 对 Junit 的支持,可以通过注解方便的测试 Spring 程序;

  • 降低 Java EE API 的使用难度,Spring 对很多难用的 Java EE API(如 JDBC、JavaMail、远程调用等)提供了一层封装,通过 Spring 的简易封装,让这些 Java EE API 的使用难度大为降低。

# 19. Spring、SpringBoot、SpringCloud 的区别是什么?

参考答案

它们的区别如下:

  • Spring Framework 简称 Spring,是整个 Spring 生态的基础。

  • Spring Boot 是一个快速开发框架,让开发者可以迅速搭建一套基于 Spring 的应用程序,并且将常用的 Spring 模块以及第三方模块,如 MyBatis、Hibernate 等都做了很好的集成,只需要简单的配置即可使用,不需要任何的 XML 配置文件,真正做到了开箱即用,同时默认支持 JSON 格式的数据,使用 Spring Boot 进行前后端分离开发也非常便捷。

  • Spring Cloud 是一套整合了分布式应用常用模块的框架,使得开发者可以快速实现微服务应用。作为目前非常热门的技术,有关微服务的话题总是在各种场景下被大家讨论,企业的招聘信息中也越来越多地出现对于微服务架构能力的要求。

# 20. Spring 中都是用了哪些设计模式?

参考答案

Spring 中使用的设计模式如下:

  • 工厂模式:通过 BeanFactory、ApplicationContext 来创建 bean 都是属于工厂模式;

  • 单例、原型模式:创建 bean 对象设置作用域时,就可以声明 Singleton(单例模式)、Prototype(原型模式)

  • 察者模式:Spring 可以定义一下监听,如 ApplicationListener 当某个动作触发时就会发出通知;

  • 责任链模式:AOP 拦截器的执行;

  • 策略模式:在创建代理类时,如果代理的是接口使用的是 JDK 自身的动态代理,如果不是接口使用的是 CGLIB 实现动态代理。