框架面试题总结

本贴最后更新于 303 天前,其中的信息可能已经时过境迁

image

Spring

Spring 框架中的单例 bean 是线程安全吗

不是线程安全的,Spring 框架中有一个 @Scope 注解,默认值是 singleton,单例的,
一般在 Spring 的 Bean 中注入的都是无状态的对象,没有线程安全问题,如果在 bean 中

定义了可修改的成员变量,要考虑线程安全问题,可以使用多例或者加锁来解决

image

1 Spring 中的 Bean 默认是单例的

对于无状态的单例 Bean 是线程安全的 对于有状态的单例 Bean 不是线程安全的(区分是否有状态,看其是否能被修改)

image

什么是 AOP,项目中怎么使用 AOP

image

概念:

AOP 称为面向切面编程,用于将与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块叫做切面,可以减少系统中的重复代码,降低模块间的耦合程度,同时提高系统的可重用性

常见 AOP 使用场景: 1.记录操作日志 2.缓存处理 3.Spring 内置的事务处理 4.公共字段的填充

1 记录操作日志(AOP 加自定义注解)

image

2 Spring 事务怎么实现

Spring 支持编程式事务和声明式事务

编程式事务:使用 TransactionTemplate 来实现,对业务代码有侵入性,在代码中开启事务,提交事务和回滚事务,侵入性很强

声明式事务:声明式事务建立在 AOP 中,通过 AOP 功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中

           在目标方法开始之前加入一个事务,在执行完目标方法之后**根据执行情况提交或者回滚事务**

@Transactional 就相当于一个声明式事务,其底层就是 AOP 的一个环绕通知

public Object around(Proceeding JoinPoint) throws Throwable{
	try{
		//开启事务
		//执行业务代码
		Object proceed = joinPoint.proceed();
		//提交事务
		return proceed;
	}catch(Exception e){
		e.printStackTrace();
		//在抛出异常之后回滚事务
	}
}

Spring 中事务失效的场景有哪些

image

1 异常捕获处理

事务通知只有捕捉到了目标抛出的异常,才能后续的回滚操作,如果目标自己处理掉异常,事务通知无法知悉

解决方法: 在 catch 块中添加 throw new RuntimeException(e)将异常再次抛出

image

2 抛出检查异常

Spring 事务默认捕获运行时异常,对于检查异常不捕获

解决方法: 配置 rollbackFor 属性 @Transactional(rollbackFor=Exception.class) 这样只要是异常就会进行捕获

image

3 非 Public 方法导致的事务失效

Spring 为方法创建代理,添加事务通知,前提条件都是该方法时 Public

解决方法:将修饰符改为 Public

Spring 中 Bean 的生命周期 #再看#

用途:了解 Spring 容器如何管理和创建 Bean 实例,从而方便调试和解决问题

**BeanDefinition **

通过 getBeanClassName 之后,获取到了类名就可以通过反射来创建 Bean** **

beanClassName bean 的类名

initMethodName 初始化方法名称

propertyName 初始化方法名称

scope 作用域

lazyInit 延迟初始化

image

Spring 容器在进行实例化时,会将 xml 配置的信息,或注解定义的 Bean 信息封装成一个 BeanDefinition 对象,Spring 根据 BeanDefinition 来

创建对象

生命周期具体过程

Bean 的创建是在构造函数处进行的,Bean 的初始化值是在下面的若干步完成的

image

1.得到 BeanDefinition 之后,调用 Bean 的构造函数实例化 Bean 的对象

2.接下来是实现依赖注入 有 @Value 和 @Autowire 注解的

3.Aware 接口,一个 Bean 若实现了 BeanNameAware,BeanFactoryAware,ApplicationContextAware,需要

重写方法,进一步对 Bean 进行扩展,在初始化过程中可以获取 Bean 的名称

4.BeanPostProcessor #before 后置处理器 此为再初始化方法之前进行回调

5.构造方法

两种情况 一种是 Bean 实现了 InitializingBean 接口,实现其中的初始化方法 第二种是自定义的初始化方法(在 Bean 中的某个方法中加上了 @PostConstruct 注解)(从 BeanDefinition 中读到的)

