用IOC和DI解决懒人老板想喝咖啡但不想自己动手的窘迫

        懒人公司里,所有人都非常的懒惰,唯独他们的程序员还在拼命的奋斗。这一天老板想喝咖啡。

public void boss(){
        System.out.println("我想喝咖啡");
    }

这时候老板走到了咖啡机。

 public void cofo_jiqi(){
        System.out.println("我是咖啡机");
    }

但是老板现在并不会泡咖啡(别问我为什么不会,这是个蠢货懒老板)这时我教给他了泡咖啡的方法。

 public void boss(){
        System.out.println("我想喝咖啡");
        //泡咖啡的方法
        Cofo cofo = new Cofo();
        cofo.cofo_jiqi();

    }

好啦,老板成功的泡好了一杯咖啡。但是没过多久,他又想喝咖啡了。由于这是懒人公司,所以老板也懒到极致,他叫了自己手下唯一勤奋的程序员:小王,去让他帮自己泡咖啡。

        chengxuyuan Mr_wang = new chengxuyuan();

程序员小王叫过来了

sequenceDiagram
老板 ->> 小王: 小王啊,我们公司对你怎么样?
小王 ->>老板:对我可是相当好!
老板->>小王: 那好,我有个重要的事情交给你
小王 ->>老板:什么事情!难道是拿年终奖了!
老板->>小王: 咳咳,帮我泡杯咖啡,什么都有。

小王被老板忽悠的团团转,屁颠屁颠的要帮老板泡咖啡。

chengxuyuan Mr_wang = new chengxuyuan();
 Mr_wang.pao_cofo();

可是小王的技能只有写代码,我们还要教会他泡咖啡才行。

class chengxuyuan{
    public void Write(){
        System.out.println("我会写代码");
    }
    public void pao_cofo(){
        System.out.println("我会泡咖啡");
        Cofo cofo = new Cofo();
        cofo.cofo_jiqi();
    }
}

好了,现在我们教会小王泡咖啡了。这时候这个懒人老板的得力泡咖啡助手就是小王了。
        讲了这么多,其实经过这个例子,我们就已经明白了iOC中控制反转的概念了。之前老板想泡咖啡,必须自己来。
在这里插入图片描述
老板需要知道,这个咖啡机是怎么用的,但是老板怎么会连这种低级的东西都学呢!!!所以这时候我们就需要用一个工具人。
在这里插入图片描述
这时候老板只需要张张嘴,让小王这个工具人来做,自己不需要动,也不需要知道咖啡机怎么用,都让小王来帮你,你需要做的就是躺在座椅上,饭来张口。我们做老板可能都喜欢这种潇洒的生活,程序也不例外。

1.控制反转

        光听名字,大家应该就可以听出个一二,控制反转嘛,不就是本来属于自己控制的事情,反过来让其他人帮你控制。其实话粗理不粗,控制反转的意思大可这样理解。即通过控制传递给我代码,但实际上我并没有直接依赖于它们。这并非我的代码调用了什么更通用的代码,而是框架允许我插入自定义的行为。像这样系统设计使用的就是所谓的控制反转(简称IOC)。
        当然肯定有人会想,这和我平常new对象有什么区别什么好处呢。当然我上面说的例子中,这些好处肯定不足以什么,那我就仔细讲讲他的好处。

通过解耦改善模块化

        在我们的实际开发过程中,都是一块大肉多人分,一个项目多人做,这样的就是模块化。要是我们平常用new来获取对象,就大大增加了代码的耦合。让两个类变的互不可缺,这个是很不应该的。假如后期的项目,各类开始分层,挪动,到时候要是还是用new的话,必定会出现很多错误。这样的话,程序员不可能一个个找错误,那太麻烦了。所以这时候我们的IOC容器就会帮我们自动找类,来给你注入这个对象。

不用考虑创建对象的过程,只需要找IOC容器拿对象

        我们可以把我们的小王当做一个ioc容器。我们只需要考虑的是,喝什么咖啡,而不用考虑这个咖啡是怎么泡出来的。大大的节省了代码量和逻辑。

        那我们该如何创建一个IOC容器,让他帮我们管理对象呢。我们用Spring来做这个例子。

配置bean

        就是将我们的对象信息,放入bean里面

<bean id = "chengxuyuan" class="com.sun.been.chengxuyuan">
    </bean>
    <bean id ="cofo" class="com.sun.been.Cofo">

    </bean>

ID放的是我们的对象名字,类似A a = new A();里的 a。
class就是这个类的地址。
那么这两个对象有了,我们想想,还需要写什么吗??对了,我们既然要让小王帮我们泡咖啡,那我们必须要学会怎么跑咖啡啊,这时候我们给他写个泡咖啡的方法。

    <bean id = "chengxuyuan" class="com.sun.been.chengxuyuan">
        <property name="cofo" ref="cofo"></property>
    </bean>
    <bean id ="cofo" class="com.sun.been.Cofo">

    </bean>

好了,那我们来看看,代码该如何获取到这些配置的呢。
首先我们要从IOC容器中获得程序员小王这个对象。

 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
        chengxuyuan Mr_wang = applicationContext.getBean("chengxuyuan",chengxuyuan.class);

        这个是个固定用法,意思是获得一个容器对象ApplicationContext,让他读取bean.xml里的配置。接下来,我们用容器的对象,将程序员小王给获取出来,之所以要用chengxuyuan.class,是因为传过去的应该是个字节码对象。需要进行类型转换。当然也可以在前面写个强转,两者都可实现。
        这时候小王叫来了,要让小王泡咖啡了,但是小王这时候还是不会的,我们需要教他,这时候我们就需要用DI来注入这个方法。

 public void setCofo(Cofo cofo) {
        this.cofo = cofo;
    }

    private Cofo cofo;

        这时,你肯定就有疑惑了,这样写不会空指针异常吗,因为我们没有给对象给到cofo啊。no!no!no!这里我们的IOC容器就已经从bean.xml,找到了这个对象,并且让这个对象,等于bean.xml里我们已经配置好了的 ,cofo这个对象。所以这就是IOC容器一个强大之处,di注入。(这里是个固定用法,想让Ioc帮你注入,就只能这样写)这时候我们来运行一下在这里插入图片描述
