spring常见面试题

介绍一下spring bean的生命周期(面试有被问到)

1.加载配置转化成spring bean的定义。

2.使用jdk反射根据bean的定义创建bean的实例并封装成beanwrapper。

3.执行populateBean()属性填充方法。

4.执行initializeBean()方法进行bean的初始化,在初始化中,如果 Bean 实现了 BeanNameAware 接⼝,调⽤ setBeanName() ⽅法,传⼊Bean的名字。如果 Bean 实现了 BeanClassLoaderAware 接⼝,调⽤ setBeanClassLoader() ⽅法,传⼊ClassLoader 对象的实例。如果Bean实现了BeanFactoryAware接口调用setBeanFactory方法传入AbstractAutowireCapableBeanFactory对象实例信息。

如果有和加载这个 Bean的 Spring 容器相关的 BeanPostProcessor 对象,执⾏ postProcessBeforeInitialization() ⽅法

如果实现了InitializingBean接口,这执行afterPropertiesSet()方法。

如果 Bean 在配置⽂件中的定义包含 init-method 属性,执⾏指定的⽅法。

如果有和加载这个 Bean的 Spring 容器相关的 BeanPostProcessor 对象,执⾏ postProcessAfterInitialization() ⽅法

5.最后执行registerDisposableBeanIfNecessarybean的销毁逻辑,当要销毁 Bean 的时候,如果 Bean 实现了 DisposableBean 接⼝,执⾏ destroy() ⽅法。当要销毁 Bean 的时候,如果 Bean 在配置⽂件中的定义包含 destroy-method 属性,执⾏指定的⽅法。
image

面试的时候,面试官可能不想听你讲具体的细节,这个时候的回答方式。
五步法:创建bean的实例,设置bean的属性,初始化bean,使用bean,销毁bean。
七步法:加载bean的定义,创建bean的实例、依赖注入、属性填充、初始化实例、应用程序使用,注册销毁。

介绍一下spring的三级缓存

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
    
    // ....
    /** 单例对象缓存:k:beanName, v: 实例。一级缓存 */
	/** Cache of singleton objects: bean name to bean instance. */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	/** 单例工厂缓存:k:beanName, v: ObjectFactory 三级缓存*/
	/** Cache of singleton factories: bean name to ObjectFactory. */
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

	/** 早期单例对象缓存:k:beanName, v: 实例 */
	/** Cache of early singleton objects: bean name to bean instance. 二级缓存*/
	private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
    // ....
    
}

创建对象的时候先看看一级缓存中有没有,如果没有再走二级缓存,如果二级缓存中没有,再走三级缓存。

说一下spring怎样利用三级缓存解决循环依赖

1.也就是说第一次会创建出来a对象,但是a对象中的b属性值为null(半成品),然后会将key为a,value为lambda表达式放入到三级缓存中。

然后进行populateBean方法中进行属性填充,说白了就是开始创建B对象。

2.会创建出来b对象,但是b对象中的a属性值为null(半成品)。然后会将key为b,value为lambda表达式放入到三级缓存中。

然后进行populateBean方法中进行属性填充,说白了给b对象中的a属性进行复制。

3.这个时候会走到我们的lambda表达式中,我们之前创建好的半成品的a对象进行返回。同时将三级缓存中的a对象移除掉,将其放入到二级缓存中(说白了二级缓存中放的是一个半成品对象key:a,value:b属性为null)。

这个时候b对象就是一个完整的成品对象了。

4.将完整的成品的b对象放入到一级缓存中,同时将它从三级缓存中移除。

5.然后将半成品中的a对象的b属性进行赋值。这个时候a也成为了成品对象了。

6.最后将成品的a对象放到一级缓存中,将二级缓存中的a对象进行移除。

说一下aop动态代理的原理

AOP(Aspect-Oriented Programming,面向切面编程)能够将那些与业务无关,说白了,他是ioc的扩展,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可扩展性和可维护性。Spring AOP是基于动态代理的,如果要代理的对象实现了某个接口,那么Spring AOP就会使用JDK动态代理去创建代理对象;而对于没有实现接口的对象,就无法使用JDK动态代理,转而使用CGlib动态代理生成一个被代理对象的子类来作为代理。

