• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

关于java:大厂常考的Spring面试题

java 搞代码 4年前 (2022-01-27) 37次浏览 已收录 0个评论
文章目录[隐藏]

筹备了一个月的八股文,经验了二十几场面试之后,发现Spring很受面试官青眼。最近有空将Spring常见的面试题总结了一下,心愿对大家有所帮忙。
文章目录:

  • Spring的长处
  • Spring 用到了哪些设计模式?
  • 什么是AOP?
  • AOP有哪些实现形式?
  • JDK动静代理和CGLIB动静代理的区别?
  • Spring AOP相干术语
  • Spring告诉有哪些类型?
  • 什么是IOC?
  • IOC的长处是什么?
  • 什么是依赖注入?
  • IOC容器初始化过程?
  • Bean的生命周期
  • BeanFactory和FactoryBean的区别?
  • Bean注入容器有哪些形式?
  • Bean的作用域
  • Spring主动拆卸的形式有哪些?
  • @Autowired和@Resource的区别?
  • @Qualifier 注解有什么作用
  • @Bean和@Component有什么区别?
  • @Component、@Controller、@Repositor和@Service 的区别?
  • Spring 事务实现形式有哪些?
  • 有哪些事务流传行为?
  • Spring怎么解决循环依赖的问题?
  • Spring启动过程
  • Spring 的单例 Bean 是否有线程平安问题?

Spring的长处

  • 轻量,根本版本大概2MB。
  • 通过管制反转和依赖注入实现松耦合
  • 反对面向切面的编程,并且把利用业务逻辑和零碎服务离开。
  • 通过切面和模板缩小样板式代码。
  • 不便集成各种优良框架。外部提供了对各种优良框架的间接反对(如:Hibernate、MyBatis等)。
  • 不便程序的测试。Spring反对Junit4,增加注解便能够测试Spring程序。

Spring 用到了哪些设计模式?

1、简略工厂模式:BeanFactory就是简略工厂模式的体现,依据传入一个惟一标识来取得 Bean 对象。

<code class="java">@Override
public Object getBean(String name) throws BeansException {
    assertBeanFactoryActive();
    return getBeanFactory().getBean(name);
}

2、工厂办法模式:FactoryBean就是典型的工厂办法模式。spring在应用getBean()调用取得该bean时,会主动调用该bean的getObject()办法。每个 Bean 都会对应一个 FactoryBean,如 SqlSessionFactory 对应 SqlSessionFactoryBean

3、单例模式:一个类仅有一个实例,提供一个拜访它的全局拜访点。Spring 创立 Bean 实例默认是单例的。

4、适配器模式:SpringMVC中的适配器HandlerAdatper。因为利用会有多个Controller实现,如果须要间接调用Controller办法,那么须要先判断是由哪一个Controller解决申请,而后调用相应的办法。当减少新的 Controller,须要批改原来的逻辑,违反了开闭准则(对批改敞开,对扩大凋谢)。

为此,Spring提供了一个适配器接口,每一种 Controller 对应一种 HandlerAdapter 实现类,当申请过去,SpringMVC会调用getHandler()获取相应的Controller,而后获取该Controller对应的 HandlerAdapter,最初调用HandlerAdapterhandle()办法解决申请,实际上调用的是Controller的handleRequest()。每次增加新的 Controller 时,只须要减少一个适配器类就能够,无需批改原有的逻辑。

罕用的处理器适配器:SimpleControllerHandlerAdapterHttpRequestHandlerAdapterAnnotationMethodHandlerAdapter

<code class="java">// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

public class HttpRequestHandlerAdapter implements HandlerAdapter {

    @Override
    public boolean supports(Object handler) {//handler是被适配的对象,这里应用的是对象的适配器模式
        return (handler instanceof HttpRequestHandler);
    }

    @Override
    @Nullable
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {

        ((HttpRequestHandler) handler).handleRequest(request, response);
        return null;
    }
}

5、代理模式:spring 的 aop 应用了动静代理,有两种形式JdkDynamicAopProxyCglib2AopProxy

6、观察者模式:spring 中 observer 模式罕用的中央是 listener 的实现,如ApplicationListener