这个咖啡就已经泡好了。
        那大家有没有发现,一直用bean.xml来配置对象信息,是不是太麻烦了。要是有一百个对象,那对于以后的运维都是一个困难,那我们有没有一个好的方法来简化xml呢。答案是有的,Spring专门为我们准备了注解,来完成bean.xml里的操作。我们需要使用以下几个注解:

@Autowired顾名思义,就是自动装配,其作用是为了消除代码Java代码里面的getter/setter与bean属性中的property。当然,getter看个人需求,如果私有属性需要对外提供的话,应当予以保留。

@Autowired默认按类型匹配的方式,在容器查找匹配的Bean,当有且仅有一个匹配的Bean时,Spring将其注入@Autowired标注的变量中。

用法就是写在你的方法的上面。

//    public void setCofo(Cofo cofo) {
//        this.cofo = cofo;
//    }
    @Autowired
    private Cofo cofo;

这个注解在XML中将

        <property name="cofo" ref="cofo"/>

替代了。

省去了set方法,可以直接匹配方法。但是要注意,要是这个bean,有多个,再或者是没有,都会报错,因为是按类型匹配这个对象。那一旦出现了这种情况该怎么办呢?

@Resource注解与@Autowired注解作用非常相似
这是详细一些的用法,说一下@Resource的装配顺序:
(1)、@Resource后面没有任何内容,默认通过name属性去匹配bean,找不到再按type去匹配
(2)、指定了name或者type则根据指定的类型去匹配bean
(3)、指定了name和type则根据指定的name和type去匹配bean,任何一个不匹配都将报错

然后,区分一下@Autowired和@Resource两个注解的区别:
(1)、@Autowired默认按照byType方式进行bean匹配,@Resource默认按照byName方式进行bean匹配
(2)、@Autowired是Spring的注解,@Resource是J2EE的注解,这个看一下导入注解的时候这两个注解的包名就一清二楚了
Spring属于第三方的,J2EE是Java自己的东西,因此,建议使用@Resource注解,以减少代码和Spring之间的耦合。

@Service
@Repository
@Component
这三个的用法是一样的,只不过是由于分层,才会出现三个注解,其底层代码都是一样的。将这个类放入IOC容器中,替代了xml中

 <bean id = "chengxuyuan" class="com.sun.been.chengxuyuan">

    </bean>
    <bean id ="cofo" class="com.sun.been.Cofo">

    </bean>

的操作。但是这样创建的bean都是单例模式,假如我们的实际开发中要用到多例模式,配置@Scope即可,默认是”singleton”即单例,”prototype”表示原型即每次都会new一个新的出来。
但是要注意通过注解来配置ioc容器,我们的bean.xml是需要添加一行标签,以此来扫描我们的注解。

    <context:component-scan base-package="com.sun"></context:component-scan>

里面的参数package就是填写需要扫描注解的包。

那么现在 我们的整体代码就已经变成这样了

package com.sun.been;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;

/**
 * SpringIoc和DI例子
 */
public class test {
    /**
     * 测试类入口
     */
    public static void main(String[] args) {
        /**
         * 扫描配置文件,获取对象
         */
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
        chengxuyuan Mr_wang = applicationContext.getBean("chengxuyuan",chengxuyuan.class);
        System.out.println("我想喝咖啡");

        Mr_wang.pao_cofo();
    }
    }

/**
 * 程序员类
 */
@Component
class chengxuyuan{

    @Autowired()
    private Cofo cofo;
    public void Write(){
        System.out.println("我会写代码");
    }
    public void pao_cofo(){
        System.out.println("我会泡咖啡");
        cofo.cofo_jiqi();
    }
}

/**
 * 咖啡类
 */
@Component
class Cofo{
    public void cofo_jiqi(){
        System.out.println("我是咖啡机");
    }
        }

bean.xml也只剩

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
             http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    <context:component-scan base-package="com.sun"></context:component-scan>
</beans>

总结

使用注解之前要开启自动扫描功能,其中base-package为需要扫描的包(含子包)。

<context:component-scan base-package=”cn.test”/>
@Configuration把一个类作为一个IoC容器,它的某个方法头上如果注册了@Bean,就会作为这个Spring容器中的Bean。
@Scope注解 作用域
@Lazy(true) 表示延迟初始化

@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。

@Repository用于标注数据访问组件,即DAO组件。

@Service用于标注业务层组件、

@Controller用于标注控制层组件(如struts中的action)

@Scope用于指定scope作用域的(用在类上)

@Autowired 默认按类型装配,如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。如下:
@Autowired @Qualifier(“personDaoBean”) 存在多个实例配合使用

@Resource默认按名称装配,当找不到与名称匹配的bean才会按类型装配。

@PostConstruct用于指定初始化方法(用在方法上)
@PreDestory用于指定销毁方法(用在方法上)
@DependsOn:定义Bean初始化及销毁时的顺序
@Primary:自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常
@PostConstruct 初始化注解
@PreDestroy 摧毁注解 默认 单例 启动就加载
@Async异步方法调用

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!