aop增强逻辑的执行时机是在initializeBean方法中,更确切一点说是在BeanPostProcessor的后置处理方法中执行。

其实底层为目标 bean 查找合适的通知器,并封装成一个排好顺序的List集合。遍历集合中的每个拦截器,并执行拦截器中对应的方法。

https://www.bilibili.com/video/BV1a84y1q7aX/?spm_id_from=333.337.search-card.all.click&vd_source=273847a809b909b44923e3af1a7ef0b1

参考博客:https://pdai.tech/md/interview/x-interview-2.html#_10-1-spring

@configuration注解接单介绍一下

@configuration注解底层其实一是个@Component注解。它主要是能够保证我们生成单例bean。如果不加@configuration注解生成的是普通bean,加上@configuration注解生成的是cglib的代理bean。比方说我们在@configuration注解中通过@Bean注解指定要生成的bean对象,如果我们的bean的类型相同,但是bean的名称不同,那么通过@configuration注解创建出来的是同一个bean对象。

@configuration注解会在beanfactorypostprocessor后置处理器执行的时候加载他。

底层执行原理:

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
    this();
    register(componentClasses);
    refresh(); // 走这个方法
}

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

        // Prepare this context for refreshing.
        prepareRefresh();

        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);

        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
            // Invoke factory processors registered as beans in the context.
            // 开始解析我们的@Configuration注解
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);
            beanPostProcess.end();

            // Initialize message source for this context.
            initMessageSource();

            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            onRefresh();

            // .......
        }
    }
}


protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
        // 会执行我们的ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry()方法	
		PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

    // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
    // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
    if (!NativeDetector.inNativeImage() && beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }
}

// 底层会走到parse方法进行@Configuration注解的解析。

https://www.bilibili.com/video/BV1sh411A7K9/?spm_id_from=333.337.search-card.all.click&vd_source=273847a809b909b44923e3af1a7ef0b1

https://www.bilibili.com/video/BV1GP4y177Mr/?spm_id_from=333.337.top_right_bar_window_history.content.click&vd_source=273847a809b909b44923e3af1a7ef0b1

spring事务失效场景:

spring的声明式事务只会帮助我们回滚RuntimeException(也就是运行时异常)的异常。

底层源码其实为我们要执行的方法包了一层大大的try catch。

失效场景:

1.我们在代码中使用try catch 手动捕捉了异常,这样事务就不会回滚了。

2.我们抛出的异常不是RuntimeException的异常,事务也就不会回滚了。

当然,如果如果我们确实需要回滚非RuntimeException 的异常,这个时候我们可以指定@Transactional注解的rollbackfor属性,将其指定为我们要指定的回滚的异常。

3.访问修饰符出错,如果我们被@Transactional修饰的方法的访问修饰符使用的是private(非public)等等,这样会导致事务失效,因为spring 要求被代理方法必须是public的。

4.如果我们被@Transactional修饰的方法被final关键字进行了修饰,也是会导致事务失效的。spring源码关于事务的实现使用到了AOP,而AOP底层的实现是通过jdk或cglib动态代理实现的。

如果某个方法被final修饰了,那么在他的代理类中,spring就没有办法为你重写该方法了,也就没有办法为你实现代理了。

5.如果我们被@Transactional修饰的方法使用了static进行修饰那么也是没有办法进行代理的,原因同上。

6.我们在被@Transactional修饰的方法中调用了本类中的另外一个事务方法,这个时候事务也是会失效的。

原因就是你没有通过spring的方式进行调用,而是自己调用的,没有办法给你进行动态代理增强。

解决方法:我们可以为另一个事务方法再新创建一个service;也可以将本类通过@Autowired的形式再注入到本类中;当然也可以使用

((Userservice)AopContext.currentProxy()).query();来进行调用。

7.如果我们被@Transactional修饰的方法所在的类没有被@Controller、@Service、@Component、@Repository注解修饰,也就以为着,这个类不会被spring容器所接管。自然你的事务也不会生效。

