Annotation processor Implementation
This commit is contained in:
parent
c1b8d92461
commit
edf94325d8
19 changed files with 484 additions and 0 deletions
1
annotation/.gitignore
vendored
Normal file
1
annotation/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
12
annotation/build.gradle
Normal file
12
annotation/build.gradle
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
plugins {
|
||||
id 'java-library'
|
||||
id 'kotlin'
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
sourceCompatibility = "8"
|
||||
targetCompatibility = "8"
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package org.fnives.library.reloadable.module.annotation
|
||||
|
||||
/**
|
||||
* Annotate your custom Annotation with this to apply the annotation processor.
|
||||
*
|
||||
* The annotation processor will generate 2 classes for you.
|
||||
*
|
||||
* First will be an interface Reload<YourAnnotation>Module with only one method, reload.
|
||||
* This Reload<YourAnnotation>Module can be injected anywhere where you want to reload the module.
|
||||
*
|
||||
* Second will be a class Reload<YourAnnotation>ModuleImpl which is the actual Module implementation for Hilt.
|
||||
* This provides every class which constructor you annotate with YourAnnotation.
|
||||
*
|
||||
* Reload in this context means, every instance will be cleared and the next time Hilt accesses it, a new will be created.
|
||||
* This newly created instance is reused until the next reload call.
|
||||
*/
|
||||
@Target(AnnotationTarget.ANNOTATION_CLASS)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class ReloadableModule
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
buildscript {
|
||||
ext.kotlin_version = "1.5.21"
|
||||
ext.hilt_version = "2.38.1"
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
|
|
|
|||
1
processor/.gitignore
vendored
Normal file
1
processor/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
32
processor/build.gradle
Normal file
32
processor/build.gradle
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
apply plugin: 'java-library'
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
testLogging {
|
||||
events 'started', 'passed', 'skipped', 'failed'
|
||||
exceptionFormat "full"
|
||||
showStandardStreams true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
implementation "com.squareup:javapoet:1.13.0"
|
||||
implementation project(":annotation")
|
||||
implementation "com.google.dagger:hilt-core:$hilt_version"
|
||||
|
||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.7.0"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-engine:5.7.0"
|
||||
testImplementation "com.github.tschuchortdev:kotlin-compile-testing:1.4.2"
|
||||
testRuntimeOnly("org.jetbrains.kotlin:kotlin-compiler-embeddable:$kotlin_version")
|
||||
testRuntimeOnly("org.jetbrains.kotlin:kotlin-annotation-processing-embeddable:$kotlin_version")
|
||||
}
|
||||
|
||||
sourceCompatibility = "8"
|
||||
targetCompatibility = "8"
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
package org.fnives.library.reloadable.module.processor
|
||||
|
||||
import com.squareup.javapoet.AnnotationSpec
|
||||
import com.squareup.javapoet.ClassName
|
||||
import com.squareup.javapoet.CodeBlock
|
||||
import com.squareup.javapoet.FieldSpec
|
||||
import com.squareup.javapoet.JavaFile
|
||||
import com.squareup.javapoet.MethodSpec
|
||||
import com.squareup.javapoet.ParameterSpec
|
||||
import com.squareup.javapoet.ParameterizedTypeName
|
||||
import com.squareup.javapoet.TypeName
|
||||
import com.squareup.javapoet.TypeSpec
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import org.fnives.library.reloadable.module.processor.GenerateReloadModuleUseCase.Companion.getModuleUseCaseName
|
||||
import javax.annotation.processing.Filer
|
||||
import javax.annotation.processing.RoundEnvironment
|
||||
import javax.inject.Provider
|
||||
import javax.lang.model.element.ExecutableElement
|
||||
import javax.lang.model.element.Modifier
|
||||
import javax.lang.model.element.TypeElement
|
||||
import javax.lang.model.util.Elements
|
||||
|
||||
class GenerateReloadModule(private val elementUtils: Elements, private val filer: Filer) {
|
||||
|
||||
fun createAndWrite(
|
||||
typeElement: TypeElement,
|
||||
roundEnvironment: RoundEnvironment
|
||||
) {
|
||||
val annotatedElements = roundEnvironment.getElementsAnnotatedWith(typeElement)
|
||||
.filterIsInstance<ExecutableElement>()
|
||||
val annotatedTypes = annotatedElements.map { it.enclosingElement as TypeElement }
|
||||
|
||||
val reloadFunctionSpec = createReloadFunctionSpec(annotatedTypes)
|
||||
val cachedProperties = annotatedTypes.map(::getPropertySpec)
|
||||
val provideMethods = annotatedElements.mapIndexed { index, it ->
|
||||
createProvideMethodForDependency(it, annotatedTypes[index])
|
||||
}
|
||||
val typeSpec = createTypeSpec(typeElement, reloadFunctionSpec, provideMethods, cachedProperties)
|
||||
|
||||
val packageName = elementUtils.getPackageOf(typeElement).toString()
|
||||
writeToSourceFile(typeSpec, packageName, filer)
|
||||
}
|
||||
|
||||
private fun writeToSourceFile(typeSpec: TypeSpec, packageName: String, filer: Filer) {
|
||||
JavaFile.builder(packageName, typeSpec).build().writeTo(filer)
|
||||
}
|
||||
|
||||
private fun getPropertySpec(typeElement: TypeElement): FieldSpec =
|
||||
FieldSpec.builder(TypeName.get(typeElement.asType()), typeElement.cachedPropertyName())
|
||||
.initializer("null")
|
||||
.addModifiers(Modifier.PRIVATE)
|
||||
.build()
|
||||
|
||||
private fun createProvideMethodForDependency(
|
||||
constructorElement: ExecutableElement,
|
||||
dependencyType: TypeElement
|
||||
): MethodSpec {
|
||||
val constructorParameters = constructorElement.parameters.map { "${it.simpleName}.get()" }.joinToString(", ")
|
||||
val functionElement = MethodSpec.methodBuilder("provide${dependencyType.simpleName}")
|
||||
.addAnnotation(Provides::class.java)
|
||||
.returns(TypeName.get(dependencyType.asType()))
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.addCode(
|
||||
CodeBlock.of(
|
||||
"if (${dependencyType.cachedPropertyName()} == null) {\n" +
|
||||
" ${dependencyType.cachedPropertyName()} = new ${"$"}T ($constructorParameters);\n" +
|
||||
"}\n" +
|
||||
"return ${dependencyType.cachedPropertyName()};",
|
||||
dependencyType
|
||||
)
|
||||
)
|
||||
|
||||
return constructorElement.parameters.fold(functionElement) { methodSpecBuilder, variableElement ->
|
||||
val className = TypeName.get(variableElement.asType())
|
||||
val providerTypeSpec = ParameterizedTypeName.get(ClassName.get(Provider::class.java), className)
|
||||
|
||||
methodSpecBuilder.addParameter(
|
||||
ParameterSpec.builder(providerTypeSpec, variableElement.simpleName.toString())
|
||||
.addAnnotations(variableElement.annotationMirrors.map { AnnotationSpec.get(it) })
|
||||
.build()
|
||||
)
|
||||
}.build()
|
||||
}
|
||||
|
||||
|
||||
private fun createProvideMethodForReloadModuleUseCase(typeElement: TypeElement): MethodSpec =
|
||||
MethodSpec.methodBuilder("provide${getModuleUseCaseName(typeElement)}")
|
||||
.addAnnotation(Provides::class.java)
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.returns(GenerateReloadModuleUseCase.getTypeName(typeElement, elementUtils))
|
||||
.addCode("return this;")
|
||||
.build()
|
||||
|
||||
private fun createTypeSpec(typeElement: TypeElement, reloadFunctionSpec: MethodSpec, provideMethods: List<MethodSpec>, cacheProperties: List<FieldSpec>) =
|
||||
TypeSpec.classBuilder(getModuleUseCaseName(typeElement) + "Impl")
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.addSuperinterface(GenerateReloadModuleUseCase.getTypeName(typeElement, elementUtils))
|
||||
.addAnnotation(Module::class.java)
|
||||
.addAnnotation(
|
||||
AnnotationSpec.builder(InstallIn::class.java)
|
||||
.addMember("value", "\$T.class", SingletonComponent::class.java)
|
||||
.build()
|
||||
)
|
||||
.addFields(cacheProperties)
|
||||
.addMethod(createProvideMethodForReloadModuleUseCase(typeElement))
|
||||
.addMethods(provideMethods)
|
||||
.addMethod(reloadFunctionSpec)
|
||||
.build()
|
||||
|
||||
private fun createReloadFunctionSpec(annotatedElements: List<TypeElement>): MethodSpec {
|
||||
val reloadFunctionSpec = MethodSpec.methodBuilder(GenerateReloadModuleUseCase.getReloadMethodName())
|
||||
.addAnnotation(Override::class.java)
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
return annotatedElements.fold(reloadFunctionSpec) { methodSpecBuilder, typeElement ->
|
||||
methodSpecBuilder.addCode("${typeElement.cachedPropertyName()} = null;")
|
||||
}.build()
|
||||
}
|
||||
|
||||
// private fun createImplementation(interfaceTypeSpec: TypeSpec, typeElement: TypeElement, roundEnvironment: RoundEnvironment): TypeSpec {
|
||||
// val annotatedElements = roundEnvironment.getElementsAnnotatedWith(typeElement)
|
||||
// .filterIsInstance<ExecutableElement>()
|
||||
//
|
||||
// val reloadFunctionSpec = MethodSpec.methodBuilder("reload")
|
||||
// .addAnnotation(Override::class.java)
|
||||
// .addModifiers(Modifier.PUBLIC)
|
||||
// val moduleImplName = "Reloadable${typeElement.simpleName}Impl"
|
||||
// val typeSpecBuilder = TypeSpec.classBuilder(moduleImplName)
|
||||
// .addModifiers(Modifier.PUBLIC)
|
||||
// .addSuperinterface(interfaceTypeSpec.asTypeName(typeElement))
|
||||
// .addAnnotation(Module::class.java)
|
||||
// .addAnnotation(
|
||||
// AnnotationSpec.builder(InstallIn::class.java)
|
||||
// .addMember("value", "\$T.class", SingletonComponent::class.java)
|
||||
// .build()
|
||||
// )
|
||||
// .addMethod(
|
||||
// MethodSpec.methodBuilder("provideReloadable${typeElement.simpleName}")
|
||||
// .addAnnotation(Provides::class.java)
|
||||
// .addModifiers(Modifier.PUBLIC)
|
||||
// .returns(interfaceTypeSpec.asTypeName(typeElement))
|
||||
// .addCode("return this;")
|
||||
// .build()
|
||||
// )
|
||||
//
|
||||
// annotatedElements.forEach { constructorElement ->
|
||||
// val annotatedType = constructorElement.enclosingElement as TypeElement
|
||||
// val annotatedTypeClassName = ClassName.bestGuess(annotatedType.qualifiedName.toString())
|
||||
// val memberElement = FieldSpec.builder(
|
||||
// annotatedTypeClassName,
|
||||
// "cached${annotatedType.simpleName}"
|
||||
// )
|
||||
// .initializer("null")
|
||||
// .addModifiers(Modifier.PRIVATE)
|
||||
// .build()
|
||||
// reloadFunctionSpec.addCode("cached${annotatedType.simpleName} = null;")
|
||||
// val constructorParameters = constructorElement.parameters.map { "${it.simpleName}.get()" }.joinToString(", ")
|
||||
// val functionElement = MethodSpec.methodBuilder("provide${annotatedType.simpleName}")
|
||||
// .addAnnotation(Provides::class.java)
|
||||
// .returns(annotatedTypeClassName)
|
||||
// .addModifiers(Modifier.PUBLIC)
|
||||
// .addCode(
|
||||
// CodeBlock.of(
|
||||
// """if (cached${annotatedType.simpleName} == null) {
|
||||
// cached${annotatedType.simpleName} = new ${"$"}T ($constructorParameters);
|
||||
// }
|
||||
// return cached${annotatedType.simpleName};
|
||||
// """.trim(),
|
||||
// annotatedType
|
||||
// )
|
||||
// )
|
||||
//
|
||||
// constructorElement.parameters.forEach { variableElement ->
|
||||
// val className = TypeName.get(variableElement.asType())
|
||||
// val providerTypeSpec = ParameterizedTypeName.get(ClassName.get(Provider::class.java), className)
|
||||
// functionElement.addParameter(
|
||||
// ParameterSpec.builder(providerTypeSpec, variableElement.simpleName.toString())
|
||||
// .addAnnotations(variableElement.annotationMirrors.map { AnnotationSpec.get(it) })
|
||||
// .build()
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// typeSpecBuilder.addField(memberElement)
|
||||
// typeSpecBuilder.addMethod(functionElement.build())
|
||||
// }
|
||||
//
|
||||
//
|
||||
// typeSpecBuilder.addMethod(reloadFunctionSpec.build())
|
||||
//
|
||||
// return typeSpecBuilder.build()
|
||||
// }
|
||||
//
|
||||
// private fun TypeSpec.asTypeName(typeElement: TypeElement) =
|
||||
// ClassName.bestGuess(elementUtils.getPackageOf(typeElement).toString() + "." + name.orEmpty())
|
||||
|
||||
companion object {
|
||||
private fun TypeElement.cachedPropertyName() = "cached${simpleName}"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package org.fnives.library.reloadable.module.processor
|
||||
|
||||
import com.squareup.javapoet.ClassName
|
||||
import com.squareup.javapoet.JavaFile
|
||||
import com.squareup.javapoet.MethodSpec
|
||||
import com.squareup.javapoet.TypeSpec
|
||||
import javax.annotation.processing.Filer
|
||||
import javax.lang.model.element.Modifier
|
||||
import javax.lang.model.element.TypeElement
|
||||
import javax.lang.model.util.Elements
|
||||
|
||||
class GenerateReloadModuleUseCase(private val elementUtils: Elements, private val filer: Filer) {
|
||||
|
||||
fun createAndWrite(typeElement: TypeElement) {
|
||||
val typeSpec = createTypeSpec(getModuleUseCaseName(typeElement))
|
||||
val packageName = elementUtils.getPackageOf(typeElement).toString()
|
||||
writeToSourceFile(typeSpec, packageName, filer)
|
||||
}
|
||||
|
||||
private fun createTypeSpec(moduleUseCaseName: String) =
|
||||
TypeSpec.interfaceBuilder(moduleUseCaseName)
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.addMethod(
|
||||
MethodSpec.methodBuilder(getReloadMethodName())
|
||||
.addModifiers(Modifier.ABSTRACT)
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
|
||||
private fun writeToSourceFile(typeSpec: TypeSpec, packageName: String, filer: Filer) {
|
||||
JavaFile.builder(packageName, typeSpec).build().writeTo(filer)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getModuleUseCaseName(typeElement: TypeElement) = "Reload${typeElement.simpleName}Module"
|
||||
|
||||
fun getReloadMethodName() = "reload"
|
||||
|
||||
fun getTypeName(typeElement: TypeElement, elementUtils: Elements) =
|
||||
ClassName.bestGuess("${elementUtils.getPackageOf(typeElement)}.${getModuleUseCaseName(typeElement)}")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package org.fnives.library.reloadable.module.processor
|
||||
|
||||
import org.fnives.library.reloadable.module.annotation.ReloadableModule
|
||||
import javax.annotation.processing.AbstractProcessor
|
||||
import javax.annotation.processing.Filer
|
||||
import javax.annotation.processing.Messager
|
||||
import javax.annotation.processing.ProcessingEnvironment
|
||||
import javax.annotation.processing.RoundEnvironment
|
||||
import javax.lang.model.SourceVersion
|
||||
import javax.lang.model.element.TypeElement
|
||||
import javax.lang.model.util.Elements
|
||||
import javax.lang.model.util.Types
|
||||
|
||||
class ReloadableModuleProcessor : AbstractProcessor() {
|
||||
|
||||
private lateinit var filer: Filer
|
||||
private lateinit var messager: Messager
|
||||
private lateinit var elementUtils: Elements
|
||||
private lateinit var typeUtils: Types
|
||||
private lateinit var generateReloadModuleUseCase: GenerateReloadModuleUseCase
|
||||
private lateinit var generateReloadModule: GenerateReloadModule
|
||||
private val supportedAnnotations = setOf(ReloadableModule::class.java)
|
||||
|
||||
@Synchronized
|
||||
override fun init(processingEnvironment: ProcessingEnvironment) {
|
||||
super.init(processingEnvironment)
|
||||
filer = processingEnvironment.filer
|
||||
messager = processingEnvironment.messager
|
||||
elementUtils = processingEnvironment.elementUtils
|
||||
typeUtils = processingEnvironment.typeUtils
|
||||
generateReloadModuleUseCase = GenerateReloadModuleUseCase(elementUtils, filer)
|
||||
generateReloadModule = GenerateReloadModule(elementUtils, filer)
|
||||
}
|
||||
|
||||
override fun getSupportedAnnotationTypes(): Set<String> =
|
||||
supportedAnnotations.map(Class<*>::getCanonicalName).toSet()
|
||||
|
||||
override fun getSupportedSourceVersion(): SourceVersion = SourceVersion.latestSupported()
|
||||
|
||||
override fun process(set: Set<TypeElement>, roundEnvironment: RoundEnvironment): Boolean {
|
||||
if (roundEnvironment.processingOver()) return false
|
||||
val annotatedElements = supportedAnnotations.flatMap(roundEnvironment::getElementsAnnotatedWith).filterIsInstance<TypeElement>()
|
||||
|
||||
annotatedElements.forEach {
|
||||
generateReloadModuleUseCase.createAndWrite(it)
|
||||
generateReloadModule.createAndWrite(it, roundEnvironment)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
org.fnives.library.reloadable.module.processor.ReloadableModuleProcessor,isolating
|
||||
|
|
@ -0,0 +1 @@
|
|||
org.fnives.library.reloadable.module.processor.ReloadableModuleProcessor
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package org.fnives.library.reloadable.module.processor
|
||||
|
||||
import java.io.File
|
||||
import java.nio.file.Paths
|
||||
|
||||
/**
|
||||
+ * Helper class which read the file in the resources folder with the given [fileName] into a string, each line separated with [lineDelimiter].
|
||||
+ */
|
||||
fun Any.readResourceFileToString(fileName: String): String {
|
||||
val path = this::class.java.classLoader.getResource(fileName).toURI().path
|
||||
return File(Paths.get(path).toUri()).readText()
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
package org.fnives.library.reloadable.module.processor
|
||||
|
||||
import com.tschuchort.compiletesting.KotlinCompilation
|
||||
import com.tschuchort.compiletesting.SourceFile
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class TestReloadableModuleProcessor {
|
||||
|
||||
@Test
|
||||
fun testSimpleSetup() {
|
||||
val userModuleInput = readResourceFileToString("simple/input/UserModuleInject.kt")
|
||||
val providedByUserModule = readResourceFileToString("simple/input/ProvidedByUserModule.kt")
|
||||
val fooDependency = readResourceFileToString("simple/input/FooDependency.kt")
|
||||
|
||||
val result = KotlinCompilation().apply {
|
||||
sources = listOf(
|
||||
SourceFile.kotlin("UserModule.kt", userModuleInput),
|
||||
SourceFile.kotlin("ProvidedByUserModule.kt", providedByUserModule),
|
||||
SourceFile.kotlin("FooDependency.kt", fooDependency)
|
||||
)
|
||||
|
||||
annotationProcessors = listOf(ReloadableModuleProcessor())
|
||||
|
||||
inheritClassPath = true
|
||||
messageOutputStream = System.out // see diagnostics in real time
|
||||
}.compile()
|
||||
|
||||
val generatedFiles = result.generatedFiles.toList()
|
||||
val reloadableUserModule = generatedFiles[0].readText()
|
||||
val reloadableUserModuleImpl = generatedFiles[1].readText()
|
||||
AssertionsAssertEqualsIgnoringMultipleSpaces(
|
||||
readResourceFileToString("simple/expected/ReloadUserModuleInjectModule.java"),
|
||||
reloadableUserModule
|
||||
)
|
||||
AssertionsAssertEqualsIgnoringMultipleSpaces(
|
||||
readResourceFileToString("simple/expected/ReloadUserModuleInjectModuleImpl.java"),
|
||||
reloadableUserModuleImpl
|
||||
)
|
||||
Assertions.assertEquals(KotlinCompilation.ExitCode.OK, result.exitCode)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun String.replaceSpacesWithOnlyOne(): String = replace(" *".toRegex(), " ")
|
||||
|
||||
@Suppress("TestFunctionName")
|
||||
private fun AssertionsAssertEqualsIgnoringMultipleSpaces(expected: String, actual: String) =
|
||||
Assertions.assertEquals(expected.replaceSpacesWithOnlyOne(), actual.replaceSpacesWithOnlyOne())
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package simple.input;
|
||||
|
||||
public interface ReloadUserModuleInjectModule {
|
||||
void reload();
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package simple.input;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import dagger.hilt.InstallIn;
|
||||
import dagger.hilt.components.SingletonComponent;
|
||||
import java.lang.Override;
|
||||
import javax.inject.Provider;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent.class)
|
||||
public class ReloadUserModuleInjectModuleImpl implements ReloadUserModuleInjectModule {
|
||||
private ProvidedByUserModule cachedProvidedByUserModule = null;
|
||||
|
||||
@Provides
|
||||
public ReloadUserModuleInjectModule provideReloadUserModuleInjectModule() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Provides
|
||||
public ProvidedByUserModule provideProvidedByUserModule(
|
||||
@NotNull Provider<FooDependency> fooDependency) {
|
||||
if (cachedProvidedByUserModule == null) {
|
||||
cachedProvidedByUserModule = new ProvidedByUserModule (fooDependency.get());
|
||||
}
|
||||
return cachedProvidedByUserModule;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
cachedProvidedByUserModule = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package simple.input
|
||||
|
||||
import javax.inject.Inject
|
||||
|
||||
class FooDependency @Inject constructor()
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package simple.input
|
||||
|
||||
import org.jetbrains.annotations.NotNull
|
||||
|
||||
class ProvidedByUserModule @UserModuleInject constructor(@NotNull fooDependency: FooDependency)
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package simple.input
|
||||
|
||||
import org.fnives.library.reloadable.module.annotation.ReloadableModule
|
||||
|
||||
@ReloadableModule
|
||||
annotation class UserModuleInject
|
||||
|
|
@ -8,3 +8,5 @@ dependencyResolutionManagement {
|
|||
}
|
||||
rootProject.name = "HiltReloadableModule"
|
||||
include ':app'
|
||||
include ':processor'
|
||||
include ':annotation'
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue