spring-review

103 阅读13分钟

Spring

什么是Spring IOC容器

Spring的核心就是提供了一个IoC容器。 容器创建对象,将它们装配在一起,配置并管理它们的整个生命周期。 容器使用依赖注入的方式来管理组成应用程序的组件。 容器通过读取提供的配置元数据来接收对象进行实例化、配置和组装的命令。该元数据可以通过xml,注解,java代码提供。

什么是依赖注入?可以通过多少种方式完成依赖注入?

Spring的依赖注入:

  • 你不必创建对象,但必须描述他们如何创建。
  • 你不是在代码中直接将组件和服务连接在一起,而是描述配置文件中哪些组件需要哪些服务。有IOC容器将他们装配在一起。

Spring 框架允许 setter注入和构造函数注入

BeanFactory 和 ApplicationContext 对比

都是IOC提供的容器,ApplicationContext 继承自 BeanFactory

BeanFactory: 懒加载(按需加载),启动占用资源小, 运行慢 ApplicationContext: 即时加载(一次性加载),启动占用资源大(内存占用大),运行快, 支持:国际化、事件、通知机制

Spring提供了哪些配置方式

  • xml
  • 注解

Spring 支持集中scope

  • Singleton, 容器中只有一个实例
  • pr'ototype, 我们每次调用getBean(Class),容器都返回一个新的实例
  • Request
  • Session
  • Global-session
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) // @Scope("prototype")
public class MailSession {
    ...
}

将一个类申明为bean的注解有哪些

  • @Component, 通用的注解,可以标注任意类为Spring组件,如果不知道一个类属于哪一层,可以使用此 @Component 注解
  • @Controller, 对应Spring MVC的控制层,主要用于接收用户请求并调用Service层返回数据给用户。
  • @Service, 对应业务层,主要涉及一些复杂的逻辑,需要用到Dao层。
  • @Repository, 对应持久层即Dao层,主要用于数据库相关操作。

Spring bean 的生命周期

这里我们说的 Spring Bean 的生命周期主要指的是 s'ingleton bean

创建过程

  1. 首先实例化bean,并设置bean属性
  2. 根据其实现的Aware接口设置依赖信息。
  3. 接下来调用 BeanPostProcess的前置初始化方法postProcessBeforInitialization方法,去做初始化前的自定义工作。 afterPropertiesSet方法,去做一些属性被设定后的自定义工作 调用bean自身的init方法,去做初始化相关的操作 调用postProcessAfterInitialization,去做初始化之后的自定义工作

销毁过程

如果实现了 disposableBean 的 destroy 方法,则调用 destroy 方法。 如果实现了自定义销毁方法,则调用自定义销毁方法

什么是Spring装配

当bean在Spring容器中组合在一起时,称为bean装配。 Spring容器需要知道需要什么bean以及容器如何使用依赖注入来将bean绑定在一起,同时装配bean。

自动装配模式

  1. no:这是Spring框架的默认设置,在该设置下自动装配是关闭的,开发者需要自行在bean定义中用标签明确的设置依赖关系。

  2. byName:它根据bean名称注入依赖。当向一个bean中自动装配一个属性时,容器将根据bean的名称自动在在配置文件中查询一个匹配的bean。

  3. byType:它根据bean类型依赖。当向一个bean中自动装配一个属性时,容器将根据bean的类型自动在在配置文件中查询一个匹配的bean。

  4. 构造函数:通过调用类的构造函数来注入依赖,它有大量的参数。

  5. autodetect:首先容器尝试使用构造函数来自动装配,如果不能,则尝试通过byType自动装配

Spring 怎么解决循环依赖问题

Spring是如何解决循环依赖的?

Spring创建对象的两个阶段

对象的实例化属性注入两个阶段

Spring解决循环依赖的核心

三个缓存 加一个标记

Spring的三级缓存

一级缓存:保存所有已经创建完成的bean。

二级缓存:保存正常创建中的bean (完成了实例化,但还未完成属性注入)。

三级缓存:保存的是ObjectFactory类型的对象的工厂,通过工厂的方法可以获取到目标对象。

标记缓存:保存着正在创建中的对象名称。

循环依赖案例

二级缓存和三缓存的区别

三级缓存里把最原始的对象封装到ObjectFactory 工厂对象的逻辑里,而这时候对象是不稳定的,在调用singletonFactory.getObject() 后实际的对象可能会需要代理的包装,才能成为我们实际程序使用的对象, 从而保存到二级缓存里去,这也是三级缓存和二级缓存的区别,二级缓存里保存的对象是经过了代理包装或替换的,三级缓存中的对象还存在不确定性

Spring无法解决的循环依赖场景

Spring可以解正常解决通过属性进行依赖注入的循环依赖场景,但是无法解决通过构造方法进行注入的循环依赖场景,

Spring中的线程安全问题

cloud.tencent.com/developer/a…

