美文网首页
Spring之旅(一):初识Spring

Spring之旅(一):初识Spring

作者: xuweizhen | 来源:发表于2018-06-06 12:06 被阅读0次

    Spring之旅

    一.Spring根本使命

    简化java开发

    1. 简化开发策略

    1. 基于POJO的轻量级和最小侵入性编程(需要理解)
    2. 通过依赖注入和面向接口实现松耦合(需要理解)
    3. 基于切面和惯例进行声明式编程(需要理解)
    4. 通过切面和模板减少样板式代码(需要理解)

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

    javaBean使用POJO而不需要继承或实现框架中的接口,就能实现Spring框架的功能。

    框架和应用代码之间松耦合。

    侵入性: 很多框架需要在应用代码中继承或实现框架的接口,让应用与框架绑定。

    实现非侵入性的方式:

    1. 依赖注入

    3. 依赖注入

    3.1 概念

    对象中可能需要其他的依赖对象,在依赖注入中对象不需要自己创建依赖的对象,而是依赖的对象将依赖关系自动交给对象。

    3.2 依赖注入方式

    (1) 构造器注入

    在javaBean的构造函数中,将所依赖的类传进来。这样所依赖的对象主动交给javaBean,完成javaBean对象的创建。

    扩展: 构造函数中传入来的依赖,最好是接口。这样所有实现接口的类都可以注入进来,屏蔽了类的具体实现。

    3.3 问题

    构造器注入在创建javaBean对象时,需要传依赖的对象,这个依赖的对象如何来?

    如果是依赖的对象是接口,传哪一个实现类?

    3.4 装配

    (1) 概念

    创建组件之间的协作行为

    即配置javaBean和依赖的类。

    (2) Spring装配方式

    Spring有多重装配方式,常见的有两种:xml文件和java

    :javaBean:Foo (接口)依赖javaBean:Bar (接口)

    FooImp实现了Foo接口,BarImp实现了Bar接口。

    (2.1) xml文件
    <bean id="foo" class="com.spring.FooImp">
        <constructor-arg ref="bar">
    </bean>
    
    <bean id="bar" class="com.spring.BarImp"/>
    
    (2.2) java配置
    @configuration
    public class FooConfig{
        
        @Bean
        public Foo foo(){
            return new FooImp(bar())
        }
        
        @Bean
        public Bar bar(){
            return new BarImp();
        }
    }
    

    装配好后的组件如何运行起来的呢?

    Spring 通过应用上下文(Application Context)装载Bean的定义并把它们组装起来。

    Spring的Application Context全权负责对象的创建与组装。

    Spring 有多种实现Application Context的方式,主要区别在于如何加载配置。

    完整例子:
    • Food接口
    public interface Food {
        
        String foodName();
    }
    
    • Apple类
    public class Apple implements Food{
    
        public String foodName() {
            String foodname = "红富士苹果";
            System.out.println("这是非常好的" + foodname);
            return foodname;
        }   
    }
    
    • Animal接口
    public interface Animal {
        
        void eat();
    }
    
    • Dog类
    public class Dog implements Animal{
        
        private Food food;
    
        public Dog(Food food) {
            this.food = food;
        }
    
        public void eat() {
            System.out.println("dog like" + this.food.foodName());  
        }
        
    }
    
    • AnimalConfig配置
    @Configuration
    public class AnimalConfig {
    
        @Bean
        public Animal animal(){
            return new Dog(food());
        }
        
        @Bean
        public Food food(){
            return new Apple();
        }
    }
    
    
    • main方法中加载注解配置上下文,并获取Bean类
    public class Demo {
        
        public static void main(String[] args) {
            ApplicationContext context = new AnnotationConfigApplicationContext(AnimalConfig.class);
            
            Animal animal = context.getBean(Animal.class);
            animal.eat();
        }
    }
    
    • 打印结果输出:
    这是非常好的红富士苹果
    dog like红富士苹果
    

    DI可以让各组件松耦合,而AOP可以把散落在系统各处的同类功能分离出来形成通用组件,接下来,让我们了解下AOP

    AOP

    AOP是啥?

    AOP是面向切面编程,aspect-oriented programming.

    可以将多个系统都调用的服务,抽离成单独的组件,以消除这些服务散落在系统各处带来的复杂性。

    为何要使用AOP?

    如果不使用AOP,散落在系统各处的同类功能代码,不仅不利于管理,而且会非常混乱。

    如日志服务和安全管理,系统很多业务都需要调用它们的服务。想想看:

    • 如果日志服务有改动,所有的业务都需要同时改动。就算抽离成一个通用方法,方法的调用也会出现在各处,不利于维护与管理。
    • 而且这些服务又不是核心的业务代码,不应该在核心业务代码中去码这些东西,代码会非常混乱。

    AOP的好处在哪里?

    AOP能够让这些服务模块化,以声明的方式去应用到需要这些服务的影响的组件中。

    这些组件更关注自身的核心业务而不需要关注其他服务。

    总之,AOP能让POJO变得简单。

    如何理解AOP?

    AOP可以想象成包裹核心业务组件的一层外壳,以声明的方式灵活的运用到业务组件中,甚至核心业务层都不知道它们的存在,将核心业务与日志事物等服务功能分离。

    例子:

    在每个animal eat food之前,都会有doctor过来给animal检查,eat food之后,又会给animal称重。

    你可能会想到:在每个eat方法的开头添加检查cheak方法,在结尾添加称重weight方法。

    但是仔细想想,animal 中需要管理 doctor的方法?check和weight不是animal的核心业务呀,这些都让doctor自己做就好了。

    如果想要管理也OK,你给我将doctor的实例传给我,依赖注入进来。但如果doctor为空呢?

    简单的事情就变得很复杂,倒不如是doctor的方法就由doctor自己来管理。

    利用AOP,可以让animal在eat food之前必须check,而animal一无所知。

    详细代码:

    Doctor类
    public class Doctor implements Person{
        
        public void check() {
            System.out.println("医生来检查啦");       
        }
    
        public void weight() {
            System.out.println("吃胖啦!");     
        }
    
    }
    
    配置AOP的xml文件
    <bean id="food" class="com.example.beans.Apple"/>
        
        <bean id="animal" class="com.example.beans.Dog">
            <constructor-arg ref="food"/>
        </bean>
        
        <bean id="person" class="com.example.beans.Doctor"/>
        
        <aop:config>
            <aop:aspect ref="person">
                <aop:pointcut expression="execution(void com.example.beans.Dog.eat(..))" id="doctorSay" />
                <aop:before method="check" pointcut-ref="doctorSay" />
                <aop:after method="weight" pointcut-ref="doctorSay" />
            </aop:aspect>
        </aop:config>
    
    输出日志:
    医生来检查啦
    这是非常好的红富士苹果
    dog eat 红富士苹果
    吃胖啦!
    

    从这个例子我们可以了解到

    • 没有任何代码说明Doctor是一个切面,它依然是一个POJO。
    • 没有任何代码说明Dog eat调用了Doctor的方法,但Doctor的方法依然在Dog eat方法执行时启用了。
    • Doctor依然是POJO,也就意味着它可以使用DI等其他POJO可以做的事。

    使用模板Template消除样板式代码

    每次在码东西的时候,经常碰到这些东西是不是在哪见过。没错,你可能真的在重复的码同一个东西,这就是样板式的代码。

    最直接的例子就是java创建jdbc的连接,执行sql,再放入Bean类中,最后释放连接,抛出异常。

    其中创建jdbc和释放连接抛出异常都是通用的写法,样板式的代码,但又不得不写。其实真正的业务只有执行SQL,并将查询结果放入Bean类中。

    使用模板可以消除这些样式代码,可以理解成将这些样式代码封装成了一个模板类,只需码真正的核心业务就行了。

    相关文章

      网友评论

          本文标题:Spring之旅(一):初识Spring

          本文链接:https://www.haomeiwen.com/subject/xblbsftx.html