> 文档中心 > 回忆当年面试的时候,面试官浅问了一下Spring【一年经验】

回忆当年面试的时候,面试官浅问了一下Spring【一年经验】


什么是Spring?

目的:解决企业应用开发的复杂性

功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能

范围:任何Java应用

Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架

IoC是一种设计思想,DI是IoC的一种实现

什么是IOC(控制反转)?

将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制 ,由 IoC 容器进行注入、组合对象。这样对象与对象之间是松耦合、便于测试、功能可复用(减少对象的创建和内存消耗),使得程序的整个体系结构可维护性、灵活性、扩展性变高。

Spring的IOC有三种注入方式 :构造器注入、setter方法注入、根据注解注入

构造器注入

public AccountServiceImpl(String name, Integer age, Date time) { this.name = name; this.age = age; this.time = time;    }                     

setter方法注入

public class Id {      private int id;      private String name;      public int getId() {   return id;      }      public void setId(int id) {   this.id = id;      }      public String getName() {   return name;      }      public void setName(String name) {   this.name = name;      }  }                       
什么是DI(依赖注入)?

IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的

什么是AOP(切面编程|面向切面)?

一般称为面向切面 ,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块

减少系统中的重复代码,降低了模块间的耦合度,提高系统的可维护性。可用于权限认证、日志、事务处理。

特点: 通过在编译期间、装载期间或运行期间实现在不修改源代码的情况下给程序动态添加功能的一种技术。通俗点说就是把可重用的功能提取出来,然后将这些通用功能在合适的时候织入到应用程序中;比如安全,日记记录,这些都是通用的功能,我们可以把它们提取出来,然后在程序执行的合适地方织入这些代码并执行它们,从而完成需要的功能并复用了这些功能。

Spring AOP中的动态代理主要有两种方式:

JDK动态代理和CGLIB动态代理

① JDK动态代理只提供接口的代理,不支持类的代理,要求被代理类实现接口JDK动态代理的核心是InvocationHandler接口和Proxy类**,在获取代理对象时,使用Proxy类来动态创建目标类的代理类(即最终真正的代理类,这个类继承自Proxy并实现了我们定义的接口),当代理对象调用真实对象的方法时, InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;

public class StarProxy implements InvocationHandler{    // 目标类,也就是被代理对象    private Object target; public void setTarget(Object target)    { this.target = target;    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable    { // 这里可以做增强 System.out.println("收钱");  Object result = method.invoke(target, args);  return result;    } // 生成代理类    public Object CreatProxyedObj()    { return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);    }    }

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

public class CglibProxy implements MethodInterceptor{    // 根据一个类型产生代理类,此方法不要求一定放在MethodInterceptor中    public Object CreatProxyedObj(Class clazz)    { Enhancer enhancer = new Enhancer();  enhancer.setSuperclass(clazz);  enhancer.setCallback(this);  return enhancer.create();    } @Override    public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable    { // 这里增强 System.out.println("收钱");  return arg3.invokeSuper(arg0, arg2);    } }
Spring通知(Advice)有哪些类型?

(1)前置通知(Before Advice):在连接点(Join point)之前执行的通知。

(2)后置通知(After Advice):当连接点退出的时候执行的通知(不论是正常返回还是异常退出)。

(3)环绕通知(Around Advice):包围一个连接点的通知,这是最强大的一种通知类型。 环绕通知可以在方法调用前后完成自定义的行为。它也可以选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。

(4)返回后通知(AfterReturning Advice):在连接点正常完成后执行的通知(如果连接点抛出异常,则不执行)

(5)抛出异常后通知(AfterThrowing advice):在方法抛出异常退出时执行的通知

Spring容器的启动流程:

(1)初始化Spring容器,注册内置的BeanPostProcessor的BeanDefinition到容器中:

(2)将配置类的BeanDefinition注册到容器中:

(3)调用refresh()方法刷新容器:

BeanFactory和ApplicationContext有什么区别?

BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。

​ (1) ApplicationContext接口作为BeanFactory的子类,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能譬如校验,国际化,监听,对Bean的管理功能比较多,一般使用ApplicationContext.

  • 继承MessageSource,因此支持国际化。

  • 资源文件访问,如URL和文件(ResourceLoader)。

  • 载入多个(有继承关系)上下文(即同时加载多个配置文件) ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。

  • 提供在监听器中注册bean的事件。

    (2) BeanFactroy采用的是延迟加载形式来注入Bean的,只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。

    ​ ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。

Spring Bean的生命周期?

1) 通过构造器或工厂方法创建 Bean 实例

  1. Bean 的属性设置值和对其他 Bean 的引用

3)将 Bean实例传递给 Bean 前置处理器的 postProcessBeforeInitialization 方法

