# 技术栈知识点巩固——Spring

Posted MarlonBrando1998

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了# 技术栈知识点巩固——Spring相关的知识,希望对你有一定的参考价值。

Spring 依赖注入
  • 基于构造函数的依赖注入
/**
  * 构造方法注入 Bean
  */
private final UserService userService;


public BeanInjectionServiceImpl(UserService userService) {
    logger.info("=====> 构造方法注入 Bean!");
    this.userService = userService;
}
  • 基于Set函数的依赖注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="student" class="com.li.entity.Student">
        <!--id属性是自定义的对象名称,class属性是类的全限定名称-->
        <property name="name" value="张三"/>
        <property name="id" value="20"/>
        <!--name是属性名,value是属性值-->
    </bean>

</beans>
@Test
public void test1() {
    ClassPathResource resource = new ClassPathResource("StudentBean.xml");
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
    XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
    reader.loadBeanDefinitions(resource);

    Student student = factory.getBean(Student.class);
    logger.info("当前 Stundet :{}", student);

}
  • 注入集合
<bean id="student" class="com.li.entity.Student">
    <!--id属性是自定义的对象名称,class属性是类的全限定名称-->
    <property name="name" value="张三"/>
    <property name="id" value="20"/>
    <!--name是属性名,value是属性值-->

    <property name="list">
        <list>
            <value>数学</value>
            <value>英语</value>
        </list>
    </property>
</bean>
@Test
public void test2() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("StundetBeanOne.xml");
    Student bean = applicationContext.getBean(Student.class);
    logger.info("当前 Student :{}", bean);
}
BeanFactory和 ApplicationContext有什么区别

Spring IOC 的理解,其初始化过程
  • SpringIOC:控制反转,将对象的创建,装配,管理等一系列操作交给Spring容器(BeanFactory、ApplicationContext)进行处理。开发者只需要从容器中拿到对象使用。
  • 读取相关Bean的配置、定义文件配置BeanFactory
  • 通过refresh()方法完成。

容器初始化

  • prepareRefresh():容器刷新前的准备、设置上下文状态、获取属性等。

  • prepareBeanFactory(beanFactory):配置标准的beanFactory,设置ClassLoader,设置SpEL表达式解析器,添加忽略注入的接口,添加bean,添加bean后置处理器等。

  • postProcessBeanFactory(beanFactory):所有的beanDefinition已经加载,但是还没有实例化。允许在子类中对beanFactory进行扩展处理。比如添加ware相关接口自动装配设置,添加后置处理器等,是子类扩展prepareBeanFactory(beanFactory)的方法。在应用上下文的内部bean factory初始化之后修改bean factory

  • registerBeanPostProcessors(beanFactory):实例化和注册beanFactory中扩展了BeanPostProcessorbean。将 BeanPostProcessor 注册到 BeanFactory 中,并没有执行

  • initMessageSource():初始化国际化工具类MessageSource

  • initApplicationEventMulticaster():初始化事件广播器

  • onRefresh():模板方法,在容器刷新的时候可以自定义逻辑,不同的Spring容器做不同的事情。

  • registerListeners():注册监听器

  • finishBeanFactoryInitialization(beanFactory):实例化所有剩余的(非懒加载)单例
    比如invokeBeanFactoryPostProcessors方法中根据各种注解解析出来的类,在这个时候都会被初始化。实例化的过程各种BeanPostProcessor开始起作用。

  • finishRefresh()refresh做完之后需要做的其他事情。清除上下文资源缓存(如扫描中的ASM元数据) 初始化上下文的生命周期处理器,并刷新(找出Spring容器中实现了Lifecycle接口的bean并执行start()方法)。发布ContextRefreshedEvent事件告知对应的ApplicationListener进行响应的操作。

  • 容器初始化详见:https://blog.csdn.net/qq_37248504/article/details/113704298

Spring Bean 的生命周期
  • 实例化Bean:对于BeanFactory容器,请求的Bean没有初始化的时候,容器会调用createBean()进行实例化。对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean
  • 属性赋值:为bean设置相关属性和依赖。
  • 初始化Bean:在返回Bean之前做一些处理
  • 调用BeanNameAwaresetBeanName()
  • 调用BeanFactoryAwaresetBeanFactory()
  • 调用ApplicationContextAwaresetAllicationContext()
  • 调用BeanPostProcessor的前置处理方法
  • 调用InitializingBeanafterPropertiesSet()
  • 调用自定义的init-method方法
  • 调用BeanPostProcessor的后置处理方法
  • 销毁:先注册销毁的相关调用接口,使用,执行自定义的销毁方法
Spring 循环注入的原理?
  • 实例化,填充属性(注入依赖),初始化完成。
  • spring在创建BeanA的时候会先去一级缓存获取,如果一级缓存没有则再从二级缓存中获取,如果二级缓存也没有,则再从三级缓存中获取,如果还获取不到,则实例化一个A,然后放入三级缓存,然后填充属性,此刻发现依赖B,于是创建B,同样的经过上述步骤,由于每级缓存都获取不到,于是实例化B,然后填充属性,发现依赖A,然后依次去每级缓存中获取,由于三级缓存中已经有一个A,于是B可以顺利注入依赖,并被正确的初始化,然后递归返回,于是A也可以被正确的初始化了。

