Spring——问题知识点总结

本文深入探讨了Spring框架的核心特性,包括IOC(控制反转)容器、依赖注入、AOP(面向切面编程)实现、事务管理以及Spring MVC在Web开发中的应用。讲解了Bean的作用域、单例bean的线程安全问题、Bean的生命周期、自动注入模式,以及Spring如何解决循环依赖。此外,还涵盖了Spring的声明式事务管理和不同事务隔离级别,以及Spring MVC的工作流程和控制器的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基本概念

1.什么是Spring

Spring是一种轻量级框架,旨在提高开发人员的开发效率以及系统的可维护性。

我们一般说的Spring框架就是Spring Framework,它是很多模块的集合,使用这些模块可以很方便地协助我们进行开发。这些模块是核心容器、数据访问/集成、Web、AOP(面向切面编程)、工具、消息和测试模块。比如Core Container中的Core组件是Spring所有组件的核心,Beans组件和Context组件是实现IOC和DI的基础,AOP组件用来实现面向切面编程。

2.Spring的模块

  • IOC
    spring核心模块实现了IOC功能,它将类与类之间的依赖从代码中脱离出来,用配置文件的方式进行依赖关系的描述,由容器负责依赖类的创建、拼接、管理、获取等工作。BeanFactory是Spring框架的核心接口,实现容器的核心功能。
    Context模块扩展了BeanFactory的功能,添加了国际化、bean生命周期控制邮件服务等功能。ApplicationContext是Context模块的核心接口。
    表达式语言是统一表达式语言的一个扩展,该表达式语言用于查询和管理运行期对象,支持设置对象属性,调用对象方法等。该模块还提供了逻辑表达式运算等功能,可以方便地通过表达式串和IOC容器进行交互。

  • AOP
    是进行横切逻辑编程的思想。还整合了AspectJ这种AOP语言级框架。instrument允许在JVM启动时开启一个代理类,通过代理类在运行期间修改字节码,改变一个类的共功能,从而实现了AOP。

  • 数据访问和集成
    Spring在DAO层后面,建立了一套面向DAO层异常体系,为整合各种持久层框架提供了基础。Spring还通过模板化技术对各种数据访问技术进行薄层封装,使得数据访问过程简化。

  • Web及远程操作
    该模块建立在ApplicationContext模块之上,提供了Web应用的各种工具类。将Spring容器注册到了Web容器中。还可以整合了MVC框架。

  • Web以远程访问
    Spring自己提供了一个完整的MVC框架。

  • WebSocket
    提供了一个在Web应用中高效、双向、即时的通信。

  • Test
    该层为使用 JUnit 和 TestNG 进行测试提供支持。

3.Spring框架中用到了哪些设计模式

  1. 工厂设计模式:Spring使用工厂模式通过BeanFactory和ApplicationContext创建bean对象。

  2. 代理设计模式:Spring AOP功能的实现。

  3. 单例设计模式:Spring中的bean默认都是单例的。

  4. 模板方法模式:Spring中的jdbcTemplate、hibernateTemplate等以Template结尾的对数据库操作的类,它们就使用到了模板模式。

  5. 包装器设计模式:我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。

  6. 观察者模式:Spring事件驱动模型就是观察者模式很经典的一个应用。

  7. 适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式、Spring MVC中也是用到了适配器模式适配Controller。

4.spring框架的事件类型

Spring的ApplicationContext具有代码层上支持事件和监听器的功能。我们可以创建bean监听通过ApplicationContext发布的事件。ApplicationContext里的事件处理通过提供ApplicationEvent类和ApplicationListener接口来完成。所以如果一个bean实现了ApplicationListener接口,当一个ApplicationEvent发布到ApplicationContext时,该bean将接到通知。

