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

4次阅读

共计 16581 个字符,预计需要花费 42 分钟才能阅读完成。

筹备了一个月的八股文,经验了二十几场面试之后,发现 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 对象。

@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

// 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()时进行初始化和依赖注入。

// 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 实例销毁前执行。

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 的配置:

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:

<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 导入到以后容器中,代码如下:

//@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 实例。能够通过标签的 autowire-candidate 设置为 false 来过滤那些不须要注入的实例 Bean

<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。

<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”过滤来解决。

<!-- 只存在 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 联合。

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

byName 模式 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 模式注入。

@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 文件如下:

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 的值,实现如下:

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

}

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 注解。

@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 中。

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 解决线程平安问题。

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

正文完
 0