何为依赖注入?
就是非自己主动初始化依赖,而是通过外部传入依赖的方式,我们就称之为依赖注入。
而好处是什么呢?
- 解耦,将依赖之间解耦。
- 方便单元测试。
何为依赖注入框架?
就是一个框架 来做依赖注入这件事,这样我们就可以把自己的重心放到核心的逻辑上去。
而一个依赖注入框架需要解决的事情:
- 生成 被注入对象。
- 将 被注入对象 与 注入的类中的 引用 绑定。
- 额外的 被注入对象 生命周期管理。
一个框架完成前两部分就ok了,而后一部分更像是基于此的扩充的额外功能。
典型的例子如ButterKnife 和 AndroidAnnotations,对于Android 控件View控件的初始化,只需要一个注解,便省去了findViewbyId的繁琐,自动完成了对象和引用的绑定。然后View 的 生命周期便被Activity或者Fragment托管。
但是从另一个方面去考虑的话,其实对象的生成是已经在inflate view 时产生出来了,和我们理解的从外部传入又有些不一致,并不是所谓的“依赖注入”。
这里发现其实View也是可以直接注入的,哈哈 ,逃)
不同的依赖注入框架实现
-
Spring IoC
IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。
Spring 容器
Spring所倡导的开发方式就是如此,所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。
最后使用xml配置,使用java的类反射机制生成实例,直接使用。
- Roboguice
这个是google提供的依赖注入框架,基于注解 ,基于反射,�效率上在Android 设备肯定不是最优的,现在google 也不再维护,而是推荐了其他框架如dagger和dagger2 这些,这里大家感兴趣的话,可以参考下 依赖注入框架性能对比。
有空的话,我再详细的分析下源码实现。
//TODO 的分割线
- dagger
square 出品,必属精品。�注入框架是以JSR-330为标准的,后面的dagger2也参照了这个标准。
下面是一个demo的例子:
public class MainActivity extends AppCompatActivity {
@Inject Test1 mTest1;
@Inject TestManager15 mTestManager15;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ObjectGraph objectGraph = ObjectGraph.create(new Dagger1Module(), new ModelModule());
objectGraph.inject(this);
objectGraph.inject(mTest1);
Log.d(MainActivity.class.getSimpleName(), "onCreate(): " + mTest1.toString());
mTestManager15.start();
}
}
dagger 用到了apt生成部分代码,在编译完成后我们可以看到下图目录是有代码的,apt生成的代码。
apt生成的代码
我们看下这个类内部实现,如下图:
具体实现
可以看出这个类在实际的代码中是没有被引用到了,但是这个类却完成在attch方法中完成了被注入的类的实例化工作,在injectMembers中完成了绑定的工作。
显而易见的,这个类是通过反射实例化出来的。
于是,我们知道了dagger的实现思路:
* 通过apt生成一个单一容器类(injectadapter),这个类完成下面两个逻辑任务:
1. 生成 被注入对象 的逻辑。
2. 将 被注入对象 与 注入的类中的 引用 绑定 的逻辑。
* 通过反射实例化这个�容器(injectadapter),�注入对象。
具体的实现我们可以参考Linker和�下面的源码:
/**
* Handles loading/finding of modules, injection bindings, and static injections by use of a
* strategy of "load the appropriate generated code" or, if no such code is found, create a
* reflective equivalent.
*/
public final class FailoverLoader extends Loader {
/*
* Note that String.concat is used throughout this code because it is the most efficient way to
* concatenate _two_ strings. javac uses StringBuilder for the + operator and it has proven to
* be wasteful in terms of both CPU and memory allocated.
*/
private final Memoizer<Class<?>, ModuleAdapter<?>> loadedAdapters =
new Memoizer<Class<?>, ModuleAdapter<?>>() {
@Override protected ModuleAdapter<?> create(Class<?> type) {
ModuleAdapter<?> result =
instantiate(type.getName().concat(MODULE_ADAPTER_SUFFIX), type.getClassLoader());
if (result == null) {
throw new IllegalStateException("Module adapter for " + type + " could not be loaded. "
+ "Please ensure that code generation was run for this module.");
}
return result;
}
};
/**
* Obtains a module adapter for {@code module} from the first responding resolver.
*/
@SuppressWarnings("unchecked") // cache ensures types match
@Override public <T> ModuleAdapter<T> getModuleAdapter(Class<T> type) {
return (ModuleAdapter<T>) loadedAdapters.get(type);
}
@Override public Binding<?> getAtInjectBinding(
String key, String className, ClassLoader classLoader, boolean mustHaveInjections) {
Binding<?> result = instantiate(className.concat(INJECT_ADAPTER_SUFFIX), classLoader);
if (result != null) {
return result; // Found loadable adapter, returning it.
}
Class<?> type = loadClass(classLoader, className);
if (type.equals(Void.class)) {
throw new IllegalStateException(
String.format("Could not load class %s needed for binding %s", className, key));
}
if (type.isInterface()) {
return null; // Short-circuit since we can't build reflective bindings for interfaces.
}
return ReflectiveAtInjectBinding.create(type, mustHaveInjections);
}
@Override public StaticInjection getStaticInjection(Class<?> injectedClass) {
StaticInjection result = instantiate(
injectedClass.getName().concat(STATIC_INJECTION_SUFFIX), injectedClass.getClassLoader());
if (result != null) {
return result;
}
return ReflectiveStaticInjection.create(injectedClass);
}
}
Dagger 相关概念
Module:也叫 ModuleClass,指被 @Module 注解修饰的类,为 Dagger 提供需要依赖注入的 Host 信息及一些 Dependency 的生成方式。
ModuleAdapter:指由 APT 根据 @Module 注解自动生成的类,父类是 Dagger 的 ModuleAdapter.java,与 ModuleClass 对应,以 ModuleClass 的 ClassName 加上 $$ModuleAdapter 命名,在 ModuleClass 的同一个 package 下。
InjectAdapter:每个属性或构造函数被 @Inject 修饰的类都会生成一个 继承自 Binding.java 的子类,生成类以修饰类的 ClassName 加上 $$InjectAdapter 命名,在该类的同一个 package 下。
ProvidesAdapter:每个被 @Provides 修饰的生成函数都会生成一个继承自 ProvidesBinding.java 的子类,ProvidesBinding.java 继承自 Binding.java,生成类以 Provide 函数名首字母大写加上 ProvidesAdapter 命名,是 Provide 函数所在 Module 对应生成的ModuleAdapter中的静态内部类。
Binding:指由 APT 根据 @Inject 注解和 @Provides 注解自动生成,最终继承自 Binding.java 的类。为下面介绍的 DAG 图中的一个节点,每个 Host 及依赖都是一个 Binding。
Binding 安装:指将 Binding 添加到 Binding 库中。对 Dagger Linker.java 代码来说是将 Binding 添加到 Linker.bindings 属性中,Linker.bindings 属性表示某个 ObjectGraph 已安装的所有 Binding。对于下面的 DAG 图来说是将节点放到图中,但尚未跟其他任何节点连接起来。
DAG
Binding 连接:把当前 Binding 和它内部依赖的 Binding 进行连接,即初始化这个 Binding 内部的所有 Binding,使它们可用。对 DAG 的角度说,就是把某个节点与其所依赖的各个节点连接起来。
UML类图上图是 Dagger 整体框架最简类关系图。大致原理可以描述为:Linker通过Loader加载需要的Binding并把它们拼装成合理的依赖关系图 ObjectGraph,由ObjectGraph(其子类DaggerObjectGraph)最终实现依赖注入的管理。
ObjectGraph 是个抽象类,DaggerObjectGraph 是它目前唯一的子类,对 Dagger 的调用实际都是对 DaggerObjectGraph 的调用。
初始化
private static ObjectGraph makeGraph(DaggerObjectGraph base, Loader plugin, Object... modules) {
//储存静态的字符串key,value 是module的类名字
Map<String, Class<?>> injectableTypes = new LinkedHashMap<String, Class<?>>();
//静态属性
Map<Class<?>, StaticInjection> staticInjections
= new LinkedHashMap<Class<?>, StaticInjection>();
//Object ,被注入的对象,以类名称为key,value为生成的$$ProvideAdapter
StandardBindings baseBindings =
(base == null) ? new StandardBindings() : new StandardBindings(base.setBindings);
BindingsGroup overrideBindings = new OverridesBindings();
Map<ModuleAdapter<?>, Object> loadedModules = Modules.loadModules(plugin, modules);
for (Entry<ModuleAdapter<?>, Object> loadedModule : loadedModules.entrySet()) {
ModuleAdapter<Object> moduleAdapter = (ModuleAdapter<Object>) loadedModule.getKey();
for (int i = 0; i < moduleAdapter.injectableTypes.length; i++) {
injectableTypes.put(moduleAdapter.injectableTypes[i], moduleAdapter.moduleClass);
}
for (int i = 0; i < moduleAdapter.staticInjections.length; i++) {
staticInjections.put(moduleAdapter.staticInjections[i], null);
}
try {
BindingsGroup addTo = moduleAdapter.overrides ? overrideBindings : baseBindings;
//从moduleadapter中获取到将要绑定的provideAdapter对象,以@Provide的类名为key,ProvidesAdapter为value,
moduleAdapter.getBindings(addTo, loadedModule.getValue());
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException(
moduleAdapter.moduleClass.getSimpleName() + ": " + e.getMessage(), e);
}
}
// Create a linker and install all of the user's bindings
Linker linker =
new Linker((base != null) ? base.linker : null, plugin, new ThrowingErrorHandler());
//将map值放进到Linker中的bindings,在injectAdapter中调用requestBinding时使用
linker.installBindings(baseBindings);
linker.installBindings(overrideBindings);
return new DaggerObjectGraph(
base, linker, plugin, staticInjections, injectableTypes, baseBindings.setBindings);
}
在MainActivity attch时,调用:
@Override
@SuppressWarnings("unchecked")
public void attach(Linker linker) {
mTest1 = (Binding<com.nimbledroid.demo.dagger1.test.Test1>) linker.requestBinding("com.nimbledroid.demo.dagger1.test.Test1", MainActivity.class, getClass().getClassLoader());
mTestManager15 = (Binding<com.nimbledroid.demo.dagger1.manager.TestManager15>) linker.requestBinding("com.nimbledroid.demo.dagger1.manager.TestManager15", MainActivity.class, getClass().getClassLoader());
}
此时调用,默认后两个参数为true:
public Binding<?> requestBinding(String key, Object requiredBy, ClassLoader classLoader,
boolean mustHaveInjections, boolean library) {
assertLockHeld();
Binding<?> binding = null;
for (Linker linker = this; linker != null; linker = linker.base) {
binding = linker.bindings.get(key);
if (binding != null) {
if (linker != this && !binding.isLinked()) throw new AssertionError();
break;
}
}
if (binding == null) {
// We can't satisfy this binding. Make sure it'll work next time!
Binding<?> deferredBinding =
new DeferredBinding(key, classLoader, requiredBy, mustHaveInjections);
deferredBinding.setLibrary(library);
deferredBinding.setDependedOn(true);
toLink.add(deferredBinding);
attachSuccess = false;
return null;
}
if (!binding.isLinked()) {
toLink.add(binding); // This binding was never linked; link it now!
}
binding.setLibrary(library);
binding.setDependedOn(true);
return binding;
}
在inject 传入需要赋给引用的对象后,会调用到:
/**
* Links all requested bindings plus their transitive dependencies. This
* creates JIT bindings as necessary to fill in the gaps.
*
* @throws AssertionError if this method is not called within a synchronized block which
* holds this {@link Linker} as the lock object.
*/
public void linkRequested() {
assertLockHeld();
Binding<?> binding;
while ((binding = toLink.poll()) != null) {
if (binding instanceof DeferredBinding) {
DeferredBinding deferred = (DeferredBinding) binding;
String key = deferred.deferredKey;
boolean mustHaveInjections = deferred.mustHaveInjections;
if (bindings.containsKey(key)) {
continue; // A binding for this key has since been linked.
}
try {
//////实例化出需要注入的对象
Binding<?> resolvedBinding =
createBinding(key, binding.requiredBy, deferred.classLoader, mustHaveInjections);
resolvedBinding.setLibrary(binding.library());
resolvedBinding.setDependedOn(binding.dependedOn());
// Fail if the type of binding we got wasn't capable of what was requested.
if (!key.equals(resolvedBinding.provideKey) && !key.equals(resolvedBinding.membersKey)) {
throw new IllegalStateException("Unable to create binding for " + key);
}
// Enqueue the JIT binding so its own dependencies can be linked.
Binding<?> scopedBinding = scope(resolvedBinding);
toLink.add(scopedBinding);
putBinding(scopedBinding);
} catch (InvalidBindingException e) {
addError(e.type + " " + e.getMessage() + " required by " + binding.requiredBy);
bindings.put(key, Binding.UNRESOLVED);
} catch (UnsupportedOperationException e) {
addError("Unsupported: " + e.getMessage() + " required by " + binding.requiredBy);
bindings.put(key, Binding.UNRESOLVED);
} catch (IllegalArgumentException e) {
addError(e.getMessage() + " required by " + binding.requiredBy);
bindings.put(key, Binding.UNRESOLVED);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
} else {
// Attempt to attach the binding to its dependencies. If any dependency
// is not available, the attach will fail. We'll enqueue creation of
// that dependency and retry the attachment later.
attachSuccess = true;
binding.attach(this);
if (attachSuccess) {
binding.setLinked();
} else {
toLink.add(binding);
}
}
}
try {
errorHandler.handleErrors(errors);
} finally {
errors.clear();
}
}
然后是检测是否有循环依赖:
private static void detectCircularDependencies(Collection<Binding<?>> bindings,
List<Binding<?>> path) {
for (Binding<?> binding : bindings) {
if (binding.isCycleFree()) {
continue;
}
if (binding.isVisiting()) {
int index = path.indexOf(binding);
StringBuilder message = new StringBuilder()
.append("Dependency cycle:");
for (int i = index; i < path.size(); i++) {
message.append("\n ").append(i - index).append(". ")
.append(path.get(i).provideKey).append(" bound by ").append(path.get(i));
}
message.append("\n ").append(0).append(". ").append(binding.provideKey);
throw new IllegalStateException(message.toString());
}
binding.setVisiting(true);
path.add(binding);
try {
ArraySet<Binding<?>> dependencies = new ArraySet<Binding<?>>();
binding.getDependencies(dependencies, dependencies);
//获取当前binding的依赖,进行递归检测
detectCircularDependencies(dependencies, path);
binding.setCycleFree(true);
} finally {
path.remove(path.size() - 1);
binding.setVisiting(false);
}
}
}
- dagger2
dagger2则更进一步的进行了优化,利于接口的特性和apt的�强大功能,完全摆脱的反射,所有的类,绑定实现都是依靠自动生成的代码进行。
对外部提供的入口类在编译时�生成,逻辑的交互也就依靠接口来实现。
在使用dagger2时,需要理解几个概念 :
@Inject: 通常在需要依赖的地方使用这个注解。换句话说,你用它告诉Dagger这个类或者字段需要依赖注入。这样,Dagger就会构造一个这个类的实例并满足他们的依赖。
@Module: Modules类里面的方法专门提供依赖,所以我们定义一个类,用@Module注解,这样Dagger在构造类的实例的时候,就知道从哪里去找到需要的依赖。modules的一个重要特征是它们设计为分区并组合在一起(比如说,在我们的app中可以有多个组成在一起的modules)。
@Provide: 在modules中,我们定义的方法是用这个注解,以此来告诉Dagger我们想要构造对象并提供这些依赖。
@Component: Components从根本上来说就是一个注入器,也可以说是@Inject和@Module的桥梁,它的主要作用就是连接这两个部分。Components可以提供所有定义了的类型的实例,比如:我们必须用@Component注解一个接口然后列出所有的@Modules组成该组件,如果缺失了任何一块都会在编译的时候报错。所有的组件都可以通过它的modules知道依赖的范围。
@Scope: Scopes可是非常的有用,Dagger2可以通过自定义注解限定注解作用域。后面会演示一个例子,这是一个非常强大的特点,因为就如前面说的一样,没必要让每个对象都去了解如何管理他们的实例。在scope的例子中,我们用自定义的@PerActivity注解一个类,所以这个对象存活时间就和activity的一样。简单来说就是我们可以定义所有范围的粒度(@PerFragment, @PerUser, 等等)。
Qualifier: 当类的类型不足以鉴别一个依赖的时候,我们就可以使用这个注解标示。例如:在Android中,我们会需要不同类型的context,所以我们就可以定义qualifier注解“@ForApplication”和“@ForActivity”,这样当注入一个context的时候,我们就可以告诉Dagger我们想要哪种类型的context。
而后,我们来分析一个简单的dagger2的例子:
定义的Subcomponent:
/**
* Scope: activity, it will be instantiated when the MainActivity starts.
*/
@ActivityScope
@Subcomponent(modules = {MainModule.class})
public interface MainComponent {
void inject(MainActivity activity);
}
而后我们在MainActivity中写下如下逻辑:
public class MainActivity extends AppCompatActivity {
@Inject
@Named("AppName")
String mAppName;
@Inject
@Named("UserName")
String mUserName;
@Inject
@Named("title")
String mTitle;
@Override
protected void onCreate(Bundle savedInstanceState) {
MainComponent mainComponent = App.userComponent().plusMain();
mainComponent.inject(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActionBar actionBar = getSupportActionBar();
actionBar.setTitle(mTitle);
TextView textView = (TextView) findViewById(R.id.text_view);
textView.setText(mAppName);
TextView userView = (TextView) findViewById(R.id.user_view);
userView.setText(getString(R.string.current_user, mUserName));
}
}
我们注入了3个�String字符串,并将MainActivity的引用传递给了MainComponent 接口。显而易见的,我们知道inject后,mainComponent 获取到MainActivity,下一步的动作便是为@Inject的成员变量赋值。
我们看下具体�apt生成的类:
@Generated(
value = "dagger.internal.codegen.ComponentProcessor",
comments = "https://google.github.io/dagger"
)
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
private final Provider<String> mAppNameProvider;
private final Provider<String> mUserNameProvider;
private final Provider<String> mTitleProvider;
public MainActivity_MembersInjector(
Provider<String> mAppNameProvider,
Provider<String> mUserNameProvider,
Provider<String> mTitleProvider) {
assert mAppNameProvider != null;
this.mAppNameProvider = mAppNameProvider;
assert mUserNameProvider != null;
this.mUserNameProvider = mUserNameProvider;
assert mTitleProvider != null;
this.mTitleProvider = mTitleProvider;
}
public static MembersInjector<MainActivity> create(
Provider<String> mAppNameProvider,
Provider<String> mUserNameProvider,
Provider<String> mTitleProvider) {
return new MainActivity_MembersInjector(mAppNameProvider, mUserNameProvider, mTitleProvider);
}
@Override
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.mAppName = mAppNameProvider.get();
instance.mUserName = mUserNameProvider.get();
instance.mTitle = mTitleProvider.get();
}
public static void injectMAppName(MainActivity instance, Provider<String> mAppNameProvider) {
instance.mAppName = mAppNameProvider.get();
}
public static void injectMUserName(MainActivity instance, Provider<String> mUserNameProvider) {
instance.mUserName = mUserNameProvider.get();
}
public static void injectMTitle(MainActivity instance, Provider<String> mTitleProvider) {
instance.mTitle = mTitleProvider.get();
}
}
dagger2通过接口的定义规避了�dagger通过 反射 获取 apt生成实现类 的问题,进一步的提高了注入的效率,确实值得学习。
延伸思考
但是dagger2也存在一些问题:
- 我们在使用一个框架或者第三方类库,这个东西首先是易于学习和上手,但是dagger2的一些概念确实让新手“望而生畏”,学习上确实需要一定成本,不能拿来就用。
- 对象生命周期的管理�,如何确保不发生内存泄漏的问题,很大程度上依赖�使用者。
基于以上两点,我们可以做一些优化拓展:
首先是接口的定义,我们是希望给使用者提供一些简单,可用,灵活性高的api,代码的生成上,我们不仅可以利用到apt,甚至可以在编译阶段使用javasist等工具改变�造类。
其次我们可以构建一个“对象容器”,就像Spring一样,管理对象的生命周期。
TODO
上面的分析�只是粗浅的概要,一些细节的实现如单例如何注入以及Scope均未分析到,可以参考文末的链接,另外我也会一直完善这个文档的。
网友评论