欢迎查看Java开发之上帝之眼系列教程,如果您正在为Java后端庞大的体系所困扰,如果您正在为各种繁出不穷的技术和各种框架所迷茫,那么本系列文章将带您窥探Java庞大的体系。本系列教程希望您能站在上帝的角度去观察(了解)Java体系。使Java的各种后端技术在你心中模块化;让你在工作中能将Java各个技术了然于心;能够即插即用。本章我们来一起了解你的老朋友Spring框架。
 
 
Spring这个词对于开发者想必不会陌生,可能你每天都在使用Spring,享受着Spring生态提供的服务,理所当然的用着SpringIOC和SpringAOP去实现老板交给你的功能  ,唔  它就是这样使用的(类声明为Bean组件,然后注入),没错 能完成老板任务,没毛病。如果向你提问什么是Spring,Spring有什么核心功能呢,你会想:这太简单了,Spring就是框架嘛,Spring核心功能就是IOC和AOP,So Easy!!可是你真的了解Spring吗?带着这个疑问让我们一起来重新了解一下Spring!本文所有示例源码下载
 

Spring的起源和根本使命

Spring是一个开源框架,最早由Rod Johnson创建,是为了解决企业级应用开发的复杂性而创建的。很多框架都宣称在某些方面针对Java开发做出了简化,但是Spring的目标是致力于全方位简化Java开发,这也是Spring的根本使命 "简化Java开发"

Spring如何简化Java开发

为了降低Java开发的复杂性,Spring采取了以下4种关键策略,请务必牢记下面4中策略于心,这样会帮助你更深的理解Spring。下面我们来逐一解读每一条策略,你就能明白Spring是如何简化Java开发的。
  • 基于POJO的轻量级和最小侵入性编程
  • 通过依赖注入和面向接口实现松耦合
  • 基于切面和惯例进行声明式编程
  • 通过切面和模板减少样版式代码

1. 基于POJO的轻量级和最小侵入性编程

Spring竭力避免因自身的API而弄乱你的代码。Spring不会强迫你实现Spring规范的接口或继承Spring规范的类。相反,在一个基于Spring构建的应用中,应用中的类没有任何痕迹表明你使用了Spring。最坏的情况就是你在应用的类中发现了Spring的相关注解,但它依然是POJO,下面我们来看一个在Spring中的一个POJO例子。example1实例源码下载
  1. /**
  2. * @Author:jimisun
  3. * @Description:
  4. * @Date:Created in 20:32 2018-09-26
  5. * @Modified By:
  6. */
  7. public class HelloSpringBean {
  8. public String sayHello() {
  9. return "Hello Spring!!!";
  10. }
  11. }

你可以看到,这就是一个POJO(简单的JAVA类),没有任何地方表明它是Spring组件,Spring非侵入式编程模型意味着这个类在Spring应用和非Spring应用中都可以发挥同样的作用。尽管看起来很简单;但Spring通过IOC(Inversion of Control)管理这个POJO,然后通过DI(Dependency Inject)来注入他们,这个POJO就变的魔力十足;那么DI是如何帮助应用对象彼此之间保持松耦合的呢?

2. 通过依赖注入和面向接口实现松耦合

来我们一起来看一下一个简单的POJO  example2实例源码下载
  1. /**
  2. * @Author:jimisun
  3. * @Description:
  4. * @Date:Created in 07:44 2018-09-27
  5. * @Modified By:
  6. */
  7. public class BeautifulGirl implements Gril {
  8.  
  9. private EatAction eat;
  10. public BeautifulGirl() {
  11. this.eat = new EatAction();
  12. }
  13. @Override
  14. public void action() {
  15. eat.action();
  16. }
  17. }

在BeautifulGirl(可爱的小女孩)这个类中,在构造方法中创建一个EatAction(吃饭动作)。这样就极大限制了BeautifulGirl(可爱的小女孩)的能力;如果现在小女孩需要去玩耍呢?或者需要去睡觉呢?真是太抱歉了,BeautifulGirl(可爱的小女孩)只会吃东西这个动作。这是什么原因呢?这就是BeautifulGirl(可爱的小女孩)和EatAction(吃饭动作)这两个类紧紧耦合在了一起!紧密耦合同时会造成代码的难以测试,难以服用,难以理解,并且典型地表现出"打地鼠“式的Bug特性(修复一个Bug,将会出现一个或多个新Bug),所以我们可以知道耦合是必须的,但必须谨慎管理耦合,但是怎么才算是谨慎处理耦合关系呢?

