分离 spring_面试官:关于Spring就问这13个点(上)

1 Spring核心组件

一句话概括:Spring是一个轻量级、非入侵式的控制反转(IoC)和面向切面(AOP)的框架

e01e245260a8a02efc16be649852817d.png

PS :目前Java 开发的标配是 Spring5 + Spring Boot 2 + JDK 8

1.1 Spring 简介

现如今的Java开发又简称为Spring开发,Spring是Java目前第一大框架,它让现有的技术更容易使用,促进良好的编程习惯,大大简化应用程序的开发。

因为你想啊,如果我们想实现某个功能,代码量一般都是固定的,要么全自己写,要么用已有的优秀框架,而Spring不仅已经给我们提供了各种优秀组件,还提供了良好的代码组织逻辑跟业务开发流程规范框架,它的主要优点如下:

  1. IOCDI的支持

Spring就是一个大工厂容器,可以将所有对象创建和依赖关系维护,交给Spring管理,Spring工厂是用于生成Bean,并且管理Bean的生命周期,实现高内聚低耦合的设计理念。

  1. AOP编程的支持

Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能。

  1. 声明式事务的支持

只需要通过配置就可以完成对事务的管理,而无需手动编程,以前重复的一些JDBC操作,统统不需我们再写了。

  1. 方便程序的测试

Spring对Junit4提供支持,可以通过注解方便的测试Spring程序。

  1. 粘合剂功能

方便集成各种优秀框架,Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis、Quartz等)的直接支持。

  1. 降低 JavaEE API的使用难度

Spring 对 JavaEE 开发中非常难用的一些API(JDBC、JavaMail、远程调用等)都提供了封装,这些API的提供使得应用难度大大降低。

1.2 Spring组件

Spring框架是分模块存在,除了最核心的Spring Core Container是必要模块之外,其他模块都是可选,大约有20多个模块。

fd2a57ba68ce0a61d7e36f313bad69f3.png

Spring框架 有很多特性,这些特性由7个定义良好的模块构成。

ffe409a83a6cbe77074e4b30266ab0d5.png

Spring Core:Spring核心,它是框架最基础的部分,提供IOC和依赖注入DI特性。

Spring Context:Spring上下文容器,它是 BeanFactory 功能加强的一个子接口。

Spring Web:它提供Web应用开发的支持。

Spring MVC:它针对Web应用中MVC思想的实现。

Spring DAO:提供对JDBC抽象层,简化了JDBC编码,同时,编码更具有健壮性。

Spring ORM:它支持用于流行的ORM框架的整合,比如:Spring + Hibernate、Spring + iBatis、Spring + JDO的整合等。

Spring AOP:即面向切面编程,它提供了与AOP联盟兼容的编程实现。

2 IOC 跟 AOP

提到Spring永远离不开的两个话题就是 IOCAOP,这是Spring的两大核心知识点,初学者不要被IOC、AOP、Aspect、Pointcut、Advisor这些术语吓着了,这些术语都是无聊的人为了发论文硬造的。

2.1 IOC

Java是个面向对象的编程语言,一般一个应用程序是由一组对象通过相互协作开发出的业务逻辑组成的,那么如何管理这些对象呢?抽象工厂、工厂方法设计模式可以帮我们创建对象,生成器模式帮我们处理对象间的依赖关系,可是这些又需要我们创建另一些工厂类、生成器类,我们又要而外管理这些类,增加了我们的负担。如果程序在对象需要的时候,就能自动管理对象的声明周期,不用我们自己再去管理Bean的声明周期了,这样不就实现解耦了么。

Spring提出了一种思想:由Spring来负责控制对象的生命周期和对象间的关系。所有的类都会在Spring容器中登记,告诉Spring这这个类是什么,需要什么,然后Spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的Bean。所有的类的创建、销毁都由Spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是Spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转(Inversion of Controller),也可以叫依赖注入 DI(Dependency Injection)。

知道大致思想后其实可以如果尝试 自己实现IOC 的话就会发现核心就是 反射 + XML解析/注解解析

读取 XML 获取 bean 相关信息,类信息、属性值信息。

通过反射机制获取到目标类的构造函数,调用构造函数,再给对象赋值。

如果想自己跟下源码你会发现IOC的源码入口是refresh(),里面包含13个提供不同功能的函数,具体流程较复杂,公众号回复 IOC 获得原图。

2.2 Context

