文章目录:
1.什么是Spring?
2.Spring由哪些模块组成?
3.Spring中都用到了哪些设计模式?
4.什么是Spring IOC容器?有什么作用?
5.Spring IoC的实现机制
6.BeanFactory 和 ApplicationContext有什么区别?
7.什么是Spring的依赖注入(Dependency Injection)?
8.什么是Spring AOP?
9.Spring AOP 与 AspectJ AOP 有什么区别?AOP 有哪些实现方式?
10.解释一下Spring AOP里面的几个名词
11.Spring AOP中的通知有哪些类型?
12.Spring支持的几种bean的作用域
13.解释一下Spring中bean的生命周期
14.Spring支持的事务管理类型, Spring事务实现方式有哪些?你更倾向用哪种事务管理类型?
15.什么是Spring事务的传播行为?
16.什么是Spring事务的隔离级别?
1.什么是Spring?
Spring是一个轻量级Java开源框架,最早由Rod Johnson创建,目的是解决企业级应用开发的复杂性,简化Java开发。Spring为开发Java应用程序提供全面的基础架构支持,因此Java开发者可以专注于应用程序的开发。
Spring可以做很多事情,它为企业级开发提供给了丰富的功能,但是这些功能的底层都依赖于它的两个核心特性,也就是控制反转(IoC)也可以说依赖注入(DI)和面向切面编程(AOP)。
为了降低Java开发的复杂性,Spring采取了以下4种关键策略
- 基于POJO的轻量级和最小侵入性编程;
- 通过依赖注入和面向接口实现松耦合;
- 基于切面和惯例进行声明式编程;
- 通过切面和模板减少样板式代码。
Spring设计目标:Spring为开发者提供一个一站式轻量级应用开发平台;
Spring设计理念:在JavaEE开发中,支持POJO和JavaBean开发方式,使应用面向接口开发,充分支持OO(面向对象)设计方法;Spring通过IoC容器实现对象耦合关系的管理,并实现依赖反转,将对象之间的依赖关系交给IoC容器,实现解耦;
Spring框架的核心:IoC容器和AOP模块。通过IoC容器管理POJO对象以及他们之间的耦合关系;通过AOP以动态非侵入的方式增强服务,把遍布于应用各层的功能分离出来形成可重用的功能组件。
2.Spring由哪些模块组成?
- spring core:提供了框架的基本组成部分,包括控制反转(Inversion of Control,IOC)和依赖注入(Dependency Injection,DI)功能。
- spring beans:提供了BeanFactory,是工厂模式的一个经典实现,Spring将管理对象称为Bean。
- spring context:构建于 core 封装包基础上的 context 封装包,提供了一种框架式的对象访问方法。
- spring jdbc:提供了一个JDBC的抽象层,消除了烦琐的JDBC编码和数据库厂商特有的错误代码解析, 用于简化JDBC。
- spring aop:提供了面向切面的编程实现,让你可以自定义拦截器、切点等。
- spring Web:提供了针对 Web 开发的集成特性,例如文件上传,利用 servlet listeners 进行 ioc 容器初始化和针对 Web 的 ApplicationContext。
- spring test:主要为测试提供支持的,支持使用JUnit或TestNG对Spring组件进行单元测试和集成测试。
3.Spring中都用到了哪些设计模式?
- 工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;
- 单例模式:Bean默认为单例模式。
- 代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;
- 模板方法:用来解决代码重复的问题。比如:RestTemplate, JmsTemplate, JpaTemplate。
- 观察者模式:定义对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被动更新,如Spring中listener的实现–ApplicationListener。
4.什么是Spring IOC容器?有什么作用?
控制反转即IoC (Inversion of Control),它把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。所谓的“控制反转”概念就是对对象组件控制权的转移,从程序代码本身转移到了外部容器。
Spring IOC 负责创建对象,管理对象(通过依赖注入(DI),装配对象,配置对象,并且管理这些对象的整个生命周期。)
IoC容器的作用:
- 管理对象的创建和依赖关系的维护。对象的创建并不是一件简单的事,在对象关系比较复杂时,如果依赖关系需要程序猿来维护的话,那是相当头疼的
- 解耦,由容器去维护具体的对象
- 托管了类的整个生命周期,比如我们需要在类的产生过程中做一些处理,最直接的例子就是代理,如果有容器程序可以把这部分处理交给容器,应用程序则无需去关心类是如何完成代理的
5.Spring IoC的实现机制
Spring 中的 IoC 的实现原理就是工厂模式加反射机制。
package com.szh.spring;
interface Fruit {
void eat();
}
class Apple implements Fruit {
public Apple() {
System.out.println("Apple类的无参构造执行了....");
}
@Override
public void eat() {
System.out.println("苹果");
}
}
class Banana implements Fruit {
public Banana() {
System.out.println("Banana类的无参构造执行了....");
}
@Override
public void eat() {
System.out.println("香蕉");
}
}
class Factory {
public static Fruit getInstance(String className) {
Fruit fruit = null;
try {
fruit = (Fruit) Class.forName(className).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return fruit;
}
}
public class Client {
public static void main(String[] args) {
Fruit fruit = Factory.getInstance("com.szh.spring.Apple");
if (fruit != null) {
fruit.eat();
}
}
}
6.BeanFactory 和 ApplicationContext有什么区别?
BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。其中ApplicationContext是BeanFactory的子接口。
依赖关系
BeanFactory:是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。我们可以称之为 “低级容器”。
ApplicationContext接口作为BeanFactory的派生,可以称之为 “高级容器”。除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:
- 继承MessageSource,因此支持国际化。
- 统一的资源文件访问方式。
- 提供在监听器中注册bean的事件。
- 同时加载多个配置文件。
- 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。
加载方式
BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。
ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例bean,确保当你需要的时候,你就不用等待,因为它们已经创建好了。
相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。
创建方式
BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader。
注册方式
BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。
7.什么是Spring的依赖注入(Dependency Injection)?
依赖注入:相对于IoC而言,依赖注入(DI)更加准确地描述了IoC的设计理念。所谓依赖注入(Dependency Injection),即组件之间的依赖关系由容器在应用系统运行期来决定,也就是由容器动态地将某种依赖关系的目标对象实例注入到应用系统中的各个关联的组件之中。组件不做定位查询,只提供普通的Java方法,让容器去决定依赖关系。
依赖注入是时下最流行的IoC实现方式,依赖注入分为接口注入(Interface Injection),Setter方法注入(Setter Injection)和构造器注入(Constructor Injection)三种方式。其中接口注入由于在灵活性和易用性比较差,现在从Spring4开始已被废弃。
构造器注入:构造器注入是容器通过调用一个类的构造器来实现的,该构造器有一系列参数,每个参数都必须注入。
Setter方法注入:Setter方法注入是容器通过调用无参构造器或无参static工厂方法实例化bean之后,调用该bean的setter方法来实现的依赖注入。
8.什么是Spring AOP?
AOP(Aspect-Oriented Programming),一般称为面向切面编程,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),通过面向切面编程减少了系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。常用于权限认证、日志、事务处理等。
9.Spring AOP 与 AspectJ AOP 有什么区别?AOP 有哪些实现方式?
AOP实现的关键在于代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。
(1)AspectJ是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。
(2)Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。
10.解释一下Spring AOP里面的几个名词
- Aspect:切面,给业务方法增加到功能。切面泛指交叉业务逻辑。常用的切面是通知(Advice)。实际就是对主业务逻辑的一种增强。
- JoinPoint:连接点,连接切面的业务方法。连接点指可以被切面织入的具体方法。通常业务接口中的方法均为连接点。
- Pointcut:切入点,切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。
- Target:目标对象,目标对象指将要被增强的对象。即包含主业务逻辑的类的对象。
- Advice:通知,通知表示切面的执行时间,Advice 也叫增强。换个角度来说,通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。
切入点定义切入的位置,通知定义切入的时间。
11.Spring AOP中的通知有哪些类型?
通知类型 | 说明 | 使用场景 |
---|
前置通知(Before) | 在目标方法被执行之前调用通知 | | 后置通知(After) | 无论如何都会在目标方法执行之后调用通知 | 记录日志(方法已经调用,但不一定成功) | 最终通知(After-returning ) | 无论目标方法是否抛出异常,该增强均会被执行。 | 记录日志(方法已经成功调用) | 异常通知(After-throwing) | 在目标方法抛出异常后调用通知 | 异常处理 | 环绕通知(Around) | 通知包裹了目标方法,在目标方法调用之前和调用之后执行自定义的行为 | 事务权限控制 |
12.Spring支持的几种bean的作用域
当定义一个bean在Spring里,我们还能给这个bean声明一个作用域。它可以通过bean的scope属性来定义。
Spring框架支持以下五种bean的作用域:
作用域 | 描述 |
---|
singleton | 单例模式,在spring IoC容器仅存在一个Bean实例,默认值 | prototype | 原型模式,每次从容器中获取Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean() | request | 每次HTTP请求都会创建一个新的Bean,该作用域仅在基于web的Spring ApplicationContext环境下有效 | session | 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean,该作用域仅在基于web的Spring ApplicationContext环境下有效 | global-session | 同一个全局的HTTP Session中共享一个Bean,一般用于Portlet应用环境,该作用域仅在基于web的Spring ApplicationContext环境下有效 |
因为经常用到的就是前两种 singleton、prototype,所以下面通过简单的代码案例演示一下。
首先演示当Spring Bean的作用域设置为 默认singleton 的情况。依次是实体类、Spring配置文件、测试方法。
package com.szh.spring;
public class Book {
private String bookName;
private String bookAuthor;
public void setBookName(String bookName) {
this.bookName = bookName;
}
public void setBookAuthor(String bookAuthor) {
this.bookAuthor = bookAuthor;
}
@Override
public String toString() {
return "Book{" +
"bookName='" + bookName + '\'' +
", bookAuthor='" + bookAuthor + '\'' +
'}';
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="book" class="com.szh.spring.Book" scope="singleton">
<property name="bookName" value="盗墓笔记"/>
<property name="bookAuthor" value="南派三叔"/>
</bean>
</beans>
@Test
public void testBook() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean1.xml");
Book book1 = context.getBean("book",Book.class);
Book book2 = context.getBean("book",Book.class);
System.out.println(book1);
System.out.println(book2);
System.out.println(book1 == book2);
}
从上面的输出结果中可以看到,在bean的作用域为 singleton 单例的情况下,通过Spring IoC容器依次拿到的这两个对象是一样的,也就是说此时容器中仅存在这一个bean(Book)。
下面再来演示 bean 作用域为prototype的情况。所有测试代码和上面的一样,只是将配置文件中 标签中的 scope 属性修改为 prototype。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="book" class="com.szh.spring.Book" scope="prototype">
<property name="bookName" value="盗墓笔记"/>
<property name="bookAuthor" value="南派三叔"/>
</bean>
</beans>
这个时候,我们可以看到从容器中拿到的这两个对象就不一样了,因为此时bean的作用域变成了 prototype 多实例情况,也就是说容器中可以有多个Book实例。
关于 singleton 和 prototype 的区别:
- singleton 单实例,prototype 多实例。
- 设置 scope 值是 singleton 的时候,加载 spring 配置文件时就会创建单实例对象。
设置 scope 值是 prototype 的时候,不是在加载 spring 配置文件的时候创建 对象,而是在调用getBean 方法时候创建多实例对象。
13.解释一下Spring中bean的生命周期
在传统的Java应用中,bean的生命周期很简单。使用Java关键字new进行bean实例化,然后该bean就可以使用了。一旦该bean不再被使用,则由Java自动进行垃圾回收。相比之下,Spring容器中的bean的生命周期就显得相对复杂多了。正确理解Spring bean的生命周期非常重要,因为你或许要利用Spring提供的扩展点来自定义bean的创建过程。
下图展示了bean装载到Spring应用上下文中的一个典型的生命周期过程。
Spring对bean进行实例化;
Spring将值和bean的引用注入到bean对应的属性中;(set方法赋值、引用类型、构造器等等)
如果bean实现了BeanNameAware接口,Spring将bean的ID传递给setBean-Name()方法;
如果bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入;
如果bean实现了ApplicationContextAware接口,Spring将调用setApplicationContext()方法,将bean所在的应用上下文的引用传入进来;
如果bean实现了BeanPostProcessor接口,Spring将调用它们的post-ProcessBeforeInitialization()方法;
如果bean实现了InitializingBean接口,Spring将调用它们的after-PropertiesSet()方法。类似地如果bean使用initmethod声明了初始化方法,该方法也会被调用;
此时,bean已经准备就绪,可以被应用程序使用了,它们将一直驻留在应用上下文中,直到该应用上下文被销毁;
如果bean实现了DisposableBean接口,Spring将调用它的destroy()接口方法。同样,如果bean使用destroy-method声明了销毁方法,该方法也会被调用。
下面简单演示一下Spring Bean的生命周期:👇👇👇
package com.szh.spring.bean;
public class Orders {
private String name;
public Orders() {
System.out.println("第一步: 执行无参构造创建bean实例");
}
public void setName(String name) {
this.name = name;
System.out.println("第二步: 调用set方法设置属性值");
}
public void initMethod() {
System.out.println("第三步: 执行bean初始化的方法");
}
public void destroyMethod() {
System.out.println("第五步: 执行bean销毁的方法");
}
}
package com.szh.spring.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPost implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("在初始化之前执行的方法");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("在初始化之后执行的方法");
return bean;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="orders" class="com.szh.spring.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
<property name="name" value="手机"/>
</bean>
<bean id="myBeanPost" class="com.szh.spring.bean.MyBeanPost"/>
</beans>
@Test
public void testBean() {
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("bean2.xml");
Orders orders = context.getBean("orders",Orders.class);
System.out.println("第四步: 获取创建好的bean实例对象 ---> " + orders);
context.close();
}
bean 生命周期有七步
- 通过无参构造创建 bean 实例
- 为 bean 的属性设置值和对其他 bean 的引用(调用 set 方法)
- 把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization(实现了BeanPostProcessor接口)
- 调用 bean 的初始化的方法(需要进行配置初始化的方法)
- 把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization(实现了BeanPostProcessor接口)
- bean 可以使用了(对象获取到了)
- 当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
14.Spring支持的事务管理类型, Spring事务实现方式有哪些?你更倾向用哪种事务管理类型?
Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。真正的数据库层的事务提交和回滚是通过bin log或者redo log实现的。
Spring支持两种类型的事务管理:
编程式事务管理:通过编程的方式管理事务,灵活性好,但是难维护。
声明式事务管理:将业务代码和事务管理分离,只需用注解和XML配置来管理事务。
大多数情况下选择声明式事务管理,虽然比编程式事务管理少了一点灵活性,最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别,但是声明式事务管理对应用代码的影响最小,更符合一个无侵入的轻量级容器的思想,具有更好的可维护性。
15.什么是Spring事务的传播行为?
spring事务的传播行为说的是,当多个事务同时存在的时候,spring如何处理这些事务的行为。
- PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
- PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
- PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
- PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。
- PROPAGATION_NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,就把当前事务挂起。
- PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
- PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。
16.什么是Spring事务的隔离级别?
spring 有五大隔离级别,默认值为 ISOLATION_DEFAULT(使用数据库的设置),其他四个隔离级别和数据库的隔离级别一致:
- ISOLATION_DEFAULT:用底层数据库的设置隔离级别,MySQL默认的为ISOLATION_REPEATABLE_READ。
- ISOLATION_READ_UNCOMMITTED:读未提交,最低的隔离级别,一个事务可以读取另一个事务更新但未提交的数据。(会出现脏读、不可重复读、幻读);
- ISOLATION_READ_COMMITTED:读已提交,一个事务提交后才能被其他事务读取到(会出现不可重复读、幻读),Oracle、SQL server 的默认级别;
- ISOLATION_REPEATABLE_READ:可重复读,对同一字段的多次读取结果都是一致的,除非数据被本身事务所修改(会出现幻读),MySQL 的默认级别;
- ISOLATION_SERIALIZABLE:可串行化,最高的隔离级别,可以防止脏读、不可重复读、幻读。
|