public class AllApplicationEventListener implements ApplicationListener < ApplicationEvent >
{
    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent)
    {
        //process event
    }
}

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

  • ContextRefreshedEvent:当ApplicationContext初始化或刷新时发布这个事件。这个事件也可以通过ConfigurableApplicationContext接口的refresh()方法来触发。
  • ContextStartedEvent:当ApplicationContext被ConfigurableApplicationContext接口的start()方法启动时发布这个事件。你可以在收到这一事件后查询你的数据库或重启/启动任何停止的应用程序。
  • ContextStoppedEvent:当ApplicationContext被ConfigurableApplicationContext接口的stop()方法关闭时发布这个事件。你可以在收到这一事件后做一些清理工作。
  • ContextClosedEvent:当ApplicationContext时被ConfigurableApplicationContext接口的close()方法关闭时发布这个事件。一个终结的上下文达到生命周期结束;它不能刷新或重启。
  • RequestHandledEvent:这是一个网络自身的事件,告诉所有bean:HTTP请求服务已经处理完成。

除了上面的事件,您可以通过扩展ApplicationEvent类创建自定义事件。

发布这个事件:

CustomApplicationEvent customEvent = new CustomApplicationEvent( applicationContext, "Test message" );
applicationContext.publishEvent ( customEvent );

5.FileSystemResource和ClassPathResource之间的区别

在FileSystemResource中你需要给出spring-config.xml(Spring配置)文件相对于您的项目的相对路径或文件的绝对位置。

在ClassPathResource中Sping查找文件使用ClassPath,因此spring-config.xml应该包含在类路径下。

一句话,ClassPathResource在类路径下搜索和FileSystemResource在文件系统下搜索。

6.不同版本的 Spring Framework 有哪些主要功能

  • Spring 2.5 发布于 2007 年。这是第一个支持注解的版本。
  • Spring 3.0 发布于 2009 年。它完全利用了 Java5 中的改进,并为 JEE6 提供了支持。
  • Spring 4.0 发布于 2013 年。这是第一个完全支持 JAVA8 的版本。
  • Spring 5.0 发布于 2017 年。最大特点之一是响应式编程,对JDK 9运行时兼容性,在Spring Framework代码中使用JDK 8特性(Lambda),函数式Web框架,对Kotlin支持。

7. Spring 应用程序有哪些不同组件

  • 接口 - 定义功能。
  • Bean 类 - 它包含属性,setter 和 getter 方法,函数等。
  • Spring 面向切面编程(AOP) - 提供面向切面编程的功能。
  • Bean 配置文件 - 包含类的信息以及如何配置它们。
  • 用户程序 - 它使用接口。

IoC

1.什么是IoC,好处是什么,实现机制

IoC(Inverse of Control)字面意思就是控制反转,其中控制表示对象实例的控制权,反转表示这种控制权转移给了第三方,也就是spring。IOC容器是Spring用来实现IOC的载体,IOC容器实际上就是一个Map(key, value),Map中存放的是各种对象。

优点:

  • 将对象之间的相互依赖关系交给IOC容器来管理,并由IOC容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。以最小的影响和最少的侵入机制促进松耦合。
  • IOC容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。在实际项目中一个Service类可能由几百甚至上千个类作为它的底层,假如我们需要实例化这个Service,可能要每次都搞清楚这个Service所有底层类的构造函数,这可能会把人逼疯。如果利用IOC的话,你只需要配置好,然后在需要的地方引用就行了,大大增加了项目的可维护性且降低了开发难度。
  • 便于测试。
  • 支持即时的实例化和延迟加载服务。

延迟加载就是在第一次getBean()调用的时候才去实例化Bean,其他的则是在容器启动的时候就进行了装配。

实现机制:
Spring 中的 IoC 的实现原理就是工厂模式加反射机制。

源码中的主要组成部分:

  • BeanDefinition:用来描述Bean的定义,从注解或者xml中解析出来。
  • BeanDefinitionRegister:提供向容器注册BeanDefinition的方法。
  • BeanDefinitionMap:这就是一个ConcurrentHashMap,Bean的名字就是key,BeanDefinition就是value。

getBean()的源码逻辑:

  1. 转换beanName;
  2. 从缓存中加载实例;
  3. 实例化Bean;
  4. 检测ParentBeanFactory;
  5. 初始化依赖的Bean;
  6. 创建Bean。

2.Bean的作用域

  • singleton
    单例模式,在容器中仅存在一个bean实例,默认饿汉模式。
  • prototype
    每次从容器中调用Bean时,都会返回一个新的实例。
  • request
    每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext。
  • session
    同一个HTTP Session共享一个Bean,不同的Session就会用不同Bean,也是仅用于- WebApplicationContext。
  • globalSession
    用一个全局Session共用一个Bean,一般用于Portlet环境,仅适用于WebApplicationContext。

