zoukankan      html  css  js  c++  java
  • 重新来认识你的老朋友Spring框架

    欢迎查看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实例源码下载
    /**
     * @Author:jimisun
     * @Description:
     * @Date:Created in 20:32 2018-09-26
     * @Modified By:
     */
    public class HelloSpringBean {
        public String sayHello() {
            return "Hello Spring!!!";
        }
    }

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

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

    来我们一起来看一下一个简单的POJO  example2实例源码下载
    /**
     * @Author:jimisun
     * @Description:
     * @Date:Created in 07:44 2018-09-27
     * @Modified By:
     */
    public class BeautifulGirl implements Gril {
    
        private EatAction eat;
    public BeautifulGirl() { this.eat = new EatAction(); } @Override public void action() { eat.action(); } }

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

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

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

    <?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-4.0.xsd">
    
        <bean class="com.jimisun.spring.example4.BeautifulGirl" id="beautifulGirl">
            <constructor-arg ref="action"/>
        </bean>
    
        <bean class="com.jimisun.spring.example4.SleepAction" id="action"></bean>
    
    </beans>
    /**
     * @Author:jimisun
     * @Description:
     * @Date:Created in 07:53 2018-09-27
     * @Modified By:
     */
    public class Main {
        public static void main(String[] args) {
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("Spring.xml");
            BeautifulGirl beautifulGirl = (BeautifulGirl) context.getBean("beautifulGirl");
            beautifulGirl.action();
            context.close();
        }
    }

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

    /**
     * @Author:jimisun
     * @Description:
     * @Date:Created in 08:40 2018-09-27
     * @Modified By:
     */
    @Configuration
    public class SpringConfig {
    
        @Bean
        public SleepAction sleepAction() {
            return new SleepAction();
        }
    
        @Bean
        public BeautifulGirl beautifulGirl() {
            return new BeautifulGirl(sleepAction());
        }
    }
    只不过Spring的上下文的实现应该使用AnnotationConfigApplicationContext进行加载配置

    /**
     * @Author:jimisun
     * @Description:
     * @Date:Created in 07:53 2018-09-27
     * @Modified By:
     */
    public class Main {
        public static void main(String[] args) {
            ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
            SleepAction action = applicationContext.getBean(SleepAction.class);
            action.action();
        }
    }

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

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

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

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

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

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

    /**
     * @Author:jimisun
     * @Description:
     * @Date:Created in 11:13 2018-09-27
     * @Modified By:
     */
    public class Main {
        public static void main(String[] args) {
            JdbcTemplate jdbcTemplate = new JdbcTemplate();
            jdbcTemplate.execute("select * from user");
        }
    }
     
     
    非常感谢大家耐心能看到这里,在本文中我尽可能地详细阐述Spring使如何简化Java开发?遵循哪些策略?以及为什么遵循的好处。
     
     
     

    本文资料来源:

     

    勘误&感谢

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

     

  • 相关阅读:
    Python接口自动化之测试用例加载到测试套件的几种方法
    python接口自动化之logging日志模块相关
    Fiddler篡改请求参数后重新发起请求操作
    python接口自动化之request模块一些注意事项
    MAC系统下python放webdriver等文件路径
    python安装suds模块提示ModuleNotFoundError: No module named 'client'
    mac系统利用命令安装jenkins
    linux命令
    python 连接数据库,查询结果写入数据到excel
    python连接mysql数据表查询表获取数据导入到txt中
  • 原文地址:https://www.cnblogs.com/jimisun/p/9712174.html
Copyright © 2011-2022 走看看