@Qualifier注解

Spring 是如何管理事务的,事务管理机制?

事务管理器

  • PlatformTransactionmangercommit()提交事务、rollback()回滚事务、getTransaction()获取事务的状态。

事务定义信息

  • TransactionDefinition:用来定义事务相关的属性(事务隔离级别、事务传播方式、超时时间、是否只读)。

事务具体运行状态

  • TransactionStatus:判断事务是否完成、当前事务是否是一个新事务等。
声明式事务
  • 不需要在业务逻辑代码中编写事务相关代码,只需要在配置文件配置或使用注解@Transaction,这种方式对代码没有侵入性。
编程式事务

Spring AOP(面向切面编程)
  • 通过Aop可以将一些非业务代码、公共的代码和业务代码进行解耦操作,定义切点、定义切面,在切面中进行公共方法的处理。使用切面操作,可以减少系统的重复代码,降低模块之间的耦合度,有利于后期的维护开发。

Aop元素

  • 切点(PoinCut):从哪些类哪个方法切入,可以配合注解使用。

  • 通知(Advice):在方法执行前、或者执行后进行需要的操作。

  • 切面(Aspect):整个切面中进行切点和通知的操作,编写代码。

  • 织入(Weaving):由Spring完成,创建代理对象。

  • Aop注解方式记录接口日志见例子见博客:https://blog.csdn.net/qq_37248504/article/details/102926592


Spring Bean的作用域
  • singleton:单例,默认作用域。
  • prototype:原型,每次创建一个新对象。
  • request:请求,每次Http请求创建一个新对象,适用于WebApplicationContext环境下。
  • session:会话,同一个会话共享一个实例,不同会话使用不用的实例。
  • global-session:全局会话,所有会话共享一个实例。

Spring框架中的单例bean是线程安全的吗?
  • 对于单例的Bean,所有的线程共享一个单例实例Bean。大多数时候Bean都是无状态的Bean,所以是安全的。
  • 对于原型(Prototype)类型的Bean,每次请求都会创建一个新的Bean,这样就可以保证线程安全了。
  • Scope范围测试:当使用原型模式的时候,普通变量可以做到线程安全,但是静态变量是有问题的。所以使用还是得要按照实际开发结合。
@RestController
@RequestMapping("/spring/testone")
@Scope(value = "prototype")
public class SpringTestTwoController {

    private static final Logger logger = LoggerFactory.getLogger(BeanTestController.class);

    private int a = 0;

    private static int b = 0;

    @RequestMapping("/test1")
    public void test1() {
        logger.info("普通变量 a:{} 静态变量 b:{}", ++a, ++b);
    }

    @RequestMapping("/test2")
    public void test2() {
        logger.info("普通变量 a:{} 静态变量 b:{}", ++a, ++b);
    }
}


你怎样定义类的作用域?
  • Spring 要在需要的时候每次生产一个,新的 bean 实例,beanscope 属性被指定为 prototype。另一方面,一个 bean每次使用的时候必须返回同一个实例,这个 beanscope 属性 必须设为singleton
  • bean 的作用域的种类可见上面的知识点。

什么是 Swagger?你用 Spring Boot 实现了它吗?

Spring的Controller是单例还是多例,怎么保证并发的安全。
  • Controller默认是单例的,正因为单例所以不是线程安全的。
  • 可以使用原型模式,通过注解@Scope(“prototype”)定义变量等。
  • 可以使用ThreadLocal

说一下Spring的核心模块

  • spring-core:基础API模块,如资源管理,泛型处理。

  • spring-beans:Spring Bean 相关,依赖查找,依赖注入。

  • spring-aop:动态代理,aop字节码提升。

  • spring-context:事件驱动,注解驱动,模块驱动。

  • spring-expression: spring表达式语音模块。


spring 中有多少种IOC 容器?
  • BeanFactorySpring底层的基础容器。常用的BeanFactory是XmlBeanFactory,根据xml定义的内容创建bean。
  • ApplicationApplicationContextBeanFactory的基础上构建,是相对比较高 级的容器实现,除了拥有BeanFactory的所有支持,ApplicationContext还提供了其他高级,比如事件发布、国际化信息支持等。
  • 两种容器的差别详细请见博客:https://blog.csdn.net/qq_37248504/article/details/113704298

Spring Boot 自动配置原理是什么?
  • 加载@SpringBootApplication
@SpringBootApplication
@EnableCaching
@EnableScheduling
@MapperScan("com.li.springbootproject.mapper")
public class TestProjectApplication {

    public static void main(String[] args) {
        SpringApplication.run(TestProjectApplication.class);
    }

}
  • @SpringBootApplication注解:开启自动配置功能
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
		@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

}
  • AutoConfigurationImportSelector选择器给Spring容器中导入一下组件。
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}
  • 扫描java jar包类路径下的META-INF/spring.factories文件
  • 将类路径下 META-INF/spring.factories 里面配置的所有 EnableAutoConfiguration 的值加入到 Spring 容器中。