3.Spring中的单例bean的线程安全问题

大部分时候我们并没有在系统中使用多线程,所以很少有人会关注这个问题。单例bean存在线程问题,主要是因为当多个线程操作同一个对象的时候,对这个对象的非静态成员变量的写操作会存在线程安全问题。

有两种常见的解决方案:

  1. 在bean对象中尽量避免定义可变的成员变量(不太现实)。

  2. 在类中定义一个ThreadLocal成员变量,将需要的可变成员变量保存在ThreadLocal中(推荐的一种方式)。

4.Spring中的bean生命周期

  1. Bean容器找到配置文件中Spring Bean的定义。

  2. Bean容器利用Java Reflection API创建一个Bean的实例。

  3. 如果涉及到一些属性值,利用set()方法设置一些属性值。

  4. 如果Bean实现了BeanNameAware接口,调用setBeanName()方法,传入Bean的名字。

  5. 如果Bean实现了BeanFactoryAware接口,调用setBeanClassFacotory()方法,传入ClassLoader对象的实例,让容器内部可以对自己感知。

  6. 与上面的类似,如果实现了其他*Aware接口,就调用相应的方法。

  7. 如果有和加载这个Bean的Spring容器相关的BeanPostProcessor对象,执行postProcessBeforeInitialization()方法。

  8. 如果Bean实现了InitializingBean接口,执行afeterPropertiesSet()方法。

  9. 如果Bean在配置文件中的定义包含init-method属性,执行指定的方法。

  10. 如果有和加载这个Bean的Spring容器相关的BeanPostProcess对象,执行postProcessAfterInitialization()方法。

  11. 当要销毁Bean的时候,如果Bean实现了DisposableBean接口,执行destroy()方法。

  12. 当要销毁Bean的时候,如果Bean在配置文件中的定义包含destroy-method属性,执行指定的方法。

在这里插入图片描述

5.@Component和@Bean的区别是什么

  1. 作⽤对象不同: @Component 注解作⽤于类,⽽ @Bean 注解作⽤于⽅法。
  2. @Component 通常是通过类路径扫描来⾃动侦测以及⾃动装配到Spring容器中(我们可以使⽤@ComponentScan 注解定义要扫描的路径从中找出标识了需要装配的类⾃动装配到 Spring 的bean 容器中)。 @Bean 注解通常是我们在标有该注解的⽅法中定义产⽣这个bean, @Bean 告诉了Spring这是某个类的示例,当我需要⽤它的时候还给我。
  3. @Bean 注解⽐ Component 注解的⾃定义性更强,⽽且很多地⽅我们只能通过@Bean 注解来注册bean。⽐如当我们引⽤第三⽅库中的类需要装配到 Spring 容器时,则只能通过@Bean 来实现。

6.将一个类声明为Spring的bean的注解有哪些

我们一般使用@Autowired注解去自动装配bean。而想要把一个类标识为可以用@Autowired注解自动装配的bean,可以采用以下的注解实现:

  1. @Component。通用的注解,可标注任意类为Spring组件。如果一个Bean不知道属于哪一个层,可以使用@Component注解标注。

  2. @Repository。对应持久层,即Dao层,主要用于数据库相关操作。

  3. @Service。对应服务层,即Service层,主要涉及一些复杂的逻辑,需要用到Dao层(注入)。

  4. @Controller。对应Spring MVC的控制层,即Controller层,主要用于接受用户请求并调用Service层的方法返回数据给前端页面。

7.BeanFactory和ApplicationContext之间的区别?

一个BeanFactory就像包含bean集合的工厂类。BeanFactory在内部持有多个Bean的定义,当客户端请求bean时,将bean进行实例化。 ApplicationContenxt建立在BeanFactory之上,提供了个更多面向应用的功能,比如国际化支持和框架事件体系,更易于创建实际应用。

我们通常称BeanFactory为IOC容器,ApplicationContenxt为应用上下文。

