本教程主要詳細講解Guice依賴注入中的一些高級選項,他們分別是Scope
,Eagerly Loading Bindings
,Stage
,Optional Injection
。我們將一一對他們進行講解。
基礎環境
技術 | 版本 |
---|---|
Java | 1.8+ |
Guice | 4.2.3 |
初始化項目
-
初始化項目
mvn archetype:generate -DgroupId=com.edurt.sli.guice -DartifactId=guice-binder-scope -DarchetypeArtifactId=maven-archetype-quickstart -Dversion=1.0.0 -DinteractiveMode=false
-
修改pom.xml增加Guice依賴
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-learn-integration</artifactId>
<groupId>com.edurt.sli</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>guice-binder-scope</artifactId>
<name>Guice依賴注入(Scope)</name>
<properties>
<system.java.version>1.8</system.java.version>
<guice.version>4.2.3</guice.version>
<lombok.version>1.18.2</lombok.version>
</properties>
<dependencies>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>${guice.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${plugin.maven.compiler.version}</version>
<configuration>
<source>${system.java.version}</source>
<target>${system.java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
guice
: guice就是我們核心要使用的依賴
Singleton
Guice支持我們在其他DI框架中逐漸習慣的Scope
和Scope
機制。Guice默認提供已定義依賴項的新實例。
-
創建 Service
接口,用於提供使用的測試使用到的方法,代碼如下
package com.edurt.sli.guice.scope;
public interface Service
{
void println(String source);
}
-
創建 ScopeService
類,用於構建對Service
的實現
package com.edurt.sli.guice.scope;
import java.time.LocalDateTime;
public class ScopeService
implements Service
{
@Override
public void println(String source)
{
System.out.println(String.format("%s on %s", source, LocalDateTime.now()));
}
}
-
創建用於測試注入的應用類 ScopeApplication
,代碼如下
package com.edurt.sli.guice.scope;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Scopes;
public class ScopeApplication
{
public static void main(String[] args)
{
Injector injector = Guice.createInjector(binder -> binder.bind(Service.class).to(ScopeService.class).in(Scopes.SINGLETON));
Service service = injector.getInstance(Service.class);
service.println("Singleton Scope");
}
}
我們運行程序輸出
Scope on 2020-12-18T16:01:37.792656
通過代碼binder.bind(Service.class).to(ScopeService.class).in(Scopes.SINGLETON)
我們指定了ScopeService的Scope,他將會被標誌爲一個單例實例。當然我們也可以使用@Singleton
標誌該類的作用域,我們修改ScopeService
類文件代碼如下:
package com.edurt.sli.guice.scope;
import javax.inject.Singleton;
import java.time.LocalDateTime;
@Singleton
public class ScopeService
implements Service
{
@Override
public void println(String source)
{
System.out.println(String.format("%s on %s", source, LocalDateTime.now()));
}
}
將ScopeApplication
中的binder.bind(Service.class).to(ScopeService.class).in(Scopes.SINGLETON)
代碼修改爲binder.bind(Service.class).to(ScopeService.class)
兩種方式實現的效果都是一致的。此時ScopeService
會被構建爲單例實例。
當然還有一個asEagerSingleton()
方法也可以用來標記單例模式。
他們的對比圖如下:
使用方式 | PRODUCTION | DEVELOPMENT |
---|---|---|
.asEagerSingleton() | eager | eager |
.in(Singleton.class) | eager | lazy |
.in(Scopes.SINGLETON) | eager | lazy |
@Singleton | eager* | lazy |
自定義Scope
Guice還支持我們用戶自定義作用域,通常情況下我們不需要自己實現Scope,一般內置的作用域對於大多數的應用已經足夠了。如果您正在編寫一個web應用程序,那麼ServletModule
爲HTTP請求和HTTP會話提供了簡單的、良好作用域實現是一個很好的想法。
-
自定義Scope註解
Scope註解用於標記當前Scope在容器中使用的作用域。將使用它來註釋guice構造的類型,@Provides
方法和bind語法中的in()
。Scope註解代碼如下:
package com.edurt.sli.guice.seso;
import com.google.inject.ScopeAnnotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target({TYPE, METHOD})
@Retention(RUNTIME)
@ScopeAnnotation
public @interface CustomScope
{
}
在使用自定義Scope時,請確保導入了正確的Scope註解。否則,您可能會得到一個SCOPE_NOT_FOUND
錯誤。
-
實現Scope接口
Scope接口確保每個Scope實例擁有一到多個類型實例。實現的Scope接口代碼如下:
package com.edurt.sli.guice.seso;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.Scope;
public class CustomScopeImpl
implements Scope
{
ThreadLocal<Object> threadLocal = new ThreadLocal<>();
@Override
public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped)
{
return () -> {
T instance = (T) threadLocal.get();
if (instance == null) {
instance = unscoped.get();
threadLocal.set(instance);
}
return instance;
};
}
}
我們在上述代碼中實現了一個簡單線程抽取Scope,我們只是爲了做測試使用,具體的Scope還需要根據業務自己使用。當我們傳遞的線程中沒有構造一個對象時,先構造一個,然後放入線程上下文中,以後每次都從線程中獲取對象。
-
使用創建 CustomScopeApplication
用來使用自定義的Scope代碼如下:
package com.edurt.sli.guice.seso;
import com.google.inject.Guice;
import com.google.inject.Injector;
public class CustomScopeApplication
{
public static void main(String[] args)
{
Injector injector = Guice.createInjector(binder -> {
binder.bind(Service.class).to(ScopeService.class).in(new CustomScopeImpl());
});
for (int i = 0; i < 3; i++) {
System.out.println(injector.getInstance(Service.class).hashCode());
}
}
}
運行程序後我們得到以下結果:
1574598287
1574598287
1574598287
我們通過結果得到運行了3次後的實例hashCode是一致的,這就說明我們的自定義Scope已經起作用了。如果新的實例構建後那麼hashCode將會被改變。
-
綁定自定義Scope註解,我們通過實現Module進行注入
package com.edurt.sli.guice.seso;
import com.google.inject.AbstractModule;
public class CustomScopeModule
extends AbstractModule
{
@Override
protected void configure()
{
bindScope(CustomScope.class, new CustomScopeImpl());
}
}
需要使用修改Module只需要在Guice.createInjector構建的時候添加該Module即可,代碼如下:
Injector injector = Guice.createInjector(new CustomScopeModule(), binder -> {
binder.bind(Service.class).to(ScopeService.class).in(new CustomScopeImpl());
});
在ScopeService
類上使用@CustomScope
註解即可。
打包文件部署
-
打包數據
mvn clean package -Dmaven.test.skip=true -X
運行打包後的文件即可
java -jar target/guice-binder-scope-1.0.0.jar
源碼地址
-
GitHub
本文分享自微信公衆號 - Spring中文網(china-spring-all)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。