7、模板模式: Spring 中 jdbcTemplatehibernateTemplate 等,就应用到了模板模式。

什么是AOP?

面向切面编程,作为面向对象的一种补充,将公共逻辑(事务管理、日志、缓存等)封装成切面,跟业务代码进行拆散,能够缩小零碎的反复代码和升高模块之间的耦合度。切面就是那些与业务无关,但所有业务模块都会调用的公共逻辑。

AOP有哪些实现形式?

AOP有两种实现形式:动态代理和动静代理。

动态代理

动态代理:代理类在编译阶段生成,在编译阶段将告诉织入Java字节码中,也称编译时加强。AspectJ应用的是动态代理。

毛病:代理对象须要与指标对象实现一样的接口,并且实现接口的办法,会有冗余代码。同时,一旦接口减少办法,指标对象与代理对象都要保护。

动静代理

动静代理:代理类在程序运行时创立,AOP框架不会去批改字节码,而是在内存中长期生成一个代理对象,在运行期间对业务办法进行加强,不会生成新类。

JDK动静代理和CGLIB动静代理的区别?

Spring AOP中的动静代理次要有两种形式:JDK动静代理和CGLIB动静代理。

JDK动静代理

如果指标类实现了接口,Spring AOP会抉择应用JDK动静代理指标类。代理类依据指标类实现的接口动静生成,不须要本人编写,生成的动静代理类和指标类都实现雷同的接口。JDK动静代理的外围是InvocationHandler接口和Proxy类。

毛病:指标类必须有实现的接口。如果某个类没有实现接口,那么这个类就不能用JDK动静代理。

CGLIB来动静代理

通过继承实现。如果指标类没有实现接口,那么Spring AOP会抉择应用CGLIB来动静代理指标类。CGLIB(Code Generation Library)能够在运行时动静生成类的字节码,动态创建指标类的子类对象,在子类对象中加强指标类。

CGLIB是通过继承的形式做的动静代理,因而如果某个类被标记为final,那么它是无奈应用CGLIB做动静代理的。

长处:指标类不须要实现特定的接口,更加灵便。

什么时候采纳哪种动静代理?

  1. 如果指标对象实现了接口,默认状况下会采纳JDK的动静代理实现AOP
  2. 如果指标对象实现了接口,能够强制应用CGLIB实现AOP
  3. 如果指标对象没有实现了接口,必须采纳CGLIB库

两者的区别

  1. jdk动静代理应用jdk中的类Proxy来创立代理对象,它应用反射技术来实现,不须要导入其余依赖。cglib须要引入相干依赖:asm.jar,它应用字节码加强技术来实现。
  2. 当指标类实现了接口的时候Spring Aop默认应用jdk动静代理形式来加强办法,没有实现接口的时候应用cglib动静代理形式加强办法。

Spring AOP相干术语

(1)切面(Aspect):切面是告诉和切点的联合。告诉和切点独特定义了切面的全部内容。

(2)连接点(Join point):指办法,在Spring AOP中,一个连接点总是代表一个办法的执行。连接点是在利用执行过程中可能插入切面的一个点。这个点能够是调用办法时、抛出异样时、甚至批改一个字段时。切面代码能够利用这些点插入到利用的失常流程之中,并增加新的行为。

(3)告诉(Advice):在AOP术语中,切面的工作被称为告诉。

(4)切入点(Pointcut):切点的定义会匹配告诉所要织入的一个或多个连接点。咱们通常应用明确的类和办法名称,或是利用正则表达式定义所匹配的类和办法名称来指定这些切点。

(5)引入(Introduction):引入容许咱们向现有类增加新办法或属性。

(6)指标对象(Target Object): 被一个或者多个切面(aspect)所告诉(advise)的对象。它通常是一个代理对象。

(7)织入(Weaving):织入是把切面利用到指标对象并创立新的代理对象的过程。在指标对象的生命周期里有以下工夫点能够进行织入:

  • 编译期:切面在指标类编译时被织入。AspectJ的织入编译器是以这种形式织入切面的。
  • 类加载期:切面在指标类加载到JVM时被织入。须要非凡的类加载器,它能够在指标类被引入利用之前加强该指标类的字节码。AspectJ5的加载时织入就反对以这种形式织入切面。
  • 运行期:切面在利用运行的某个时刻被织入。个别状况下,在织入切面时,AOP容器会为指标对象动静地创立一个代理对象。SpringAOP就是以这种形式织入切面。

