時間:2017年10月14日星期六
說明:本文部份內容均來自慕課網。@慕課網:http://www.imooc.com
教學源碼:https://github.com/zccodere/s...
學習源碼:https://github.com/zccodere/s...java
Google Guice:Guice是啥git
Guice讀音:juice Guice is a lightweight dependency injection framework for java Guice 是輕量級的依賴注入框架
Google Guice:說明github
Java:一個java的框架、須要有java基礎 dependency injection:什麼是dependency、剝離dependency、注入dependency lightweight:輕量級(代碼少、易維護、性能優異),跟Spring比較。 可是:學習曲線陡峭、對開發者的要求過高了
課程目標算法
理解、從新理解dependency injection 掌握Guice的語法,更要掌握Guice的設計理念 開始在你的下一個項目中使用Guice
課程要點spring
什麼是Guice dependency injection:改造Hello World程序 注入(Injection) 綁定(Binding) 做用域或生命週期(Scope) Guice AOP 使用Guice和SpringBoot協做搭建一個簡單的Web應用 Guice vs Spring
Spring的不足數據庫
手動配置:使用xml進行配置,配置太過龐大 自動配置:Spring提供了自動配置,複雜項目沒法實現
Guice不一樣於Springapache
取消xml 取消bean的概念 使用Constructor來注入 泛型支持 一個專一於Dependency Injection的框架
參考資料編程
https://github.com/google/guice/wiki/Motivation https://github.com/google/guice/wiki/ExternalDocumentation 更多利用英文資料,如Stackoverflow
Hello Guicebootstrap
配置Guice環境 Dependency Injection基礎 改造Hello World
建立名爲guicedemo的maven工程pom以下安全
<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"> <modelVersion>4.0.0</modelVersion> <groupId>com.myimooc</groupId> <artifactId>guicedemo</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>guicedemo</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion> </properties> <dependencies> <dependency> <groupId>com.google.inject</groupId> <artifactId>guice</artifactId> <version>4.1.0</version> </dependency> <dependency> <groupId>com.google.inject.extensions</groupId> <artifactId>guice-multibindings</artifactId> <version>4.1.0</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies> </project>
經典Hello World分析
核心算法:將指定內容輸出至指定目標
改造Hello World程序
面向對象化 消除Dependency 用Guice來配置Dependency
代碼編寫
1.編寫MyApplet類
package com.myimooc.guicedemo; /** * @title Applet類 * @describe 提供run()方法 * @author zc * @version 1.0 2017-10-15 */ public interface MyApplet extends Runnable{ }
2.編寫HelloWorldPrinter類
package com.myimooc.guicedemo.helloworlddemo; import com.myimooc.guicedemo.MyApplet; /** * @title HelloWorldPrinter類 * @describe 提供打印HelloWorld的功能 * @author zc * @version 1.0 2017-10-15 */ public class HelloWorldPrinter implements MyApplet { private void printHelloWorld() { System.out.println("Hello World!"); } @Override public void run() { printHelloWorld(); } }
3.編寫Configuration類
package com.myimooc.guicedemo; import com.myimooc.guicedemo.helloworlddemo.HelloWorldPrinter; /** * @title Configuration類 * @describe 程序啓動配置類 * @author zc * @version 1.0 2017-10-15 */ public class Configuration { public static MyApplet getMainApplet() { return new HelloWorldPrinter(); } }
4.編寫App類
package com.myimooc.guicedemo; /** * @title 啓動類 * @describe 改造HelloWorld類 * @author zc * @version 1.0 2017-10-15 */ public class App { /** * main方法的做用 * bootstrap: * parse command line:解析命令行參數 * set up environment:配置環境參數 * kick off main logic:啓動程序邏輯 * @param args */ public static void main(String[] args) { MyApplet mainApplet = Configuration.getMainApplet(); mainApplet.run(); } }
面向對象化小結
善於運用IDE提供的重構能力 Ctrl + 1:猜想下一步動做 Ctrl + shift + r:重命名 先寫代碼,再讓其編譯經過 先思考肯定須要什麼,而後再機械性勞動實現 函數命名 從實現角度:精確描述函數幹什麼 從調用者角度:描述調用者需求 二者不匹配:須要進行抽象的點
代碼編寫
1.編寫MyApplet類
package com.myimooc.guicedemo.noguice; /** * @title MyApplet類 * @describe 提供run()方法 * @author zc * @version 1.0 2017-10-15 */ public interface MyApplet extends Runnable{ }
2.編寫StringWritingApplet類
package com.myimooc.guicedemo.noguice.helloworlddemo; import com.myimooc.guicedemo.noguice.MyApplet; /** * @title HelloWorldPrinter類 * @describe 提供打印HelloWorld的功能 * @author zc * @version 1.0 2017-10-15 */ public class StringWritingApplet implements MyApplet { private MyDestination destination; private StringProvider stringProvider; public StringWritingApplet(MyDestination destination,StringProvider stringProvider) { super(); this.destination = destination; this.stringProvider = stringProvider; } private void writeString() { destination.write(stringProvider.get()); } @Override public void run() { writeString(); } }
3.編寫StringProvider類
package com.myimooc.guicedemo.noguice.helloworlddemo; /** * @title StringProvider類 * @describe 提供get()方法 * @author zc * @version 1.0 2017-10-15 */ public interface StringProvider { String get(); }
4.編寫MyDestination類
package com.myimooc.guicedemo.noguice.helloworlddemo; /** * @title MyDestination類 * @describe 提供write()方法 * @author zc * @version 1.0 2017-10-15 */ public interface MyDestination { void write(String string); }
5.編寫PrintStreamWriter類
package com.myimooc.guicedemo.noguice.helloworlddemo; import java.io.PrintStream; /** * @title PrintStreamWriter類 * @describe 實現write()方法 * @author zc * @version 1.0 2017-10-15 */ public class PrintStreamWriter implements MyDestination { private PrintStream destination; public PrintStreamWriter(PrintStream destination) { super(); this.destination = destination; } @Override public void write(String string) { destination = System.out; destination.println(string); } }
6.編寫Configuration類
package com.myimooc.guicedemo.noguice; import com.myimooc.guicedemo.noguice.helloworlddemo.PrintStreamWriter; import com.myimooc.guicedemo.noguice.helloworlddemo.StringProvider; import com.myimooc.guicedemo.noguice.helloworlddemo.StringWritingApplet; /** * @title Configuration類 * @describe 程序啓動配置類 * @author zc * @version 1.0 2017-10-15 */ public class Configuration { public static MyApplet getMainApplet() { return new StringWritingApplet( new PrintStreamWriter(System.out), new StringProvider() { @Override public String get() { return "Hello World"; } }); } }
7.編寫App類
package com.myimooc.guicedemo.noguice; /** * @title 啓動類 * @describe 改造HelloWorld類 * @author zc * @version 1.0 2017-10-15 */ public class App { /** * main方法的做用 * bootstrap: * parse command line:解析命令行參數 * set up environment:配置環境參數 * kick off main logic:啓動程序邏輯 * @param args */ public static void main(String[] args) { MyApplet mainApplet = Configuration.getMainApplet(); mainApplet.run(); } }
消除Dependency小結
代碼編寫
1.編寫MyApplet類
package com.myimooc.guicedemo.useguice; /** * @title MyApplet類 * @describe 提供run()方法 * @author zc * @version 1.0 2017-10-15 */ public interface MyApplet extends Runnable{ }
2.編寫StringWritingApplet類
package com.myimooc.guicedemo.useguice.helloworlddemo; import com.google.inject.Inject; import com.google.inject.Provider; import com.myimooc.guicedemo.useguice.MyApplet; /** * @title HelloWorldPrinter類 * @describe 提供打印HelloWorld的功能 * @author zc * @version 1.0 2017-10-15 */ public class StringWritingApplet implements MyApplet { private MyDestination destination; private Provider<String> stringProvider; @Inject public StringWritingApplet(MyDestination destination,@Output Provider<String> stringProvider) { super(); this.destination = destination; this.stringProvider = stringProvider; } private void writeString() { destination.write(stringProvider.get()); } @Override public void run() { writeString(); } }
3.編寫MyDestination類
package com.myimooc.guicedemo.useguice.helloworlddemo; /** * @title MyDestination類 * @describe 提供write()方法 * @author zc * @version 1.0 2017-10-15 */ public interface MyDestination { void write(String string); }
4.編寫PrintStreamWriter類
package com.myimooc.guicedemo.useguice.helloworlddemo; import java.io.PrintStream; import javax.inject.Inject; /** * @title PrintStreamWriter類 * @describe 實現write()方法 * @author zc * @version 1.0 2017-10-15 */ public class PrintStreamWriter implements MyDestination { private PrintStream destination; @Inject public PrintStreamWriter(PrintStream destination) { super(); this.destination = destination; } @Override public void write(String string) { destination = System.out; destination.println(string); } }
5.編寫Output類
package com.myimooc.guicedemo.useguice.helloworlddemo; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import com.google.inject.BindingAnnotation; /** * @title Output註解 * @describe * @author zc * @version 1.0 2017-10-15 */ @Retention(RetentionPolicy.RUNTIME) @BindingAnnotation public @interface Output { }
6.編寫HelloWorldModule類
package com.myimooc.guicedemo.useguice.helloworlddemo; import java.io.PrintStream; import com.google.inject.AbstractModule; import com.myimooc.guicedemo.useguice.MyApplet; /** * @title HelloWorldModule類 * @describe HelloWorld模塊的依賴配置 * @author zc * @version 1.0 2017-10-15 */ public class HelloWorldModule extends AbstractModule{ @Override protected void configure() { bind(MyApplet.class).to(StringWritingApplet.class); bind(MyDestination.class).to(PrintStreamWriter.class); bind(PrintStream.class).toInstance(System.out); bind(String.class).annotatedWith(Output.class).toInstance("Hello World"); } }
7.編寫MainModule類
package com.myimooc.guicedemo.useguice; import com.google.inject.AbstractModule; import com.myimooc.guicedemo.useguice.helloworlddemo.HelloWorldModule; /** * @title MainModule類 * @describe Guice用來配置的類 * @author zc * @version 1.0 2017-10-15 */ public class MainModule extends AbstractModule{ @Override protected void configure() { install(new HelloWorldModule()); } }
8.編寫App類
package com.myimooc.guicedemo.useguice; import com.google.inject.Guice; /** * @title 啓動類 * @describe 改造HelloWorld類 * @author zc * @version 1.0 2017-10-15 */ public class App { /** * main方法的做用 * bootstrap: * parse command line:解析命令行參數 * set up environment:配置環境參數 * kick off main logic:啓動程序邏輯 * @param args */ public static void main(String[] args) { MyApplet mainApplet = Guice .createInjector(new MainModule()) .getInstance(MyApplet.class); mainApplet.run(); } }
Guice配置Dependency小結
注入圖解
注入(Injection)
構造函數注入 使用final來區分dependency和狀態 注入時不考慮如何實現或綁定 成員變量注入 用於測試 使用injectMembers來注入測試用例
代碼編寫
1.編寫OrderService類
2.編寫PaymentService類
3.編寫PriceService類
4.編寫OrderServiceImpl類
5.編寫PaymentServiceImpl類
6.編寫PriceServiceImpl類
7.編寫SessionManager類
8.編寫ServerModule類
9.編寫OrderServiceTest類
受篇幅限制,源碼請到個人github地址查看
注入Provider
如何注入Provider
DatabaseConnection dbConn Provider< DatabaseConnection > dbConnProvider Guice會考慮對象生命週期 須要時能夠本身實現Provider
命名注入
@Inject @Named(「dbSpec」) private String dbSpec; @Inject @LogFileName private String logFileName; 使用@Named:參數來自配置文件或命令行、或者爲了開發方便 使用屬性:一般採用此方法
代碼編寫
1.修改SessionManager類
package com.myimooc.guicedemo.server.impl; import javax.inject.Inject; import com.google.inject.Provider; /** * @title session管理類 * @describe 模擬訂單系統 * @author zc * @version 1.0 2017-10-15 */ public class SessionManager { private final Provider<Long> sessionIdProvider; @Inject public SessionManager(@SessionId Provider<Long> sessionIdProvider) { super(); this.sessionIdProvider = sessionIdProvider; } public Long getSessionId() { return sessionIdProvider.get(); } }
2.修改ServerModule類
package com.myimooc.guicedemo.server.impl; import com.google.inject.AbstractModule; import com.google.inject.Provides; import com.myimooc.guicedemo.server.OrderService; import com.myimooc.guicedemo.server.PaymentService; import com.myimooc.guicedemo.server.PriceService; /** * @title ServerModule類 * @describe 綁定依賴 * @author zc * @version 1.0 2017-10-15 */ public class ServerModule extends AbstractModule{ @Override protected void configure() { bind(OrderService.class).to(OrderServiceImpl.class); bind(PaymentService.class).to(PaymentServiceImpl.class); bind(PriceService.class).to(PriceServiceImpl.class); } @Provides @SessionId Long generateSessionId(){ return System.currentTimeMillis(); } }
3.編寫SessionId類
package com.myimooc.guicedemo.server.impl; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import com.google.inject.BindingAnnotation; /** * @title SessionId註解 * @describe 用來綁定數據 * @author zc * @version 1.0 2017-10-15 */ @Retention(RetentionPolicy.RUNTIME) @BindingAnnotation public @interface SessionId { }
4.編寫SessionManagerTest類
package com.myimooc.guicedemo.server.impl; import static org.junit.Assert.assertNotEquals; import javax.inject.Inject; import org.junit.Before; import org.junit.Test; import com.google.inject.Guice; /** * @title 測試類 * @describe 測試@Provides * @author zc * @version 1.0 2017-10-15 */ public class SessionManagerTest { @Inject SessionManager sessionManager; @Before public void setUp(){ Guice.createInjector(new ServerModule()) .injectMembers(this); } @Test public void testGetSessionId() throws InterruptedException{ Long sessionId1 = sessionManager.getSessionId(); Thread.sleep(1000); Long sessionId2 = sessionManager.getSessionId(); assertNotEquals(sessionId1.longValue(), sessionId2.longValue()); } }
綁定圖解
綁定
類名綁定 實例綁定 鏈接綁定 Provider綁定 命名綁定 泛型綁定 集合綁定
Module的相互關係
並列:Guice.createInjector(module1,module2,…) 嵌套:install(module) 覆蓋:Modules.override(module1).with(module2)
Module什麼時候被運行
Module裏存放了不少表達式 Module不被「運行」 Guice.createInjector()時記錄全部表達式
系統什麼時候初始化
沒有「初始化」概念,沒有Spring的Configuration Time injector.getInstance()時根據表達式調用構造函數
案例:HelloWorld與命令行
讓HelloWorld打印命令行參數 讓HelloWorld經過命令行決定啓動哪一個Applet
代碼編寫
1.編寫MyApplet類
2.編寫Applets類
3.編寫StringWritingApplet類
4.編寫PrintLineApplet類
5.編寫MyDestination類
6.編寫PrintStreamWriter類
7.編寫Output類
8.編寫Args類
9.編寫HelloWorldModule類
10.編寫PrintLineModule類
11.編寫CommandLineModule類
12.編寫MainModule類
13.編寫App類
受篇幅限制,源碼請到個人github地址查看
生命週期或做用域
選擇做用域
默認:適用於:通常實例,stateless,構造速度快 如:Parser、PriceCalulator Singleton:適用於:stateful的實例,構造速度慢的實例,必須線程安全 如:數據庫、網絡鏈接 Session/Request:含有session/request信息的實例、stateful的實例 如:SessionSate
做用域的使用
做爲類或者@Provides方法的屬性 在綁定時使用In語句 @Singleton的線程安全性
Guice AOP
符合AOP Alliance的MethodInterceptor接口 MethodInterceptor可用於「Aspects」 獲取函數調用類、方法、參數 控制是否執行函數調用
實現AOP
綁定MethodInterceptor 實現MethodInterceptor 在MethodInterceptor中注入Dependency
代碼編寫
1.編寫Logged類
package com.myimooc.guicedemo.aop; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * @title Logged註解 * @describe 用於標識須要記錄日誌的方法 * @author zc * @version 1.0 2017-10-15 */ @Retention(RetentionPolicy.RUNTIME) public @interface Logged { }
2.編寫LoggedInterceptor類
package com.myimooc.guicedemo.aop; import java.lang.reflect.Method; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import com.google.common.base.Joiner; /** * @title LoggedMethodInterceptor類 * @describe 切面類,用於處理被攔截的方法,實現日誌記錄 * @author zc * @version 1.0 2017-10-15 */ public class LoggedInterceptor implements MethodInterceptor{ @Override public Object invoke(MethodInvocation invocation) throws Throwable { // 得到調用參數 invocation.getArguments(); // 得到調用對象 invocation.getClass(); // 得到調用方法 Method method = invocation.getMethod(); // 記錄日誌 System.out.println(String.format("Calling %s#%s(%s)", method.getDeclaringClass().getName(), method.getName(), Joiner.on(",").join(invocation.getArguments()))); // 執行調用方法 Object result = invocation.proceed(); // 返回調用結果 return result; } }
3.編寫LoggedModule類
package com.myimooc.guicedemo.aop; import com.google.inject.AbstractModule; import com.google.inject.matcher.Matchers; /** * @title LoggedModule類 * @describe 用於配置依賴綁定 * @author zc * @version 1.0 2017-10-15 */ public class LoggedModule extends AbstractModule{ @Override protected void configure() { // 配置攔截任意類,帶有@Logged註解修飾的方法 bindInterceptor( Matchers.any(), Matchers.annotatedWith(Logged.class), new LoggedInterceptor()); } }
使用Guice與SpringBoot協做搭建Web應用
使用SpringBoot搭建簡單的Web應用 使用Guice搭建業務邏輯
協做框架
建立名爲guicespring的maven工程
完成後的項目結構以下
受篇幅限制,源碼請到個人github地址查看
協做小結
SpringBoot進行總控 各自綁定Guice Injector和Spring ApplicationContext 注意對象生命週期
Guice與Spring
Guice不是Spring的重製版 Spring綁定 配置文件體現完整裝配結構 大量使用字符串:實例名:屬性名 在Config Time完成組裝 Guice綁定 Java代碼描述綁定規則 每一個注入和綁定僅描述局部依賴 沒有Config Time
Guice的優勢
代碼量少 性能優異 支持泛型 Constructor綁定:Immutable objects,再也不須要getter/setter 強類型 易於重構
Guice的缺點
Module和綁定規則不易理解 文檔教程少,社區資源少 沒法方便搭出特殊結構:如循環依賴 Guice僅僅是依賴注入框架,而Spring涵蓋較多
從Spring遷移到Guice
不建議 Spring與Guice整合
新項目須要選擇Dependency Injection方案
不妨嘗試Guice 與其它組件或解決方案整合:注意對象生命週期
課程回顧
什麼是Guce Dependency Injection:改造Hello World程序 注入(Injection) 綁定(Binding) 做用域或生命週期(Scope) Guice AOP 使用Guice與SpringBoot協做搭建Web應用