spring面试笔记

spring mvc流程?

在整个 Spring MVC 框架中, DispatcherServlet 处于核心位置,负责协调和组织不同组件以完成对请求处理并返回响应的工作
SpringMVC 处理请求过程:

  1. 当一个请求来时,首先会被DispatcherServlet拦截,然后根据请求信息查找HandlerMapping,返回一个执行链
  2. 依次执行,到达controller,执行业务逻辑,返回ModelAndView给DispatcherServlet;
  3. DispatcherServlet在请求进行视图解析,然后然会View

    最后进行视图渲染
spring mvc执行流程

spring AOP实现原理?

AOP:面向切面的编程.是对传统 OOP(面向对象编程) 的补充.AOP 的主要对象是切面(aspect),Filter(过滤器)也是一种 AOP.比如事务和日志等也会用AOP实现。
实现原理是java动态代理机制和CGLIB动态代理

  1. JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类,InvocationHandler 通过invoke()方法反射调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着Proxy利用 InvocationHandler动态创建一个符合某一接口的实例, 生成目标类的代理对象。
  2. 如果代理类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个字节码层面的代码生成类库,可以在运行时为指定类动态的生成一个子类对象,并覆盖其中特定方法,从而实现AOP。CGLIB是通过继承的方式做动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。

spring IOC?