8.比方说在多线程的场景中,一个事务方法调用了另外一个事务方法,但是在在调用另外一个事务方法的时候,你起了一个多线程。如果多线程中的这个方法出现了异常,那么你的第一个方法也是不会进行回滚的。因为两个事务获取到的数据库连接都不一样。

通过spring源码,我们能够知道spring的事务是通过数据库连接来实现的。

9.如果我们要操作的那张表使用的储存引擎是myisam的话,那么事务也是不生效的。

10.我们可以通过@Transactional注解的propagation属性来指定事务的传播行为,spring支持八种事务传播行为。@Transactional(propagation = Propagation.NEVER)这种类型的传播特性不支持事务,如果有事务则会抛异常。

11.如果我们自定义了异常,但是出现事务报错的那个异常又恰恰不是我们自定义的异常,那么事务也是没有办法进行回滚的。

针对异常问题:

在 spring 中为了支持编程式事务,专门提供了一个类:TransactionTemplate,在它的 execute 方法中,就实现了事务的功能。

相较于@Transactional注解声明式事务,我更建议大家使用基于TransactionTemplate的编程式事务。主要原因如下:

避免由于 spring aop 问题导致事务失效的问题。

能够更小粒度地控制事务的范围,更直观。

说一下springmvc的执行流程源码 (面试有被问到)

1、DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出请求,Dispatcherservlet接收
请求并拦截请求。
2、HandlerMapping为处理器映射。DispatcherServlet调用HandlerMapping,HandlerMapping根据请求url查找处理器执行链HandlerExecutionChain(里面包含handler和拦截器链)
Handler.
3、根据获取到的Handler匹配对应的HandlerAdapter。

4、执行拦截器的前置处理方法。

4、通过HandlerAdapter,去执行对应的Handler(也就是我们常说的Controller)。首先会通过参数解析器解析所有的参数,然后通过反射执行Handler,最后经过执行会返回一个ModelAndView对象。
5、执行拦截器的后置处理方法。
6、DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析。

7、DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)(你是jsp了还是freemarker了)。

8、将响应数据返回给客户端(也就是添加到response对象中)。

image

HandlerExecutionChain里面就包含了Handler和一个List的拦截器链。

springmvc的出现主要是用来取代servlet的。

因为每个handler的实现可能是不同的,这个时候我就有了一个HandlerAdapter来进行统一适配。

Spring bean的作用域

分为singleton,prototype,request,session,groupsession(也叫集群session,可以用来做单点登录)。

你们项目中会使用到本地事务吗?(面试中有被问到)

你们如果对两张表操作会使用本地事务吗,我当时回答的是基本上也不会失败。

后来我想了一下,我回答错了。现在我来总结一下正确的答案:

其实还得看业务功能的影响范围了,如果你程序中的某一个交易需要操作两张表。

1、如果对业务功能的影响范围较小,或者没有影响。不使用事务就是你自己程序控制,第一个表插入成功了再插入另一个表,插入失败了就抛出异常。如果插入第二张表的时候失败了,看业务的具体要求了,如果第一张表已经插入的数据对业务没有影响的话,那么可以不用回滚第一张表的数据,这个时候直接抛个异常就可以了。当然你也可以自己通过代码的方式回滚第一张表中的数据。
2、如果对业务功能的影响范围较大,第一个插入正常第二个失败了就要删除第一个表的数据,可以使用程序内部控制,当然也可以使用本地事务来进行控制,比如说使用@Transactional注解来进行控制,或者使用编程式事务来进行控制。

我个人比较推荐实用编程式事务来进行控制。因为编程式事务它的控制力度是比较细的。而@Transactional注解只能添加到方法上面。它的控制力度是比较粗糙的,而且使用不当可能会导致事务失效。

编程式事务我们可以使用spring框架提供的TransactionTemplate来进行实现。

你能详细说说spring aop你当时在项目中是怎么写的吗?(面试的时候有被问到)

https://www.cnblogs.com/dongyaotou/p/18510627

spring 都有哪些事务传播行为(面试有被问到)

https://www.bilibili.com/video/BV1yD421V7fE/?spm_id_from=333.337.search-card.all.click&vd_source=273847a809b909b44923e3af1a7ef0b1