Spring告诉有哪些类型?

在AOP术语中,切面的工作被称为告诉。告诉实际上是程序运行时要通过Spring AOP框架来触发的代码段。

Spring切面能够利用5种类型的告诉:

  1. 前置告诉(Before):在指标办法被调用之前调用告诉性能;
  2. 后置告诉(After):在指标办法实现之后调用告诉,此时不会关怀办法的输入是什么;
  3. 返回告诉(After-returning ):在指标办法胜利执行之后调用告诉;
  4. 异样告诉(After-throwing):在指标办法抛出异样后调用告诉;
  5. 盘绕告诉(Around):告诉包裹了被告诉的办法,在被告诉的办法调用之前和调用之后执行自定义的逻辑。

什么是IOC?

IOC:管制反转,由Spring容器治理bean的整个生命周期。通过反射实现对其余对象的管制,包含初始化、创立、销毁等,解放手动创建对象的过程,同时升高类之间的耦合度。

IOC的益处:升高了类之间的耦合,对象创立和初始化交给Spring容器治理,在须要的时候只需向容器进行申请。

IOC的长处是什么?

  • IOC 和依赖注入升高了利用的代码量。
  • 松耦合。
  • 反对加载服务时的饿汉式初始化和懒加载。

什么是依赖注入?

在Spring创建对象的过程中,把对象依赖的属性注入到对象中。依赖注入次要有两种形式:结构器注入和属性注入。

IOC容器初始化过程?

ioc 容器初始化过程:BeanDefinition 的资源定位、解析和注册。

  1. 从XML中读取配置文件。
  2. 将bean标签解析成 BeanDefinition,如解析 property 元素, 并注入到 BeanDefinition 实例中。
  3. 将 BeanDefinition 注册到容器 BeanDefinitionMap 中。
  4. BeanFactory 依据 BeanDefinition 的定义信息创立实例化和初始化 bean。

单例bean的初始化以及依赖注入个别都在容器初始化阶段进行,只有懒加载(lazy-init为true)的单例bean是在利用第一次调用getBean()时进行初始化和依赖注入。

<code class="java">// AbstractApplicationContext
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);

多例bean 在容器启动时不实例化,即便设置 lazy-init 为 false 也没用,只有调用了getBean()才进行实例化。

loadBeanDefinitions采纳了模板模式,具体加载 BeanDefinition 的逻辑由各个子类实现。

Bean的生命周期

1.对Bean进行实例化

2.依赖注入

3.如果Bean实现了BeanNameAware接口,Spring将调用setBeanName(),设置 Bean的 id(xml文件中bean标签的id)

4.如果Bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()

5.如果Bean实现了ApplicationContextAware接口,Spring容器将调用setApplicationContext()

6.如果存在BeanPostProcessor,Spring将调用它们的postProcessBeforeInitialization(预初始化)办法,在Bean初始化前对其进行解决

7.如果Bean实现了InitializingBean接口,Spring将调用它的afterPropertiesSet办法,而后调用xml定义的 init-method 办法,两个办法作用相似,都是在初始化 bean 的时候执行

8.如果存在BeanPostProcessor,Spring将调用它们的postProcessAfterInitialization(后初始化)办法,在Bean初始化后对其进行解决

9.Bean初始化实现,供给用应用,直到利用被销毁

10.如果Bean实现了DisposableBean接口,Spring将调用它的destory办法,而后调用在xml中定义的 destory-method办法,这两个办法作用相似,都是在Bean实例销毁前执行。

<code class="java">public interface BeanPostProcessor {
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

}

public interface InitializingBean {
    void afterPropertiesSet() throws Exception;
}

BeanFactory和FactoryBean的区别?

BeanFactory:治理Bean的容器,Spring中生成的Bean都是由这个接口的实现来治理的。

