引言
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
里用不了依赖注入,需要手动从容器里拿。
@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
真厉害,什么时候我们也能写出如此优秀的框架?
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。