技术栈知识点巩固——Spring
文章目录
- 技术栈知识点巩固——Spring
- Spring 依赖注入
- BeanFactory和 ApplicationContext有什么区别
- Spring IOC 的理解,其初始化过程
- Spring Bean 的生命周期
- Spring 循环注入的原理?
- @Qualifier注解
- Spring 是如何管理事务的,事务管理机制?
- Spring AOP(面向切面编程)
- Spring Bean的作用域
- Spring框架中的单例bean是线程安全的吗?
- 你怎样定义类的作用域?
- 什么是 Swagger?你用 Spring Boot 实现了它吗?
- Spring的Controller是单例还是多例,怎么保证并发的安全。
- 说一下Spring的核心模块
- spring 中有多少种IOC 容器?
- Spring Boot 自动配置原理是什么?
- RequestMapping 和 GetMapping
- Spring Boot异常处理?
- Spring Boot 中如何解决跨域问题
- Spring Boot打成的 jar 和普通的jar有什么区别呢?
- Springboot启动
- SpringBoot 资源初始化方法(启动后执行)
- Spring 事件机制
- SpringBoot服务监控
- SpringBoot定时任务
- 获取当前的容器上下文
- banner.txt(自定义控制台输出)
- 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
中,BeanFactory
是IoC
容器的核心接口。 -
ApplicationContext
是BeanFactory
的子接口,也被称为Spring
上下文。Application Context
是spring
中较高级的容器。 -
两者详细比较见:https://blog.csdn.net/qq_37248504/article/details/113704298
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
中扩展了BeanPostProcessor
的bean
。将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
之前做一些处理 - 调用
BeanNameAware
的setBeanName()
- 调用
BeanFactoryAware
的setBeanFactory()
- 调用
ApplicationContextAware
的setAllicationContext()
- 调用
BeanPostProcessor
的前置处理方法 - 调用
InitializingBean
的afterPropertiesSet()
- 调用自定义的
init-method
方法 - 调用
BeanPostProcessor
的后置处理方法 - 销毁:先注册销毁的相关调用接口,使用,执行自定义的销毁方法
Spring 循环注入的原理?
- 实例化,填充属性(注入依赖),初始化完成。
spring
在创建BeanA
的时候会先去一级缓存获取,如果一级缓存没有则再从二级缓存中获取,如果二级缓存也没有,则再从三级缓存中获取,如果还获取不到,则实例化一个A
,然后放入三级缓存,然后填充属性,此刻发现依赖B
,于是创建B
,同样的经过上述步骤,由于每级缓存都获取不到,于是实例化B
,然后填充属性,发现依赖A
,然后依次去每级缓存中获取,由于三级缓存中已经有一个A
,于是B
可以顺利注入依赖,并被正确的初始化,然后递归返回,于是A
也可以被正确的初始化了。
@Qualifier注解
@Autowired
通过Bean
的类型注入Bean
,当容器中含有多个相同类型的Bean
时候,可以使用@Qualifier
指定Bean
的名称@Resource
通过Bean
的Name
进行注入,是Java
中的注解。- 常用注解的使用详见:https://blog.csdn.net/qq_37248504/article/details/108859121
Spring 是如何管理事务的,事务管理机制?
事务管理器
PlatformTransactionmanger
:commit()
提交事务、rollback()
回滚事务、getTransaction()
获取事务的状态。
事务定义信息
TransactionDefinition
:用来定义事务相关的属性(事务隔离级别、事务传播方式、超时时间、是否只读)。
事务具体运行状态
TransactionStatus
:判断事务是否完成、当前事务是否是一个新事务等。
声明式事务
- 不需要在业务逻辑代码中编写事务相关代码,只需要在配置文件配置或使用注解
@Transaction
,这种方式对代码没有侵入性。
编程式事务
-
需要手动写事务提交等相关的代码
-
事务传播机制等内容见博客:https://blog.csdn.net/qq_37248504/article/details/107008217
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
实例,bean
的scope
属性被指定为prototype
。另一方面,一个bean
每次使用的时候必须返回同一个实例,这个bean
的scope
属性 必须设为singleton
。 bean
的作用域的种类可见上面的知识点。
什么是 Swagger?你用 Spring Boot 实现了它吗?
Swagger
是接口API文档,可以更加清晰的看到接口的参数、类型等。Swagger
的使用请见:https://blog.csdn.net/qq_37248504/article/details/106773321Swagger
常用注解请见博客:https://blog.csdn.net/qq_37248504/article/details/106797105
Spring的Controller是单例还是多例,怎么保证并发的安全。
Controller
默认是单例的,正因为单例所以不是线程安全的。- 可以使用原型模式,通过注解
@Scope(“prototype”)
定义变量等。 - 可以使用
ThreadLocal
。
说一下Spring的核心模块
-
spring-core
:基础API
模块,如资源管理,泛型处理。 -
spring-beans
:Spring Bean
相关,依赖查找,依赖注入。 -
spring-aop
:动态代理,aop
字节码提升。 -
spring-context
:事件驱动,注解驱动,模块驱动。 -
spring-expression
:spring
表达式语音模块。
spring 中有多少种IOC 容器?
BeanFactory
:Spring
底层的基础容器。常用的BeanFactory是XmlBeanFactory,根据xml定义的内容创建bean。Application
:ApplicationContext
在BeanFactory
的基础上构建,是相对比较高 级的容器实现,除了拥有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
-
RequestMapping
不仅能相应Post
请求,还能相应Get
请求。GetMapping
只能相应get
请求。 -
其它常用注解,详细见博客:https://blog.csdn.net/qq_37248504/article/details/108859121
Spring Boot异常处理?
Spring Boot 中如何解决跨域问题
-
跨域:浏览器不能执行其他网站的脚本。
-
解决办法:使用
nginx
做代理,代理到同一个域下;再后端配置拦截器做请求拦截校验处理。 -
详细见博客:https://blog.csdn.net/qq_37248504/article/details/107347488
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
(例如Eureka
,Consul
)发现UI
是Spring Boot Actuator
端点上的Vue.js
应用程序 。- 详细配置请移步:https://blog.csdn.net/qq_37248504/article/details/109346898
SpringBoot定时任务
Quartz
定时任务框架:https://blog.csdn.net/qq_37248504/article/details/106874496Scheduled
定时任务器:https://blog.csdn.net/qq_37248504/article/details/106874449
获取当前的容器上下文
- 实现
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