1 Spring核心组件

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

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

1.1 Spring 简介

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

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

1.1.1 IOC和DI的支持

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

1.1.2 AOP编程支持

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

1.1.3 声明事物的支持

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

1.1.4 方便程序的测试

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

1.1.5 粘合剂功能

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

降低JavaEE API的使用难度

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

1.2 Spring组件

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

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

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 相关信息,类信息、属性值信息。

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

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 的底层源码:

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

2.3 AOP

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

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

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

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

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

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

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

前置通知 (@Before)

返回通知 (@AfterReturning)

异常通知 (@AfterThrowing)

后置通知 (@After)

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

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 {@Overridepublic void makeNoodle() {System.out.println("make noodle");}@Overridepublic 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;}// 第一个参数:代理对象.一般不使用;第二个参数:需要增强的方法;第三个参数:方法中的参数@Overridepublic 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.创建EnhancerEnhancer enhancer = new Enhancer();//2.传递目标对象的classenhancer.setSuperclass(target.getClass());//3.设置回调操作enhancer.setCallback(this);return enhancer.create();}//参数一:代理对象;参数二:需要增强的方法;参数三:需要增强方法的参数;参数四:需要增强的方法的代理@Overridepublic 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 对比

Spring AOP AspectJ
在纯Java中实现 用Java编程语言扩展实现
编译器Javac 一般需要ajc
只可运行织入 支持编译时、编译后、加载时织入
仅支持方法级编织 可编织字段、方法、构造函数、静态初始值等
只可在spring管理的Bean上实现 可在所有域对象实现
比AspectJ慢很多 速度比AOP快很多
医学系使用 比AOP更复杂
代理由目标对象创建、切面应用在代理上 执行程序前,各方面直接织入代码

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个部分。

如果仅仅是实例化跟依赖注入当然简单,问题是如果我们要完成自定义的要求,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, 容器关闭时不会调用销毁方法。

转载自今日头条

Spring学习(上)相关推荐

  1. 【Spring学习笔记-MVC-13.2】Spring MVC之多文件上传

    作者:ssslinppp       1. 摘要 前篇文章讲解了单文件上传<[Spring学习笔记-MVC-13]Spring MVC之文件上传>http://www.cnblogs.co ...

  2. spring学习12 -Spring 框架模块以及面试常见问题注解等

    以下为spring常见面试问题: 1.Spring 框架中都用到了哪些设计模式? Spring框架中使用到了大量的设计模式,下面列举了比较有代表性的: 代理模式-在AOP和remoting中被用的比较 ...

  3. 送给 Java 程序员的 Spring 学习指南

    https://www.infoq.cn/article/Ad-8ghcGGCNU572U6oEX 学习 Spring 的基础要求 Spring 官网首页是这么介绍自己的--"Spring: ...

  4. Spring学习总结三

    Spring框架JdbcTemplate与事务 Spring学习总结三 0.需要的jar包 1.JdbcTemplate模板 1.1.JdbcTemplateAPI的操作 1.1.1.User类 1. ...

  5. Spring学习总结二

    Spring框架的代理与AOP.AspectJ Spring学习总结二 0.在理解什么是AOP之前的一些话 1.什么是AOP 2.AOP的重要概念 3.代理模式 3.1.静态代理 3.2.动态代理 3 ...

  6. Spring学习总结一

    Spring框架IoC与DI思想及应用 Spring学习总结一 1.Spring是什么 2.Spring的优点 2.1.关键概念 2.2.Spring的优点 3.Spring的架构图 3.1.核心容器 ...

  7. Spring学习(八)AOP详解

    本文借鉴:Spring学习 一.一个例子 在上面的例子中,包租婆的核心业务就是签合同,收房租,那么这就够了,灰色框起来的部分都是重复且边缘的事,交给中介商就好了,这就是 AOP 的一个思想:让关注点代 ...

  8. Spring学习(七)bean装配详解之 【通过注解装配 Bean】【自动装配的歧义解决】...

    本文借鉴:Spring学习,@Bean 的用法(特此感谢!) 自动装配 1.歧义性 我们知道用@Autowired可以对bean进行注入(按照type注入),但如果有两个相同类型的bean在IOC容器 ...

  9. Spring学习(六)bean装配详解之 【通过注解装配 Bean】【基础配置方式】

    本文借鉴:Spring学习(特此感谢!) 通过注解装配 Bean 1.前言 优势 1.可以减少 XML 的配置,当配置项多的时候,XML配置过多会导致项目臃肿难以维护 2.功能更加强大,既能实现 XM ...

最新文章

  1. 计算机学院会会,学生分会——计算机学院学生会
  2. Lesson 2 Installing the Oracle Database Software
  3. 1.5 字符串大小写转换(toLowerCase()和toUpperCase())
  4. OpenCASCADE:简介
  5. chattr和lsattr命令的使用(对于root用户也无法修改删除的操作问题)
  6. 【全网最全】一文搞定 Linux 压缩、解压哪些事儿
  7. 分布式任务队列:Celery使用记录
  8. NP、OSPF 缺省路由
  9. 使用Micrisoft.net设计方案 第三章Web表示模式 Web模式集群详细介绍 Observer(观察器)...
  10. 健康管理系统案列/APP/小程序/网站
  11. 联想启动Kind City项目:交互式全球合作鼓励同理心,共创建立于善意之上的未来
  12. 机器学习之随机森林算法
  13. iPad/iPhone与电脑共享文件
  14. react-router-dom ^6.0.2使用过程中报错 Error: A <Route> is only ever to be used as the child of <Routes>
  15. 可调背光板(二)__DW01FA锂电池保护IC电路
  16. JavaScript之 高性能读书笔记
  17. 我对移动支付的看法_对移动支付的看法作文_作文写作问答 - 归教作文网
  18. 各种靠谱教程总结(后续更新)
  19. php文件怎么打开?怎么打开PHP文件?
  20. android 360replugin,360插件框架RePlugin的坑

热门文章

  1. python解包exe_Pyinstaller打包的EXE之解包
  2. 针对严峻的网络安全环境,公司就当如何应对?
  3. PHP技能架构思维导图(高清大图)详解
  4. MD编辑器就是不告诉你之表情
  5. 【ROS小车课设】虚拟机端编译riki工作空间问题解决
  6. ios App支付宝开放平台申请支付宝支付具体步骤
  7. 【视频课】10大真实金融量化交易案例,20多小时Python进阶课!
  8. PHP报错:Classes\\PHPExcel\\Cell.php Line(594) Invalid cell coordinate ESIGN1
  9. wps里面函数怎么使用_WPS中函数的使用方法 - 卡饭网
  10. 泰勒求三角函数近似值