二者用途的划分:BeanFactory是Spring框架的基础设施,面向Spring本身;
ApplicationContext面向使用Spring框架的开发者。一个是底层,一个是上层。

表面上看,applicationContext和BeanFactory是一样。同样加载bean定义,将bean连接在一起,分发bean。但applicationContext还提供:

  • 一种解析消息的手段,包括对国际化的支持。
  • 一个更通用的加载文件资源的方法。
  • bean事件注册为监听器。
  • 支持基于依赖的注解。
  • 即时加载,而BeanFactory用的是懒加载。

三个常用的ApplicationContext实现是:

  • ClassPathXmlApplicationContext:它从classpath路径下的一个XML文件加载context的,将Context作为classpath下的资源。加载应用程序classpath下的context使用的代码如下:
ApplicationContext context = new ClassPathXmlApplicationContext(“bean.xml”);
  • FileSystemXmlApplicationContext:它从文件系统的一个XML文件加载上下文定义的。从文件系统加载应用程序上下文通过如下代码实现。
ApplicationContext context = new FileSystemXmlApplicationContext(“bean.xml”);
  • XmlWebApplicationContext:它从一个web应用程序中包含的XML文件加载context。

8.spring的Bean是什么?内部Bean是什么?

Bean:

  • 它们是构成用户应用程序主干的对象。
  • 它们由 Spring IoC 容器实例化,配置,装配和管理。
  • Bean 是基于用户提供给容器的配置元数据创建。

内部Bean:

在Spring框架中,当一个bean只用于一个特定属性,建议将它声明为一个内在的bean。内部bean同时支持setter注入属性和构造函数注入“constructor-arg”。

例如,假设一个Customer类的引用Person类。在我们的应用程序中,我们将只创建一个Person类的实例,并在Customer使用它。

<bean id="CustomerBean" class="com.howtodoinjava.common.Customer">
    <property name="person">
        <!-- This is inner bean -->
        <bean class="com.howtodoinjava.common.Person">
            <property name="name" value="adminis"></property>
            <property name="address" value="India"></property>
            <property name="age" value="34"></property>
        </bean>
    </property>
</bean>

9.如何在Spring里注入Java集合?请给个例子好吗?

Spring提供了四种类型的配置元素集合,如下:

<list>:帮助注入一组值,允许重复。
<set>:帮助注入一组值,不允许重复。
<map>:帮助注入一个K-V的集合,名称和值可以是任何类型的。
<props>:帮助注入一个名称-值对集合,名称和值都是字符串。

<beans>
 
   <!-- Definition for javaCollection -->
   <bean id="javaCollection" class="com.howtodoinjava.JavaCollection">
 
      <!-- java.util.List -->
      <property name="customList">
        <list>
           <value>INDIA</value>
           <value>Pakistan</value>
           <value>USA</value>
           <value>UK</value>
        </list>
      </property>
 
     <!-- java.util.Set -->
     <property name="customSet">
        <set>
           <value>INDIA</value>
           <value>Pakistan</value>
           <value>USA</value>
           <value>UK</value>
        </set>
      </property>
 
     <!-- java.util.Map -->
     <property name="customMap">
         
        <map>
           <entry key="1" value="INDIA"/>
           <entry key="2" value="Pakistan"/>
           <entry key="3" value="USA"/>
           <entry key="4" value="UK"/>
        </map>
 
      </property>
       
      <!-- java.util.Properties -->
    <property name="customProperies">
        <props>
            <prop key="admin">admin@nospam.com</prop>
            <prop key="support">support@nospam.com</prop>
        </props>
    </property>
 
   </bean>
 
</beans>

10.如何将一个java.util.属性注入到Spring Bean

一个方法是props

<bean id="adminUser" class="com.exmple.common.Customer">
  
    <!-- java.util.Properties -->
    <property name="emails">
        <props>
            <prop key="admin">admin@nospam.com</prop>
            <prop key="support">support@nospam.com</prop>
        </props>
    </property>
 
</bean>

也可以用util

<util:properties id="emails" location="classpath:com/foo/emails.properties" />

11.Spring Bean的自动注入及其局限

在spring框架中,在配置文件中设置bean的依赖是一个很好的办法,但spring容器也能够自动注入不同bean之间的关系。这意味着,通过检查BeanFactory的内容它可以为您的bean自动注入其他bean。

