实际项目中的spring的 DI 和 IOC

前两篇文章讲到了项目用到的消息中心和工作流模块,这篇文章来了解一下怎么样使用模块里面的功能。


spring的核心是DI 和IOC,那到底什么是DI(依赖注入)和IOC(控制反转)呢,依赖在core java里面讲过,依赖是对象之间的关系,A对象依赖B对象,就是说A对象中有关于B对象的引用,比如说我们自己写的类里面需要输入输出,用到了InputStream或者是时间DateFormat,在我们使用这些工具类的时候我们都要实例化它们,这就是依赖。依赖注入我们就可以简单的理解为,我们A对象依赖的B对象是Spring像打针一样把B对象的实例注入到A对象中了,一想到打针就觉得屁股一阵酸爽。而我们都知道设计原则告诉我们,要依赖接口不要依赖实现类,这样就降低了耦合,增加了灵活性,不得不说一个是实现类一个是接口,带来了翻天覆地的变化。

而整个上面的过程即:A对象靠Spring的注入依赖B对象的过程,叫控制反转。

用我们生活中的例子讲:大熊想要造一座自己的房子(好开心),在没有哆啦A梦(Spring)这个逆天的助手之前,他要自己买制砖机,水泥搅拌机等等工具(光联系厂家,决定什么牌子就花费好多时间,而且房子造完之后这些大型机器就没有用了)。但是自从有了哆啦A梦就不一样啦,腰不疼了,腿不酸了,上楼梯不费劲!他只要告诉哆啦A梦自己需要一个能造砖头的机器(我不管你是哪个牌子的),一个搅拌水泥的机器(我也不管是哪个牌子的),哆啦A梦就在自己的小口袋里面找啊,找到以前自己装进口袋的宝贝,翻出来一盒咸鱼,不是,一盒皮皮虾,不是,一个不耐烦就给大熊一记大飞脚。最终找到了需要的机器,大熊造完房子之后哆啦A梦又把机器放回小口袋,是不是很方便。

那我们就看看源码中Bean的配置和依赖注入

首先我们知道Facade是提供对外能力输入的接口,这个就要先放到Spring的小口袋里:

//第一个Bean是具体的实现类

 <bean id="carrier_bidItemQueryFacade" class="com.opengroup.hongshi.carrier.biz.facadeimpl.bid.BidItemQueryFacadeImpl"></bean>

//第二个Bean是像容器中注入上面的实现类,注意,此处一起配置了接口和版本号。容器使用了Map,key就是接口名称+版本号,而value就是包含接口信息和版本号以及实现类的整个类。

    <bean id="bidItemQueryFacade_provider" class="com.opengroup.middleware.rpc.provider.FacadeProvider" init-method="init">
        <property name="delegateInterface" value="facade.bid.interfaces.BidItemQueryFacade"></property>
        <property name="version" value="0.0.1"></property>
        <property name="target" ref="bidItemQueryFacade"></property>
    </bean>

     String key = provider.getDelegateInterface() + "_" + provider.getVersion();
        if (pool.get(key) != null) {
            throw new CriticalSystemError("重复注册服务导致系统无法启动[" + key + "]");
        }
        pool.put(key, provider);

上面这个是Spring的bean的配置过程,宝贝已经放入Spring的小口袋里面了,下面的工程需要使用这个宝贝的时候是按照能力(接口和版本)检索的,比如需要实现了BidItemQueryFacade接口版本是0.0.1的实现类

 <bean id="bidItemQueryFacade_consumer" class="com.opengroup.middleware.rpc.consumer.FacadeConsumer">
        <property name="delegateInterface" value="facade.bid.interfaces.BidItemQueryFacade"></property>
        <property name="version" value="0.0.1"></property>
    </bean>
    <bean id="bidItemQueryFacade" factory-bean="bidItemQueryFacade_consumer" factory-method="init"></bean>
    <bean id="bidItemQueryFacadeClient" class="integration.BidItemQueryClientImpl">
        <property name="bidItemQueryFacade" ref="bidItemQueryFacade"></property>
    </bean>
    上面的配置就是先把需要的接口和版本号告诉工程类bidItemQueryFacade_consumer,然后用工程类init方法来生成具体的实例对象。(这里面用到了发射和代理,很刺激)

第一个bean生成FacadeConsumer实例,并把我们希望的接口和版本号注入,

第二个bean就是用init方法生成具体的实例对象


            if (StringUtil.isBlank(version) || StringUtil.isBlank(delegateInterface)) {
                throw new CriticalSystemError("服务消费者,version[" + version + "]和delegateInterface["
                                              + delegateInterface + "]不可为空:");
            }
            if (proxyObj == null) {
                Class<?> delegateInterfaceCls = Class.forName(delegateInterface, false,
                    Thread.currentThread().getContextClassLoader())
;//Class.forName(delegateInterface);
                Class<?>[] tmp = new Class<?>[1];
                tmp[0] = delegateInterfaceCls;
                //                proxyObj = Proxy.newProxyInstance(this.getClass().getClassLoader(), tmp, this);
                proxyObj = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                    tmp, this);

            }
            return proxyObj;

我们看到这个地方实际上就是用到反射生成接口的实例,然后用到代理实现接口,

每次调用代理的方法时,我们就会

/**
     * 动态代理回调接口
     * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
     */
    public Object invoke(Object proxy, Method method, Object[] args) {
        if (LogUtil.getUniqueId() == null) {
            LogUtil.myThreadStart(null);
        }

        FacadeProvider provider = null;
        switch (consumerType) {
            case JVM_LOCAL:
                provider = JvmFacadeProviderRegister.fetchProvider(delegateInterface, version);
                break;
            default:
                throw new RpcException("consumerType is not support:" + consumerType);
        }
        if (provider == null) {
            throw new RpcException(
                "找不到对应的服务提供者:interface[" + delegateInterface + "], version[" + version + "]");
        }
        String invoker = provider.getTarget().getClass().getName() + "." + method.getName() + "("
                         + consumerType.name() + ")";
        try {
            PerfLog.printStart(invoker);
            return method.invoke(provider.getTarget(), args);

、、我们看到代理回调的时候,先找到我们之前注入的provider。然后找出具体实现类,调用method.invoke(provider.getTarget(), args)。




我们不得不佩服Spring在帮助我们完成自己的心愿时付出的努力。也不得不佩服大熊有这么好的伙伴

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值