{"id":22707653,"url":"https://github.com/xvik/guice-ext-annotations","last_synced_at":"2025-09-11T12:38:53.090Z","repository":{"id":18189670,"uuid":"21307670","full_name":"xvik/guice-ext-annotations","owner":"xvik","description":"Guice annotations extensions","archived":false,"fork":false,"pushed_at":"2024-04-12T23:34:34.000Z","size":623,"stargazers_count":22,"open_issues_count":6,"forks_count":5,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-27T03:35:14.976Z","etag":null,"topics":["guice","java"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/xvik.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2014-06-28T17:28:53.000Z","updated_at":"2024-01-24T04:34:02.000Z","dependencies_parsed_at":"2023-11-18T06:25:22.109Z","dependency_job_id":"46328610-84d4-43fc-8ba2-c737b54fc2e7","html_url":"https://github.com/xvik/guice-ext-annotations","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xvik%2Fguice-ext-annotations","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xvik%2Fguice-ext-annotations/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xvik%2Fguice-ext-annotations/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xvik%2Fguice-ext-annotations/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xvik","download_url":"https://codeload.github.com/xvik/guice-ext-annotations/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248714649,"owners_count":21149931,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["guice","java"],"created_at":"2024-12-10T10:13:45.095Z","updated_at":"2025-04-13T12:34:24.966Z","avatar_url":"https://github.com/xvik.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Guice annotations extensions\n[![License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](http://www.opensource.org/licenses/MIT)\n[![CI](https://github.com/xvik/guice-ext-annotations/actions/workflows/CI.yml/badge.svg)](https://github.com/xvik/guice-ext-annotations/actions/workflows/CI.yml)\n[![Appveyor build status](https://ci.appveyor.com/api/projects/status/github/xvik/guice-ext-annotations?svg=true)](https://ci.appveyor.com/project/xvik/guice-ext-annotations)\n[![codecov](https://codecov.io/gh/xvik/guice-ext-annotations/branch/master/graph/badge.svg)](https://codecov.io/gh/xvik/guice-ext-annotations)\n\nSupport: [Gitter chat](https://gitter.im/xvik/guice-ext-annotations)\n\n### About\n\nGuice annotations support extensions.\n\nFeatures:\n* Allows using interfaces and abstract classes as guice beans: abstract methods must be handled with guice aop\n* Additional annotations:\n  * @Log: auto inject slf4j logger\n  * JSR-250 @PostConstruct: annotated method called after bean initialization\n  * JSR-250 @PreDestroy: annotated method called before shutdown\n* Simplifies [TypeListener](http://google.github.io/guice/api-docs/latest/javadoc/index.html?com/google/inject/spi/TypeListener.html) api for cases:\n  * Add custom field annotation support\n  * Add custom method annotation support\n  * Add post processing for beans of some type (e.g. implementing interface or extending some abstract class)\n\n### Thanks to\n\n* [Derric Gilling](https://github.com/dgilling) for help with playframework related issues\n\n### Setup\n\n[![Maven Central](https://img.shields.io/maven-central/v/ru.vyarus/guice-ext-annotations.svg?style=flat)](https://maven-badges.herokuapp.com/maven-central/ru.vyarus/guice-ext-annotations)\n\nMaven:\n\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003eru.vyarus\u003c/groupId\u003e\n  \u003cartifactId\u003eguice-ext-annotations\u003c/artifactId\u003e\n  \u003cversion\u003e2.0.1\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nGradle:\n\n```groovy\nimplementation 'ru.vyarus:guice-ext-annotations:2.0.1'\n```\n\n**NOTE**: version 2.0 compatible with jakarta.annotation-api 2 (jakarta.annotation namespace for `@PostConstruct` \nand `@PreDestroy` annotations), but guice still use `javax.inject` namespace.\n\nFor `javax.annotation` use [version 1.4.1](https://github.com/xvik/guice-ext-annotations/tree/1.x) \n\n### Abstract types support\n\nFeature was developed to support repositories in [guice-persist-orient](https://github.com/xvik/guice-persist-orient#defining-repository)\n(look usage examples).\n\n#### Problem\n\nSuppose you have annotation to simplify sql query definition:\n\n```java\n@Query(\"select from Model\")\nList\u003cModel\u003e list() {\n    throw new UnsupportedOperationException();\n}\n```\n\nSupport for such annotation could be easily implemented with [guice aop](https://github.com/google/guice/wiki/AOP).\nBut we will always have to declare method body.\n\nTo avoid this, [guice-persist](https://github.com/google/guice/wiki/GuicePersist) creates JDK proxies from interfaces with annotated methods.\nIt solves problem with redundant method body, but did not allow using guice aop on such beans.\n\n#### Solution\n\nGuice needs to control bean instance creation to properly apply aop, so solution is simple: dynamically create\nimplementation class from abstract class or interface, and let guice instantiate bean from it.\n\nAdditional actions during class generation:\n* Annotations copied from abstract type (class or interface) to allow aop correctly resolve them\n* If abstract bean use constructor injection, the same constructor will be created in implementation (including all\nconstructor and parameters annotations and generic signatures).\n\nAll this allows thinking of abstract type as of usual guice bean.\n\n#### Setup\n\nIn order to use dynamic proxies, add dependency on javassist library:\n\n```groovy\nimplementation 'org.javassist:javassist:3.28.0-GA'\n```\n\nMinimal required version is 3.24.\n\nNOTE: javassist used instead of cglib, because cglib can't manipulate annotations.\n\n#### Usage\n\nThere are two options: declare bean directly in module or rely on JIT based declaration.\n\nFor first option, generate class in your guice module:\n\n```java\nbind(MyAbstractType.class).to(DynamicClassGenerator.generate(MyAbstractType.class));\n```\n\nIf you don't want to declare manually and prefer minimal configuration with\n[JIT](https://github.com/google/guice/wiki/JustInTimeBindings) bindings, use `@ProvidedBy`:\n\n```java\n@ProvidedBy(DynamicClassProvider.class)\npublic interface MyAbstractType {...}\n```\n\n(the same for abstract class)\n\nAfter all, bean could be injected as usual:\n\n```java\n@Inject MyAbstractType myBean;\n```\n\nDon't forget that all abstract methods must be handled with aop: otherwise you will get abstract method call exception.\n\n#### Class loaders\n\nMay be used within complex classloader hierarchies (like playframework dev mode). \n\nClass loader of abstract type is used to generate implementation class. Generated class is first checked fo existence in this \nclass loader and, if not found, generated and attached to that class loader. For example, if the same class will be loaded by different\nclass loaders, then generator will generate different implementations.\nIn play dev mode this will mean that after hot reload (classloader with your abstract class replace) generator will \ngenerate new implementation for new (updated) abstract class.\n\nClass generation is thread safe: synchronized on abstract type to allow concurrent generation for different classes and prevent\nduplicate generations.\n\n#### Limitation\n\nThere is only one limitation: you can't use scope annotations directly on abstract types - guice doesn't allow it.\nTo workaround it use wrapper annotation:\n\n```java\n@ScopeAnnotation(Singleton.class)\n@ProvidedBy(DynamicClassProvider.class)\npublic interface MyAbstractType {...}\n```\n\nNOTE: yes, annotation named the same as guice's own annotation, but name is so good and they will never met in one class.\n\n#### Singletons\n\nSingletons are pretty common case. To simplify singletons definition special provider could be used:\n\n```java\n@ProvidedBy(DynamicSingletonProvider.class)\npublic interface MyAbstractType {...}\n```\n\nThis is completely equivalent to code in limitations section, but requires just one annotation.\n\nIf, by accident, singleton provider will be used with `@ScopeAnnotation`, error will be thrown.\n\n#### Child injectors and private modules\n\nWhen JIT (dynamic) resolution is used (bindings not described in module) then, during dynamic binding creation,\nguice will try to create binding in upper most injector (in order to re-use instance in possibly other\nchild injectors (java class loaders works the same way by the same reason)).\n\nFor example, suppose there is some root injector and your module with aop (used to handle annotations on abstract classes)\nis declared in child injector.\n\n```java\nGuice.createInjector().createChildInjector(new MyAopModule());\n```\n\nSome abstract type without explicit binding:\n\n```java\n@ProvidedBy(DynamicClassProvider.class)\npublic interface MyAbstractBean {}\n```\n\nIf some service depends on this abstract type (injects it), then JIT binding for MyAbstractBean will be created\nin root(!) module. But aop to handle your custom annotations is declared in child module and so you will get\nabstract method call exception when try to call any method of abstract bean.\n\nFor example, such case could appear with test-ng guice integration, which always start your modules as child injector.\n\nIn general, there are two ways to workaround such situation:\n* Manually declare all required bindings in child module\n* Abstract type must depend on some other bean in child injector (this will force JIT binding to be created in child module (prevent bubbling up))\n\n##### Solution \n\nOut of the box, special module is available to solve this problem: `GeneratorAnchorModule`.\n\nIf it will be used in example above, then JIT bindings for abstract types will be created in child injector automatically (fixing behaviour):\n\n```java\nGuice.createInjector().createChildInjector(new MyAopModule(), new GeneratorAnchorModule());\n```\n\nModule use \"anchor\" technic: dummy bean (AnchorBean) is registered in child injector and all dynamically generated classes\n(when `@ProvidedBy` used) become dependent of this dummy bean (AnchorBean added to generated implementation class constructor). \n\nAlso, module will allow using dynamic bindings inside private module:\n\n```java\n    public class MyPrivateModule extends PrivateModule {\n        protected void configure() {\n            install(new MyAopModule());\n            install(new GeneratorAnchorModule());\n        }\n\n        @Provides \n        @Exposed \n        @Named(\"myBean\")\n        MyAbstractBean provide(MyAbstractBean bean) {\n            return bean;\n        }\n    }\n\n     Injector injector = Injector.createModule(new MyPrivateModule());\n     injector.getInstance(Key.get(MyAbstractBean.class, Names.named(\"myBean\")))\n```\n\nNote that MyAbstractBean is not bound explicitly, but still correct instance exposed from private module.\n\n### Additional annotations\n\nGuice module adds three annotations support (`@Log`, `@PostConstruct`, `@PreDestroy`) and `Destroyable` types.\n\n#### Install the Guice module\n\n```java\ninstall(new ExtAnnotationsModule());\n```\n\nTo limit processed beans to specific package use:\n\n```java\ninstall(new ExtAnnotationsModule(\"your.package\"));\n```\n\nAlternatively custom object matcher may be used to reduce processed beans:\n\n```java\ninstall(new ExtAnnotationsModule(new YourCustomMatcher()));\n```\n\n#### Usage\n\n##### @Log\n\nAnnotate logger filed:\n\n```java\n@Log\nprivate org.slf4j.Logger logger\n```\n\nOnly Slf4j logger supported. Trying to use annotation with other logger will throw exception on initialization.\n\n##### @PostConstruct\n\nAnnotate bean method to be called after bean initialization.\n\n```java\n@PostConstruct\nprivate void init() { \n    // init logic \n}\n```\n\nAnnotated method must not contain parameters or exception will be thrown on initialization.\n\n##### @PreDestroy\n\nAnnotate bean method to be called before shutdown (by default before jvm shutdown).\n\n```java\n@PreDestroy\nprivate void destroy() { \n    // destroy logic \n}\n```\n\nAnnotated method must not contain parameters or exception will be thrown on initialization.\n\nDestroy processing may be triggered manually by:\n\n```java\ninjector.getBean(ru.vyarus.guice.ext.managed.destroyable.DestroyableManager.class).destroy()\n```\nUseful if guice used in conjunction with some other container (e.g. web container). Destroy may be called any number of times, \nbut actual destroy will be processed only on first execution.\n\n##### Destroyable\n\nAs an alternative to `@PreDestroy`, bean may implement `ru.vyarus.guice.ext.managed.destroyable.Destroyable`\n\n```java\npublic class MyBean implements Destroyable\n```\n\n### Additional api\n\nApi simplifies work with [TypeListener](http://google.github.io/guice/api-docs/latest/javadoc/index.html?com/google/inject/spi/TypeListener.html).\n\n##### Custom field annotation post processor\n\nImplement `ru.vyarus.guice.ext.core.field.FieldPostProcessor`. \nHandle only recoverable exceptions, otherwise it will be handled by `AnnotatedFieldTypeListener`.\n\nFor example:\n\n```java\npublic class Slf4jLogAnnotationProcessor implements FieldPostProcessor\u003cLog\u003e {\n\n    @Override\n    public void process(final Log annotation, final Field field, final Object instance) throws Exception {\n        final Logger logger = LoggerFactory.getLogger(field.getDeclaringClass());\n        field.set(instance, logger);\n    }\n}\n```\n\nRegister in module:\n\n```java\nbindListener(typeMatcher, new AnnotatedFieldTypeListener\u003cLog\u003e(\n                Log.class, new Slf4jLogAnnotationProcessor()));\n```\n\n##### Custom method annotation post processor\n\nImplement `ru.vyarus.guice.ext.core.method.MethodPostProcessor`. \nHandle only recoverable exceptions, otherwise it will be handled by `AnnotatedMethodTypeListener`.\n\nFor example:\n\n```java\npublic class PostConstructAnnotationProcessor implements MethodPostProcessor\u003cPostConstruct\u003e {\n\n    @Override\n    public void process(final PostConstruct annotation, final Method method, final Object instance) throws Exception {\n        Utils.checkNoParams(method);\n        method.invoke(instance);\n    }\n}\n```\n\nRegister in module:\n\n```java\nbindListener(typeMatcher, new AnnotatedMethodTypeListener\u003cPostConstruct\u003e(\n                PostConstruct.class, new PostConstructAnnotationProcessor()));\n```\n\n##### Custom type post processor\n\nImplement `ru.vyarus.guice.ext.core.type.TypePostProcessor`.\nHandle only recoverable exceptions, otherwise it will be handled by `GeneralTypeListener`.\n\nFor example:\n\n```java\npublic class DestroyableTypeProcessor implements TypePostProcessor\u003cDestroyable\u003e {\n    private DestroyableManager manager;\n\n    public DestroyableTypeProcessor(final DestroyableManager manager) {\n        this.manager = manager;\n    }\n\n    @Override\n    public void process(final Destroyable instance) {\n        manager.register(instance);\n    }\n}\n```\n\nRegister in module:\n\n```java\nbindListener(typeMatcher, new GeneralTypeListener\u003cDestroyable\u003e(\n                Destroyable.class, new DestroyableTypeProcessor(manager)));\n```\n\n`GeneralTypeListener` match bean by exact type, super type or interface (direct or super type interface).\n\nExample of [post processing beans extending abstract class](https://github.com/xvik/guice-ext-annotations/blob/master/src/test/java/ru/vyarus/guice/ext/postprocess/TypePostProcessorTest.java)\n\nListener binding may seem to be more complicated than it could: typeMatcher could filter types instead of custom logic.\nThis was done on purpose - to allow using mather for appliance scoping. For example, if only beans from exact packages\nshould be processed, or any other conditions.\n\n---\n[![java lib generator](http://img.shields.io/badge/Powered%20by-%20Java%20lib%20generator-green.svg?style=flat-square)](https://github.com/xvik/generator-lib-java)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxvik%2Fguice-ext-annotations","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxvik%2Fguice-ext-annotations","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxvik%2Fguice-ext-annotations/lists"}