事务的传播特性指的是当一个事务方法被另一个事务方法调用的时候,这个事务方法应该如何进行?
image
image

说一下applicationContext和beanfactory之间有什么区别(面试的时候有被问到)

不管是这两个中的哪一个,他们都相当于是bean的工厂。

beanfactory接口里面给我们提供很多getBean的方法,以及判断是否是单例,是否是原型的方法。说白了它提供的功能比较简单。

applicationContext接口是继承至beanfactory接口。也就是说applicationcontext接口具有beanfactory接口的所有功能,并在它的基础上又提供了一些其他额外的功能,applicationcontext接口它又继承了7个接口。

继承了EnvironmentCapable接口,表示它具有获取环境变量的功能。比方说获取操作系统的环境变量,或者是配置文件中的信息。

继承了ListableBeanFactory接口,表示它具有一次性获取多个bean的功能。

继承了HierarchicalBeanFactory接口,表示它支持bean的层次结构管理

继承了MessageSource接口,表示具有国际化的功能

继承了HierarchicalBeanFactory接口,表示具有继承的功能

继承了ApplicationEventPublisher接口,表示具有时间传递的功能。

继承了ResourcePatternResolver接口,表示具有资源加载的功能。

并且BeanFactory和ApplicationContext的最大区别就是ApplicationContext采用的是预加载,它在启动时就把所有的Bean都进行加载,所以启动时间久,但是之后的运行就迅速,不再需要加载Bean,类似于线程池。而beanfactory采用的是懒加载。

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
    @Nullable
    String getId();

    String getApplicationName();

    String getDisplayName();

    long getStartupDate();

    @Nullable
    ApplicationContext getParent();

    AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}

说一下@Autowire和@Rsource注解有什么作用(面试有被问到)

@Autowire在获取对象的时候,先通过类型进行匹配,再根据名称进行匹配。

它是spring框架的注解,基于类型进行依赖注入,支持通过@Qulifier注解指定具体的bean的名称。

可以用于构造器、字段或者方法上面。

image

@Resource注解,在获取bean的时候,先通过名称进行匹配,再根据类型进行匹配。

它是Java提供的一个注解。

只能用于字段、setter方法或者参数上。

image

说一下@Compnent注解和@Bean注解和@Configrution注解标注的bean,spring对他们有什么不一样的处理(面试有被问到)

@component注解是一个通用注解,可以用在任何类上。包括普通Java类、业务逻辑组件、持久化对象中等等,spring会使用@componentscan注解扫描并把它把他创建成一个对象并且注入到spring容器中。

@bean注解用于在配置类中声明一个类的。通常使用在配置类的方法上面,表示把这个方法的返回对象注册到spring 容器中,通过bean注解我们可以自定义bean的创建和初始化过程,包括指定bean的名称和作用域、依赖关系等等等等

@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。

@configuration能够真正的保证单例bean ,它底层能给我们返回一个cglib的动态代理对象。

https://www.bilibili.com/video/BV17p4y137z9/?spm_id_from=333.337.search-card.all.click&vd_source=273847a809b909b44923e3af1a7ef0b1

说一下dispatcherservlet 的init方法什么时候执行 TODO 下来再找老师好好确定一下吧(面试有被问到)

protected void initStrategies(ApplicationContext context) {
    //初始化文件上传使用的MultiResolver
	initMultipartResolver(context);
    //初始化LocaleResolver,用于国际化
	initLocaleResolver(context);
    //初始化主题解析器ThemeResolver
	initThemeResolver(context);
    //初始化请求映射的HandlerMaping
	initHandlerMappings(context);
    //初始化请求处理的适配器HandlerAdapter
	initHandlerAdapters(context);
    //初始化异常处理解析器HandlerExceptionResolver
	initHandlerExceptionResolvers(context);
    //初始化用于从请求中获取viewname的RequtstToviewNameResolver
	initRequestToViewNameTranslator(context);
    //初始化视图解析器ViewResolver
	initViewResolvers(context);
    //初始化FlashMapperManager
	initFlashMapManager(context);
}

就先说是servlet初始化的时候吧。