6.BeanPostProcessor #after

AOP 通常使用这个后置处理器增强的,AOP 底层使用动态代理机制,动态代理分为 JDK 动态代理和 CGLIB 动态代理


//一定要注册为组件
@Component
public class MyBeanPostProcessor implements BeanPostProcessor{

@Override
public Object postProcessBeforeInitialization(Object bean,String beanName){

.......
}

@Override
public Object postProcessAfterInitiallization(Object bean,String beanName) throws BeansException{

	//可以在postProcessAfterInitiallization中创建代理对象
	if(beanName.equal("user"){
		System.out.println("postProcessAfterInitiallization--->user");
		//cglib代理对象
		Enhancer enhancer = new Enhancer();
		//设置要增强的类
		enhancer.setSuperclass(bean.getClass());
		//执行回调方法,增强方法
		enhancer.setCallBack(new InvocationHandler(){

		@Override
		public Object invoke(Object o,Method method,Object[] objects) throws Throwable{
			//执行目标方法
			return method.invoke(method,objects);


              }
		//创建代理对象
		return enhancer.create();
  
         }
    )

 }
else return bean;

}
}
public static void main(String[] args){

	ApplicationContext ctx = new AnnotationConfigApplication(SpringConfig.class);
	//如果在postProcessAfterInitiallization使用了代理机制,那么创建的User就是代理对象
	User user = ctx.getBean(User.class);




}

7.Bean 中存在方法加入 @PreDestory,再容器关闭时会执行此方法

image

Spring 的循环引用问题

imageimage

场景: A 依赖于 B,B 依赖于 A A 依赖于 B,B 依赖于 C,C 依赖于 A A 依赖于 A

有循环引用问题,可能出现死循环

image

解决方法:

通过三级缓存来实现(可以解决绝大部分的循环依赖,初始化过程中)

image

一级缓存 singletonObjects 单例池,缓存生命周期已经走完的 Bean 对象(注意此处是单例池,不存放多例对象,对于 ApplicationContext 下,单例 Bean 是立即加载的,多例 Bean 是在调用 getBean 时加载)
二级缓存 earlySingletonObjects 缓存早期的 bean 对象(生命周期未走完)
三级缓存 singletonFactories 缓存的是 ObjectFactory,表示对象工厂,用于创建某个对象

一级缓存无法解决循环依赖问题,在图中没有一个 Bean 可以完整走完生命周期(存在循环依赖)

一级缓存加二级缓存可以解决普通对象的循环依赖

image

一级缓存加三级缓存可以解决普通或**代理对象(获得增强的对象)**的循环依赖,其中的对象工厂可以帮你创建一个代理对象

image

构造方法出现循环依赖(三级缓存无法解决,使用 @Lazy 注解延迟加载)

A 与 B 存在构造方法的循环依赖,三级缓存只能处理初始化数据时的循环依赖,构造函数阶段的不能处理,可以使用延迟加载

(并不是在初始化 Bean 时就依赖注入进去,而是在用到的时候再进行初始化 B)

public class A{
	privte B b;
	public A(@Lazy B b){
		this.b=b;
   }
}


public class B{
	private A a;