FactoryBean:通常是用来创立比较复杂的bean,个别的bean 间接用xml配置即可,但如果一个bean的创立过程中波及到很多其余的bean 和简单的逻辑,间接用xml配置比拟麻烦,这时能够思考用FactoryBean,能够暗藏实例化简单Bean的细节。

当配置文件中bean标签的class属性配置的实现类是FactoryBean时,通过 getBean()办法返回的不是FactoryBean自身,而是调用FactoryBean#getObject()办法所返回的对象,相当于FactoryBean#getObject()代理了getBean()办法。如果想得到FactoryBean必须应用 ‘&’ + beanName 的形式获取。

Mybatis 提供了 SqlSessionFactoryBean,能够简化 SqlSessionFactory 的配置:

<code class="java">public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {

  @Override
  public void afterPropertiesSet() throws Exception {
    notNull(dataSource, "Property 'dataSource' is required");
    notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
    state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
              "Property 'configuration' and 'configLocation' can not specified with together");

    this.sqlSessionFactory = buildSqlSessionFactory();
  }
    
  protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
    //简单逻辑
  }
    
  @Override
  public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
      afterPropertiesSet();
    }

    return this.sqlSessionFactory;
  }
}

在 xml 配置 SqlSessionFactoryBean:

<code class="xml"><bean id="tradeSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="trade" />
    <property name="mapperLocations" value="classpath*:mapper/trade/*Mapper.xml" />
    <property name="configLocation" value="classpath:mybatis-config.xml" />
    <property name="typeAliasesPackage" value="com.bytebeats.mybatis3.domain.trade" />
</bean>

Spring 将会在利用启动时创立 SqlSessionFactory,并应用 sqlSessionFactory 这个名字存储起来。

Bean注入容器有哪些形式?

将一般类交给Spring容器治理,通常有以下办法:

1、应用@Configuration@Bean注解

2、应用@Controller@Service@Repository@Component 注解标注该类,而后启用@ComponentScan主动扫描

3、应用@Import 办法。应用@Import注解把bean导入到以后容器中,代码如下:

<code class="java">//@SpringBootApplication
@ComponentScan
/*把用到的资源导入到以后容器中*/
@Import({Dog.class, Cat.class})
public class App {
 
    public static void main(String[] args) throws Exception {
 
        ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
        System.out.println(context.getBean(Dog.class));
        System.out.println(context.getBean(Cat.class));
        context.close();
    }
}

Bean的作用域

1、singleton:单例,Spring中的bean默认都是单例的。

2、prototype:每次申请都会创立一个新的bean实例。

3、request:每一次HTTP申请都会产生一个新的bean,该bean仅在以后HTTP request内无效。

4、session:每一次HTTP申请都会产生一个新的bean,该bean仅在以后HTTP session内无效。

5、global-session:全局session作用域。

Spring主动拆卸的形式有哪些?

Spring的主动拆卸有三种模式:byType(依据类型),byName(依据名称)、constructor(依据构造函数)。

byType

找到与依赖类型雷同的bean注入到另外的bean中,这个过程须要借助setter注入来实现,因而必须存在set办法,否则注入失败。

当xml文件中存在多个雷同类型名称不同的实例Bean时,Spring容器依赖注入依然会失败,因为存在多种适宜的选项,Spring容器无奈晓得该注入那种,此时咱们须要为Spring容器提供帮忙,指定注入那个Bean实例。能够通过<bean>标签的autowire-candidate设置为false来过滤那些不须要注入的实例Bean

<code class="xml"><bean id="userDao"  class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl" />

<!-- autowire-candidate="false" 过滤该类型 -->
<bean id="userDao2" autowire-candidate="false" class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl" />

<!-- byType 依据类型主动拆卸userDao-->
<bean id="userService" autowire="byType" class="com.zejian.spring.springIoc.service.impl.UserServiceImpl" />

byName

将属性名与bean名称进行匹配,如果找到则注入依赖bean。

<code class="xml"><bean id="userDao"  class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl" />
<bean id="userDao2" class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl" />

<!-- byName 依据名称主动拆卸,找到UserServiceImpl名为 userDao属性并注入-->
<bean id="userService" autowire="byName" class="com.zejian.spring.springIoc.service.impl.UserServiceImpl" />

