当前位置:网站首页>Analysis of dagger2 principle
Analysis of dagger2 principle
2022-07-05 03:40:00 【Internet Panda】
One 、 What is? Dagger2
Dagger2 yes Dagger Upgraded version , It's a dependency injection framework , Now by Google Take over maintenance . Okay , Here is a keyword dependency injection , So we have to know what dependency injection is first , In order to better understand Dagger2.
Dependency injection is a design pattern of object-oriented programming , The goal is to reduce program coupling , This coupling is caused by the dependencies between classes .
for instance : When we write object-oriented programs , Combinations are often used , That is, reference another class in one class , Thus, the method of the referenced class can be called to complete some functions , It looks like this .
public class ClassA {
...
ClassB b;
...
public ClassA() {
b = new ClassB();
}
public void do() {
...
b.doSomething();
...
}
}
At this time, the problem of dependency arises ,ClassA Depend on ClassB, Must use ClassB Methods , To complete some functions . There seems to be no problem in this way , But we are ClassA Directly create ClassB Example , That's the problem , stay ClassA Directly create ClassB example , Against the principle of single responsibility ,ClassB Instance creation should not be ClassA To complete ; Secondly, the coupling degree increases , Expandability of , If we want to instantiate ClassB The parameter is passed in when the , Then we have to change ClassA Construction method of , Inconsistent with the open and close principle .
So we need an injection method , Inject dependencies into host classes ( Or called target class ) in , So as to solve the problems mentioned above . There are several ways of dependency injection :
- Injection through interface
interface ClassBInterface {
void setB(ClassB b);
}
public class ClassA implements ClassBInterface {
ClassB classB;
@override
void setB(ClassB b) {
classB = b;
}
}
- adopt set Methods to inject
public class ClassA {
ClassB classB;
public void setClassB(ClassB b) {
classB = b;
}
}
- Injected by constructor
public class ClassA {
ClassB classB;
public void ClassA(ClassB b) {
classB = b;
}
- adopt Java annotation
public class ClassA {
// Injection will not be completed at this time , We also need the support of dependency injection framework , Such as RoboGuice,Dagger2
@inject ClassB classB;
...
public ClassA() {
}
stay Dagger2 The last injection method is used in , By way of annotation , Inject dependencies into the host class .
Two 、 How to introduce Dagger2
To configure apt plug-in unit ( stay build.gradle(Project:xxx) Add the following code to )
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0'
// add to apt plug-in unit
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
Add dependency ( stay build.gradle(Module:app) Add the following code to )
apply plugin: 'com.android.application'
// Add the following code , application apt plug-in unit
apply plugin: 'com.neenbedankt.android-apt'
...
dependencies {
...
compile 'com.google.dagger:dagger:2.4'
apt 'com.google.dagger:dagger-compiler:2.4'
//java annotation
compile 'org.glassfish:javax.annotation:10.0-b28'
...
}
3、 ... and 、 Use Dagger2
Here is a chestnut to illustrate , How to use Dagger2, It should be noted that , This chestnut is based on mvp Mode , So if you don't understand mvp Words , You can learn about mvp, Continue to read the following .
stay mvp in , The most common kind of dependency , Namely Activity hold presenter References to , And in Activity Instantiate this presenter, namely Activity rely on presenter,presenter We need to rely on View Interface , To update UI, It looks like this :
public class MainActivity extends AppCompatActivity implements MainContract.View {
private MainPresenter mainPresenter;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Instantiation presenter take view Pass to presenter
mainPresenter = new MainPresenter(this);
// call Presenter Method load data
mainPresenter.loadData();
...
}
}
public class MainPresenter {
//MainContract It's an interface ,View It's his internal interface , Here as View Interface can
private MainContract.View mView;
MainPresenter(MainContract.View view) {
mView = view;
}
public void loadData() {
// call model Layer method , Load data
...
// When the callback method succeeds
mView.updateUI();
}
such Activity And presenter Just coupled together , When it needs to change presenter The construction method of , You need to modify the code here . If you use dependency injection , That's true :
public class MainActivity extends AppCompatActivity implements MainContract.View {
@Inject
MainPresenter mainPresenter;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainComponent.builder()
.mainModule(new MainModule(this))
.build()
.inject(this);
// call Presenter Method load data
mainPresenter.loadData();
...
}
}
public class MainPresenter {
//MainContract It's an interface ,View It's his internal interface , Here as View Interface can
private MainContract.View mView;
@Inject
MainPresenter(MainContract.View view) {
mView = view;
}
public void loadData() {
// call model Layer method , Load data
...
// When the callback method succeeds
mView.updateUI();
}
@Module
public class MainModule {
private final MainContract.View mView;
public MainModule(MainContract.View view) {
mView = view;
}
@Provides
MainView provideMainView() {
return mView;
}
}
@Component(modules = MainModule.class)
public interface MainComponent {
void inject(MainActivity activity);
}
forehead , It seems to be getting more complicated , Better not Dagger2 Well . But it's understandable to think about it carefully , Although the direct combination method is simple , But it has coupling , To solve this coupling , There may be more auxiliary classes , Let this direct dependency , Become indirect , To reduce the coupling . Like most design patterns , In order to achieve high cohesion and low coupling , There are often many interfaces and classes ,Daager2 So it is with , Although it seems complicated , But it's worth it in software engineering . below , Let's analyze what the above code means .
We see first MainActivity Code in , It was stated directly before MainPresenter, Now add a comment on the basis of the declaration @Inject, indicate MainPresenter It needs to be injected into MainActivity in , namely MainActivity Depend on MainPresenter, What we should pay attention to here is , Use @Inject when , Out-of-service private Modifiers modify the member properties of a class .
And then we were in MainPresenter The constructor of is also added @Inject annotation . such MainActivity Inside mainPresenter With his constructor . This connection can be understood in this way , When you see a class being @Inject When the tag , It will be in his construction method , If this construction method is also @Inject Marked words , This class will be initialized automatically , To complete dependency injection .
then , They will not establish contact with each other out of thin air , So we think of , A bridge is definitely needed , Connect them , That's what we're going to introduce Component.
Component It's an interface or an abstract class , use @Component annotations ( Let's ignore the bracketed modules), We define a inject() Method , Parameter is Mainactivity. then rebuild Next item , It will generate one with Dagger Prefixed Component class , Here is DaggerMainComponent, And then in MainActivity Complete the following code in .
DaggerMainComponent.builder()
.mainModule(new MainModule(this))
.build()
.inject(this);
here Component will @Inject Annotated mainPresenter Associated with its constructor . here , See here , If you are a beginner , It must be very confusing , How on earth did they establish contact , Where is the instantiation process ? Don't worry. , The principle of this process will be explained later .
By this time we have finished presenter The injection process , But we found another MainModule class , What does this class do ?MainModlue Is an annotation class , use @Module annotations , Mainly used to provide dependencies . wait , Just passed @Inject You can complete the dependency , Why is it used here Module Class to provide dependencies ? It has been Module Class is mainly used to provide the dependencies of classes without constructors , These classes cannot be used @Inject mark , For example, third-party class libraries , System class , And the above example View Interface .
We are MainModule Class declares MainContract.View Member attribute , It is transmitted from the outside in the construction method view Assign a value to mView, And through a @Provides Marked with provide Opening method , Put this view return , This is based on provide The first method is to provide dependencies , We can create multiple methods to provide different dependencies . So how does this class work ? You can think of the above @Component Annotate the things in brackets . This is the next one
@Component(modules = MainModule.class)
public interface MainComponent {
void inject(MainActivity activity);
}
therefore Module To make a difference , It still depends on Component class , One Component Class can contain multiple Module class , Used to provide dependency . Let's move on to the following code :
DaggerMainComponent.builder()
.mainModule(new MainModule(this))
.build()
.inject(this);
Through here new MainModule(this) take view Pass on to MainModule in , then MainModule Inside provideMainView() Method returns this View, When de instantiating MainPresenter when , Found that the constructor has a parameter , It will be in Module Find the method to provide this dependency in , Will be View Pass in , That's it presenter in View The injection of .
Let's go through the above injection process again , First, clarify the following concepts :
- @Inject A property or constructor with this annotation will participate in dependency injection ,Dagger2 Will instantiate the class with this annotation
- @Module Classes with this annotation , Used to provide dependency , There are some definitions in it @Provides The purpose of the note is to provide Opening method , These are the dependencies provided ,Dagger2 You will find the dependencies needed to instantiate a class in this class .
- @Component Used to put @Inject and @Module A bridge of connection , from @Module Get the dependency in and inject the dependency into @Inject
Then let's review the injection process above : First MainActivity Need to rely on MainPresenter, therefore , We use it inside @Inject Yes MainPresenter Annotate , Indicates that this is the class to be injected . then , We are right. MainPresenter The constructor of is also annotated @Inject, At this time, there is a parameter in the constructor MainContract.View, because MainPresenter Need to rely on MainContract.View, So we define a class , be called MainModule, Provide a method provideMainView, To provide this dependency , This MainView It's through MainModule Constructor injected into , Then we need to define Component Interface class , And will Module Include , namely
@Component(modules = MainModule.class)
public interface MainComponent {
void inject(MainActivity activity);
}
At the same time, it declares a inject Method , Method parameters are ManActivity, Used to obtain MainActivity example , To initialize the MainPresenter
DaggerMainComponent.builder()
.mainModule(new MainModule(this))
.build()
.inject(this);
here , The injection process is complete , Maybe by this time , There will still be some doubts , Because we can't see where the instantiation process is , Why write code like this , So below , Based on this example , analysis Dagger2 What has been done inside .
Four 、Dagger2 Principle of injection
Dagger2 Unlike other dependency injection frameworks , It's through apt The plug-in generates the corresponding injection code at the compilation stage , Let's take a specific look at Dagger2 What injection code is generated ?
We see first MainPresenter This class , In this class, we use @Inject mark , then Rebuild Project,Dagger2 Will be in /app/build/generated/apt/debug/ Generate a corresponding factory class under the directory MainPresenter_Factory, Let's look at the following specific code ( For ease of understanding , I put MainPresenter Also posted )
public class MainPresenter {
MainContract.View mView;
@Inject
MainPresenter(MainContract.View view) {
mView = view;
}
}
public final class MainPresenter_Factory implements Factory<MainPresenter> {
private final Provider<MainContract.View> viewProvider;
public MainPresenter_Factory(Provider<MainContract.View> viewProvider) {
assert viewProvider != null;
this.viewProvider = viewProvider;
}
@Override
public MainPresenter get() {
return new MainPresenter(viewProvider.get());
}
public static Factory<MainPresenter> create(Provider<MainContract.View> viewProvider) {
return new MainPresenter_Factory(viewProvider);
}
}
contrast MainPresenter, We found out MainPre_Factory The corresponding code is also generated in . First of all viewProvide, This is a Provider type , Generic parameters are our MainContract.View, Then through the construction method , Yes viewProvider instantiate . In fact, there is a doubt here , Why is the above member attribute not directly MainContract.View, It is Provider type ? notice provider We should think of this MainContract.View It's a dependency , And the provider we depend on is MainModule, So the viewProvider It must be from MainModul Provided . Let's continue to look at the following get() Method , See this method , I think we have a feeling of enlightenment , original MainPresenter The instantiation of is here , The parameters in the constructor are what we depend on MainContract.View, It is from viewProvider adopt get() Provide . Then there is a create() Method , And there is a parameter viewProvider, Used to create this MainPresenter_Factory class .
From above we can see that ,viewProvider By MainModule Provided , So let's take a look at MainModule The corresponding injection class .
@Module
public class MainModule {
private final MainContract.View mView;
public MainModule(MainContract.View view) {
mView = view;
}
@Provides
MainContract.View provideMainView() {
return mView;
}
}
public final class MainModule_ProvideMainViewFactory implements Factory<MainContract.View> {
private final MainModule module;
public MainModule_ProvideMainViewFactory(MainModule module) {
assert module != null;
this.module = module;
}
@Override
public MainContract.View get() {
return Preconditions.checkNotNull(
module.provideMainView(), "Cannot return null from a [email protected] @Provides method");
}
public static Factory<MainContract.View> create(MainModule module) {
return new MainModule_ProvideMainViewFactory(module);
}
}
See the class name above , We found a correspondence , stay MainModule As defined in @Provides The decorated method will correspondingly generate a factory class , Here is MainModule_ProvideMainViewFactory. We see one in this class get() Method , Which calls MainModule Inside provideMainView() Method to return the dependencies we need MainContract.View. Remember when MainPresenter_Factory Inside get() In the method , Instantiation MainPresenter Time parameters viewProvider.get() Do you ? By this point, we will understand , The original viewProvider That's generated MainModule_ProvideMainViewFactory, And then invoked it. get() Method , Will be what we need MainContract.View Injection into MainPresenter in .
Seeing this, we should understand MainPresenter The instantiation process .MainPresenter There will be a corresponding factory class , In this class get() Method MainPresenter establish , and MainPresenter The required View rely on , By MainModule As defined in provide The factory class corresponding to the method at the beginning provides .
Although we understand the creation process of instantiation , But at this time, I'm still a little confused ,MainPresenter_Factory Created by create() Accomplished , that crate Where was it called , The MainPresenter How does the instance relate to @Inject Annotated MainPresenter Related to , I think you already know the answer , That's right is Component. As I said before Component Is the connection @Module and @Inject The bridge , So the above doubts will be after compilation Component Find the answer in the corresponding class .
@Component(modules = MainModule.class)
public interface MainComponent {
void inject(MainActivity activity);
}
public final class DaggerMainComponent implements MainComponent {
private Provider<MainContract.View> provideMainViewProvider;
private Provider<MainPresenter> mainPresenterProvider;
private MembersInjector<MainActivity> mainActivityMembersInjector;
private DaggerMainComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.provideMainViewProvider = MainModule_ProvideMainViewFactory.create(builder.mainModule);
this.mainPresenterProvider = MainPresenter_Factory.create(provideMainViewProvider);
this.mainActivityMembersInjector = MainActivity_MembersInjector.create(mainPresenterProvider);
}
@Override
public void inject(MainActivity activity) {
mainActivityMembersInjector.injectMembers(activity);
}
public static final class Builder {
private MainModule mainModule;
private Builder() {
}
public MainComponent build() {
if (mainModule == null) {
throw new IllegalStateException(MainModule.class.getCanonicalName() + " must be set");
}
return new DaggerMainComponent(this);
}
public Builder mainModule(MainModule mainModule) {
this.mainModule = Preconditions.checkNotNull(mainModule);
return this;
}
}
}
From the code above, we can see the definition MainComponent Will generate a corresponding DaggerMainComponent, And implemented MainComponent The method in . We see it in the code again Provide Member properties of type , I said this before Provide Type is the dependency provided , We are looking at where they are instantiated . We see a initialize() Method
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.provideMainViewProvider = MainModule_ProvideMainViewFactory.create(builder.mainModule);
this.mainPresenterProvider = MainPresenter_Factory.create(provideMainViewProvider);
this.mainActivityMembersInjector = MainActivity_MembersInjector.create(mainPresenterProvider);
}
Seeing this, I guess I understand my doubts just now . First, I created MainModule_ProvideMainViewFactory example , To provide with MainContract.View rely on . There may be a little doubt here ,create() Method returns Factory type , and provideMainViewProvider It's a Provider type , In fact, you can see from the source code ,Factory Inherited from Provider.
public interface Factory<T> extends Provider<T> {
}
And then provideMainViewProvider Pass on to MainPresenter_Factory in , That is, as mentioned above viewProvider. And then put this mainPresenterProvider And then to MainActivity_MembersInjector To instantiate , We see that the front of this class is MainActivity start , So we can think of MainActivity The corresponding injection class , We will analyze this class later .
Then we are MainComponent The definition of Inject Method implementation , Here we call mainActivityMembersInjector.injectMembers(activity) Method , Take us MainActivity Inject into this class .
Then there is Builder Inner class , Used to create our module And its own instance . So in DaggerMainComponent Is mainly used to initialize dependencies , And really rely on Inject What is related is just MainActivity_MembersInjector class , Let's see what's done in this class .
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
private final Provider<MainPresenter> mainPresenterProvider;
public MainActivity_MembersInjector(Provider<MainPresenter> mainPresenterProvider) {
assert mainPresenterProvider != null;
this.mainPresenterProvider = mainPresenterProvider;
}
public static MembersInjector<MainActivity> create(
Provider<MainPresenter> mainPresenterProvider) {
return new MainActivity_MembersInjector(mainPresenterProvider);
}
@Override
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.mainPresenter = mainPresenterProvider.get();
}
public static void injectMainPresenter(
MainActivity instance, Provider<MainPresenter> mainPresenterProvider) {
instance.mainPresenter = mainPresenterProvider.get();
}
}
The key to this class is injectMembers() Method , Remember where this method is called ? I'm sure you remember , Just mentioned DaggerMainComponent Class inject() In the method , So here instance The example is by DaggerMainComponent Provided , Then we see the most critical code
instance.mainPresenter = mainPresenterProvider.get();
See this , I think everything should be clear , take mainPresenterProvider Created in MainPresenter Instance assigned to instance(MainActivity) Members of mainPresenter, So we use @Inject Dimensioned mainPresenter You get instantiation , Then it can be used in the code .
Come here , That's the end of the analysis Dagger2 The injection process , If you don't look at these generated classes , It's hard to understand how the whole process happened , As a result, I still don't know how to use this dependency injection framework . So it is very important to understand the internal implementation principle , At the beginning of learning, I was also confused , I can't figure out the relationship between them , I don't know how to write , After understanding the whole context , Find out how to use .
About Dagger2 I won't talk about other uses of , You can see other excellent blogs , I will attach a link later , Easy to learn .Dagger2 It is a dependency injection tool , As for how to use it, it's entirely personal , So don't worry about how to write correctly , Just understand the principle , Flexible use of , Be able to decouple as much as possible , The best way to write it is to suit your own business .
边栏推荐
- Share the newly released web application development framework based on blazor Technology
- Sqoop command
- JWT漏洞复现
- How to define a unified response object gracefully
- Qrcode: generate QR code from text
- Yyds dry goods inventory embedded matrix
- Kubernetes - Multi cluster management
- Delphi free memory
- About MySQL database connection exceptions
- [groovy] string (string injection function | asBoolean | execute | minus)
猜你喜欢
[positioning in JS]
[luat-air105] 4.1 file system FS
[an Xun cup 2019] not file upload
[web Audit - source code disclosure] obtain source code methods and use tools
线程基础知识
[software reverse analysis tool] disassembly and decompilation tool
Tencent cloud, realize image upload
Azkaban installation and deployment
The perfect car for successful people: BMW X7! Superior performance, excellent comfort and safety
Azkaban actual combat
随机推荐
UE4 DMX和grandMA2 onPC 3.1.2.5的操作流程
Clickhouse物化视图
[web Audit - source code disclosure] obtain source code methods and use tools
[2022 repair version] community scanning code into group activity code to drain the complete operation source code / connect the contract free payment interface / promote the normal binding of subordi
Accuracy problem and solution of BigDecimal
天干地支纪年法中为什么是60年一个轮回,而不是120年
Sqoop installation
有個疑問 flink sql cdc 的話可以設置並行度麼, 並行度大於1會有順序問題吧?
Jd.com 2: how to prevent oversold in the deduction process of commodity inventory?
error Couldn‘t find a package.json file in “你的路径“
问下,这个ADB mysql支持sqlserver吗?
【web審計-源碼泄露】獲取源碼方法,利用工具
ICSI213/IECE213 Data Structures
Daily question 2 12
[Chongqing Guangdong education] 2777t green space planning reference questions of National Open University in autumn 2018
Mongodb common commands
线程基础知识
Logstash、Fluentd、Fluent Bit、Vector? How to choose the appropriate open source log collector
Azkaban overview
The architect started to write a HelloWorld