4)调用 Bean 的初始化方法(init-method)将 Bean 实例传递给 Bean 后置处理器的postProcessAfterInitialization 方法,Bean 可以使用了 e

5)当容器关闭时, 调用 Bean 的销毁方法(destroy-method)

Spring中bean的作用域:

(1)singleton:默认作用域,单例bean,每个容器中只有一个bean的实例。

(2)prototype:为每一个bean请求创建一个实例。

(3)request:为每一个request请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。

(4)session:与request范围类似,同一个session会话共享一个实例,不同会话使用不同的实例。

(5)global-session:全局作用域,所有会话共享一个实例。如果想要声明让所有会话共享的存储变量的话,那么这全局变量需要存储在global-session中。

Spring基于xml注入bean的几种方式
  • set()方法注入;
  • 构造器注入:①通过index设置参数的位置;②通过type设置参数类型;
  • 静态工厂注入;
  • 实例工厂;

Spring的自动装配:

在spring中,使用autowire来配置自动装载模式,对象无需自己查找或创建与其关联的其他对象,由容器负责把需要相互协作的对象引用赋予各个对象。

(1)在Spring框架xml配置中共有5种自动装配:

no:默认的方式是不进行自动装配的,通过手工设置ref属性来进行装配bean。
byName:通过bean的名称进行自动装配,如果一个bean的 property 与另一bean 的name 相同,就进行自动装配。
byType:通过参数的数据类型进行自动装配。
constructor:利用构造函数进行装配,并且构造函数的参数通过byType进行装配。
autodetect:自动探测,如果有构造方法,通过 construct的方式自动装配,否则使用 byType的方式自动装配。

(2)基于注解的自动装配方式:

使用@Autowired、@Resource注解来自动装配指定的bean。在使用@Autowired注解之前需要在Spring配置文件进行配置,。在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean:

如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据;

如果查询的结果不止一个,那么@Autowired会根据名称来查找;

如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。

@Autowired可用于:构造函数、成员变量、Setter方法注:@Autowired和@Resource之间的区别:(1) @Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。(2) @Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。

Spring事务的实现方式和实现原理:

Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。Spring只提供统一事务管理接口,具体实现都是由各数据库自己实现,数据库事务的提交和回滚是通过binlog或者undo log实现的。Spring会在事务开始时,根据当前环境中设置的隔离级别,调整数据库隔离级别,由此保持一致。

(1)Spring事务的种类:

spring支持编程式事务管理和声明式事务管理两种方式:

①编程式事务管理使用TransactionTemplate。

②声明式事务管理建立在AOP之上的。其本质是通过AOP功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前启动一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

声明式事务最大的优点就是不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明或通过@Transactional注解的方式,便可以将事务规则应用到业务逻辑中,减少业务代码的污染。唯一不足地方是,最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。
(2)spring的事务传播机制:

spring事务的传播机制说的是,当多个事务同时存在的时候,spring如何处理这些事务的行为。事务传播机制实际上是使用简单的ThreadLocal实现的,所以,如果调用的方法是在新线程调用的,事务传播实际上是会失效的。

① PROPAGATION_REQUIRED:(默认传播行为)如果当前没有事务,就创建一个新事务;如果当前存在事务,就加入该事务。② PROPAGATION_REQUIRES_NEW:无论当前存不存在事务,都创建新事务进行执行。③ PROPAGATION_SUPPORTS:如果当前存在事务,就加入该事务;如果当前不存在事务,就以非事务执行。‘④ PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。⑤ PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则按REQUIRED属性执行。⑥ PROPAGATION_MANDATORY:如果当前存在事务,就加入该事务;如果当前不存在事务,就抛出异常。⑦ PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。

(3)Spring中的隔离级别:

① ISOLATION_DEFAULT:这是个 PlatfromTransactionManager 默认的隔离级别,使用数据库默认的事务隔离级别。② ISOLATION_READ_UNCOMMITTED:读未提交,允许事务在执行过程中,读取其他事务未提交的数据。③ ISOLATION_READ_COMMITTED:读已提交,允许事务在执行过程中,读取其他事务已经提交的数据。④ ISOLATION_REPEATABLE_READ:可重复读,在同一个事务内,任意时刻的查询结果都是一致的。⑤ ISOLATION_SERIALIZABLE:所有事务逐个依次执行。

Spring框架中有哪些不同类型的事件?

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

(1)上下文更新事件(ContextRefreshedEvent):在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发。

(2)上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。

(3)上下文停止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。

(4)上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。

(5)请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结束触发该事件。

如果一个bean实现了ApplicationListener接口,当一个ApplicationEvent 被发布以后,bean会自动被通知。