## 什么是spring Spring 是一款开源的轻量级 Java 开发框架,它使创建 Java 企业应用程序变得容易。它提供了在企业环境中使用 Java 语言所需的一切,并可以根据应用程序的需求灵活地创建多种架构。 Spring 提供的核心功能主要是 IoC 和 AOP ## Spring IoC **IoC(Inversion of Control:控制反转)** 是一种设计思想,而不是一个具体的技术实现。IoC 的思想就是将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理。 **为什么叫控制反转?** - **控制**:指的是对象创建(实例化、管理)的权力 - **反转**:控制权交给外部环境(Spring 框架、IoC 容器) ## 依赖查找和依赖注入的区别是什么? * 依赖查找:主动查找依赖的方式。依赖查找的对象包含spring beandefinition以及单例对象 * 依赖注入:spring容易自动依赖绑定的方式。依赖注入还包含Resolvable Dependency以及@value对象 ## Spring Bean 作用域 | 作用域 | 说明 | | ------------- | ---------------------------------------------------------- | | **singleton** | 默认 Spring Bean 作用域,一个 BeanFactory 有且仅有一个实例 | | **prototype** | 原型作用域,每次依赖查找和依赖注入生成新 Bean 对象 | | request | 将 Spring Bean 存储在 ServletRequest 上下文中 | | session | 将 Spring Bean 存储在 HttpSession 中 | | application | 将 Spring Bean 存储在 ServletContext 中 | ## @Autowired 和 @Resource 的区别是什么? * `@Autowired` 是 Spring 提供的注解,`@Resource` 是 JDK 提供的注解。 * `Autowired` 默认的注入方式为`byType`(根据类型进行匹配),`@Resource`默认注入方式为 `byName`(根据名称进行匹配)。 * 当一个接口存在多个实现类的情况下,`@Autowired` 和`@Resource`都需要通过名称才能正确匹配到对应的 Bean。`Autowired` 可以通过 `@Qualifier` 注解来显式指定名称,`@Resource`可以通过 `name` 属性来显式指定名称。 ## Bean 是线程安全的吗? Spring 框架中的 Bean 是否线程安全,取决于其作用域和状态。 大部分 Bean 实际都是无状态(没有定义可变的成员变量)的(比如 Dao、Service),这种情况下, Bean 是线程安全的。 对于有状态单例 Bean 的线程安全问题,常见的有两种解决办法: 1. 在 Bean 中尽量避免定义可变的成员变量。 2. 在类中定义一个 `ThreadLocal` 成员变量,将需要的可变成员变量保存在 `ThreadLocal` 中(推荐的一种方式)。 ## Spring Boot自动装配原理 1. 在Spring Boot项目中有一个注解`@SpringBootApplication`,这个注解是对三个注解进行了封装:`@SpringBootConfiguration`、`@EnableAutoConfiguration`、`@ComponentScan` 其中`@EnableAutoConfiguration`是实现自动化配置的核心注解。 2. 该注解通过`@Import`注解导入`AutoConfigurationImportSelector`,这个类实现了一个导入器接口`ImportSelector`。在该接口中存在一个方法`selectImports`, 3. 该方法的返回值是一个数组,数组中存储的就是要被导入到`spring`容器中的类的全类名。在`AutoConfigurationImportSelector`类中重写了这个方法, 4. 该方法内部就是读取了项目的`classpath`路径下`META-INF/spring.factories`文件中的所配置的类的全类名。 5. 在这些配置类中所定义的`Bean`会根据条件注解所指定的条件来决定是否需要将其导入到`Spring`容器中。 ## springboot三个注解 ### @SpringBootConfiguration * 作用: 声明定义Bean,嵌套了@Component组件 * @SpringBootConfiguration源码是@Configuration:表示该类为主配置类,可用来装配bean * @Configuration的源码是@Component:说明Spring的配置类也是Spring的一个组件。 ![在这里插入图片描述](assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1NjczMzY3,size_16,color_FFFFFF,t_70#pic_center.png) * 它是JavaConfig形式的基于Spring IOC容器的配置类使用的一种注解。SpringBoot本质上就是一个Spring应用,通过这个注解来加载IOC容器的配置。所以在启动类里面标注了@Configuration,意味着它也是一个IOC容器的配置类 ### @ComponentScan - 作用:扫描主配置类包的所有包下的类,相当于xml配置文件中的context:component-scan。eg:pojo中的User类 ### @EnableAutoConfiguration(重点!!!) ![在这里插入图片描述](assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1NjczMzY3,size_16,color_FFFFFF,t_70#pic_center-16899533239744.png) 1. **@AutoConfigurationPackage**:自动配置包 * 作用:给Spring容器中导入一个Registrar注册器组件 `@AutoConfigurationPackage`和`@ComponentScan`一样,都是将`Spring Boot`启动类所在的包及其子包里面的组件扫描到IOC容器中,但是区别是`@AutoConfigurationPackage`扫描`@Enitity、@Mapper`等第三方依赖的注解,`@ComponentScan`只扫描`@Controller/@Service/@Component/@Repository`这些常见注解。所以这两个注解扫描的对象是不一样的。当然这只是直观上的区别,更深层次说,@AutoConfigurationPackage是自动配置的提醒,是Spring Boot中注解,而@ComponentScan是Spring的注解 2. **@Import(AutoConfigurationImportSelector.class)**——核心注解 * 该注解通过`@Import`注解导入`AutoConfigurationImportSelector`,这个类实现了一个导入器接口`ImportSelector`。在该接口中存在一个方法`selectImports`, * 通过与BeanClassLoaderAware结合注入bean ## SpringBoot启动流程 * 首先从main找到run()方法,在执行run()方法之前new一个SpringApplication对象 * 进入run()方法,创建应用监听器SpringApplicationRunListeners开始监听 * 然后加载SpringBoot配置环境(ConfigurableEnvironment),然后把配置环境(Environment)加入监听对象中 * 然后加载应用上下文(ConfigurableApplicationContext),当做run方法的返回对象 * 最后创建Spring容器,refreshContext(context),实现starter自动化配置和bean的实例化等工作。 ## autowired变量都是单例的吗? 默认都是单例对象,就算对象标注@Scope(value = “prototype”)也无法生效 特殊情况是多例对象,当注入方和被注入方都被@Scope(value = “prototype”)标注才会使多例 ## bean生命周期 - BeanDefinition 注册阶段 - `registerBeanDefinition` - BeanDefinition 合并阶段 - `getMergedBeanDefinition` - Bean 实例化前阶段 - `resolveBeforeInstantiation` - Bean 实例化阶段 - `createBeanInstance` - Bean 实例化后阶段 - `populateBean` - Bean 属性赋值阶段 - `populateBean` - Bean Aware 接口回调阶段 - `initializeBean` - Bean 初始化前阶段 - `initializeBean` - Bean 初始化阶段 - `initializeBean` - Bean 初始化后阶段 - `initializeBean` - Bean 初始化完成阶段 - `preInstantiateSingletons` - Bean 销毁前阶段 - `destroyBean` - Bean 销毁阶段 - `destroyBean` ## AOP AOP(Aspect-Oriented Programming:面向切面编程) AOP 切面编程设计到的一些专业术语: | 术语 | 含义 | | :---------------- | :----------------------------------------------------------: | | 目标(Target) | 被通知的对象 | | 代理(Proxy) | 向目标对象应用通知之后创建的代理对象 | | 连接点(JoinPoint) | 目标对象的所属类中,定义的所有方法均为连接点 | | 切入点(Pointcut) | 被切面拦截 / 增强的连接点(切入点一定是连接点,连接点不一定是切入点) | | 通知(Advice) | 增强的逻辑 / 代码,也即拦截到目标对象的连接点之后要做的事情 | | 切面(Aspect) | 切入点(Pointcut)+通知(Advice) | | Weaving(织入) | 将通知应用到目标对象,进而生成代理对象的过程动作 | ## AOP流程 1. 创建Advice 一个advice就是一个方法,即目标对象必须要执行的完成的工作。 因为spring aop只能代理目标方法,所有advice就会被解析成`MethodAdvice`。 这里以`BeforeAdvice`举例。 2. 创建Interceptor `MethodBeforeAdvice`,但只有这个类还不够,因为我们的方法还无法继续往下执行,需要将`MethodBeforeAdvice`转化为`methodInterceptor`。它封装了`MethodBeforeAdvice`,将其变成真正的**增强方法**,因为`methodInterceptor`封装了代理方法,提供了统一的逻辑。 3. 创建Advisor 一个advisor对应一个pointcut和一个advice。