| If you provide |
| [`Closeable`](https://docs.oracle.com/javase/7/docs/api/java/io/Closeable.html) |
| resources through dependency injection, it can be difficult to effectively |
| manage the lifecycle of the Closable: |
| |
| ```java |
| class MyModule extends AbstractModule() { |
| @Provides |
| FileOutputStream provideFileStream() { |
| return new FileOutputStream("/tmp/outfile"); |
| } |
| } |
| |
| ... |
| |
| class Client { |
| private final FileOutputStream fos; |
| @Inject Client(FileOutputStream fos, OtherDependency other, ...) { |
| this.fos = fos; |
| } |
| void doSomething() throws IOException { |
| fos.write("hello!"); |
| } |
| } |
| ``` |
| |
| There are a number of issues with this approach as it relates to resource |
| management: |
| |
| * If multiple Client classes are constructed, multiple output streams are |
| opened against the same file, and writes to the file may clash with each |
| other. |
| * It's not clear which class has the responsibility of closing the |
| `FileOutputStream` resource: |
| |
| * If the resource is created for the sole ownership of `Client`, then it |
| makes sense for the client to close it. |
| * However, if the resource is scoped (e.g.: you add `@Singleton` to the |
| `@Provides` method), then suddenly it's *not* the responsibility of |
| `Client` to close the resource. If one `Client` closes the stream, then |
| all of the other users of that stream will be dealing with a closed |
| resource. There needs to be some other 'resource manager' object that |
| also gets that stream, and its closing functions are call in the right |
| place. That can be tricky to do correctly. |
| |
| * If, for example, the construction of the other dependencies of `Client` |
| fails (resulting in a `ProvisionException`), then the `FileOutputStream` |
| that may have been constructed leaks and isn't properly closed, even if |
| `Client` normally closes its resources correctly. |
| |
| The preferred solution is to not inject closable resources, but instead, objects |
| that can expose short-lived closable resources that are used as necessary. The |
| following example uses Guava's |
| [CharSink](https://github.com/google/guava/wiki/IOExplained#sources-and-sinks) |
| as the resource manager object: |
| |
| ```java |
| class MyModule extends AbstractModule() { |
| @Provides |
| CharSink provideCharSink() { |
| return Files.asCharSink(new File("/tmp/outfile"), StandardCharsets.UTF_8); |
| } |
| } |
| |
| ... |
| |
| class Client { |
| private final CharSink sink; |
| @Inject Client(CharSink sink, OtherDependency other, ...) { |
| this.sink = sink; |
| } |
| void doSomething() throws IOException { |
| sink.write("hello!"); // Opens the file at this point, and closes once its done. |
| } |
| } |
| ``` |
| |
| If there's not a similar non-closable resource, you can write a simple wrapper: |
| |
| ```java |
| class ResourceManager { |
| @Inject ResourceManager(@Config String configs, ...) {} |
| |
| /** |
| * Returns a new thing for you to use and dispose of |
| */ |
| OutputStream provideInstance() { return new...(); } |
| } |
| |
| ... |
| class Client { |
| private final ResourceManager resource; |
| @Inject Client(ResourceManager resource, OtherDependency other, ...) { |
| this.resource = resource; |
| } |
| void doSomething() { |
| try (OutputStream actualStream = resource.provideInstance()) { |
| // write to actualStream, closing with try-with-resources |
| } |
| } |
| } |
| |
| ``` |
| |
| This pattern can be extended to other resources: as opposed to injecting |
| database connection handles directly, inject connection pool objects that |
| require your object to ask for those connection objects when they're needed and |
| close them safely. |