什么情况下是线程安全的

原型模式Bean: 对于原型Bean,每次创建一个新对象,也就是线程之间并不存在Bean共享,自然是不会有线程安全的问题。

单例模式Bean: 对于单例Bean,所有线程都共享一个单例实例Bean,因此是存在资源的竞争。

     单例模式下的无状态bean是线程安全的,有状态bean是线程不安全的。

怎么解决线程安全问题

  1. 有状态的bean改为原型模式
  2. 使用ThreadLocal

总结

模型模式的bean是线程安全的。 单例模式的bean,如果是无状态bean是线程安全的,有状态bean是线程不安全的。 所有,在单例模式的bean里面最好不要定义变量,如果非要定义的化,使用ThreadLocal包装。

什么是AOP

面向切面的编程

而AOP是一种新的编程方式,它和OOP不同,OOP把系统看作多个对象的交互,AOP把系统分解为不同的关注点,或者称之为切面(Aspect)。

AOP有哪些实现方式

AOP实现方式

developer.aliyun.com/article/641… www.cnblogs.com/tuyang1129/…

AOP主要是它以横切面的方式,将增强的代码插入到主流程中。

切面织入的方法:
1、编译期织入---->Aspectj是静态织入,即编译时期就织入 2、类装载期织入 3、动态代理织入---->在运行期为目标类添加增强生成子类的方式,Spring AOP采用动态代理织入切面

动态代理

  1. JDK动态代理 :
  • 定义:

JDK的动态代理是基于反射实现。JDK通过反射,生成一个代理类,这个代理类实现了原来那个类的全部接口,并对接口中定义的所有方法进行了代理。当我们通过代理对象执行原来那个类的方法时,代理类底层会通过反射机制,回调我们实现的InvocationHandler接口的invoke方法。并且这个代理类是Proxy类的子类(记住这个结论,后面测试要用)。这就是JDK动态代理大致的实现方式。

  • 优点:
    1. JDK动态代理是JDK原生的,不需要任何依赖即可使用;
    2. 通过反射机制生成代理类的速度要比CGLib操作字节码生成代理类的速度更快;
  • 缺点:
    1. 如果要使用JDK动态代理,被代理的类必须实现了接口,否则无法代理;
    2. JDK动态代理无法为没有在接口中定义的方法实现代理,假设我们有一个实现了接口的类,我们为它的一个不属于接口中的方法配置了切面,Spring仍然会使用JDK的动态代理,但是由于配置了切面的方法不属于接口,为这个方法配置的切面将不会被织入。
    3. JDK动态代理执行代理方法时,需要通过反射机制进行回调,此时方法执行的效率比较低;
  1. CGLib动态代理
  • 定义:

CGLib实现动态代理的原理是,底层采用了ASM字节码生成框架,直接对需要代理的类的字节码进行操作,生成这个类的一个子类,并重写了类的所有可以重写的方法,在重写的过程中,将我们定义的额外的逻辑(简单理解为Spring中的切面)织入到方法中,对方法进行了增强。而通过字节码操作生成的代理类,和我们自己编写并编译后的类没有太大区别。

  • 优点:

    1. 使用CGLib代理的类,不需要实现接口,因为CGLib生成的代理类是直接继承自需要被代理的类;
    2. CGLib生成的代理类是原来那个类的子类,这就意味着这个代理类可以为原来那个类中,所有能够被子类重写的方法进行代理;
    3. CGLib生成的代理类,和我们自己编写并编译的类没有太大区别,对方法的调用和直接调用普通类的方式一致,所以CGLib执行代理方法的效率要高于JDK的动态代理;
  • 缺点

    1. 由于CGLib的代理类使用的是继承,这也就意味着如果需要被代理的类是一个final类,则无法使用CGLib代理;
    2. 由于CGLib实现代理方法的方式是重写父类的方法,所以无法对final方法,或者private方法进行代理,因为子类无法重写这些方法;
    3. CGLib生成代理类的方式是通过操作字节码,这种方式生成代理类的速度要比JDK通过反射生成代理类的速度更慢;

Spring AOP实现方案

Spring AOP的实现是通过动态代理,并且有两种实现方式,分别是JDK动态代理和CGLib动态代理。Spring默认使用JDK动态代理,只有在类没有实现接口时,才会使用CGLib。

使用

  • 首先,我们通过Maven引入Spring对AOP的支持:
  • 然后,定义一个带@Aspect注解的bean,并在方法上加上 @Before 或 @Around 注解
  • 紧接着,我们需要给@Configuration类加上一个@EnableAspectJAutoProxy注解: Spring的IoC容器看到这个注解,就会自动查找带有@Aspect的Bean,然后根据每个方法的@Before、@Around等注解把AOP注入到特定的Bean中

Spring 中用到了哪些设计模式

  • 工厂模式
  • 代理模式
  • 单例模式
  • 模版方法模式