可以为每个bean指定是否自动注入,因此可以支持一些Bean支持自动注入,而一些bean不会自动注入。
下面从XML配置文件摘录了自动根据名称注入的bean。

<bean id="employeeDAO" class="com.howtodoinjava.EmployeeDAOImpl" autowire="byName" />

除了提供的自动装配模式bean配置文件,也可以在bean类中指定自动装配使用 @Autowired注释。

注意:在bean类使用@Autowired注解,您必须在spring应用程序中先启用下面的注解。

<context:annotation-config />

也可以通过在配置文件使用AutowiredAnnotationBeanPostProcessorbean来完成。

<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>

现在,当注释配置已经启用,您可以自由使用@Autowired来自动注入bean依赖关系,以你喜欢的方式。

@Autowired
public EmployeeDAOImpl ( EmployeeManager manager ) {
    this.manager = manager;
}

自动注入的局限

  • 覆盖的可能性 - 您始终可以使用 <constructor-arg><property> 设置指定依赖项,这将覆盖自动装配。
  • 基本元数据类型 - 简单属性(如原数据类型,字符串和类)无法自动装配。
  • 令人困惑的性质 - 总是喜欢使用明确的装配,因为自动装配不太精确。

12.bean自动注入模式

在Spring有五个自动注入模式。让我们逐个讨论。

  • no:默认情况下,spring框架的自动注入选项,即默认情况不开启自动注入。这意味着你必须使用标签在bean定义中显式地设置依赖项。
  • byName:这个选项使基于bean的名称的依赖项注入。当自动装配在bean属性,用属性名搜索匹配的bean定义配置文件。如果找到这样的bean,注入属性。如果没有找到这样的bean,就会产生一个错误。
  • byType:该选项允许基于bean的类型的依赖项注入。当在bean属性需要自动注入时,使用属性类的类型来搜索匹配的bean定义配置文件。如果找到这样的bean,注入属性。如果没有找到这样的bean,就会产生一个错误。
  • constructor:构造方法类似于byType自动注入,但适用于构造方法的参数。在自动注入bean时,它在所有构造函数参数类型中寻找匹配的构造函数的类类型参数,然后进行自动注入。注意,如果在容器中没有一个bean构造函数参数类型满足,会抛出一个致命错误。
  • autodetect:自动侦测使用两种模式即构造函数或byType模式的自动注入。首先它将试图寻找有效的构造方法参数,如果发现构造方法模式则选择。如果没有构造方法中定义bean,或者明确的默认无参构造方法,则选择byType模式自动注入。

13.@required注解

在大规模的应用程序中,IoC容器中可能会有成百上千的bean声明,以及它们之间的依赖关系通常是非常复杂的。

setter注入的缺点之一是,很难给你检查出所需的所有属性是否已经注入。

为了克服这个问题,您可以设置bean的“dependency-check”属性,可以设置四个属性的其中之一即 none, simple, objects or all (没有一个是默认选项)。

在现实生活中应用程序中,您将不会感兴趣检查所有上下文中的bean属性配置文件。而你想要检查一些特定的bean是否已设置特定的属性。在这种情况下,Spring的依赖项检查功能将不再适用,。

为了解决这个问题,您可以使用@Required注解。在bean属性使用@Required注解的setter方法类文件如下:

public class EmployeeFactoryBean extends AbstractFactoryBean<Object>
{
    private String designation;
      
    public String getDesignation() {
        return designation;
    }
  
    @Required
    public void setDesignation(String designation) {
        this.designation = designation;
    }
      
    //more code here
}

RequiredAnnotationBeanPostProcessor是一个spring bean后置处理程序,检查@Required注解的所有的bean属性是否已设置。使用这个bean属性检查后置处理程序,您必须注册在Spring IoC容器中。

<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor" />

如果@Required注解的任何属性没有设置,这个bean的处理器会抛出一个BeanInitializationException异常。

14.@qualifier注解

@Qualifier限定哪个bean应该被自动注入。当Spring无法判断出哪个bean应该被注入时,@Qualifier注解有助于消除歧义bean的自动注入,指定注入Bean的id。

