基于Spring Framework 4.x 总结的常见面试题,系统学习建议还是官方文档走起:https://spring.io/projects/spring-framework#learn
spring overview
AOP:Aspect Oriented Program, 面向(方面)切面的编程;Filter(过滤器)也是一种 AOP. AOP 是一种新的 方法论, 是对传统 OOP(Object-OrientedProgramming, 面向对象编程) 的补充. AOP 的主要编程对象是切面(aspect),而切面模块化横切关注点.可以举例通过事务说明.
IOC:Invert Of Control, 控制反转. 也称为 DI(依赖注入)其思想是反转资源获取的方向. 传统的资源查找方式要求组件向容器发起请求查找资源.作为回应, 容器适时的返回资源. 而应用了 IOC 之后, 则是容器主动地将资源推送给它所管理的组件,组件所要做的仅是选择一种合适的方式来接受资源. 这种行为也被称为查找的被动形式
IoC(Inverse of Control:控制反转)是一种设计思想,就是 将原本在程序中手动创建对象的控制权,交由Spring框架来管理。 IoC 在其他语言中也有应用,并非 Spring 特有。IoC 容器是 Spring 用来实现 IoC 的载体, IoC 容器实际上就是个Map(key,value),Map 中存放的是各种对象。
将对象之间的相互依赖关系交给 IoC 容器来管理,并由 IoC 容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。 在实际项目中一个 Service 类可能有几百甚至上千个类作为它的底层,假如我们需要实例化这个 Service,你可能要每次都要搞清这个 Service 所有底层类的构造函数,这可能会把人逼疯。如果利用 IoC 的话,你只需要配置好,然后在需要的地方引用就行了,这大大增加了项目的可维护性且降低了开发难度。
Spring 框架的核心是 Spring 容器。容器创建对象,将它们装配在一起,配置它们并管理它们的完整生命周期。Spring 容器使用依赖注入来管理组成应用程序的组件。容器通过读取提供的配置元数据来接收对象进行实例化,配置和组装的指令。该元数据可以通过 XML,Java 注解或 Java 代码提供。
container magic
依赖注入(DI,Dependency Injection)是在编译阶段尚未知所需的功能是来自哪个的类的情况下,将其他对象所依赖的功能对象实例化的模式。这就需要一种机制用来激活相应的组件以提供特定的功能,所以依赖注入是控制反转的基础。否则如果在组件不受框架控制的情况下,框架又怎么知道要创建哪个组件?
依赖注入有以下三种实现方式:
在 Spring IOC 容器读取 Bean 配置创建 Bean 实例之前,必须对它进行实例化。只有在容器实例化后, 才可以从 IOC 容器里获取 Bean 实例并使用
Spring 提供了两种类型的 IOC 容器实现
BeanFactory 是 Spring 框架的基础设施,面向 Spring 本身;ApplicationContext 面向使用 Spring 框架的开发者,几乎所有的应用场合都直接使用 ApplicationContext 而非底层的 BeanFactory;
无论使用何种方式, 配置文件是相同的。
BeanFactory | ApplicationContext |
---|---|
懒加载 | 即时加载 |
它使用语法显式提供资源对象 | 它自己创建和管理资源对象 |
不支持国际化 | 支持国际化 |
不支持基于依赖的注解 | 支持基于依赖的注解 |
ApplicationContext
ApplicationContext 的主要实现类:
javadoop.com
从 IOC 容器中获取 Bean
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
HelloWorld helloWorld = (HelloWorld) ctx.getBean("helloWorld");
helloWorld.hello();
Spring 中的 IoC 的实现原理就是工厂模式加反射机制,示例:
interface Fruit {
public abstract void eat();
}
class Apple implements Fruit {
public void eat(){
System.out.println("Apple");
}
}
class Orange implements Fruit {
public void eat(){
System.out.println("Orange");
}
}
class Factory {
public static Fruit getInstance(String ClassName) {
Fruit f=null;
try {
f=(Fruit)Class.forName(ClassName).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return f;
}
}
class Client {
public static void main(String[] a) {
Fruit f=Factory.getInstance("priv.starfish.spring.Apple");
if(f!=null){
f.eat();
}
}
}
基于 xml 配置
bean 所需的依赖项和服务在 XML 格式的配置文件中指定。这些配置文件通常包含许多 bean 定义和特定于应用程序的配置选项。它们通常以 bean 标签开头。例如:
<bean id="studentbean" class="org.edureka.firstSpring.StudentBean">
<property name="name" value="Edureka"></property>
</bean>
基于注解配置
您可以通过在相关的类,方法或字段声明上使用注解,将 bean 配置为组件类本身,而不是使用 XML 来描述 bean 装配。默认情况下,Spring 容器中未打开注解装配。因此,您需要在使用它之前在 Spring 配置文件中启用它。例如:
<beans>
<context:annotation-config/>
<!-- bean definitions go here -->
</beans>
基于 Java API 配置
Spring 的 Java 配置是通过使用 @Bean 和 @Configuration 来实现。
<bean/>
元素相同的角色。例如:
@Configuration
public class StudentConfig {
@Bean
public StudentBean myStudent() {
return new StudentBean();
}
}
getBean()
调用和 Bean 引用都将返回这个唯一的 Bean 实例。该作用域被称为 singleton,它是所有 Bean 的默认作用域。Spring 容器中的 bean 可以分为 5 个范围。所有范围的名称都是自说明的,但是为了避免混淆,还是让我们来解释一下:
全局作用域与Servlet中的session作用域效果相同。
Spring IOC 容器可以管理 Bean 的生命周期, Spring 允许在 Bean 生命周期的特定点执行定制的任务。
Spring bean 容器的生命周期流程如下:
*.Aware
接口,就调用相应的方法;<bean>
的 init-method 属性),那么将调用它;<bean>
的 destroy-method 属性),那么将调用它在 bean 初始化时会经历几个阶段,要与容器对 bean 生命周期的管理交互,可以实现 InitializingBean
和 DisposableBean
接口。容器对前者调用 afterPropertiesSet()
,对后者调用 destroy()
,以允许 bean 在初始化和销毁 bean 时执行某些操作。
官方不建议使用这两个接口,而是建议使用 @PostConstruct
和 @PreDestroy
,或者 XML 配置中使用 init-method
和destroy-method
属性
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean {
public void init() {
// do some initialization work
}
}
等价于
public class AnotherExampleBean implements InitializingBean {
public void afterPropertiesSet() {
// do some initialization work
}
}
Spring Bean生命周期回调——初始化回调和销毁回调方法
实现 Bean 初始化回调和销毁回调各有三种方法,一是实现接口方法,二是在XML配置,三是使用注解
@PostConstruct
和 @PreDestroy
InitializingBean
和 DisposableBean
接口init-method
和 destroy-method
在一个 bean 中,如果配置了多种生命周期回调机制,会按照上边从上到下的次序调用
Bean 的配置方式: 通过全类名 (反射)、 通过工厂方法 (静态工厂方法 & 实例工厂方法)、FactoryBean
当 bean 在 Spring 容器中组合在一起时,它被称为装配或 bean 装配,装配是创建应用对象之间协作关系的行为。Spring 容器需要知道需要什么 bean 以及容器应该如何使用依赖注入来将 bean 绑定在一起,同时装配 bean。
依赖注入的本质就是装配,装配是依赖注入的具体行为。
注入是实例化的过程,将创建的bean放在Spring容器中,分为属性注入(setter方式)、构造器注入
Spring 容器可以自动配置相互协作 beans 之间的关联关系。这意味着 Spring 可以自动配置一个 bean 和其他协作bean 之间的关系,通过检查 BeanFactory 的内容里有没有使用< property>元素。
在Spring框架中共有5种自动装配,让我们逐一分析
组件扫描(component scanning): Spring 能够从 classpath下自动扫描, 侦测和实例化具有特定注解的组件。
特定组件包括:
对于扫描到的组件,,Spring 有默认的命名策略:使用非限定类名,,第一个字母小写。也可以在注解中通过 value 属性值标识组件的名称。
当在组件类上使用了特定的注解之后,,还需要在 Spring 的配置文件中声明 <context:component-scan>
:
base-package
属性指定一个需要扫描的基类包,Spring 容器将会扫描这个基类包里及其子包中的所有类
当需要扫描多个包时, 可以使用逗号分隔
如果仅希望扫描特定的类而非基包下的所有类,可使用 resource-pattern
属性过滤特定的类,示例:
<context:component-scan base-package="priv.starfish.front.web.controller"
annotation-config="true" resource-pattern="autowire/*.class"/>
默认情况下,Spring 容器中未打开注解装配。因此,要使用基于注解装配,我们必须通过配置<context:annotation-config />
元素在 Spring 配置文件中启用它。
?:描述一下Spring AOP 呗? 你有没有⽤过Spring的AOP? 是⽤来⼲嘛的? ⼤概会怎么使⽤?
AOP(Aspect-Oriented Programming,面向切面编程):是一种新的方法论,是对传统 OOP(Object-Oriented Programming,面向对象编程) 的补充。在 OOP 中, 我们以类(class)作为我们的基本单元,而 AOP 中的基本单元是 Aspect(切面)
AOP 的主要编程对象是切面(aspect)
在应用 AOP 编程时, 仍然需要定义公共功能,但可以明确的定义这个功能在哪里,,以什么方式应用,,并且不必修改受影响的类。这样一来横切关注点就被模块化到特殊的对象(切面)里。
AOP 的好处:
org.springframework.aop.Pointcut
接口进行描述,它使用类和方法作为连接点的查询条件Spring AOP
在 Spring 中启用 AspectJ 注解支持
aopalliance.jar
、aspectj.weaver.jar
和 spring-aspects.jar
<beans>
根元素中.<aop:aspectj-autoproxy>
<aop:aspectj-autoproxy>
元素时, 会自动为与 AspectJ切面匹配的 Bean 创建代理.实现 AOP 的技术,主要分为两大类:
JdbcTemplate简介
Hibernate、iBatis、JPA、JDO、OJB
作为企业级应用程序框架,,Spring 在不同的事务管理 API 之上定义了一个抽象层,而应用程序开发人员不必了解底层的事务管理 API,就可以使用 Spring 的事务管理机制
Spring 既支持编程式事务管理,也支持声明式的事务管理
Spring 并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给 Hibernate 或者 JTA 等持久化机制所提供的相关平台框架的事务来实现。
Spring 事务管理器的接口是 org.springframework.transaction.PlatformTransactionManager
,通过这个接口,Spring为各个平台如 JDBC、Hibernate 等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。
事务管理器以普通的 Bean 形式声明在 Spring IOC 容器中
在应用程序中只需要处理一个数据源, 而且通过 JDBC 存取
org.springframework.jdbc.datasource.DataSourceTransactionManager
在 JavaEE 应用服务器上用 JTA(Java Transaction API) 进行事务管理
org.springframework.transaction.jta.JtaTransactionManager
用 Hibernate 框架存取数据库
org.springframework.orm.hibernate3.HibernateTransactionManager
事务管理器以普通的 Bean 形式声明在 Spring IOC 容器中
<tx:annotation-driven>
元素, 并为之指定事务管理器就可以了<tx:annotation-driven>
元素中省略 transaction-manager
属性,这个元素会自动检测该名称的事务处理器传播行为 | 意义 |
---|---|
PROPAGATION_MANDATORY | 表示该方法必须运行在一个事务中。如果当前没有事务正在发生,将抛出一个异常 |
PROPAGATION_NESTED | 表示如果当前正有一个事务在进行中,则该方法应当运行在一个嵌套式事务中。被嵌套的事务可以独立于封装事务进行提交或回滚。如果封装事务不存在,行为就像PROPAGATION_REQUIRES一样。 |
PROPAGATION_NEVER | 表示当前的方法不应该在一个事务中运行。如果一个事务正在进行,则会抛出一个异常。 |
PROPAGATION_NOT_SUPPORTED | 表示该方法不应该在一个事务中运行。如果一个现有事务正在进行中,它将在该方法的运行期间被挂起。 |
PROPAGATION_SUPPORTS | 表示当前方法不需要事务性上下文,但是如果有一个事务已经在运行的话,它也可以在这个事务里运行。 |
PROPAGATION_REQUIRES_NEW | 表示当前方法必须在它自己的事务里运行。一个新的事务将被启动,而且如果有一个现有事务在运行的话,则将在这个方法运行期间被挂起。 |
PROPAGATION_REQUIRES | 表示当前方法必须在一个事务中运行。如果一个现有事务正在进行中,该方法将在那个事务中运行,否则就要开始一个新事务。 |
隔离级别 | 含义 |
---|---|
ISOLATION_DEFAULT | 使用后端数据库默认的隔离级别。 |
ISOLATION_READ_UNCOMMITTED | 允许读取尚未提交的更改。可能导致脏读、幻影读或不可重复读。 |
ISOLATION_READ_COMMITTED | 允许从已经提交的并发事务读取。可防止脏读,但幻影读和不可重复读仍可能会发生。 |
ISOLATION_REPEATABLE_READ | 对相同字段的多次读取的结果是一致的,除非数据被当前事务本身改变。可防止脏读和不可重复读,但幻影读仍可能发生。 |
ISOLATION_SERIALIZABLE | 完全服从ACID的隔离级别,确保不发生脏读、不可重复读和幻影读。这在所有隔离级别中也是最慢的,因为它通常是通过完全锁定当前事务所涉及的数据表来完成的。 |
事务的隔离级别要得到底层数据库引擎的支持,而不是应用程序或者框架的支持;
Oracle 支持的 2 种事务隔离级别,Mysql支持 4 种事务隔离级别。
用 @Transactional 注解声明式地管理事务时可以在 @Transactional 的 isolation 属性中设置隔离级别
在 Spring 事务通知中, 可以在 <tx:method>
元素中指定隔离级别
设置超时和只读事务属性
列出两种方式的示例:
@Transactional(propagation = Propagation.NESTED, timeout = 1000, isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- all methods starting with 'get' are read-only -->
<tx:method name="get*" read-only="true" propagation="REQUIRES_NEW" isolation="READ_COMMITTED" timeout="30" no-rollback-for="java.lang.ArithmeticException"/>
<!-- other methods use the default transaction settings (see below) -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!-- ensure that the above transactional advice runs for any execution
of an operation defined by the FooService interface -->
<aop:config>
<aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
</aop:config>
Spring Web MVC 框架提供 模型-视图-控制器 架构和随时可用的组件,用于开发灵活且松散耦合的 Web 应用程序。MVC 模式有助于分离应用程序的不同方面,如输入逻辑,业务逻辑和 UI 逻辑,同时在所有这些元素之间提供松散耦合。
在整个 Spring MVC 框架中, DispatcherServlet 处于核心位置,负责协调和组织不同组件以完成请求处理并返回响应的工作
SpringMVC 处理请求过程:
controller默认是单例的,不要使用非静态的成员变量,否则会发生数据逻辑混乱。正因为单例所以不是线程安全的
@Controller
//@Scope("prototype")
public class ScopeTestController {
private int num = 0;
@RequestMapping("/testScope")
public void testScope() {
System.out.println(++num);
}
@RequestMapping("/testScope2")
public void testScope2() {
System.out.println(++num);
}
}
我们首先访问 http://localhost:8080/testScope
,得到的答案是1
;然后我们再访问 http://localhost:8080/testScope2
,得到的答案是 2
。
接下来我们再来给controller
增加作用多例 @Scope("prototype")
我们依旧首先访问 http://localhost:8080/testScope
,得到的答案是1
;然后我们再访问 http://localhost:8080/testScope2
,得到的答案还是 1
。
单例是不安全的,会导致属性重复使用。
基于Java的配置,允许你在少量的Java注解的帮助下,进行你的大部分Spring配置而非通过XML文件。
以@Configuration 注解为例,它用来标记类可以当做一个bean的定义,被Spring IOC容器使用。
另一个例子是@Bean注解,它表示此方法将要返回一个对象,作为一个bean注册进Spring应用上下文。
@Configuration
public class StudentConfig {
@Bean
public StudentBean myStudent() {
return new StudentBean();
}
}
注解装配在默认情况下是不开启的,为了使用注解装配,我们必须在Spring配置文件中配置 <context:annotation-config/>
元素。
在SpringMVC 中,控制器Controller 负责处理由DispatcherServlet 分发的请求,它把用户请求的数据经过业务处理层处理之后封装成一个Model ,然后再把该Model 返回给对应的View 进行展示。在SpringMVC 中只需使用@Controller 标记一个类是Controller ,然后使用@RequestMapping 和@RequestParam 等一些注解用以定义URL 请求和Controller 方法之间的映射,这样的Controller 就能被外界访问到。
RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上
Spring Framework 4.3 之后引入的基于HTTP方法的变体
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping
用于将请求URL中的模板变量映射到功能处理方法的参数上,即取出uri模板中的变量作为参数
使用@RequestParam绑定请求参数值,在处理方法入参处使用@RequestParam可以把请求参数传递给请求方法
@RequestBody 表明方法参数应该绑定到HTTP请求体的值
@Responsebody 表示该方法的返回结果直接写入HTTP response body中
一般在异步获取数据时使用,在使用@RequestMapping后,返回值通常解析为跳转路径,加上@Responsebody后返回结果不会被解析为跳转路径,而是直接写入HTTP response body中。比如异步获取 json 数据,加上@Responsebody后,会直接返回 json 数据。
@Resource和@Autowired都是做bean的注入时使用,其实@Resource并不是Spring的注解,它的包是javax.annotation.Resource,需要导入,但是Spring支持该注解的注入。
javax.annotation.Resource
。@Resource 有两个重要的属性:name 和 type,而 Spring 将@ Resource 注解的 name 属性解析为bean 的名字,而 type 属性则解析为 bean 的类型。所以,如果使用 name 属性,则使用 byName 的自动注入策略,而使用 type 属性时则使用 byType 自动注入策略。如果既不制定 name 也不制定 type 属性,这时将通过反射机制使用 byName 自动注入策略。方法入参标注该注解后, 入参的对象就会放到数据模型中
将模型中的某个属性暂存到HttpSession中,以便多个请求之间可以共享这个属性
@CookieValue可让处理方法入参绑定某个Cookie 值
请求头包含了若干个属性,服务器可据此获知客户端的信息,通过@RequestHeader即可将请求头中的属性值绑定到处理方法的入参中
这个注解表明bean的属性必须在配置的时候设置,通过一个bean定义的显式的属性值或通过自动装配,若@Required注解的bean属性未被设置,容器将抛出 BeanInitializationException。示例:
public class Employee {
private String name;
@Required
public void setName(String name){
this.name=name;
}
public string getName(){
return name;
}
}
@Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。@Autowired 注解提供了更细粒度的控制,包括在何处以及如何完成自动装配。它的用法和@Required一样,修饰setter方法、构造器、属性或者具有任意名称和/或多个参数的PN方法。
public class Employee {
private String name;
@Autowired
public void setName(String name) {
this.name=name;
}
public string getName(){
return name;
}
}
用途:做bean的注入时使用
org.springframework.beans.factory.annotation.Autowired
javax.annotation.Resource
共同点:都用来装配bean。写在字段上,或写在setter方法
不同点:@Autowired 默认按类型装配。依赖对象必须存在,如果要允许null值,可以设置它的required属性为false @Autowired(required=false),也可以使用名称装配,配合@Qualifier注解
@Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入
当创建多个相同类型的 bean 并希望仅使用属性装配其中一个 bean 时,可以使用 @Qualifier 注解和 @Autowired 通过指定应该装配哪个确切的 bean 来消除歧义。
@RequestMapping 注解用于将特定 HTTP 请求方法映射到将处理相应请求的控制器中的特定类/方法。此注释可应用于两个级别:
BeanFactory
、ApplicationContext
创建 bean 对象。jdbcTemplate
、hibernateTemplate
等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。Controller
。https://www.edureka.co/blog/interview-questions/spring-interview-questions/
https://crossoverjie.top/2018/07/29/java-senior/ThreadPool/