Spring 事务实现有哪些方式

编程式事务管理 : 灵活但难以维护

TransactionStatus tx = null;
try {
    // 开启事务:
    tx = txManager.getTransaction(new DefaultTransactionDefinition());
    // 相关JDBC操作:
    jdbcTemplate.update("...");
    jdbcTemplate.update("...");
    // 提交事务:
    txManager.commit(tx);
} catch (RuntimeException e) {
    // 回滚事务:
    txManager.rollback(tx);
    throw e;
}

申明式事务管理 :

@Transactional
@EnableTransactionManagement

事务传播规则

@Transactional(propagation = Propagation.REQUIRES_NEW)
  • REQUIRED(常用) : 如果当前没有事务,就创建一个新事务,如果当前有事务,就加入到当前事务中执行
  • SUPPORTS(偶尔) : 表示如果有事务,就加入到当前事务,如果没有,那也不开启事务执行
  • MANDATORY :
  • REQUIRES_NEW(偶尔): 表示不管当前有没有事务,都必须开启一个新的事务执行
  • NOT_SUPPORTED :
  • NEVER :
  • NESTED :

Spring MVC 工作原理

www.cnblogs.com/xiaoxi/p/61… (稍有不对)

  1. 用户发送请求至前端控制器DispatcherServlet

  2. DispatcherServlet调用HandlerMapping处理器映射器,解析请求对应的handler。

  3. 处理器映射器找到具体的handler(可以根据xml配置、注解进行查找)。

  4. DispatcherServlet调用 HandlerAdapter 处理器适配器执行 handler。

  5. 执行完成返回 ModelAndView

  6. ViewReslover解析后返回具体View。

  7. DispatcherServlet 把返回的 Model传给view(视图渲染)。

  8. DispatcherServlet 把view返回给请求者。

@Controller 注解有什么用?

@Controller 标注一个类为SpringMVC 控制器Controller,SpringMVC 会扫描到该注解的类,然后扫描这个类下面带有 @RequestMapping 注解的方法,为该方法生成对应的处理器对象。

@RequestMapping 注解有什么用?

配置处理器的http请求方法,URI等信息,这样才能将请求和方法进行映射。这个方法可以作用在方法上,也可以作用在类上。

@Controller 和 @RestController 的区别

@RestController 在 @Controller 的基础上增加了 @ResponseBody 注解

@RequestMapping 和 @GetMapping 的区别

@GetMapping 是 @RequestMapping Get请求的一个特例

@RequestParam 和 @PathVariable 的区别

@RequestParam 注解的参数从请求体里面获取 @PathVariable 注解的参数从请求URI里面获取

@ResponseBody

返回json格式的数据

为什么使用 SpringBoot

在使用Spring框架进行开发的过程中,需要配置很多Spring框架包的依赖,如spring-core、spring-bean、spring-context等,而这些配置通常都是重复添加的,而且需要做很多框架使用及环境参数的重复配置,如开启注解、配置日志等。 Spring Boot致力于弱化这些不必要的操作,提供默认配置,当然这些默认配置是可以按需修改的,快速搭建、开发和运行Spring应用。

SpringBoot 如何实现对不同环境的属性配置文件的支持?

  1. 创建各个环境的配置文件
  2. 然后设置:spring.profiles.active = dev #默认为开发环境

SpringBoot 的核心注解是哪个,它主要由哪几个注解组成

启动类上面的 @SpringBootApplication 注解,它是SpringBoot的核心注解,主要包含了以下三个注解

  • @SpringBootConfiguration 组合了 @Configuration, 实现配置文件的功能
  • @EnableAutoConfiguration 打开了自动配置的功能
  • @ComponentScan 用于Spring组件扫描

你如何理解 Spring Boot 中的 Starters?

Starters可以理解为启动器,
它包含了一系列可以集成到应用里面的依赖包,
你可以一站式集成 某项技术,
而不需要到处找示例代码和依赖包。
如你想使用 Spring JPA 访问数据库,
只要加入spring-boot-starter-data-jpa 启动器依赖就能使用了。

SpringBoot Starter的工作原理?

SpringBoot 在启动的时候会干这几件事情

  • SpringBoot 在启动时会去依赖的Starts包中寻找spring.factories文件,然后根据文件中配置的jar包去扫描项目所依赖的jar包。
  • 根据spring.factories配置加载AutoConfigure类。
  • 根据 @Conditional 注解的条件,进行自动配置。

@EnableAutoConfiguration 底层是怎么实现自动装配的

www.cnblogs.com/javaguide/p…

Spring Boot 通过@EnableAutoConfiguration开启自动装配,通过 SpringFactoriesLoader 最终加载META-INF/spring.factories中的自动配置类实现自动装配,自动配置类其实就是通过@Conditional按需加载的配置类,想要其生效必须引入spring-boot-starter-xxx包实现起步依赖