手记

慕课网《使用Google Guice实现依赖注入》学习总结

慕课网《使用Google Guice实现依赖注入》学习总结

时间:2017年10月14日星期六
说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com 
教学源码:https://github.com/zccodere/study-imooc
学习源码:https://github.com/zccodere/study-imooc
第一章:课程介绍
1-1 课程简介

Google Guice:Guice是啥

Guice读音:juice
Guice is a lightweight dependency injection framework for java
Guice 是轻量级的依赖注入框架

Google Guice:说明

Java:一个java的框架、需要有java基础
dependency injection:什么是dependency、剥离dependency、注入dependency
lightweight:轻量级(代码少、易维护、性能优异),跟Spring比较。
但是:学习曲线陡峭、对开发者的要求太高了

课程目标

理解、重新理解dependency injection
掌握Guice的语法,更要掌握Guice的设计理念
开始在你的下一个项目中使用Guice

课程要点

什么是Guice
dependency injection:改造Hello World程序
注入(Injection)
绑定(Binding)
作用域或生命周期(Scope)
Guice AOP
使用Guice和SpringBoot协作搭建一个简单的Web应用
Guice vs Spring
1-2 产生原因

Spring的不足

手动配置:使用xml进行配置,配置太过庞大
自动配置:Spring提供了自动配置,复杂项目无法实现

Guice不同于Spring

取消xml
取消bean的概念
使用Constructor来注入
泛型支持
一个专注于Dependency Injection的框架

参考资料

https://github.com/google/guice/wiki/Motivation
https://github.com/google/guice/wiki/ExternalDocumentation
更多利用英文资料,如Stackoverflow
第二章:理解依赖
2-1 基础配置

Hello Guice

配置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>
2-2 依赖分析

经典Hello World分析

核心算法:将指定内容输出至指定目标

改造Hello World程序

面向对象化
消除Dependency
用Guice来配置Dependency
2-3 面向对象

代码编写

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:重命名
    先写代码,再让其编译通过
    先思考确定需要什么,然后再机械性劳动实现
函数命名
    从实现角度:精确描述函数干什么
    从调用者角度:描述调用者需求
    两者不匹配:需要进行抽象的点
2-4 提取依赖

代码编写

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小结

2-5 配置依赖

代码编写

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小结

第三章:注入依赖
3-1 基本注入

注入图解

注入(Injection)

构造函数注入
    使用final来区分dependency和状态
    注入时不考虑如何实现或绑定
成员变量注入
    用于测试
    使用injectMembers来注入测试用例

代码编写

1.编写OrderService类

2.编写PaymentService类

3.编写PriceService类

4.编写OrderServiceImpl类

5.编写PaymentServiceImpl类

6.编写PriceServiceImpl类

7.编写SessionManager类

8.编写ServerModule类

9.编写OrderServiceTest类

受篇幅限制,源码请到我的github地址查看

3-2 其他注入

注入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());
    }

}
第四章:绑定依赖
4-1 绑定详解

绑定图解

绑定

类名绑定
实例绑定
连接绑定
Provider绑定
命名绑定
泛型绑定
集合绑定
4-2 模块组织

Module的相互关系

并列:Guice.createInjector(module1,module2,…)
嵌套:install(module)
覆盖:Modules.override(module1).with(module2)

Module何时被运行

Module里存放了很多表达式
Module不被“运行”
Guice.createInjector()时记录所有表达式

系统何时初始化

没有“初始化”概念,没有Spring的Configuration Time
injector.getInstance()时根据表达式调用构造函数
4-3 绑定示例

案例: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地址查看

第五章:生命周期
5-1 基本介绍

生命周期或作用域

选择作用域

默认:适用于:一般实例,stateless,构造速度快
如:Parser、PriceCalulator
Singleton:适用于:stateful的实例,构造速度慢的实例,必须线程安全
如:数据库、网络连接
Session/Request:含有session/request信息的实例、stateful的实例
如:SessionSate
5-2 使用介绍

作用域的使用

作为类或者@Provides方法的属性
在绑定时使用In语句
@Singleton的线程安全性
第六章:切面编程
6-1 面向切面

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());

    }
}
第七章:框架集成
7-1 协作框架

使用Guice与SpringBoot协作搭建Web应用

使用SpringBoot搭建简单的Web应用
使用Guice搭建业务逻辑

协作框架

7-2 案例实战

创建名为guicespring的maven工程

完成后的项目结构如下

受篇幅限制,源码请到我的github地址查看

协作小结

SpringBoot进行总控
各自绑定Guice Injector和Spring ApplicationContext
注意对象生命周期
第八章:课程总结
8-1 适用场景

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
与其它组件或解决方案整合:注意对象生命周期
8-2 课程回顾

课程回顾

什么是Guce
Dependency Injection:改造Hello World程序
注入(Injection)
绑定(Binding)
作用域或生命周期(Scope)
Guice AOP
使用Guice与SpringBoot协作搭建Web应用
9人推荐
随时随地看视频
慕课网APP

热门评论

感谢分享,很详细啊!

查看全部评论