constructor

存在单个实例则优先按类型进行参数匹配(无论名称是否匹配),当存在多个类型雷同实例时,按名称优先匹配,如果没有找到对应名称,则注入失败,此时能够应用autowire-candidate=”false” 过滤来解决。

<code class="xml"><!--只存在userDao2,userDao3 无奈胜利注入-->
<bean id="userDao2" class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl" />

<bean id="userDao3" class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl" />

<bean id="userService" autowire="constructor" class="com.zejian.spring.springIoc.service.impl.UserServiceImpl" />

@Autowired 能够传递了一个required=false的属性,false指明当userDao实例存在就注入不存就疏忽,如果为true,就必须注入,若userDao实例不存在,就抛出异样。因为默认状况下@Autowired是按类型匹配的(byType),如果须要按名称(byName)匹配的话,能够应用@Qualifier注解与@Autowired联合。

<code class="java">public class UserServiceImpl implements UserService {
    //标注成员变量
    @Autowired
    @Qualifier("userDao1")
    private UserDao userDao;   
 }

byName模式 xml 配置:

<code class="xml"><!-- 依据@Qualifier("userDao1")自动识别 -->
<bean id="userDao1" class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl" />
<bean id="userDao2" class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl" />

<bean id="userService" class="com.zejian.spring.springIoc.service.impl.UserServiceImpl" />

@Resource,默认按 byName模式主动注入。@Resource有两个中重要的属性:name和type。Spring容器对于@Resource注解的name属性解析为bean的名字,type属性则解析为bean的类型。因而应用name属性,则按byName模式的主动注入策略,如果应用type属性则按 byType模式主动注入策略。假使既不指定name也不指定type属性,Spring容器将通过反射技术默认按byName模式注入。

<code class="java">@Resource(name=“userDao”)
private UserDao  userDao;//用于成员变量

//也能够用于set办法标注
@Resource(name=“userDao”)
public void setUserDao(UserDao userDao) {
   this.userDao= userDao;
}

上述两种主动拆卸的依赖注入并不适宜简略值类型,如int、boolean、long、String以及Enum等,对于这些类型,Spring容器也提供了@Value注入的形式。@Value接管一个String的值,该值指定了将要被注入到内置的java类型属性值,Spring 容器会做好类型转换。个别状况下@Value会与properties文件联合应用。

jdbc.properties文件如下:

<code class="properties">jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/test?characterEncoding=UTF-8&allowMultiQueries=true
jdbc.username=root
jdbc.password=root

利用注解@Value获取jdbc.url和jdbc.username的值,实现如下:

<code class="java">public class UserServiceImpl implements UserService {
    //占位符形式
    @Value("${jdbc.url}")
    private String url;
    //SpEL表达方式,其中代表xml配置文件中的id值configProperties
    @Value("#{configProperties['jdbc.username']}")
    private String userName;

}

xml配置文件:

<code class="xml">    <!--基于占位符形式 配置单个properties -->
    <!--<context:property-placeholder location="conf/jdbc.properties"/>-->
    <!--基于占位符形式 配置多个properties -->
    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer">
        <property name="location" value="conf/jdbc.properties"/>
    </bean>

    <!--基于SpEL表达式 配置多个properties id值为configProperties 提供java代码中应用 -->
    <bean id="configProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
        <property name="locations">
            <list>
                <value>classpath:/conf/jdbc.properties</value>
            </list>
        </property>
    </bean>

@Autowired和@Resource的区别?

@Autowired注解是依照类型(byType)拆卸依赖对象的,然而存在多个类型⼀致的bean,⽆法通过byType注⼊时,就会再使⽤byName来注⼊,如果还是⽆法判断注⼊哪个bean则会UnsatisfiedDependencyException。
@Resource会⾸先依照byName来拆卸,如果找不到bean,会⾃动byType再找⼀次。

@Qualifier 注解有什么作用

当须要创立多个雷同类型的 bean 并心愿仅应用属性拆卸其中一个 bean 时,能够应用@Qualifier 注解和 @Autowired 通过指定应该拆卸哪个 bean 来打消歧义。

@Bean和@Component有什么区别?