IOC 容器只是提供一个管理对象的空间而已,如何向容器中放入我们需要容器代为管理的对象呢?这就涉及到Spring的应用上下文Context

应用上下文Context :

基于 Core 和 Beans,提供了大量的扩展,包括国际化操作(基于 JDK )、资源加载(基于 JDK properties)、数据校验(Spring 自己封装的数据校验机制)、数据绑定(Spring 特有,HTTP 请求中的参数直接映射称 POJO)、类型转换,ApplicationContext 接口是 Context 的核心,可以理解为Bean的上下文或背景信息

可以简单的理解 应用上下文 是Spring容器的一种抽象化表述,而我们常见的ApplicationContext 本质上就是一个维护Bean定义以及对象之间协作关系的高级接口。Spring 框架本身就提供了很多个容器的实现,大概分为两种类型:

  1. 一种是不常用的BeanFactory,这是最简单的容器,只能提供基本的DI功能。
  2. 另外一种就是继承了BeanFactory后派生而来的应用上下文,其抽象接口也就是我们上面提到的的ApplicationContext,它能提供更多企业级的服务,例如解析配置文本信息等等,这也是应用上下文实例对象最常见的应用场景。有了上下文对象,我们就能向容器注册需要Spring管理的对象了。对于上下文抽象接口,Spring也为我们提供了多种类型的容器实现,供我们在不同的应用场景选择。

AnnotationConfigApplicationContext:从一个或多个基于java的配置类中加载上下文定义,适用于java注解的方式。

ClassPathXmlApplicationContext:从类路径下的一个或多个xml配置文件中加载上下文定义,适用于xml配置的方式。

FileSystemXmlApplicationContext:从文件系统下的一个或多个xml配置文件中加载上下文定义,也就是说系统盘符中加载xml配置文件。

AnnotationConfigWebApplicationContext:专门为web应用准备的,适用于注解方式。

XmlWebApplicationContext:从web应用下的一个或多个xml配置文件加载上下文定义,适用于xml配置方式。

工作中通过XML配置或注解 将需要管理的Bean跟Bean之间的协作关系配置好,然后利用应用上下文对象Context加载进Spring容器,容器就能为你的程序提供你想要的对象管理服务了。比如追踪下 ClassPathXmlApplicationContext 的底层源码:

56fc1926100ea730b64ff82a111ddae3.png

可以看到一个XML文件的解析就可以上延8层,可见Spring容器为了实现IOC进行了全面性的考虑。

2.3 AOP

如果想编码实现计算器功能,我们的目标是实现加减乘除的运算,可是如何在每种运算前后进行打印日志跟数字合规的校验呢。

把日志记录和数据校验可重用的功能模块分离出来,然后在程序的执行的合适的地方动态地植入这些代码并执行。这样就简化了代码的书写。

业务逻辑代码中没有参和通用逻辑的代码,业务模块更简洁,只包含核心业务代码。实现了业务逻辑和通用逻辑的代码分离,便于维护和升级,降低了业务逻辑和通用逻辑的耦合性。

7fc73890dcc419c9facdf46e6da3c769.png

思路:代码最终是要加载到内存中实现new出对象,那么如果我们把可重用的功能提取出来,然后将这些通用功能在内存中通过入的方式实现构造出一个新的目标对象不就OK了么!

Spring AOP(Aspect Oriented Programming) 恰恰提供从另一个角度来考虑程序结构以完善面向对象编程,如果说依赖注入的目的是让相互协作的组件保持一种较为松散的耦合状态的话,AOP则是将遍布应用各处的功能分离出来形成可重用的组件。在编译期间、装载期间或运行期间实现在不修改源代码的情况下给程序动态添加功能的一种技术。从而实现对业务逻辑的隔离,提高代码的模块化能力。

AOP 的核心其实就是动态代理,如果是实现了接口的话就会使用 JDK 动态代理,否则使用 CGLIB 代理,主要应用于处理一些具有横切性质的系统级服务,如日志收集、事务管理、安全检查、缓存、对象池管理等。

Spring主要提供了 Aspect 切面、JoinPoint 连接点、PointCut 切入点、Advice 增强等实现方式,AOP一般有5种环绕方式:

前置通知 (@Before)

返回通知 (@AfterReturning)

异常通知 (@AfterThrowing)

后置通知 (@After)

环绕通知 (@Around) :(优先级最高)

8608bfc9f3471c8fd1cca36968dc800f.png