spring中都使用到了哪些设计模式(面试有被问到)

工厂模式
工厂模式(Factor Patter)封装了对象创建的过程,使用者不需要关心对象创建的细节。工厂模式分为三种:简单工厂模式、工厂方法模式和抽象工厂模式。在编码中该模式通常都是以 *Factory 的形式呈现。
Soring 中的 BeanFactory 就是简单工厂模式的体现,我们可以根据 beanName 在 BeanFactory 中获取 bean 对象;而 Spring 中的 FactoryBean 就是工厂方法模式的体现,我们可以通过实现 FactoryBean 接口并实现其 getObject(0)方法来自定义 bean 对象。

单例模式

单例模式(Singleton Pattern)是最常使用的一种设计模式,它可以确保某一个类只有一个实例,并提供了一个全局访问点来访问该实例。
在 Spring 中,bean 的作用域默认就是单例的。它是通过单例池来确保在 Spring 容器中只会存在一个对象实例。

代理模式

代理模式(Proxy Patern)主要是通过创建一个代理对象来实现对目标对象的访问,以达到保护和增强目标对象的目的。代理模式有两种具体的实现:静态代理和动态代理。
Spring 中的 AOP 就是基于 JDK 和 CGlib 这两种动态代理实现的。

适配器模式
适配器模式(Adapter Pattern)可以作为两个不兼容的接口之间的桥梁,它可以使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。在编码中该模式通常都是以 *Adapter 的形式呈现。
Spring AOP 中的 AdvisorAdapter 类、Spring MVC中的 HandlerAdapter 接口,就是适配器模式的典型体现。

责任链模式
责任链模式(Chain of Responsibiity Pattern)为请求创建了一个接收者对象的链,在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。在编码中该模式通常都是以
*Chain 的形式呈现。
Spring MVC 中的 HandlerExecutionChain 类,就使用到了责任链模式。

观察者模式
观察者模式(Observer Pattern)定义了一种一对多的依赖关系,当一个对象的状态发生改变时,其所有依赖者都会收到通知并自动更新。
Spring 中的事件监听机制是观察者模式典型的应用,如 ApplicationListener。

模板模式
模板模式(Template Pattern)就是抽象父类提供一套定义好的方法供子类调用,而其中的某些方法子类会根据自己的情况而进行定制。就好像我们平时用模板写一些东西,但是内容却各不相同。在编码中该模式通常都是以 *Template 的形式呈现。
Spring JDBC 中的 JdbcTemplate、Spring 事务管理中的 TransactionTemplate 就是模板模式的体现。

装饰器模式
装饰器模式(Decorator Pattern)允许我们向一个现有的对象添加新的功能,同时又不改变其现有的结构。当我们需要为一个类扩展功能时可以考虑使用装饰器模式,但是该模式的缺点就是需要增加额外的代码。
在 Spring 中,装饰器模式在类名上有两种表现:一种是类名中含有 Wrapper,另一种是类名中含有 Decorator.

策略模式
策略模式(Strategy Pattern)定义了一系列算法或策略,并将每个算法封装在独立的类中,使得它们可以互相替换。通过使用策略模式,可以在运行时根据需要选择不同的算法,而不需要修改客户端代码。在编码中该模式通常都是以 *Strategy 的形式呈现。
Spring 中的 IinstantiationStrategy 接口,根据创建对象情况的不同,提供了Spring Bean 实例化的三种策略:默认构造方法、指定构造方法 或者 工厂方法。这是一种典型的策略模式。

说说你们在实际项目开发中都有用到了那种设计模式(面试有被问到)

门面模式:比方说外接的请求进来了之后,先会走到我一个公共的处理类的一个方法里面,然后再由这个方法根据里面的交易码进行交易的分发。
单例模式:比方说我们项目中的一些常用工具类,我们可以直接使用。
责任链模式:比方说分期办理的时候,要经过很多步骤,比方说办理时间校验,办理金额校验,黑灰名单校验,等等,使用的是责任链模式。
适配器模式:调一个类的时候,参数类型不一致,这个时候需要转换。更多的提现是方法的一种重载。

spring bean是线程安全的吗(面试有被问到)

