4

引言

IOC,全称Inversion of Control,控制反转。也算是老生常谈了。

老生常谈:原指老书生的平凡议论;今指常讲的没有新意的老话。

同时另一个话题,依赖注入,用什么就声明什么,直接就声明,或者构造函数或者加注解,控制反转是实现依赖注入的一种方式。

通过依赖注入:我们无需管理对象的创建,通过控制反转:我们可以一键修改注入的对象。

最近在做Android实验与小程序相关的开发,发现用惯了IOC的我们再去手动new对象的时候总感觉心里不舒服,以后改起来怎么办呢?既然没有IOC,我们就自己写一个吧。

实现

就像我在标题中描述的一样,我先给大家讲解一下标配IOC的原理,使大家更清晰明了,但是受Android与小程序相关的限制,我具体的实现,是低配版IOC

个人扯淡

不管是Spring还是Angular,要么是开源大家,要么是商业巨头,具体的框架实现都是相当优秀。我还没水平也没精力去研读源码,只希望分享自己对IOC的理解,帮到更多的人。

毕竟现在小学生都开始写Python,以后的框架设计会越来越优秀,学习成本越来越低,开发效率越来越高。可能是个人报个培训班学个俩月也会设计微服务,也能成为全栈工程师。所以我们应该想的是如何设计框架,而不是仅停留在使用的层面,渐渐地被只会写增删改查天天搬砖的人取代。

996加班的工程师都开始挤时间写框架扩大影响力,让社会听到程序员的呐喊,我们还有什么不努力的理由?

clipboard.png

“标配”IOC

不管是Spring还是Angular,它们的核心是什么呢?打上mvn spring-boot:run后台就Started Application in xxx seconds了,它到底干什么了呢?

clipboard.png

容器

SpringAngular就是一个大的IOC容器,所以应用启动的过程,其实就是构造容器的过程。

clipboard.png

容器,肯定是装东西的啊?IOC容器里装的是什么?

装的是对象。控制器Controller,服务Service,自定义的组件Component,所有被Spring管理的对象都将被放进IOC容器里。

clipboard.png

所以,大家应该能明白,为什么IOC是依赖注入的一种实现方式?

因为这个对象不是你自己new的,是从容器中拿的,容器初始化的时候,就已经把这个对象构造好了,该注的都注进来了。

思考

从上面大家可以看到,依赖注入的前提是什么?是要求这个对象必须是从容器中拿的,所以才能依赖注入成功。

Spring Boot中没问题,Tomcat转发的路由直接交给容器中相应的对象去处理,同理,Angular也一样。

Android呢?

clipboard.png

手机调用的并不是我们构造的Activity,而是它自己实例化的,小程序也与之类似,Page的实例化不归我们管。

所以“标配”IOC在这里不适用,所以我设计了“低配”IOC容器。

“低配”IOC

找点自己能管得了的对象放在IOC容器里,这样再需要对象就不用去new了,Service有变更直接修改注入就行了。

受我们管理的只有Service,计划设计一个管理所有ServiceIOC容器,然后ActivityPage里用的时候,直接从容器中拿。(低配在这里,不能依赖注入了,得自己拿)。

clipboard.png

Android

一个单例的Configuration负责注册Bean和获取Bean,存储着一个context上下文。看着挺高级的,其实就是一个HashMap存储着接口类型容器对象的映射。

/**
 * 全局配置类
 */
public class Configuration {

    private static Map<Class<?>, Object> context = new HashMap<>();

    private static final class Holder {
        private static final Configuration INSTANCE = new Configuration();
    }

    public static Configuration getInstance() {
        return Holder.INSTANCE;
    }

    public Configuration registerBean(Class<?> clazz, Object bean) {
        context.put(clazz, bean);
        return this;
    }

    public <T> T getBean(Class<?> clazz) {
        return (T) context.get(clazz);
    }
}

写一个静态方法,更加方便配置。

/**
 * 云智,全局配置辅助类
 */
public class Yunzhi {

    ...

    public static <T> T getBean(Class<?> clazz) {
        return Configuration.getInstance().getBean(clazz);
    }
}

一个Application负责容器中所有对象的创建。

public class App extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        Yunzhi.init()
                .setApi("http://192.168.2.110:8888")
                .setTimeout(1L)
                .registerBean(AuthService.class, new AuthServiceImpl())
                .registerBean(LetterService.class, new LetterServiceImpl());
    }
}

使用方法和原来就一样了,一个接口,一个实现类。这里用到了RxJava,看上去倒是类似我们的Angular了。

public interface AuthService {

    Observable<Auth> login(String username, String password);
}
public class AuthServiceImpl implements AuthService {

    private static final String TAG = "AuthServiceImpl";

    @Override
    public Observable<Auth> login(String username, String password) {
        Log.d(TAG, "BASIC 认证");
        String credentials = username + ":" + password;
        String basicAuth = "Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);

        Log.d(TAG, "请求登录");
        return HttpClient.request(AuthRequest.class)
                .login(basicAuth)
                .subscribeOn(Schedulers.io())                   // 在IO线程发起网络请求
                .observeOn(AndroidSchedulers.mainThread());     // 在主线程处理
    }
}

因为Activity我们管不着,所以在Activity里用不了依赖注入,需要手动从容器里拿。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    this.authService = Yunzhi.getBean(AuthService.class);
}

这里有一处没考虑到的问题,就是手机端的性能问题,手机和服务器的处理能力肯定是比不了的,服务器在初始化的时候把所有对象都创建处理无可厚非,但是感觉手机端这样做还是会对性能产生一定影响的。

应该是某些对象用到的时候再创建,实验要求时间很紧,这个就先这样吧,不改了。

小程序端

小程序是在Android之后写的,想到了之前设计的部分缺陷,对容器使用了另一种思想进行实现。

export class YunzhiService {

    private static context = new Map<string, object>();

    public static getBean(beanName: string): object {
        // 从context里拿对象
        let bean = this.context.get(beanName);
        // 如果没有,构造一个,并放进context
        if (!bean) {
            bean = this.createBeanByName(beanName);
            this.context.set(beanName, bean);
        }
        // 返回
        return bean;
    }

    public static createBeanByName(beanName: string): object {
        // 根据不同名称构造不同的Bean
        switch (beanName) {
            case Bean.AUTH_SERVICE:
                return new AuthServiceImpl();
            case Bean.SCORE_SERVICE:
                return new ScoreServiceImpl();
            case Bean.SEMESTER_SERVICE:
                return new SemesterServiceImpl();
            default:
                throw '错误,未注册的bean';
        }
    }
}

总结

Spring Boot真厉害,什么时候我们也能写出如此优秀的框架?


张喜硕
2.1k 声望423 粉丝

浅梦辄止,书墨未浓。