Spring中的Bean是线程安全的吗?
Spring 中的 bean 默认都是单例的,所有线程都共享一个单例实例Bean,因此是存在资源的竞争,是线程不安全的。
但是spring中大部分bean都是在方法内部操作,例如:Controller、Service、Dao等,不会对bean的成员变量做查询以外的操作,这种bean是线程安全的。如果非要在这些bean中定义成员变量并修改它,可以声明@Scope("prototype")
保证线程安全。
在spring内部有一些bean会对成员变量操作,例如:RequestContextHolder等,这种bean一般通过ThreadLocal来解决线程安全的问题。
@Component 和 @Bean 的区别是什么?
- @Component注解作用于类;而@Bean作用于方法。
- @Component一般是用过类路径来自动装配到spring中;@Bean一般是在方法中产生一个bean,然后将这个bean交给spring管理。
- @Bean注解比@Component注解的自定义性更强,很多时候我们需要将第三方类库中的bean装配到spring中时,只能通过@Bean来实现。
SpringMVC 工作原理
- 客户端发送请求,到达DispatcherServlet。
- DispatcherServlet根据请求信息调用HandlerMapping。
- HandlerMapping解析请求到对应的Handler(Controller)后,开始由HandlerAdapter来处理。
- HandlerAdapter根据Handler调用真正的处理器来处理请求。
- 处理器处理完以后,返回一个ModelAndView对象。
- ViewResolver根据ModelAndView中的View查找实际的View。
- DispaterServlet将ModelAndView中的Model传给View。
- 最后把实际的View返回给请求者。
在Spring容器初始化的过程中,所有定义的bean都会被初始化吗?
不是,默认只初始化所有未初始化的非懒加载的单例Bean,scope为其它值的bean会在使用到的时候进行初始化,如prototype。
Spring如何解决循环依赖?
spring注入属性方式有多种,但是只有一种循环依赖被解决:setter依赖注入。其他的注入方式,Spring也没有办法解决,只是抛出BeanCurrentlyInCreationException异常。
spring解决循环依赖的方式是采用三级缓存,未等bean创建完成之前就先将实例曝光出去,方便其他bean引用。
核心代码:
//singletonObjects指单例对象的cache (一级缓存)
private final Map singletonObjects = new ConcurrentHashMap(256);//singletonFactories指单例对象工厂的cache(三级缓存)private final Map> singletonFactories = new HashMap>(16);//earlySingletonObjects指提前曝光的单例对象的cache(二级缓存)private final Map earlySingletonObjects = new HashMap(16);
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//从一级缓存获取
Object singletonObject = this.singletonObjects.get(beanName);
//isSingletonCurrentlyInCreation():判断当前单例bean是否正在创建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
//从二级缓存获取
singletonObject = this.earlySingletonObjects.get(beanName);
//allowEarlyReference:是否允许从singletonFactories中通过getObject拿到对象
if (singletonObject == null && allowEarlyReference) {
//从三级缓存获取
ObjectFactory> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
图解:
![958d0d41779d85c4d6c382435bbfe463.png](https://i-blog.csdnimg.cn/blog_migrate/36e07f224a1ed5bb98bfb45acd6bd143.png)
A首先完成了初始化的第一步,并且将自己提前曝光到singletonFactories中,
此时进行初始化的第二步,发现自己依赖对象B,此时就尝试去get(B),发现B还没有被create,所以走create流程,
B在初始化第一步的时候发现自己依赖了对象A,于是尝试get(A),尝试一级缓存singletonObjects(没有,因为A还没初始化完全),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,由于A通过ObjectFactory将自己提前曝光了,所以B能够通过ObjectFactory.getObject拿到A对象(此时A还没有初始化完全),
B拿到A对象后顺利完成了初始化阶段1、2、3,完全初始化之后将自己放入到一级缓存singletonObjects中。
此时返回A中,A此时能拿到B的对象顺利完成自己的初始化阶段2、3,最终A也完成了初始化,进去了一级缓存singletonObjects中,而此时,由于B拿到了A的对象引用,所以B现在引用的A对象完成了初始化。
springboot原理,如何实现的自动装配?
首先看启动类上的@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 {
再看里面的@EnableAutoConfiguration注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
然后通过@Import注入AutoConfigurationImportSelector类,这个类通过实现DeferredImportSelector来重写selectImports方法去加载META-INF/spring.factories这个外部文件,这个外部文件里面,默认有很多自动配置的类,这些类的定义信息将会被SpringBoot批量的注入到spring容器中,从而实现了自动装配。
相关的starter和自定义starter都是根据这个实现的。
spring事务以及传播机制
spring事务是封装在数据库事务之上的一种事务处理机制,它有两种管理方式:编程式事务和声明式事务。
spring事务的传播机制有七种:REQUIRED、REQUIRES_NEW、NESTED、SUPPORTS、NOT_SUPPORTED、MANDATORY和NEVER。
详细介绍参考以前的文章:传送门
springboot打包成jar之后,怎么做到不重新打包修改他的properties配置文件?
Spring程序会按优先级从下面这些路径来加载application.properties配置文件
- 当前目录下的/config目录
- 当前目录
- classpath里的/config目录
- classpath 跟目录
因此,要外置配置文件就很简单了,在jar所在目录新建config文件夹,然后放入配置文件,或者直接放在jar同级目录
bootstrap.properties 和 application.properties 有何区别 ?
单纯做 Spring Boot 开发,可能不太容易遇到 bootstrap.properties 配置文件,但是在结合 Spring Cloud 时,这个配置就会经常遇到了。
- bootstrap (. yml 或者 . properties):boostrap 由父 ApplicationContext 加载的,比 applicaton 优先加载,配置在应用程序上下文的引导阶段生效。一般来说我们在 Spring Cloud 配置就会使用这个文件。且 boostrap 里面的属性不能被覆盖;
- application (. yml 或者 . properties):由ApplicatonContext 加载,用于 spring boot 项目的自动化配置。
Spring Boot 打成的 jar 和普通的 jar 有什么区别 ?
- Spring Boot 项目最终打包成的 jar 是可执行 jar ,这种 jar 可以直接通过
java -jar xxx.jar
命令来运行。 - Spring Boot 的 jar 无法被其他项目依赖,主要还是他和普通 jar 的结构不同。普通的 jar 包,解压后直接就是包名,包里就是我们的代码,而 Spring Boot 打包成的可执行 jar 解压后,在
\BOOT-INF\classes
目录下才是我们的代码,因此无法被直接引用。
![08060cc416f2617cfb040c1f9bdf2d89.png](https://i-blog.csdnimg.cn/blog_migrate/8c28178ee329741b8cf0bad32a4f809d.jpeg)
Java面试系列-redis相关
2020-09-29
![d0b0b72d28d2828ef9e7259bb9e9529e.png](https://i-blog.csdnimg.cn/blog_migrate/78a95627925de09480554ba9908005a1.jpeg)
Java面试系列-线程相关(一)
2020-09-03
![f6b933b4a804355ca4c6efa5a5d08962.png](https://i-blog.csdnimg.cn/blog_migrate/6a3e843ab0d862e40bbc4716973c2424.jpeg)
Java到底是引用传递还是值传递
2020-08-07
![584e1adee29df06f2c2fa29c0e0d2bfc.png](https://i-blog.csdnimg.cn/blog_migrate/5dd16f1c782f7c242e7f2deb713700ab.jpeg)
redis分布式锁
2020-06-05
![31d667aec708f65fd9ebc2d2ee668027.png](https://i-blog.csdnimg.cn/blog_migrate/2f8999c63193908bd7b8c54564b9f5bf.jpeg)
手摸手教你搭建免费图床
2020-05-28
![4232523efb43b4dce34422cc2e181f96.png](https://i-blog.csdnimg.cn/blog_migrate/053e3225c3f958c0852b51da9fb77b1c.jpeg)