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开发?遵循哪些策略?以及为什么遵循的好处。
     
     
     

    本文资料来源:

     

    勘误&感谢

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

     

  • 相关阅读:
    Java实现 蓝桥杯VIP 算法训练 一元三次方程
    Java实现 蓝桥杯VIP 算法训练 乘法表
    Java实现 蓝桥杯VIP 算法训练 矩阵加法
    Java实现 蓝桥杯VIP 算法训练 一元三次方程
    Java实现 蓝桥杯VIP 算法训练 平方计算
    Java实现 蓝桥杯VIP 算法训练 平方计算
    Java实现 蓝桥杯VIP 算法训练 平方计算
    Java实现 蓝桥杯VIP 算法训练 乘法表
    Java实现 蓝桥杯VIP 算法训练 乘法表
    监管只是压倒网盘业务的一根稻草,但不是主要原因(答案只有一个:成本!)
  • 原文地址:https://www.cnblogs.com/jimisun/p/9712174.html
Copyright © 2011-2022 走看看