参见下面的例子,

public class Customer
{
    @Autowired
    private Person person;
}

我们有两个bean定义为Person类的实例。

<bean id="customer" class="com.howtodoinjava.common.Customer" />
 
<bean id="personA" class="com.howtodoinjava.common.Person" >
    <property name="name" value="lokesh" />
</bean>
 
<bean id="personB" class="com.howtodoinjava.common.Person" >
    <property name="name" value="alex" />
</bean>

Spring 知道哪个bean应该自动注入?不。当您运行上面的例子时,抛出如下异常:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
    No unique bean of type [com.howtodoinjava.common.Person] is defined:
        expected single matching bean but found 2: [personA, personB]

要解决以上问题,你需要使用@Quanlifier注解告诉Spring 哪个bean应该被autowired的。

public class Customer
{
    @Autowired
    @Qualifier("personA")
    private Person person;
}

15.构造方法注入和属性setter注入之间的区别

  • 在Setter注入,可以将依赖项部分注入,构造方法注入不能部分注入,因为调用构造方法如果传入所有的参数就会报错。
  • 如果我们为同一属性提供Setter和构造方法注入,Setter注入将覆盖构造方法注入。但是构造方法注入不能覆盖setter注入值。显然,构造方法注入被称为创建实例的第一选项。
  • 使用setter注入你不能保证所有的依赖都被注入,这意味着你可以有一个对象依赖没有被注入。在另一方面构造方法注入直到你所有的依赖都注入后才开始创建实例。
  • 在构造函数注入,如果A和B对象相互依赖:A依赖于B,B也依赖于A,此时在创建对象的A或者B时,Spring抛出ObjectCurrentlyInCreationException。所以Spring可以通过setter注入,从而解决循环依赖的问题。

构造方法注入:

<bean id="person" class="com.example.Person">
        <constructor-arg value="lippon"/>
        <constructor-arg value="1"/>
</bean>

属性注入:

<bean id="person" class="com.njupt.Person">
    <property name="name" value="chaiMaoMao"/>
</bean>

16.@Component, @Repository, @Service的区别

@Component, @Service, @Controller, @Repository是spring注解,注解后可以被spring框架所扫描并注入到spring容器来进行管理。

  • @Component是通用注解,其他三个注解是这个注解的拓展,并且具有了特定的功能
  • @Repository注解在持久层中,具有将数据库操作抛出的原生异常翻译转化为spring的持久层异常的功能。
  • @Controller层是spring-mvc的注解,具有将请求进行转发,重定向的功能。
  • @Service层是业务逻辑层注解,这个注解只是标注该类处于业务逻辑层。

容器如何解决Bean循环依赖的问题

Spring在实例化一个bean的时候,是首先递归的实例化其所依赖的所有bean,直到某个bean没有依赖其他bean,此时就会将该实例返回,然后反递归的将获取到的bean设置为各个上层bean的属性的。

例如A和B循环依赖,先实例化A,因为A的属性中存在B,所以需要注入B,但是A在注入属性之前,先完成了一个半成品A,在B的实例化中,也需要注入属性A,然后发现缓存中存在一个半成品A,就把这个半成品注入到B,完成一个成品的B,再如注入到A,完成了一个成品的A 。

参考博客

AOP

1.什么是AOP,好处是什么