	public B(A a){
		this.a=a;
	}

}

image

SpringMVC 的执行流程

1 视图时期(老旧 JSP 时期)

image

所有的请求都会经过 DispatcherServlet(前端控制器,由 TomCat 初始化,调度中心),DishpatcherServlet 初始化后,会在其中加载三个组件 HandlerMapping(处理器映射器),ViewResolver(视图解析器),HandlerAdaptoor(处理器适配器)

处理器就是值得一个具体方法,处理器适配器就是对方法中的参数和返回值做出处理,例如参数中写入 @RequestBody,还有处理返回值

image

2 前后端分离时期(接口开发,异步)

image

image

SpringBoot 的自动配置原理

image

image

SpringBoot 默认扫描的是引导类所在的包及其子包

import 一个 AutoConfigurationImportSelector.class 表示将这个类交给 Spring 来管理,这个类会去加载 META-INF 下的 spring.factories 文件

image

Spring,SpringBoot,SpringMVC 的常用注解

Spring 相关

@Component,@Controller,@Service,@Repository 用于在类上实例化 Bean

@Autowired 在字段上使用,根据类型注入

@Qualifier 结合 @Autowired 使用,根据名称进行依赖注入

@Scope 标注 Bean 的作用范围

@Configuration 指定当前类为 Spring 的配置类,容器创建时会从该类加载注解

@ComponentScan 指定 Spring 在初始化容器时要扫描的包

@Bean 标注在方法上,表示该方法的返回值存储到 Spring 容器中

@Import 使用 @Import 导入的类会被 Spring 加载到 IOC 容器中

@Aspect,@Before,@After,@Around,@Ponitcut 用于切面编程

SpringMVC 相关

@RequestMapping 映射请求路径,定义在类和方法上(衍生注解 postmapping,getmapping,putmapping,deletemapping)

@RequestBody 实现接受 http 请求的 json 数据,将 json 转化为 java 对象

@RequestParam 指定请求路径的参数(传递多个参数时,且前端传递的参数与后端接收的参数不一致,可以用于映射)

@PathViriable 从请求路径中获取参数,传递给方法的形参

@ResponseBody 实现将 controller 方法返回对象转化为 json 返回到客户端

@RequestHeader 获取指定请求头的数据

@RestController @Controller+@ResponseBody

SpringBoot 相关注解

@SpringBootConfiguration 组合了 @Configuration 实现配置文件的功能

@EnableAutoConfiguration 打开自动配置功能

@ComponentScan

@SpringBootApplication

MyBatis(持久型框架)

MyBatis 的执行流程

image

1 需要有 MyBatis 的核心配置文件

加载映射文件时,可以使用 resource 指定特定的映射文件,也可以使用包扫描 package 的方式

image

2 构建 SqlSessionFactory 会话工厂,全局只有一个,可以创建 SqlSession

3 创建 SqlSession,其中包括了要执行的 sql 语句

4 Exector 执行器,真正执行数据库的操作接口,也负责维护一些缓存

5 MappedStatement 封装一些具体信息,代表某一次数据库的操作,里面存放了具体的映射文件,对应的具体方法,和 sql 语句

image

image

MyBatis 是否支持延迟加载

Mybatis 支持延迟加载,但是默认没有开启,也叫做按需加载

image

过程:

1 首先实体类,数据库查出数据后封装到 User 类中,但是 User 类中的 List需要查 Order 表才可以查到

image

2 查询数据库时,在 UserMapper 中定义了一个方法

image

3 那在 UserService 调用了 mapper 中的该方法,是连同嵌套的 sql 一起查还是,需要使用 OrderList

的时候再查(也就是 User 的实例对象调用 getOrderList 的时候),这就涉及到是否延迟加载的问题,

mybatis 默认是关闭的,如果想要开启有两个方法,见下图

image

image

延迟加载的原理:

image

image

MyBatis 的缓存

MyBatis 有一级和二级缓存

image

一级和二级缓存都是基于本地缓存 PerpetualCache(一个类),本质是一个 HashMap

一级缓存作用域是 Session 级别(SQLSession),默认是开启的,当 session 进行 flash 或 close 之后缓存中的数据会被清空

SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper1 = sqlSession.getMapper(UserMapper.class);
UserMapper userMapper2 = sqlSession.getMapper(UserMapper.class);

//由于userMapper1与userMaper2处于同一个Session,第一个查询之后会将结果保存至一级缓存中
User user = userMapper1.selectById(6);
//查询相同的语句时,会直接从一级缓存中获取
User user1 = userMapper2.selectById(6);

二级缓存作用域是 namespace 和 mapper 作用域,不依赖于 SQL session,默认采用 PerpetualCache,HashMap 存储,

如果开启了二级缓存,当会话提交或关闭时,一级缓存中的数据会转移到二级缓存

image

image

当两个查询语句位于不同的 SqlSession 时,一级缓存就不起作用了,可以使用二级缓存,默认此配置时关闭的,开启需要

//全局配置文件中加入
<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>
// 在映射文件中加入
<cache/>

image

  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    941 引用 • 1458 回帖 • 152 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...