引言IOC,全称Inversion of Control,控制反转。也算是老生常谈了。老生常谈:原指老书生的平凡议论;今指常讲的没有新意的老话。同时另一个话题,依赖注入,用什么就声明什么,直接就声明,或者构造函数或者加注解,控制反转是实现依赖注入的一种方式。通过依赖注入:我们无需管理对象的创建,通过控制反转:我们可以一键修改注入的对象。最近在做Android实验与小程序相关的开发,发现用惯了IOC的我们再去手动new对象的时候总感觉心里不舒服,以后改起来怎么办呢?既然没有IOC,我们就自己写一个吧。实现就像我在标题中描述的一样,我先给大家讲解一下标配IOC的原理,使大家更清晰明了,但是受Android与小程序相关的限制,我具体的实现,是低配版IOC。个人扯淡不管是Spring还是Angular,要么是开源大家,要么是商业巨头,具体的框架实现都是相当优秀。我还没水平也没精力去研读源码,只希望分享自己对IOC的理解,帮到更多的人。毕竟现在小学生都开始写Python,以后的框架设计会越来越优秀,学习成本越来越低,开发效率越来越高。可能是个人报个培训班学个俩月也会设计微服务,也能成为全栈工程师。所以我们应该想的是如何设计框架,而不是仅停留在使用的层面,渐渐地被只会写增删改查天天搬砖的人取代。996加班的工程师都开始挤时间写框架扩大影响力,让社会听到程序员的呐喊,我们还有什么不努力的理由?“标配”IOC不管是Spring还是Angular,它们的核心是什么呢?打上mvn spring-boot:run后台就Started Application in xxx seconds了,它到底干什么了呢?容器Spring与Angular就是一个大的IOC容器,所以应用启动的过程,其实就是构造容器的过程。容器,肯定是装东西的啊?IOC容器里装的是什么?装的是对象。控制器Controller,服务Service,自定义的组件Component,所有被Spring管理的对象都将被放进IOC容器里。所以,大家应该能明白,为什么IOC是依赖注入的一种实现方式?因为这个对象不是你自己new的,是从容器中拿的,容器初始化的时候,就已经把这个对象构造好了,该注的都注进来了。思考从上面大家可以看到,依赖注入的前提是什么?是要求这个对象必须是从容器中拿的,所以才能依赖注入成功。Spring Boot中没问题,Tomcat转发的路由直接交给容器中相应的对象去处理,同理,Angular也一样。Android呢?手机调用的并不是我们构造的Activity,而是它自己实例化的,小程序也与之类似,Page的实例化不归我们管。所以“标配”IOC在这里不适用,所以我设计了“低配”IOC容器。“低配”IOC找点自己能管得了的对象放在IOC容器里,这样再需要对象就不用去new了,Service有变更直接修改注入就行了。受我们管理的只有Service,计划设计一个管理所有Service的IOC容器,然后Activity或Page里用的时候,直接从容器中拿。(低配在这里,不能依赖注入了,得自己拿)。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里用不了依赖注入,需要手动从容器里拿。@Overrideprotected 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真厉害,什么时候我们也能写出如此优秀的框架?