AOP(Aspect-Oriented Programming:⾯向切⾯编程)能够将那些与业务⽆关,却为业务模块所共同调⽤的逻辑或责任(例如事务处理、⽇志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
Spring AOP就是基于动态代理的,如果要代理的对象,实现了某个接⼝,那么Spring AOP会使⽤JDK Proxy,去创建代理对象,⽽对于没有实现接⼝的对象,就⽆法使⽤ JDK Proxy 去进⾏代理了,这时候Spring AOP会使⽤Cglib ,这时候Spring AOP会使⽤ Cglib ⽣成⼀个被代理对象的⼦类来作为代理。

2.为什么不能将代码提取到一个父类或者一个依赖类中呢?

  • 如果用一个父类或者添加依赖的方式,还是需要将代码添加到切面处,这样有大量的代码重复,不利于代码的长期维护。
  • 核心代码和辅助日志等非业务性代码混合在一起,非业务代码分散了对方法核心逻辑的注意力,影响了方法的可读性。
  • 如果需要移除非业务性代码,则面临着不小的工作量。如果需要获取记录更多的信息,例如类名,则需要手工添加漏掉的类名。如果决定记录异常,也面临着同样的问题,必须在很多地方重复记录。
  • 总之,业务代码和非业务代码进行交织,不利于开发。

3.为什么不直接用JDK的动态代理技术实现AOP

  • 目标类的所有方法都被添加横向逻辑,有时候我们希望在特定的方法添加特定的逻辑。
  • 动态代理技术用硬编码的方式指定了织入代码。
  • 手工编写代理实例的创建过程,在为不同类创建代理时,需要分别编写相应的创建代码,无法做到通用。

4.Spring AOP 和 AspectJ AOP 有什么区别?

Spring AOP 属于运⾏时增强,⽽ AspectJ 是编译时增强。 Spring AOP 基于代理(Proxying),⽽AspectJ 基于字节码操作(Bytecode Manipulation)。
Spring AOP 已经集成了 AspectJ ,AspectJ 应该算的上是 Java ⽣态系统中最完整的 AOP 框架了。
AspectJ 相⽐于 Spring AOP 功能更加强⼤,但是 Spring AOP 相对来说更简单,
如果我们的切⾯比较少,那么两者性能差异不⼤。但是,当切⾯太多的话,最好选择 AspectJ ,它⽐Spring AOP 快很多。

5.什么是Aspect

Aspect 由 pointcount 和 advice 组成, 它既包含了横切逻辑的定义, 也包括了连接点的定义. 。Spring AOP 就是负责实施切面的框架, 它将切面所定义的横切逻辑编织到切面所指定的连接点中.
AOP 的工作重心在于如何将增强编织目标对象的连接点上, 这里包含两个工作:

如何通过 pointcut 和 advice 定位到特定的 joinpoint 上
如何在 advice 中编写切面代码.
可以简单地认为, 使用 @Aspect 注解的类就是切面.

6.有哪些类型的增强Advice

  • Before - 这些类型的 Advice 在 joinpoint 方法之前执行,并使用 @Before 注解标记进行配置。
  • After Returning - 这些类型的 Advice 在连接点方法正常执行后执行,并使用@AfterReturning 注解标记进行配置。
  • After Throwing - 这些类型的 Advice 仅在 joinpoint 方法通过抛出异常退出并使用 @AfterThrowing 注解标记配置时执行。
  • After (finally) - 这些类型的 Advice 在连接点方法之后执行,无论方法退出是正常还是异常返回,并使用 @After 注解标记进行配置。
  • Around - 这些类型的 Advice 在连接点之前和之后执行,并使用 @Around 注解标记进行配置。

7.spring aop 中 concern 和 cross-cutting concern 的不同之处

concern 是我们想要在应用程序的特定模块中定义的行为。它可以定义为我们想要实现的功能。

cross-cutting concern 是一个适用于整个应用的行为,这会影响整个应用程序。例如,日志记录,安全性和数据传输是应用程序几乎每个模块都需要关注的问题,因此它们是跨领域的问题。

8.三种织入方式

  • 编译时织入:需要特殊的Java编译器,如AspectJ。

  • 类加载时织入:需要特殊的Java编译器,如AspectJ,AspectWerkz。

  • 运行时织入:Spring采用的方式,通过动态代理实现。

事务

1.Spring事务管理的方式有几种

  1. 编程式事务:在代码中硬编码(不推荐使用)。

  2. 声明式事务:在配置文件中配置(推荐使用),分为基于XML的声明式事务和基于注解的声明式事务。

2.Spring事务中的隔离级别有哪几种?

在TransactionDefinition接口中定义了五个表示隔离级别的常量:

ISOLATION_DEFAULT:使用后端数据库默认的隔离级别,Mysql默认采用的REPEATABLE_READ隔离级别;Oracle默认采用的READ_COMMITTED隔离级别。

ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。

ISOLATION_READ_COMMITTED:允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生

ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。

ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

3.Spring事务中有哪几种事务传播行为?

在TransactionDefinition接口中定义了八个表示事务传播行为的常量。

支持当前事务的情况:

  • PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。

  • PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

  • PROPAGATION_MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)。