PS :多个切面的情况下,可以通过@Order指定先后顺序,数字越小,优先级越高。

3 JDK 动态代理和 CGLIB 代理区别

JDK 动态代理 与 CGLib动态代理均是实现Spring AOP的基础,它们的实现方式有所不同。

3.1 JDK动态代理

特点

Interface:对于JDK动态代理,业务类需要一个Interface。

Proxy:Proxy类是动态产生的,这个类在调用 Proxy.newProxyInstance() 方法之后,产生一个Proxy类的实例。实际上,这个Proxy类也是存在的,不仅仅是类的实例,这个Proxy类可以保存在硬盘上。

Method:对于业务委托类的每个方法,现在Proxy类里面都不用静态显示出来。

InvocationHandler:这个类在业务委托类执行时,会先调用invoke方法。invoke方法在执行想要的代理操作,可以实现对业务方法的再包装

总结:

JDK动态代理类实现了InvocationHandler接口,重写的invoke方法。

JDK动态代理的基础是反射机制(method.invoke(对象,参数))Proxy.newProxyInstance()

3.2 CGLib动态代理

特点

使用字节码处理框架ASM,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。

CGLib创建的动态代理对象性能比JDK创建的动态代理对象的性能高不少,但是CGLib在创建代理对象时所花费的时间却比JDK多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些。同时,由于CGLib由于是采用动态创建子类的方法,对于final方法,无法进行代理。

注意

JDK的动态代理只可以为接口去完成操作,而 CGlib 它既可以为没有实现接口的类去做代理,也可以为实现接口的类去做代理。

3.3 代码实现部分

公共代码

//接口类public interface FoodService {    public void makeNoodle();    public void makeChicken();}
//实现接口public class FoodServiceImpl implements FoodService {    @Override    public void makeNoodle() {        System.out.println("make noodle");    }    @Override    public void makeChicken() {        System.out.println("make Chicken");    }}

jdk动态代理代码

