Spring

36 阅读3分钟

SpringBoot和Spring的关系

好的我下面从两个方面来回答SpringBoot和Spring的区别

  1. 首先就是依赖管理
    Spring需要需要手动生命每个依赖的版本,并且需要确保各个组件版本之间的兼容性。而SpringBoot通过starter依赖自动管理版本。
<!--SpringBoot 引入MyBatis starter,自动整合MyBatis与Spring -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.2.2</version> <!-- 仅需指定starter版本,内部依赖由其管理 -->
    </dependency>
<!--Spring 引入第三方框架(如MyBatis)需手动添加依赖及版本 -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.9</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.7</version> <!-- 需与MyBatis和Spring版本兼容 -->
    </dependency>
  1. Bean与第三方的整合 Spring需要手动定义基础的Bean(DataSource,SqlSessionFactory)并配置整合第三方框架的细节 SpringBoot通过自动配置类(AutoConfiguration)默认创建常用的Bean,仅需在application.yml中配置参数即可。
// Spring传统项目的核心配置类
@Configuration
// 需手动开启组件扫描,指定扫描路径
@ComponentScan(basePackages = "com.example.service")
// 需手动开启MVC注解驱动
@EnableWebMvc
public class SpringConfig {

    // 需手动定义DispatcherServlet(SpringMVC核心)
    @Bean
    public DispatcherServlet dispatcherServlet() {
        return new DispatcherServlet();
    }

    // 需手动注册DispatcherServlet到Servlet容器
    @Bean
    public ServletRegistrationBean<DispatcherServlet> dispatcherServletRegistration() {
        ServletRegistrationBean<DispatcherServlet> registration = new ServletRegistrationBean<>(
            dispatcherServlet(), "/");
        registration.setLoadOnStartup(1);
        return registration;
    }

    // 需手动定义数据源
    @Bean
    public DataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3306/test");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        return dataSource;
    }

    // 需手动定义MyBatis的SqlSessionFactory
    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        // 需手动指定Mapper位置
        sessionFactory.setMapperLocations(
            new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
        return sessionFactory.getObject();
    }
}


// SpringBoot项目的核心启动类(一键开启所有核心功能)
@SpringBootApplication
// 无需手动@ComponentScan(默认扫描当前包及子包)
// 无需手动@EnableWebMvc(web starter自动开启)
public class SpringBootDemoApplication {

    // 主方法直接启动嵌入式服务器
    public static void main(String[] args) {
        SpringApplication.run(SpringBootDemoApplication.class, args);
    }

    // 大部分配置无需手动定义,仅需在application.yml中配置参数
    // 如需自定义Bean,直接定义即可(自动被Spring容器管理)
    @Bean
    public UserService userService() {
        return new UserService();
    }
}

Spring的三级缓存是什么,如何通过三级缓存解决循环依赖

image.png 一个新对象在创建的过程中要先去缓存中查询,依次查询一级缓存、二级缓存、三级缓存,如果都没有查到,那么就通过反射创建出这个新对象,之后包装为ObjectFactory类型放在三级缓存中。下一步就是填充A对象的属性,如果A对象的属性有一个是B对象,那么找到B对象的过程也是以依次进行三级查询,如果查不到B就使用反射创建B,然后包装为ObjectFactory类型放入三级缓存中,之后进行B对象的属性填充,发现B对象的属性有一个是A,然后依次查询A在三级缓存中是否存在,这一次发现A存在于第三级缓存中。
然后使用ObjectFactory类型的getObject获取真正的的对象A,并将这个A放入到二级缓存中,然后删除三级缓存中的A对象的ObjectFactory,这样就完成了B对象的属性填充工作,之后会进行初始化使用@PostConstruct注解。之后会进行缓存转移,从三级缓存中移除B,然后在一级缓存中加入B。
之后继续进入到A对象的初始化和缓存转移。

放入二级缓存之后再进行初始化。
二级缓存可能存放的可能是原对象也可能是代理对象
生成代理的时期:如果没有循环依赖,代理在Bean初始化之后生成;如果出现循环依赖代理在初始化之前生成