不支持当前事务的情况:

  • PROPAGATION_REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。

  • PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。

  • PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。

其他情况:

  • PROPAGATION_NESTED: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于PROPAGATION_REQUIRED。

4.@Transactional(rollbackFor = Exception.class)注解

Exception分为运⾏时异常RuntimeException和⾮运⾏时异常。事务管理对于企业应⽤来说是⾄关重要的,即使出现异常情况,它也可以保证数据的⼀致性。

当 @Transactional 注解作⽤于类上时,该类的所有 public ⽅法将都具有该类型的事务属性,同时,我们也可以在⽅法级别使⽤该标注来覆盖类级别的定义。如果类或者⽅法加了这个注解,那么这个类⾥⾯的⽅法抛出异常,就会回滚,数据库⾥⾯的数据也会回滚。

在 @Transactional 注解中如果不配置 rollbackFor 属性,那么事物只会在遇到 RuntimeException 的时候才会回滚,加上 rollbackFor=Exception.class ,可以让事物在遇到⾮运⾏时异常时也回滚。

非运行时异常是RuntimeException以外的异常,类型上都属于Exception类及其子类。如IOException、SQLException等以及用户自定义的Exception异常。对于这种异常,JAVA编译器强制要求我们必需对出现的这些异常进行catch并处理,否则程序就不能编译通过。所以,面对这种异常不管我们是否愿意,只能自己去写一大堆catch块去处理可能的异常。

5.spring事务和数据库事务的区别

本质上其实是同一个概念,spring的事务是对数据库的事务的封装,最后本质的实现还是在数据库,假如数据库不支持事务的话,spring的事务是没有作用的.数据库的事务说简单就只有开启,回滚和关闭,spring对数据库事务的包装,原理就是拿一个数据连接,根据spring的事务配置,操作这个数据连接对数据库进行事务开启,回滚或关闭操作.但是spring除了实现这些,还配合spring的传播行为对事务进行了更广泛的管理.其实这里还有个重要的点,那就是事务中涉及的隔离级别,以及spring如何对数据库的隔离级别进行封装.事务与隔离级别放在一起理解会更好些。

Spring MVC

1.什么是Spring MVC

MVC是一种设计模式,Spring MVC是一款很优秀的MVC框架。Spring MVC可以帮助我们进行更简洁的Web层的开发,并且它天生与Spring框架集成。Spring MVC下我们一般把后端项目分为Service层(处理业务)、Dao层(数据库操作)、Entity层(实体类)、Controller层(控制层,返回数据给前台页面)

2.Spring MVC的工作原理

  1. 客户端发出HTTP请求,web应用服务器收到这个请求,如果匹配DispatcherServlet的请求映射路径,则Web容器将该请求交给DispatcherServlet;
  2. DispatcherServlet收到请求之后,根据HTTP请求信息从handlerMapping的配置找到处理请求的处理器(Handler)。可以将Handlder看作路由控制器,将Handler作为目标主机。
  3. 当DispatcherServlet得到handlder之后,通过HandlerAdapter对Handler进行封装,再以统一的适配器接口调用Handler。
  4. 处理器完成业务逻辑的处理之后,将返回一个ModelAndView给DispatcherServlet,其中包含了视图逻辑名和模型数据信息。
  5. DispatcherServlet将Model信息给ViewResolver完成逻辑视图名到真实视图对象的解析工作。
  6. DispatcherServlet就是用这个真实对象给ModelAndView中的模型数据进行视图渲染。
  7. 最终客户端得到的响应信息可能是一个普通的HTML页面、JSON或者是文件。

3.@RestController 和 @Controller

Controller 返回⼀个⻚⾯,@RestController 返回JSON 或 XML 形式数据,@Controller +@ResponseBody 返回JSON 或 XML 形式数据。

所以,@RestController就是@Controller +@ResponseBody。

4.WebApplicationContext

WebApplicationContext 是 ApplicationContext 的扩展。它具有 Web 应用程序所需的一些额外功能。它与普通的 ApplicationContext 在解析主题和决定与哪个 servlet 关联的能力方面有所不同。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值