import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class JDKProxyFactory implements InvocationHandler{ private Object target; public JDKProxyFactory(Object target) {  super();  this.target = target; } // 创建代理对象 public Object createProxy() {  // 1.得到目标对象的类加载器  ClassLoader classLoader = target.getClass().getClassLoader();  // 2.得到目标对象的实现接口  Class>[] interfaces = target.getClass().getInterfaces();  // 3.第三个参数需要一个实现invocationHandler接口的对象  Object newProxyInstance = Proxy.newProxyInstance(classLoader, interfaces, this);  return newProxyInstance; } // 第一个参数:代理对象.一般不使用;第二个参数:需要增强的方法;第三个参数:方法中的参数 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  System.out.println("这是增强方法前......");  Object invoke = method.invoke(target, args);  System.out.println("这是增强方法后......");  return invoke; } public static void main(String[] args) {  // 1.创建对象  FoodServiceImpl foodService = new FoodServiceImpl();  // 2.创建代理对象  JDKProxyFactory proxy = new JDKProxyFactory(foodService);  // 3.调用代理对象的增强方法,得到增强后的对象  FoodService createProxy = (FoodService) proxy.createProxy();  createProxy.makeChicken(); }}

Cglib动态代理代码

import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class CglibProxyFactory implements MethodInterceptor{    //得到目标对象    private Object target;    //使用构造方法传递目标对象    public CglibProxyFactory(Object target) {        super();        this.target = target;    }    //创建代理对象    public Object createProxy(){        //1.创建Enhancer        Enhancer enhancer = new Enhancer();        //2.传递目标对象的class        enhancer.setSuperclass(target.getClass());        //3.设置回调操作        enhancer.setCallback(this);        return enhancer.create();    }    //参数一:代理对象;参数二:需要增强的方法;参数三:需要增强方法的参数;参数四:需要增强的方法的代理 @Override    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {        System.out.println("这是增强方法前......");        Object invoke = methodProxy.invoke(target, args);        System.out.println("这是增强方法后......");        return invoke;    }    public static void main(String[] args) {        // 1.创建对象        FoodServiceImpl foodService = new FoodServiceImpl();        // 2.创建代理对象        CglibProxyFactory proxy = new CglibProxyFactory(foodService);        // 3.调用代理对象的增强方法,得到增强后的对象        FoodService createProxy = (FoodService) proxy.createProxy();        createProxy.makeChicken();    }}

4. Spring AOP 和 AspectJ AOP区别

4.1 Spring AOP

Spring AOP 属于运行时增强,主要具有如下特点:

  1. 基于动态代理来实现,默认如果使用接口的,用JDK提供的动态代理实现,如果是方法则使用CGLIB实现
  2. Spring AOP 需要依赖 IOC 容器来管理,并且只能作用于Spring容器,使用纯Java代码实现
  3. 在性能上,由于Spring AOP是基于动态代理来实现的,在容器启动时需要生成代理实例,在方法调用上也会增加栈的深度,使得Spring AOP的性能不如AspectJ的那么好。
  4. Spring AOP致力于解决企业级开发中最普遍的AOP(方法织入)。

4.2 AspectJ

AspectJ 是一个易用的功能强大的AOP框架,属于编译时增强, 可以单独使用,也可以整合到其它框架中,是 AOP 编程的完全解决方案。AspectJ需要用到单独的编译器ajc。

AspectJ属于静态织入,通过修改代码来实现,在实际运行之前就完成了织入,所以说它生成的类是没有额外运行时开销的,一般有如下几个织入的时机:

  1. 编译期织入(Compile-time weaving):如类 A 使用 AspectJ 添加了一个属性,类 B 引用了它,这个场景就需要编译期的时候就进行织入,否则没法编译类 B。
  2. 编译后织入(Post-compile weaving):也就是已经生成了 .class 文件,或已经打成 jar 包了,这种情况我们需要增强处理的话,就要用到编译后织入。
  3. 类加载后织入(Load-time weaving):指的是在加载类的时候进行织入,要实现这个时期的织入,有几种常见的方法

4.3 对比

cca83aaabe5256cbc1afbc81e50f2f46.png

5. BeanFactory 和 FactoryBean

5.1 BeanFactory

  1. BeanFactory 以 Factory 结尾,表示它是一个工厂类(接口),BeanFacotry 是 Spring 中比较原始的Factory。
  2. BeanFactory 无法支持 Spring 的许多插件,如AOP功能、Web应用等。ApplicationContext 接口由BeanFactory接口派生而来,提供了国际化访问、事件传播等多个功能。
  3. BeanFactory 是 IOC 容器的核心,负责生产和管理 Bean 对象。

5.2 FactoryBean

  1. FactoryBean 以 Bean 结尾,表示它是一个Bean。
  2. FactoryBean 是工厂类接口,用户可以通过实现该接口定制实例化 Bean 的逻辑。FactoryBean 接口对于 Spring 框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现。
  3. 当在IOC容器中的Bean实现了 FactoryBean 后,通过getBean(String BeanName)获取到的 Bean 对象并不是 FactoryBean 的实现类对象,而是这个实现类中的 getObject() 方法返回的对象。要想获取FactoryBean的实现类,就要 getBean(String &BeanName),在BeanName之前加上 &

6. Spring生命周期

Spring IOC 初始化跟销毁 Bean 的过程大致分为Bean定义、Bean初始化、Bean的生存期 跟 Bean的销毁4个部分。

2041346ac41bca00b8a58e957279441d.png

如果仅仅是实例化跟依赖注入当然简单,问题是如果我们要完成自定义的要求,Spring提供了一系列接口跟配置来完成 Bean 的初始化过程,看下整个IOC容器初始化Bean的流程。

一般情况下我们自定义Bean的初始化跟销毁方法下面三种:

  1. 通过 XML 或者 @Bean配置

通过xml或者@Bean(initMethod="init", destroyMethod="destory")来实现。

  1. 使用 JSR250 规则定义的(java规范)两个注解来实现

@PostConstruct: 在Bean创建完成,且属于赋值完成后进行初始化,属于JDK规范的注解。

@PreDestroy: 在bean将被移除之前进行通知,在容器销毁之前进行清理工作。

提示:JSR是由JDK提供的一组规范。

  1. 通过继承实现类方法

实现InitializingBean接口的afterPropertiesSet()方法,当beanFactory创建好对象,且把bean所有属性设置好之后会调这个方法,相当于初始化方法。

实现DisposableBean的destory()方法,当bean销毁时会把单实例bean进行销毁

对于单实例的bean,可以正常调用初始化和销毁方法。对于多实例的bean,容器只负责调用时候初始化,但不会管理bean, 容器关闭时不会调用销毁方法。

e085b738476ea3dc3abd4733dda89272.png

篇幅限制,接下文:面试官:关于Spring就问这13个点(下)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值