都是应用注解定义 Bean。@Bean 是应用 Java 代码拆卸 Bean,@Component 是主动拆卸 Bean。

@Component 注解用在类上,表明一个类会作为组件类,并告知Spring要为这个类创立bean,每个类对应一个 Bean。

@Bean 注解用在办法上,示意这个办法会返回一个 Bean。@Bean 须要在配置类中应用,即类上须要加上@Configuration注解。

<code class="java">@Component
public class Student {
    private String name = "lkm";
 
    public String getName() {
        return name;
    }
}

@Configuration
public class WebSocketConfig {
    @Bean
    public Student student(){
        return new Student();
    }
}

@Bean 注解更加灵便。当须要将第三方类拆卸到 Spring 容器中,因为没方法源代码上增加@Component注解,只能应用@Bean 注解的形式,当然也能够应用 xml 的形式。

@Component、@Controller、@Repositor和@Service 的区别?

@Component:最一般的组件,能够被注入到spring容器进行治理。

@Controller:将类标记为 Spring Web MVC 控制器。

@Service:将类标记为业务层组件。

@Repository:将类标记为数据拜访组件,即DAO组件。

Spring 事务实现形式有哪些?

事务就是一系列的操作原子执行。Spring事务机制次要包含申明式事务和编程式事务。

  • 编程式事务:通过编程的形式治理事务,这种形式带来了很大的灵活性,但很难保护。
  • 申明式事务:将事务管理代码从业务办法中分离出来,通过aop进行封装。Spring申明式事务使得咱们无须要去解决取得连贯、敞开连贯、事务提交和回滚等这些操作。应用 @Transactional 注解开启申明式事务。

@Transactional相干属性如下:

属性 类型 形容
value String 可选的限定描述符,指定应用的事务管理器
propagation enum: Propagation 可选的事务流传行为设置
isolation enum: Isolation 可选的事务隔离级别设置
readOnly boolean 读写或只读事务,默认读写
timeout int (in seconds granularity) 事务超时工夫设置
rollbackFor Class对象数组,必须继承自Throwable 导致事务回滚的异样类数组
rollbackForClassName 类名数组,必须继承自Throwable 导致事务回滚的异样类名字数组
noRollbackFor Class对象数组,必须继承自Throwable 不会导致事务回滚的异样类数组
noRollbackForClassName 类名数组,必须继承自Throwable 不会导致事务回滚的异样类名字数组

有哪些事务流传行为?

在TransactionDefinition接口中定义了七个事务流传行为:

  1. PROPAGATION_REQUIRED如果存在一个事务,则反对以后事务。如果没有事务则开启一个新的事务。如果嵌套调用的两个办法都加了事务注解,并且运行在雷同线程中,则这两个办法应用雷同的事务中。如果运行在不同线程中,则会开启新的事务。
  2. PROPAGATION_SUPPORTS 如果存在一个事务,反对以后事务。如果没有事务,则非事务的执行。
  3. PROPAGATION_MANDATORY 如果曾经存在一个事务,反对以后事务。如果不存在事务,则抛出异样IllegalTransactionStateException
  4. PROPAGATION_REQUIRES_NEW 总是开启一个新的事务。须要应用JtaTransactionManager作为事务管理器。
  5. PROPAGATION_NOT_SUPPORTED 总是非事务地执行,并挂起任何存在的事务。须要应用JtaTransactionManager作为事务管理器。
  6. PROPAGATION_NEVER 总是非事务地执行,如果存在一个流动事务,则抛出异样。
  7. PROPAGATION_NESTED 如果一个流动的事务存在,则运行在一个嵌套的事务中。如果没有流动事务, 则按PROPAGATION_REQUIRED 属性执行。

PROPAGATION_NESTED 与PROPAGATION_REQUIRES_NEW的区别:

应用PROPAGATION_REQUIRES_NEW时,内层事务与外层事务是两个独立的事务。一旦内层事务进行了提交后,外层事务不能对其进行回滚。两个事务互不影响。

应用PROPAGATION_NESTED时,外层事务的回滚能够引起内层事务的回滚。而内层事务的异样并不会导致外层事务的回滚,它是一个真正的嵌套事务。