上图所示,DI(依赖注入)会将所依赖关系自动交给目标对象,而不是让对象本身创建所依赖对象。对象的依赖关系将由系统中负责协调个对象依赖关系的第三方组件(类似Spring)在创建对象的时候设定。现在我们明白了通过DI依赖注入可以将对象之间依赖关系轻松解耦,那么我们来看一下简单的实例吧。 example3实例源码下载
  1. /**
  2. * @Author:jimisun
  3. * @Description:
  4. * @Date:Created in 07:44 2018-09-27
  5. * @Modified By:
  6. */
  7. public class BeautifulGirl implements Gril {
  8.  
  9. private Action action;
  10.  
  11. public BeautifulGirl(Action action) {
  12. this.action = action;
  13. }
  14.  
  15. @Override
  16. public void action() {
  17. action.action();
  18. }
  19. }

从上面实例代码中可以看到BeautifulGirl本身并没有创建任何的动作,而是通过构造方法传入一个实现了Action(动作)接口的实现类即可,也就是说BeautifulGirl可以完成任意实现了Action接口的动作(睡觉啦...玩耍啦...旅行啦....)。这里的要点是BeautifulGirl没有与任何特定的Action发生耦合。BeautifulGirl只需要的是一个实现Action接口就行,对象本身只是通过接口(而非具体实现或初始化过程)来表明依赖关系,那么这种依赖就能够在BeautifulGirl不知情的情况下替换不同的具体动作。好了我们现在明白了DI进行依关系解耦的原理了,下面我们看一下如何在Spring中应用DI。example4实例源码下载

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
  5.  
  6. <bean class="com.jimisun.spring.example4.BeautifulGirl" id="beautifulGirl">
  7. <constructor-arg ref="action"/>
  8. </bean>
  9.  
  10. <bean class="com.jimisun.spring.example4.SleepAction" id="action"></bean>
  11.  
  12. </beans>
  1. /**
  2. * @Author:jimisun
  3. * @Description:
  4. * @Date:Created in 07:53 2018-09-27
  5. * @Modified By:
  6. */
  7. public class Main {
  8. public static void main(String[] args) {
  9. ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("Spring.xml");
  10. BeautifulGirl beautifulGirl = (BeautifulGirl) context.getBean("beautifulGirl");
  11. beautifulGirl.action();
  12. context.close();
  13. }
  14. }

这样执行Main方法,从Context中获取BeautifulGirl实例执行action方法。当然Spring提供了基于Java的配置,可作为XML配置文件的代替方案example5实例源码下载

  1. /**
  2. * @Author:jimisun
  3. * @Description:
  4. * @Date:Created in 08:40 2018-09-27
  5. * @Modified By:
  6. */
  7. @Configuration
  8. public class SpringConfig {
  9.  
  10. @Bean
  11. public SleepAction sleepAction() {
  12. return new SleepAction();
  13. }
  14.  
  15. @Bean
  16. public BeautifulGirl beautifulGirl() {
  17. return new BeautifulGirl(sleepAction());
  18. }
  19. }
只不过Spring的上下文的实现应该使用AnnotationConfigApplicationContext进行加载配置

  1. /**
  2. * @Author:jimisun
  3. * @Description:
  4. * @Date:Created in 07:53 2018-09-27
  5. * @Modified By:
  6. */
  7. public class Main {
  8. public static void main(String[] args) {
  9. ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
  10. SleepAction action = applicationContext.getBean(SleepAction.class);
  11. action.action();
  12. }
  13. }

3. 基于切面和惯例进行声明式编程

现在我们已经明白DI能够将互相协作的软件保持松耦合,而面向切面编程(aspect-oriented-programming,AOP)可以将遍布应用各处的功能分离出来形成可从用的组件。我们仍然使用上面的例子,BeautifulGirl随心所欲的执行各种动作真的没问题吗?如果可爱的小女孩执行的动作是去河边玩耍,吃一些危险的东西呢?那么我们势必需要一个家长(Parent类)进行监督小女孩的动作是否是安全的,下面我们来看一下Parent类。example6实例源码下载
  1. /**
  2. * @Author:jimisun
  3. * @Description:
  4. * @Date:Created in 09:32 2018-09-27
  5. * @Modified By:
  6. */
  7. public class Parent {
  8. public void check() {
  9. System.out.println("检查动作是否安全.......");
  10. }
  11. }