❤前提:

首先说明一点:Spring 中的 bean 对象是否线程安全与 Spring 容器本身无关,Spring 只是帮我们创建和管理 bean 而已。但是,Spring 中的 bean 对象是否线程安全,我们可以通过 bean 的作用域来分析。
Spring 中的 bean 对象有多种作用域,其中,我们最常用的作用域有两种:singleton 和 prototype,即单例 bean 和原型 bean。
❤说说Spring中bean的作用域
面试题解析在 Spring 中,通常使用 @Scope 注解声明在一个 bean 对象上,来定义该 bean 对象的作用域。那么...WeiJavaNotes
可能很多小伙伴潜意识里老是会觉得,单例 bean 就是线程安全的,而原型 bean 就是线程不安全的。其实,这是完全不对的。
首先,我们先来看下原型 bean。使用了该作用域的 bean 对象、并不会被 Soring 所管理;并月每次从 Soring 容器中获取 bean 对象时,都会创建一个新的对象实例。由此可见,原型 bean 的对象实例并不会被多个线程所共享,所以原型 bean 是线程安全的!
接着,在让我们重点看下单例 bean。单例 bean 一般分为两种:非懒加载的单例 bean 和使用了 @Lazy 注解声明了的懒加载的单例 bean。其中,非懒加载的单例 bean,在 Spring 容器启动时就会被创建;而懒加载的单例只有在使用的时候才会被创建;单例bean 创建完成后,会被放入到单例池中进行存储,用以保证在 Spring 容器中只会存在一个对象实例。

既然单例 bean 在 Spring 容器中只会存在一个对象实例,那么它就会被多个线程所共享。一个对象实例被多个线程所共享就会存在线程安全的隐患,这主要取决于该单例 bean 是有状态的单例 bean 还是无状态的单例 bean。所谓无状态的单例 bean 就是没有成员变量或者仅存在只读的成员变量;反之就是有状态的单例 bean。无状态的单例 bean 是线程安全的;而有状态的单例 bean 则是线程不安全的。
总结:Spring 中,只有有状态的单例 bean才存在线程安全的问题!
此时,面试官可能还会追问:怎么保证这种有状态的单例 bean 的线程安全呢?一般有以下几种方案:1.使用线程安全的工具类,比如原子类、线程安全的容器类、ThreadLocal 等
2.尽可能的避免在单例 bean 中使用共享的成员变量。
3.将单例 bean 改为原型 bean。

Spring 中的 HandlerInterceptor 拦截器、过滤器、AOP(面试有被问到)

拦截器:

说白了就是在spring mvc中,涉及前后端请求的时候,如果我们的controller在执行前后或者执行完成的时候,需要插入额外的逻辑的话,比方说用户是否登录判断、用户权限校验等等逻辑,这个时候就可以使用拦截器来做。

它是spring框架为我们提供的一个接口。

image

image

image

过滤器:Filter 是javax.servlet包下面的一个接口。它可以拦截HTTP请求并对其进行预处理或后处理,不涉及到业务逻辑。比方我们可以使用它做一下请求字符编码的设置或者是响应数据压缩等功能。

aop,就给面试官多扯一点,主要用于实现更加复杂的业务逻辑,比方说日志记录、事务管理、性能监控等等。

参考博客:https://blog.csdn.net/u010425839/article/details/142052122

说一下controller里面一般都会用到哪些注解(面试有被问到)

@Controller

@RestController‌‌‌

@RequestMapping:这个注解用于映射Web请求到相应的处理方法上。它可以标注在类或方法上,提供路由信息,完成从URL到Controller的映射‌

@GetMapping

@PostMapping

@RequestBody

@ResponseBody

@Autowired

@Resource

@Value

@Validated

@PathVariable :带占位符的 URL,可以通过 @PathVariable 将 URL 中的占位符绑定到控制器的处理方法的参数中,占位符使用{}括起来。

@RequestParam:用于将 HTTP 请求中的参数绑定到控制器方法的参数上。相当于可以进行参数别名的转换。

posted on 2024-09-21 15:56  ~码铃薯~  阅读(7)  评论(0编辑  收藏  举报

导航