ioc:控制反转,将bean的创建交由spring管理,由spring维护bean的生命周期.
两种实现:

  1. 实现 BeanFactory接口的简单容器(包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系)
  2. ApplicationContext应用上下文,作为容器的高级形态存在( 继承MessageSource,因此支持国际化; 统一的资源文件访问方式; 提供在监听器中注册bean的事件; 同时加载多个配置文件; 载入多个(有继承关系)上下文

@Autowired与@Resource有什么区别??

  1. @Autowired按byType自动注入,而@Resource默认按 byName自动注入;
  2. 当不止一个实例时,@Autowired需要配合@Qualifier来定位需要装载的实例;
  3. Autowired与Resource的后置处理器类不一样

spring IOC 的初始化过程?

  1. 第一个过程是:BeanDefinition的Resource定位
  2. 第二个过程是:BeanDefinition的载入和解析
  3. 第三个过程是:BeanDefinition在IoC容器中的注册

IOC容器的初始化过程-refresh()?

public void refresh() throws BeansException, IllegalStateException {
    synchronized(this.startupShutdownMonitor) {
        this.prepareRefresh();
        // 这里是子类中启动FreshBeanFactory的地方
        ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
        this.prepareBeanFactory(beanFactory);
        try {
            // 设置BeanFactory的后置处理
            this.postProcessBeanFactory(beanFactory);
            // 调用BeanFactory的后置处理,这些处理器是在Bean定义中向容器注册的,
            // 当这个方法执行完成,所有的Bean的配置信息就会被放到BeanDefinitionMap中
            this.invokeBeanFactoryPostProcessors(beanFactory);
            // 注册后置处理器,在bean创建的时候调用
            this.registerBeanPostProcessors(beanFactory);
            // 对消息源进行初始化
            this.initMessageSource();
            // 初始化上下文中的事件机制
            this.initApplicationEventMulticaster();
            // 初始化其他特殊Bean
            this.onRefresh();
            // 检查监听器并向容器注册
            this.registerListeners();
            // 初始化所有非懒加载的bean,并完成了依赖注入
            this.finishBeanFactoryInitialization(beanFactory);
            // 发布容器事件,结束refresh过程
            this.finishRefresh();
        } catch (BeansException var9) {
            // 防止资源占用,销毁之前创建的Bean(finishBeanFactoryInitialization创建的)
            this.destroyBeans();
            // 重置‘active’标志
            this.cancelRefresh(var9);
            throw var9;
        } finally {
            this.resetCommonCaches();
        }

    }
}

那IOC有什么优缺点吗?

  1. 优点:很明显,实现了组件之间的解耦,提高程序的灵活性和可维护性。
  2. 缺点:对象生成是反射实现,在效率上有些损耗。但相对于IOC提高的维护性和灵活性来说,这点损耗是微不足道的,除非某对象的生成对效率要求特别高

Spring Bean 的生命周期?

  1. 通过构造器或工厂方法创建 Bean 实例
  2. 为 Bean 的属性设置值和对其他 Bean 的引用
  3. Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给Bean
  4. 将 Bean 实 例 传 递 给 Bean 后 置 处 理 器 的postProcessBeforeInitialization 方法
  5. 调用 Bean 的初始化方法(init-method)
  6. 将 Bean 实 例 传 递 给 Bean 后 置 处 理 器 的postProcessAfterInitialization 方法
  7. Bean 可以使用了
  8. 当容器关闭时, 调用 Bean 的销毁方法(destroy-method)

在这里插入图片描述

BeanFactory 和 ApplicationContext 有什么区别?

  1. BeanFactory可以理解为含有 bean 集合的工厂类。BeanFactory 包含了各种 bean 的定义,以便在接收到客户端请求时将对应的 bean 实例化(懒加载)。 BeanFactory 还能在实例化对象时生成协作类之间的关系。此举将 bean 自身与bean 客户端的配置中解放出来。BeanFactory 还包含 了 bean 生命周期的控制,调用客户端的初始化方法(initialization methods)和销毁方法(destruction methods)。
  2. applicationcontext 拥有beanfactory的所有功能。applicationcontext 在此基础上还提供了其他的功能。提供了支持国际化、统一的资源文件读取方式、已在监听器中注册的 bean 的事件等

Spring Bean 的作用域之间有什么区别?

  1. singleton:这种 bean 范围是默认的,这种范围确保不管接受到多少个请求,每个容器中只有一个 bean 的实例,单例的模式由bean factory 自身来维护
  2. prototype:原形范围与单例范围相反,为每一个 bean 请求提供一个实例。
  3. request:在请求 bean 范围内会每一个来自客户端的网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。
  4. Session:与请求范围类似,确保每个 session 中有一个 bean 的实例,在 session 过期后,bean
    会随之失效。
  5. global- session:全局变量。

Spring 框架中的单例 Bean是线程安全的么?

Spring 框架并没有对单例 bean 进行任何多线程的封装处理。

关于单例 bean的线程安全和并发问题需要开发者自行去处理。但实际上,大部分的 Spring bean 并没有可变的状态(比如 Servies类 和 DAO类),所以在某种程度上说 Spring 的单例 bean 是线程安全的。

如果你的 bean 有多种状态 的话(比如 View Model对象),就需要自行保证线程安全。 最浅显的解决办法就是将多态 bean 的作用域由“singleton”变更为“prototype”,还有使用ThreadLocal来处理一些值

请解释 Spring Bean 的自动装配?

在 Spring 框架中共有 5 种自动装配

  1. no:这是 Spring 框架的默认设置,在该设置下自动装配是关闭的,开发者需要自行在 bean 定义中用标签明确的设置依赖关系。
  2. byName:该选项可以根据 bean 名称设置依赖关系。当向一个 bean 中自动装配一个属性时,容器将根据 bean 的名称自动在在配置文件中查询一个匹配的 bean。如果找到的话,就装配这个属性,
    如果没找到的话就报错。
  3. byType:该选项可以根据 bean 类型设置依赖关系。当向一个 bean 中自动装配一个属性时,容器
    将根据 bean 的类型自动在在配置文件中查询一个匹配的 bean。如果找到的话,就装配这个属性,
    如果没找到的话就报错。
  4. constructor:构造器的自动装配和 byType 模式类似,但是仅仅适用于与有构造器相同参数的 bean,如果在容器中没有找到与构造器参数类型一致的 bean,那么将会抛出异常。
  5. autodetect:该模式自动探测使用构造器自动装配或者 byType 自动装配。首先,首先会尝试找合
    适的带参数的构造器,如果找到的话就是用构造器自动装配,如果在 bean 内部没有找到相应的构造
    器或者是无参构造器,容器就会自动选择 byTpe 的自动装配方式。

Spring 框架中有哪些不同类型的事件?

Spring 提供了以下 5 中标准的事件:

  1. 上下文更新事件(ContextRefreshedEvent):该事件会在 ApplicationContext 被初始化或者更 新时发布。也可以在调用 ConfigurableApplicationContext 接口中的 refresh()方法时被触发。
  2. 上下文开始事件(ContextStartedEvent):当容器调用 ConfigurableApplicationContext 的 Start()方法开始/重新开始容器时触发该事件。
  3. 上下文停止事件(ContextStoppedEvent):当容器调用 ConfigurableApplicationContext 的 Stop()方法停止容器时触发该事件。
  4. 上下文关闭事件(ContextClosedEvent):当 ApplicationContext 被关闭时触发该事件。容器被 关闭时,其管理的所有单例 Bean 都被销毁。
  5. 请求处理事件(RequestHandledEvent):在 Web 应用中,当一个 http 请求(request)结束触 发该事件。

Spring 框架中都用到了哪些设计模式?

Spring 框架中使用到了大量的设计模式:

  1. 代理模式—在 AOP 和 remoting 中被用的比较多。
  2. 单例模式—在 spring 配置文件中定义的 bean 默认为单例模式。
  3. 模板方法—用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。
  4. 工厂模式—BeanFactory 用来创建对象的实例
  5. 委派模式:Spring提供了DispatcherServlet来对请求进行分发

spring的事务传播行为?

  1. PROPAGATION_REQUIRED 如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
  2. PROPAGATION_SUPPORTS 支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
  3. PROPAGATION_MANDATORY 支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
  4. PROPAGATION_REQUIRES_NEW 创建新事务,无论当前存不存在事务,都创建新事务
  5. PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  6. PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。
  7. PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。

spring 循环依赖和如何处理?

循环依赖

Spring在进行bean的创建时,如果有两个类,A需要B,B又需要A,此时就会出现循环依赖,循环依赖只有单例模式才支持。

解决方式

  1. 我们向BeanFactory获取A类型的bean,第一次没有于是创建对象。

  2. 接着完成两件事

    1. 将自己的工厂放到二级缓存
    2. 将beanName放入正在创建的集合中
  3. A类型bean进行属性注入(populdateBean),触发了依赖注入B,这时b也不存在,于是也开始构建b,重复步骤1

  4. 接着B进行构造(populdateBean),这是需要a,于是触发getSingleton(String beanName, boolean allowEarlyReference)

  5. 这时候就可以在二级缓存中获取到a的工厂,从而得到a,再将a放入三级缓存中,

  6. b得到的a,于是成功初始化,接着a也就能得到b,初始化成功

获取对象的源码如下


protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {
	...
	// 将自己的工厂放到二级缓存
	addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))
	...
}


protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
	...	
	// 将Factory放入二级缓存
	this.singletonFactories.put(beanName, singletonFactory);
	...
}


public  Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		...
		// 将该beanName放入正在创建的集合列表
		beforceSingletonCreation(beanName)
		...
}

protected Object getSingleton(String beanName, boolean allowEarlyReference) {

    //从一级缓存中获取对象
    Object singletonObject = this.singletonObjects.get(beanName);
    // 如果没有获取到,则判断是否正在创建中
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            //从三级缓存中获取尚未完成初始化的对象
            singletonObject = this.earlySingletonObjects.get(beanName);
            //如果不存在,且允许循环依赖
            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);
}

spring 为什么不一开始直接将对象放到三级缓存,还要有二级缓存?

由于spring还需要对未完成的实例做封装,比如AOP代理等,如果一开始将未完成的实例放到三级缓存,则无法提前完成aop代理,则会在使用中可能出现其他问题。

spring 三级缓存?

singletonFactories : 单例对象工厂的缓存

earlySingletonObjects :缓存未初始化完成但已经被spring代理的对象

singletonObjects:缓存完成的单例对象

spring注入方式?

  1. Set注入
    这是最简单的注入方式,假设有一个SpringAction,类中需要实例化一个SpringDao对象,那么就可以定义一个private的SpringDao成员变量,然后创建SpringDao的set方法(这是ioc的注入入口)
  2. 构造器注入
    这种方式的注入是指带有参数的构造函数注入,看下面的例子,我创建了两个成员变量SpringDao和User,但是并未设置对象的set方法,所以就不能支持第一种注入方式,这里的注入方式是在SpringAction的构造函数中注入,也就是说在创建SpringAction对象时要将SpringDao和User两个参数值传进来
  3. 静态工厂的方法注入
    静态工厂顾名思义,就是通过调用静态工厂的方法来获取自己需要的对象,为了让spring管理所有对象,我们不能直接通过"工程类.静态方法()"来获取对象,而是依然通过spring注入的形式获取
  4. 实例工厂的方法注入
    实例工厂的意思是获取对象实例的方法不是静态的,所以你需要首先new工厂类,再调用普通的实例方法

spring通知?

  1. 前置通知:
  2. 后置通知:
  3. 最终通知:
  4. 环绕通知:
  5. 异常通知:

Spring如何处理线程并发问题?

在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理,解决线程安全问题。

ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了“时间换空间”的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队。而ThreadLocal采用了“空间换时间”的方式。

ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。

Spring解决跨域?

  1. 通过注解的方式允许跨域(spring 4.2)
    我们可以在Controller类或其方法上加@CrossOrigin注解,来使之支持跨域。
@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
@RequestMapping("/User")
public class UserController {
}
//其中origins为CrossOrigin的默认参数,即跨域来源,*即任何来源,也可以是其他域名。即可以以以下形式:
@CrossOrigin("http://test.com") @CrossOrigin(origins="http://test.com",maxAge=3600)
  1. 用filter过滤器,重写请求头
