Guice是Google開源的一個依賴注入類庫,相比於Spring IoC來講更小更快。Elasticsearch大量使用了Guice,本文簡單的介紹下Guice的基本概念和使用方式css
基本使用
1. 引入依賴
若是使用gradle的話,添加下列依賴html
compile group: 'com.google.inject.extensions', name: 'guice-multibindings', version: '4.2.0'
compile group: 'com.google.inject', name: 'guice', version: '4.2.0'
當構建工具解決完項目的依賴以後,咱們就能夠開始使用Guice了java
項目骨架
咱們來假設一個簡單的項目框架。首先咱們須要一個業務接口,簡單的包含一個方法用於執行業務邏輯。
它的實現也很是簡單mysql
// UserService接口
public interface UserService {
void process();
}
// 實現類
public class UserServiceImpl implements UserService {
@Override
public void process() {
System.out.println("我須要作一些業務邏輯");
}
}
而後咱們須要一個日誌接口,它和它的實現也很是簡單web
// 日誌接口
public interface LogService {
void log(String msg);
}
// 實現類
public class LogServiceImpl implements LogService {
@Override
public void log(String msg) {
System.out.println("------LOG:" + msg);
}
}
最後是一個系統接口和相應的實現。
在實現中咱們使用了業務接口和日誌接口處理業務邏輯和打印日誌信息。sql
public interface Application {
void work();
}
public class MyApp implements Application {
private UserService userService;
private LogService logService;
@Inject
public MyApp(UserService userService, LogService logService) {
this.userService = userService;
this.logService = logService;
}
@Override
public void work() {
userService.process();
logService.log("程序正常運行");
}
}
在 MyApp 類中定義了 UserService 和 LogService 兩個變量,可是尚未給它們建立對象,而 word 方法中分別調用了 process 和 log 方法,它們的實際執行結果由最終注入的對象決定api
簡單的依賴注入
首先來配置依賴關係。咱們繼承AbstractModule
類,並重寫configure
方法便可。在configure
方法中,咱們能夠調用AbstractModule
類提供的一些方法來配置依賴關係。安全
最經常使用的方式就是 bind(接口或父類).to(實現類或子類)
的方式來設置依賴關係。微信
public class MyAppModule extends AbstractModule {
@Override
protected void configure() {
bind(LogService.class).to(LogServiceImpl.class);
bind(UserService.class).to(UserServiceImpl.class);
bind(Application.class).to(MyApp.class);
}
}
這樣一來,當Guice遇到接口或父類須要注入具體實現的時候,就會使用這裏配置的實現類或子類來注入。
若是但願在構造器中注入依賴的話,只須要添加 @Inject
註解便可session
Guice配置完以後,咱們須要調用 Guice.createInjector
方法傳入配置類來建立一個注入器,而後使用注入器的 getInstance
方法獲取目標類,Guice會按照配置幫咱們注入全部依賴。
咱們使用單元測試來看看效果
public class MyAppTest {
private static Injector injector;
@BeforeClass
public static void init() {
injector = Guice.createInjector(new MyAppModule());
}
@Test
public void testMyApp() {
Application myApp = injector.getInstance(Application.class);
myApp.work();
}
}
//程序結果
//我須要作一些業務邏輯
//------LOG:程序正常運行
依賴綁定
下面這些例子都是Guice文檔上的例子
鏈式綁定
咱們在綁定依賴的時候不只能夠將父類和子類綁定,還能夠將子類和更具體的子類綁定。
下面的例子中,當咱們須要 TransactionLog
的時候,Guice最後會爲咱們注入 MySqlDatabaseTransactionLog
對象。
// 鏈式綁定:TransactionLog -> DatabaseTransactionLog -> MySqlDatabaseTransactionLog
public class BillingModule extends AbstractModule {
@Override
protected void configure() {
bind(TransactionLog.class).to(DatabaseTransactionLog.class);
bind(DatabaseTransactionLog.class).to(MySqlDatabaseTransactionLog.class);
}
}
註解綁定
當咱們須要將多個同一類型的對象注入不一樣對象的時候,就須要使用註解區分這些依賴了。
最簡單的辦法就是使用 @Named
註解進行區分
首先須要在要注入的地方添加 @Named
註解
public class RealBillingService implements BillingService {
@Inject
public RealBillingService(@Named("Checkout") CreditCardProcessor processor,
TransactionLog transactionLog) {
...
}
而後在綁定中添加 annotatedWith
方法指定 @Named
中指定的名稱。
因爲編譯器沒法檢查字符串,因此Guice官方建議咱們保守地使用這種方式
bind(CreditCardProcessor.class)
.annotatedWith(Names.named("Checkout"))
.to(CheckoutCreditCardProcessor.class);
若是但願使用類型安全的方式,能夠自定義註解
@BindingAnnotation
@Target({ FIELD, PARAMETER, METHOD })
@Retention(RUNTIME)
public @interface PayPal {
}
而後在須要注入的類上應用
public class RealBillingService implements BillingService {
@Inject
public RealBillingService(@PayPal CreditCardProcessor processor,
TransactionLog transactionLog) {
...
}
在配置類中,使用方法也和@Named相似
bind(CreditCardProcessor.class)
.annotatedWith(PayPal.class)
.to(PayPalCreditCardProcessor.class);
實例綁定
有時候須要直接注入一個對象的實例,而不是從依賴關係中解析。
若是咱們要注入基本類型的話只能這麼作
bind(String.class)
.annotatedWith(Names.named("JDBC URL"))
.toInstance("jdbc:mysql://localhost/pizza");
bind(Integer.class)
.annotatedWith(Names.named("login timeout seconds"))
.toInstance(10);
若是使用 toInstance
方法注入的實例比較複雜的話,可能會影響程序啓動。這時候可使用 @Provides
方法代替
@Provides方法
當一個對象很複雜,沒法使用簡單的構造器來生成的時候,咱們可使用 @Provides
方法,也就是在配置類中生成一個註解了 @Provides
的方法。
在該方法中咱們能夠編寫任意代碼來構造對象
public class BillingModule extends AbstractModule {
@Override
protected void configure() {
...
}
@Provides
TransactionLog provideTransactionLog() {
DatabaseTransactionLog transactionLog = new DatabaseTransactionLog();
transactionLog.setJdbcUrl("jdbc:mysql://localhost/pizza");
transactionLog.setThreadPoolSize(30);
return transactionLog;
}
}
@Provides
方法也能夠應用 @Named
和自定義註解,還能夠注入其餘依賴,Guice會在調用方法以前注入須要的對象
@Provides
@PayPal
CreditCardProcessor providePayPalCreditCardProcessor(
@Named("PayPal API key") String apiKey) {
PayPalCreditCardProcessor processor = new PayPalCreditCardProcessor();
processor.setApiKey(apiKey);
return processor;
}
Provider綁定
若是項目中存在多個比較複雜的對象須要構建,使用 @Provide
方法會讓配置類變得比較亂。咱們可使用Guice提供的 Provider接口
將複雜的代碼放到單獨的類中。辦法很簡單,實現 Provider<T>
接口的get方法便可。在 Provider
類中,咱們可使用 @Inject
任意注入對象
public class DatabaseTransactionLogProvider implements Provider<TransactionLog> {
private final Connection connection;
@Inject
public DatabaseTransactionLogProvider(Connection connection) {
this.connection = connection;
}
public TransactionLog get() {
DatabaseTransactionLog transactionLog = new DatabaseTransactionLog();
transactionLog.setConnection(connection);
return transactionLog;
}
}
在配置類中使用 toProvider
方法綁定到 Provider
上便可
public class BillingModule extends AbstractModule {
@Override
protected void configure() {
bind(TransactionLog.class)
.toProvider(DatabaseTransactionLogProvider.class);
}
做用域
默認狀況下Guice會在每次注入的時候建立一個新對象。若是但願建立一個單例依賴的話,能夠在實現類上應用 @Singleton
註解
@Singleton
public class InMemoryTransactionLog implements TransactionLog {
/* everything here should be threadsafe! */
}
或者也能夠在配置類中指定
bind(TransactionLog.class).to(InMemoryTransactionLog.class).in(Singleton.class);
在@Provides方法中也能夠指定單例
@Provides
@Singleton
TransactionLog provideTransactionLog() {
...
}
若是一個類型上存在多個衝突的做用域,Guice會使用 bind()
方法中指定的做用域。若是不想使用註解的做用域,能夠在 bind()
方法中將對象綁定爲 Scopes.NO_SCOPE
Guice和它的擴展提供了不少做用域,有單例Singleton,Session做用域SessionScoped,Request請求做用域RequestScoped等等。咱們能夠根據須要選擇合適的做用域
Servlet集成
Guice也能夠和Servlet項目集成,這樣咱們就能夠不用編寫冗長的 web.xml
,以依賴注入的方式使用Servlet和相關組件
安裝Guice Servlet擴展
要在Servlet項目中集成Guice,首先須要安裝Guice Servlet擴展。若是使用Maven,添加下面的依賴。
compile group: 'com.google.inject.extensions', name: 'guice-servlet', version: '4.2.0'
加依賴以後,在 web.xml
中添加下面的代碼,讓Guice過濾全部web請求
<filter>
<filter-name>guiceFilter</filter-name>
<filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>guiceFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
配置Injector
和普通的項目同樣,Servlet項目一樣須要配置Injector。一種比較好的辦法就是在 ContextListener
中配置 Injector
。Guice的Servlet集成提供了 GuiceServletContextListener
,咱們繼承該類並在 getInjector
方法中配置 Injector
便可。
public class MyGuiceServletConfigListener extends GuiceServletContextListener {
@Override
protected Injector getInjector() {
return Guice.createInjector(new ServletModule());
}
}
固然僅僅繼承 GuiceServletContextListener
仍是不夠的。咱們還須要在 web.xml
中添加幾行代碼
<listener>
<listener-class>yitian.study.servlet.MyGuiceConfigListener</listener-class>
</listener>
配置Servlet和過濾器
默認的ServletModule就會啓用一些功能。若是須要自定義Servlet和Filter,就須要繼承ServletModule並重寫configureServlets()方法。配置Servlet和Filter的方法和配置普通依賴注入相似
public class MyServletConfig extends ServletModule {
@Override
protected void configureServlets() {
serve("/*").with(MainServlet.class);
filter("/*").through(CharEncodingFilter.class);
}
}
而後將ServletModule替換爲咱們本身的實現類
public class MyGuiceConfigListener extends GuiceServletContextListener {
@Override
protected Injector getInjector() {
return Guice.createInjector(new MyServletConfig());
}
}
注入Servlet相關對象
除了配置Servlet以外,Guice還容許咱們把Request、Response和Session對象注入到非Servlet對象中。下面是Guice的一個例子
@RequestScoped
class SomeNonServletPojo {
@Inject
SomeNonServletPojo(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
...
}
}
咱們還可使用Guice注入請求參數。下面這個類的做用是獲取全部請求參數並轉換爲字符串形式。
@RequestScoped
public class Information {
@Inject
@RequestParameters
Map<String, String[]> params;
public String getAllParameters() {
return params.entrySet()
.stream()
.map(entry -> entry.getKey() + " : " + Arrays.toString(entry.getValue()))
.collect(Collectors.joining(", "));
}
}
以後,咱們就能夠將該對象注入到Servlet中使用,將結果返回給頁面
@Singleton
public class MainServlet extends HttpServlet {
@Inject
private Injector injector;
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
req.setAttribute("name", name);
Information info = injector.getInstance(Information.class);
req.setAttribute("params", info.getAllParameters());
req.getRequestDispatcher("index.jsp").forward(req, resp);
}
@Override
public void init() throws ServletException {
super.init();
}
}
Guice Servlet擴展還有其餘功能,這裏就不在列舉了。詳情請參看Guice文檔
JSR-330標準
JSR-330是一項Java EE標準,指定了Java的依賴注入標準。Spring、Guice和Weld等不少框架都支持JSR-330。下面這個表格來自於Guice文檔,咱們能夠看到JSR-330和Guice註解基本上能夠互換。
![JSR-330](http://static.javashuo.com/static/loading.gif)
Guice官方推薦咱們首選JSR-330標準的註解。
以上就是Guice的基本知識了。若是須要更詳細的使用方法,請參考Guice文檔
本文代碼可在 CSDN code 下載
本文轉載自:http://www.what21.com/article/b_java_1491385837669.html
本文分享自微信公衆號 - 小旋鋒(whirlysBigData)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。