Java的Spring知识面试题(收录全网总结最全面的面试题)

149 阅读10分钟

Spring的核心功能是轻量级容器,帮我们管理业务对象,对象的生命周期,以及对象和对象之间的关系,他有两个核心组件,就是IOC(依赖注入)和AOP(面向切面编程)

1.什么是loc?

loc是控制反转spring来创建对象,管理对象(装配对象,配置对象),并且管理对象的生命周期

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

依赖注入是指在Spring创建对象的过程中,把对象依赖的属性注入到对象中

2.IOC容器在Spring中的实现

1.BeanFactory:是IOC的基本实现,Spring内部容器使用,不提供给开发人员使用

2.ApplicationContext:BeanFactory的子接口,面向Spring的使用者

3.ApplicationContext的子类:BeanFactory的主要实现类

loc在spring中的实现.png

3.依赖注入的实现方式

1.构造器注入:将被依赖对象通过构造函数的参数注入给依赖对象,并且在初始化对象的时候注入

<bean id="studentTwo" class="com.atguigu.spring.bean.Student"> 
<!-- name属性:指定属性名--> 
<!-- index属性:指定参数所在位置的索引(从0开始)--> 
<constructor-arg value="1002"></constructor-arg> 
<constructor-arg value="李四"></constructor-arg> 
<constructor-arg value="33" name="age"></constructor-arg>
<constructor-arg value="女"></constructor-arg>
</bean>

2.Setter方法注入:通过调用成员变量提供的 setter 函数将被依赖对象注入给依赖类

<bean id="studentOne" class="com.atguigu.spring.bean.Student"> 
<!-- property标签:通过组件类的setXxx()方法给组件对象设置属性 -->
<!-- name属性:指定属性名(这个属性名是getXxx()、setXxx()方法定义的,和成员变量无关) --> 
<!-- value属性:指定属性值 --> 
<property name="id" value="1001"></property>
<property name="name" value="张三"></property>
<property name="age" value="23"></property>
<property name="sex" value="男"></property> 
</bean>

3.注解注入:使用@Autowired和@Resource等注解注入

4.基于XML管理bean

1.在Spring的配置文件中配置bean

<!--
配置HelloWorld所对应的bean,即将HelloWorld的对象交给Spring的IOC容器管理 
通过bean标签配置IOC容器所管理的bean 
属性:
id:设置bean的唯一标识
class:设置bean所对应类型的全类名 
-->
<bean id="helloworld" class="com.atguigu.spring.bean.HelloWorld"></bean>

2.获取bean

2.1根据id获取bean

ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
HelloWorld helloworld = (HelloWorld) ac.getBean("helloworld");
bean.sayHello();

2.2 根据类型获取bean

ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
HelloWorld bean = ac.getBean(HelloWorld.class);
bean.sayHello();

2.3 根据id和类型获取bean

ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
HelloWorld bean = ac.getBean("helloworld", HelloWorld.class);
bean.sayHello();

5.bean的作用域

在Spring中可以通过配置bean标签的scope属性来指定bean的作用域范围

singleton(默认):bean在IOC容器中只有一个实例,Spring 在读取 xml 文件的时候,就会创建对象

prototype:这个bean在IOC容器中有多个实例,每一次访问该对象的时候,Spring 容器都会创建这个对象

request:每次http请求都会创建一个bean,该作用域仅在基于web的Spring ApplicationContext情形下有效。

session:在一个http session中一个bean定义对应一个实例,该作用域仅在基于web的Spring ApplicationContext情形下有效

global-session:在一个全局的http session中一个bean定义对应一个实例,该作用域仅在基于web的Spring ApplicationContext情形下有效

<!-- scope属性:取值singleton(默认值),bean在IOC容器中只有一个实例,IOC容器初始化时创建 对象 -->
<!-- scope属性:取值prototype,bean在IOC容器中可以有多个实例,getBean()时创建对象 -->
<bean class="com.atguigu.bean.User" scope="prototype"></bean>

6.bean的生命周期

1.bean对象的实例化(调用无参构造器)

2.bean的属性设值

3.bean的初始化

4.bean的使用

5.bean的销毁

7.基于xml的自动装配

1.no:默认的方式是不进行自动装配的,通过手工设置ref属性来进行装配bean

2.byName:通过bean的名称进行自动装配,如果一个bean的 property 与另一bean 的name 相同,就进行自动装配

3.byType:通过参数的数据类型进行自动装配

<bean id="userController"
class="com.atguigu.autowire.xml.controller.UserController" autowire="byType"> 
</bean>

4.constructor:利用构造函数进行装配,并且构造函数的参数通过byType进行装配。

<bean id="userController"
class="com.atguigu.autowire.xml.controller.UserController" autowire="byName">
</bean>

5.autodetect:自动探测,如果有构造方法,通过 construct的方式自动装配,否则使用 byType的方式自动装配

8.基于注解的自动装配

注解装配在默认情况下是不开启的,为了使用注解装配,我们必须在Spring配置文件中配置 <context:annotation-config/>元素

@Component:将类标识为普通组件

@Controller:将类标识为控制层组件

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

@Repository:将类标识为持久层组件

@Controller、@Service、@Repository这三个注解只是在@Component注解的基础上起了三个新的名字,它们本质上一样,让我们能够便于分辨组件的作用

@AutoWired默认按照bean的类型进行自动装配

@Resource中的name和type,如果指定 name 属性,则按实例名称进行装配,如果指定 type 属性,则按 Bean 类型进行装配,如果都不指定,则先按 Bean 实例名称装配,如果不能匹配,则再按照 Bean 类型进行装配;如果都无法匹配,则抛出NoSuchBeanDefinitionException 异常

@Qualifier:和@AutoWired注解配合使用,会将默认的按Bean类型装配修改为按Bean的实例名称装配,Bean的实例名称由@Qualifier注解的参数指定

9.Spring支持的事务管理类型, spring 事务实现方式有哪些?

编程式事务管理:这意味你通过编程的方式管理事务,给你带来极大的灵活性,但是难维护

声明式事务管理:这意味着你可以将业务代码和事务管理分离,你只需用注解和XML配置来管理事务

10.Spring中如何处理线程并发

将变量放入ThreadLocal中,ThreadLocal为每个多线程提供了一个独立的变量副本,隔离了数据访问冲突

11.事务的失效场景

1.事务方法的修饰符非public导致事务失效

2.@Transactional注解的方法抛出的异常不是spring的事务支持的异常,导致事务失效

3.catch掉异常之后,没有再次抛出异常,导致事务失效

12.@Autoweried和@Resource的区别

@Autowired 和 @Resource 都是用来实现依赖注入的注解

查找顺序不同:@Autoweried是先根据类型再根据名称去查找,@Resource是先根据名称再根据类型去查找

支持的参数不同:@Autowired 只支持设置 1 个参数,而 @Resource 支持设置 7 个参数

13.什么是AOP?

通过预编译方式和运行期间的动态代理(动态代理是通过反射来实现的)实现在不改变源代码的情况下添加新的功能,只做增强不做改变,在事务处理、日志管理、权限控制方面应用

14.Aop的名词

切面(Aspect):切面是通知和切点的结合。通知和切点共同定义了切面的全部内容。 在Spring AOP中,切面可以使用通用类(基于模式的风格) 或者在普通类中以 @AspectJ 注解来实现。

连接点(Join point):指方法,在Spring AOP中,一个连接点 总是 代表一个方法的执行。 应用可能有数以千计的时机应用通知。这些时机被称为连接点。连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。

通知(Advice):在AOP术语中,切面的工作被称为通知。

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

引入(Introduction):引入允许我们向现有类添加新方法或属性。

目标对象(Target Object): 被一个或者多个切面(aspect)所通知(advise)的对象。它通常是一个代理对象。也有人把它叫做 被通知(adviced) 对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个 被代理(proxied) 对象。

织入(Weaving):织入是把切面应用到目标对象并创建新的代理对象的过程。在目标对象的生命周期里有多少个点可以进行织入:

  • 编译期:切面在目标类编译时被织入。AspectJ的织入编译器是以这种方式织入切面的。
  • 类加载期:切面在目标类加载到JVM时被织入。需要特殊的类加载器,它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5的加载时织入就支持以这种方式织入切面。
  • 运行期:切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。SpringAOP就是以这种方式织入切面

15.动态代理和静态代理

JDK动态代理:jdk的原生实现方式,需要被代理的目标类实现接口。核心InvocationHandler接口和Proxy类,InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实例, 生成目标类的代理对象。JDK动态代理是Java提供的动态代理技术,可以在运行时创建接口的代理实例

cglib:通过继承被代理的目标类实现代理,所以不需要目标类实现接口。如果代理类没有实现 InvocationHandler 接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。CGLib动态代理采用底层的字节码技术,在运行时创建子类代理的实例

AspectJ:本质上是静态代理,将代码逻辑"织入"被代理的目标类编译得到的字节码文件,所以最终效果是动态的。weaver就是织入器。Spring只是借用了AspectJ中的注解。

静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理

16.Aop的通知类型

前置通知:使用@Before注解标识,在被代理的目标方法前执行

返回通知:使用@AfterReturning注解标识,在被代理的目标方法成功结束后执行

异常通知:使用@AfterThrowing注解标识,在被代理的目标方法异常结束后执行

后置通知:使用@After注解标识,在被代理的目标方法最终结束后执行

环绕通知:使用@Around注解标识,使用try...catch...finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置