@Component  
public class CorsFilter implements Filter {  
  
    final static org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(CorsFilter.class);  
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {  
        HttpServletResponse response = (HttpServletResponse) res;  
        response.setHeader("Access-Control-Allow-Origin", "*");  
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");  
        response.setHeader("Access-Control-Max-Age", "3600");  
        response.setHeader("Access-Control-Allow-Headers", "x-requested-with");  
        System.out.println("*********************************过滤器被使用**************************");  
        chain.doFilter(req, res);  
    }  
    public void init(FilterConfig filterConfig) {}  
    public void destroy() {}  
}

spring boot 自动装配原理?

自动装配的实现是基于@Conditional(条件化配置)实现的

@Conditional:Spring的条件化配置允许配置存在于应用中,但是若未满足某些特定条件,则会忽略这些配置。

在spring boot项目中,会存在一个名为spring-boot-autoconfigure的jar包,条件化配置就是在这个jar里面实现的,它用到了如下的条件化注解,这些注解都是以@ConditionalOn开头的,他们都是应用了@Conditional的组合注解:在这里插入图片描述

步骤

  1. spring boot项目的启动类用的注解 @SpringBootApplication 是一个组合注解,其中@EnableAutoConfiguration是自动配置相关的;
    在这里插入图片描述

  2. 而这个@EnableAutoConfiguration注解里面有个@Import注解导入了 AutoConfigurationImportSelector 用来实现具体的功能
    在这里插入图片描述

  3. 进入父类,里面有个方法 selectImports() 调用了方法getCandidateConfigurations(),进而调用了SpringFactoriesLoader.loadFactoryNames()方法
    在这里插入图片描述

  4. SpringFactoriesLoader.loadFactoryNames方法会扫描具有META-INF/spring.factories文件的jar包,而我们的spring-boot-autoconfigure.jar里面就有一个这样的文件,此文件中声明了具体有哪些自动配置:

在这里插入图片描述
在这里插入图片描述

这样就实现了自动装配

spring 转换器?

Spring定义了3中类型的转换器接口:

  1. Converter<S,T>接口:提供单一方法:T convert(S source);该方法负责将S类型的对象转换为T类型的对象;
    ConverterFactory<S,R> :提供单一方法 Converter<S,T> getConverter(Class targetType);该方法负责将一种类型的对象转换为另一种类型及其子类对象,例如String转换为Number及Number子类Integer、Double等对象;
  2. GenericConverter接口:该接口会根据源类对象及目标类对象的上下文信息进行类型转换。

    接口定义的方法如下:
    1. Set<GenericConverter.ConvertiblePair> getConvertibleTypes();
    2. Object convent(Object source ,TypeDescriptor sourceType,TypeDescriptor targetType);

Spring boot 启动流程?

  1. 创建SpringBootApplication对象
1)保存主配置类
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
2)判断当前是否为一个WEB应用
this.webApplicationType = WebApplicationType.deduceFromClasspath();
3)查找类路径下META-INF/spring.factories中配置的ApplicationContextInitializer
setInitializers() --> SpringFactoriesLoader.loadFactoryNames(type, classLoader)
4) 在查找ApplicationListener
setListeners() --> SpringFactoriesLoader.loadFactoryNames(type, classLoader)
5)从多个配置类中找到有main方法的主配置类
this.mainApplicationClass = deduceMainApplicationClass();
  1. 启动应用 run
1) 获取SpringApplicationRunListener也是从META-INF/spring.factories中
SpringApplicationRunListeners listeners = getRunListeners(args);
2)启动SpringApplicationRunListeners 
listeners.starting();
3)准备环境:创建环境并回调listeners.environmentPrepared(environment);
为环境准备完成
prepareEnvironment(listeners, applicationArguments);
4)创建ApplicationContext,会根据是否是WEB环境创建Web IOC或者普通IOC
createApplicationContext();
5) 准备上下文环境:
prepareContext()
这个方法中会执行
①保存environment到IOC;
②调用 applyInitializers(context);回调之前从配置中读取的ApplicationContextInitializer的Initialize方法
③调用listeners.contextPrepared(context);;回调listeners.contextPrepared
④调用listeners.contextLoaded(context); 同理
6)IO容器初始化
refreshContext(context);
7)调用listeners的started方法
listeners.started(context);
8)从IOC容器中获取ApplicationRunner和CommandLineRunner进行回调;ApplicationRunner先回调,CommandLineRunner后回调
callRunners(context, applicationArguments);
9) 调用listeners.running方法
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值