RequestMapping 和 GetMapping

Spring Boot异常处理?

Spring Boot 中如何解决跨域问题

Spring Boot打成的 jar 和普通的jar有什么区别呢?
  • SpringBoot打的jar包不能被其他类引用,不能被其他项目所依赖。
  • SpringBoot打包成的可执行 jar 解压后,在 \\BOOT-INF\\classes 目录下才是我们的代码。

Springboot启动
  • SpringBoot主要通过实例化SpringApplication来启动,启动的过程中主要进行:配置属性、获取监听器、发布应用开始启动事件、初始化输入参数、配置环境、输出banner、创建上下文、预处理上下文、刷新容器、发布应用已启动事件。
public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
    this.configureHeadlessProperty();
    // 启动监听器
    SpringApplicationRunListeners listeners = this.getRunListeners(args);
    listeners.starting();

    Collection exceptionReporters;
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        //第二步,根据SpringApplicationRunListeners以及参数来准备环境
        ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
        this.configureIgnoreBeanInfo(environment);
        // 在控制台打印 banner
        Banner printedBanner = this.printBanner(environment);
        // 创建 Spring 容器
        context = this.createApplicationContext();
        exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
        // Spring 容器 前置处理
        this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        // 刷新容器
        this.refreshContext(context);
        // Spring 容器后置处理
        this.afterRefresh(context, applicationArguments);
        stopWatch.stop();
        if (this.logStartupInfo) {
            (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
        }
		// 发出结束执行的事件
        listeners.started(context);
        // 执行 Runners
        this.callRunners(context, applicationArguments);
    } catch (Throwable var10) {
        this.handleRunFailure(context, var10, exceptionReporters, listeners);
        throw new IllegalStateException(var10);
    }

    try {
        listeners.running(context);
        // 返回容器
        return context;
    } catch (Throwable var9) {
        this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
        throw new IllegalStateException(var9);
    }
}

SpringBoot 资源初始化方法(启动后执行)
  • @Order标识类的顺序,数字越小越靠前。

  • 实现ApplicationRunner接口

@Component
@Order(1)
public class TestProjectApplicationRunner implements ApplicationRunner {

    private static final Logger logger = LoggerFactory.getLogger(TestProjectApplicationRunner.class);

    @Autowired
    private Environment environment;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        logger.info("----------> TestProjectApplicationRunner 初始化!");
        logger.info("----------> 当前服务端口为:{}",environment.getProperty("server.port"));
    }
}
  • 实现CommandLineRunner接口
@Component
@Order(2)
public class TestProjectCommandLineRunner implements CommandLineRunner {

    private static final Logger logger = LoggerFactory.getLogger(TestProjectCommandLineRunner.class);

    private Environment environment;

    public TestProjectCommandLineRunner(Environment environment) {
        this.environment = environment;
    }

    @Override
    public void run(String... args) throws Exception {
       logger.info("----------> TestProjectCommandLineRunner 初始化!");
       logger.info("----------> 当前服务端口为:{}",environment.getProperty("server.port"));
    }
}

Spring 事件机制
  • 事件:自定义事件需要继承ApplicationEvent,可以用来传递需要处理的数据。
  • 事件监听者:事件监听器接口,事件的业务逻辑封装在监听器里面。
  • 事件发布:ApplicationEventPublisherAware通过实现这个接口,来触发事件。
  • SpringBoot 事件同步异步处理详见博客:https://blog.csdn.net/qq_37248504/article/details/115269995

SpringBoot服务监控
  • Spring Boot Admin用来管理和监控Spring Boot应用程序。应用程序向我们的Spring Boot Admin Client注册(通过HTTP)或使用SpringCloud(例如 EurekaConsul)发现UISpring Boot Actuator端点上的Vue.js应用程序 。
  • 详细配置请移步:https://blog.csdn.net/qq_37248504/article/details/109346898

SpringBoot定时任务

获取当前的容器上下文
  • 实现ApplicationContextAware接口,重写setApplicationContext()在这个方法中可以拿到上下文ApplicationContext
@Component
public class SpringContextUtils implements ApplicationContextAware {
 
     @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
      // 在这里可以拿到当前容器的上下文
    }
}
  • 直接注入
@Autowired
private ApplicationContext applicationContext;

banner.txt(自定义控制台输出)
  • 只需要在classpath路径下创建一个banner.txt即可。
  • 在线生成网站:https://www.bootschool.net/ascii

Spring 开发中实用的技巧

以上是关于# 技术栈知识点巩固——Spring的主要内容,如果未能解决你的问题,请参考以下文章

# 技术栈知识点巩固——Nginx

# 技术栈知识点巩固——Nginx

# 技术栈知识点巩固——Mybatis

# 技术栈知识点巩固——Mybatis

# 技术栈知识点巩固——Css

# 技术栈知识点巩固——Css