Spring怎么解决循环依赖的问题?

结构器注入的循环依赖:Spring解决不了,间接抛出BeanCurrentlylnCreationException异样。

单例模式下属性注入的循环依赖:通过三级缓存解决循环依赖。

非单例循环依赖:无奈解决。

上面剖析单例模式下属性注入的循环依赖是怎么解决的:

首先,Spring单例对象的初始化大略分为三步:

  1. createBeanInstance:实例化bean,应用构造方法创建对象,为对象分配内存。
  2. populateBean:进行依赖注入。
  3. initializeBean:初始化bean。

Spring为了解决单例的循环依赖问题,应用了三级缓存:

singletonObjects:实现了初始化的单例对象map,bean name –> bean instance

earlySingletonObjects :实现实例化未初始化的单例对象map,bean name –> bean instance

singletonFactories : 单例对象工厂map,bean name –> ObjectFactory,单例对象实例化实现之后会退出singletonFactories。

在调用createBeanInstance进行实例化之后,会调用addSingletonFactory,将单例对象放到singletonFactories中。

<code class="java">protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }
}

如果A依赖了B的实例对象,同时B也依赖A的实例对象。

  1. A首先实现了实例化,并且将本人增加到singletonFactories中
  2. 接着进行依赖注入,发现自己依赖对象B,此时就尝试去get(B)
  3. 发现B还没有被实例化,对B进行实例化
  4. 而后B在初始化的时候发现自己依赖了对象A,于是尝试get(A),尝试一级缓存singletonObjects和二级缓存earlySingletonObjects没找到,尝试三级缓存singletonFactories,因为A初始化时将本人增加到了singletonFactories,所以B能够拿到A对象,而后将A从三级缓存中移到二级缓存中
  5. B拿到A对象后顺利完成了初始化,而后将本人放入到一级缓存singletonObjects中
  6. 此时返回A中,A此时能拿到B的对象顺利完成本人的初始化

由此看出,属性注入的循环依赖次要是通过将实例化实现的bean增加到singletonFactories来实现的。而应用结构器依赖注入的bean在实例化的时候会进行依赖注入,不会被增加到singletonFactories中。比方A和B都是通过结构器依赖注入,A在调用结构器进行实例化的时候,发现自己依赖B,B没有被实例化,就会对B进行实例化,此时A未实例化实现,不会被增加到singtonFactories。而B依赖于A,B会去三级缓存寻找A对象,发现不存在,于是又会实例化A,A实例化了两次,从而导致抛异样。

总结:1、利用缓存辨认曾经遍历过的节点; 2、利用Java援用,先提前设置对象地址,后欠缺对象。

Spring启动过程

  1. 读取web.xml文件。
  2. 创立 ServletContext,为 ioc 容器提供宿主环境。
  3. 触发容器初始化事件,调用 contextLoaderListener.contextInitialized()办法,在这个办法会初始化一个利用上下文WebApplicationContext,即 Spring 的 ioc 容器。ioc 容器初始化实现之后,会被存储到 ServletContext 中。
  4. 初始化web.xml中配置的Servlet。如DispatcherServlet,用于匹配、解决每个servlet申请。

Spring 的单例 Bean 是否有线程平安问题?

当多个用户同时申请一个服务时,容器会给每一个申请调配一个线程,这时多个线程会并发执行该申请对应的业务逻辑,如果业务逻辑有对单例状态的批改(体现为此单例的成员属性),则必须思考线程平安问题。

若每个线程中对全局变量、动态变量只有读操作,而无写操作,那么不会有线程平安问题;若有多个线程同时执行写操作,个别都须要思考线程同步,否则就可能影响线程平安。

无状态bean和有状态bean

  • 有实例变量的bean,能够保留数据,是非线程平安的。
  • 没有实例变量的对象。不能保留数据,是线程平安的。

在Spring中无状态的Bean适宜用单例模式,这样能够共享实例进步性能。有状态的Bean在多线程环境下不平安,个别用Prototype模式或者应用ThreadLocal解决线程平安问题。

有用的话,点个赞,反对一下~


搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:关于java:大厂常考的Spring面试题

喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址