非常简单!Parent(家长类)只有一个方法就是check,那么现在就让Parent对BeautifulGirl的执行动作进行检查吧。

  1. <bean class="com.jimisun.spring.example6.BeautifulGirl" id="beautifulGirl">
  2. <constructor-arg ref="action"/>
  3. <constructor-arg ref="parent"/>
  4. </bean>
  5.  
  6. <bean class="com.jimisun.spring.example6.SleepAction" id="action"></bean>
  7. <bean class="com.jimisun.spring.example6.Parent" id="parent"></bean>
  8. </beans>
  1. /**
  2. * @Author:jimisun
  3. * @Description:
  4. * @Date:Created in 07:44 2018-09-27
  5. * @Modified By:
  6. */
  7. public class BeautifulGirl implements Girl {
  8.  
  9. private Action action;
  10.  
  11. private Parent parent;
  12.  
  13. public BeautifulGirl(Action action, Parent parent) {
  14. this.action = action;
  15. this.parent = parent;
  16. }
  17.  
  18. @Override
  19. public void action() {
  20. parent.check();
  21. action.action();
  22. }
  23. }
我们非常聪明,使用了DI将Parent类注入到了BeautifulGirl类中,在BeautifulGirl执行action动作之前执行 parent.check()方法,这样我们要的效果就达到了。等等....好像有什么不对的地方?
  • 管理Parent家长的check动作真的是美丽的小女孩的职责吗?
  • 将Parent家长注入到美丽的小女孩类中不是将代码复杂化了吗?
  • 我们需不需要一个不需要家长注入的美丽的小女孩呢?
  • 如果注入的Parent为NULL我们是否应该在美丽的小女孩中进行校验呢?
真的是太糟糕了,注入一个Parent(家长)将会产生那么多的问题,为什么呢?因为Parent类的业务于BeautifulGirl无关;BeautifulGirl将会承担Parent类的职责。
 
在一个项目中,很多系统服务都会分布到各个业务模块中,这使得业务模块的逻辑变得十分复杂,而使用AOP能够使这些服务模块化,并以声明式的方式将他们应用到他们所需要影响的组件中,使得这些组件将会具有更高的内聚性并且更加关注自身的业务逻辑,完全不需要了解系统服务所带来的复杂性。
下面我们使用SpringAOP对上面代码进行改造  example7实例源码下载
  1.   <!--声明Bean-->
  2. <bean class="com.jimisun.spring.example7.Parent" id="parent"></bean>
  3.  
  4. <!--声明切面-->
  5. <aop:config>
  6. <aop:aspect ref="parent">
  7. <aop:pointcut id="girlAction" expression="execution(* com.jimisun.spring.example7.Action.*(..))"/>
  8. <aop:before pointcut-ref="girlAction" method="check"/>
  9. </aop:aspect>
  10. </aop:config>
  1. /**
  2. * @Author:jimisun
  3. * @Description:
  4. * @Date:Created in 07:44 2018-09-27
  5. * @Modified By:
  6. */
  7. public class BeautifulGirl implements Girl {
  8.  
  9. private Action action;
  10.  
  11. public BeautifulGirl(Action action) {
  12. this.action = action;
  13. }
  14.  
  15. @Override
  16. public void girlAction() {
  17. action.action();
  18. }
  19. }
我们只需要在Spring配置文件中声明Parent为Spring Bean,然后将配置在SpringAOP中即可,我们可以发现BeautifulGirl类中没有发现任何关于Parent的信息,这就是AOP的魅力所在——以声明的方式将组件应用到他们所需要影响的组件中!
 

4. 通过切面和模板减少样版式代码

在通过JavaAPI进行编程,我们经常会编写很多重复的代码例如JDBC访问数据库,JMS,JNDI等场景下会重复编写大量的样版式代码,而Spring皆在通过模板封装来消除模板式代码,例如Spring针对JDBC访问数据库进行封装的JdbcTemplate等,这里就是Spring使用封装的模板来减少JavaAPI样版式代码。example8实例源码下载

  1. /**
  2. * @Author:jimisun
  3. * @Description:
  4. * @Date:Created in 11:13 2018-09-27
  5. * @Modified By:
  6. */
  7. public class Main {
  8. public static void main(String[] args) {
  9. JdbcTemplate jdbcTemplate = new JdbcTemplate();
  10. jdbcTemplate.execute("select * from user");
  11. }
  12. }

 
 
非常感谢大家耐心能看到这里,在本文中我尽可能地详细阐述Spring使如何简化Java开发?遵循哪些策略?以及为什么遵循的好处。
 
 
 

本文资料来源:

 

勘误&感谢

  本系列文章资料来源很多出自于互联网和在下本身的见解,受限于个人技术能力水平和其他相关知识的限制,相关见解错误或者资料引用错误请各位帮助留言校正!引用资料多来自于互联网,在下在引用前会遵循各位前辈或者博主的引用说明表示感谢,但互联网资料多是转发再转发或存在遗漏请原作者内信联系指正。

 

重新来认识你的老朋友Spring框架的更多相关文章

  1. Spring框架概述

    Spring是最流行的Java企业级应用开发框架,全球数以百万的开发者在使用Spring框架创建高性能.易测试.可重用的代码. Spring框架的核心特性可以应用于任何Java应用,但扩展的JavaE ...

  2. 初识Spring框架实现IOC和DI(依赖注入)

    学习过Spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的, IoC是 ...

  3. Spring 框架的架包分析、功能作用、优点,及jar架包简介

    Spring 框架的架包详解    Spring的作用     Spring的优势  由于刚搭建完一个MVC框架,决定分享一下我搭建过程中学习到的一些东西.我觉得不管你是个初级程序员还是高级程序员抑或 ...

  4. 最新 Eclipse IDE下的Spring框架配置及简单实例

    前段时间开始着手学习Spring框架,又是买书又是看视频找教程的,可是鲜有介绍如何配置Spring+Eclipse的方法,现在将我的成功经验分享给大家. 本文的一些源代码来源于码农教程:http:// ...

  5. spring框架学习(三)

    一.Spring自动组件扫描 Spring 提供组件扫描(component scanning)功能.它能从指定的classpath里自动扫描.侦测和实例化具有特定注解的组件. 基本的注解是@Comp ...

  6. Spring框架学习(一)

    一. spring概述 Spring 框架是一个分层架构,由 7 个定义良好的模块组成.Spring 模块构建在核心容器之上,核心容器定义了创建.配置和管理 bean 的方式,如图 1 所示. 图 1 ...

  7. Spring 系列: Spring 框架简介 -7个部分

    Spring 系列: Spring 框架简介 Spring AOP 和 IOC 容器入门 在这由三部分组成的介绍 Spring 框架的系列文章的第一期中,将开始学习如何用 Spring 技术构建轻量级 ...

  8. 使用 Spring Boot 快速构建 Spring 框架应用--转

    原文地址:https://www.ibm.com/developerworks/cn/java/j-lo-spring-boot/ Spring 框架对于很多 Java 开发人员来说都不陌生.自从 2 ...

  9. 【Spring】浅析Spring框架的搭建

    c目录结构: // contents structure [-] Spring是什么 搭建Spring框架 简单Demo 1,建立User类 2,建立Test类 3,建立ApplicationCont ...

随机推荐

  1. 重要:C/C++变量的自动初始化

    对于内置变量的自动初始化 代码1: #include<stdio.h> #define CONST 100 int *p1; ]; int b; static int c; main() ...

  2. C++ IO流小结

    撒花庆祝下,终于看完了(C++Primer)第一部分,即将进入第二部分! IO部分,最基本的是iostream(istream.ostream),子类有fstream(ifstream.ofstrea ...

  3. Swift数值运算

    Swift 让全部数值类型都支持了主要的四则运算: 加法(+) 减法(-) 乘法(*) 除法(/) 1 + 2 // 等于 3 5 - 3 // 等于 2 2 * 3 // 等于 6 10.0 / 2 ...

  4. Ubuntu 12.04.2 安装 Oracle11gR2

    #step 1: groupadd -g 2000 dbauseradd -g 2000 -m -s /bin/bash -u 2000 griduseradd -g 2000 -m -s /bin/ ...

  5. MathType有哪些功能

    随着寒流呼呼来临,期末考也不放过我们,不知道你们是在题海里遨游,还是已做好了挑战准备呢.不管你是哪种情况,现在就有一款玩转理工科的强大编辑公式强悍来袭,不管你是学生.教师,还是理科专业工作者,Math ...

  6. Cookie示例

    //caozuocookie        var webusername = "";        function getCookie(name){ var arr,reg=n ...

  7. C语言对文件的操作函数用法详解2

    fopen(打开文件) 相关函数 open,fclose 表头文件 #include<stdio.h> 定义函数 FILE * fopen(const char * path,const  ...

  8. 一、SDWebImage分析--库处理流程分析

    二.SDWebImage分析--源码具体分析 这阵子看了SDWebImage的实现跟源代码.也看了下网上的一些总结. 这里我自己画了个流程图来辅助理解下SDWebImage这个库的实现流程.相信也是有 ...

  9. python2.0 s12 day3

    s12 day3 视频每节的内容 03 python s12 day3 本节内容概要 第三天的主要内容 上节没讲完的: 6.集合 7.collections 1)计数器 2)有序字典 3)默认字典 4 ...

  10. ASP代码审计学习笔记-1.SQL注入

    ASP注入漏洞 一.SQL注入的原因 按照参数形式:数字型/字符型/搜索型 1.数字型sql查询 sql注入原因: ID=49 这类注入的参数是数字型,SQL语句原貌大致如下: id=request. ...