Edited at

Google Guice 使い方メモ

More than 5 years have passed since last update.

Google Guice の使い方メモ。

Wiki の User's Guide をざっと試してみた。


特徴とか


  • 読みは「ジュース」

  • Google が開発してる

  • DI コンテナ

  • ver 3.0 からは JSR330(Dependency Injection for Java)のリファレンス実装

  • 設定は XML ではなく Java コード中に書く

  • アノテーションと型引数をフル活用

  • 2013/10/31 現在の最新は 3.0(4.0 の Beta 版が公開されてる)


環境


Java

1.7.0_40


Google Guice

3.0


pom.xml

<dependency>

<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>3.0</version>
</dependency>


使い方(基本)


HelloWorld.java

package google.guice;

public class HelloWorld {
public void hello() {
System.out.println("Hello Google Guice!!");
}
}



GuiceMain.java

package google.guice;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;

public class GuiceMain {

@Inject
private HelloWorld helloWorld;

public static void main(String[] args) {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override protected void configure() {}
});

GuiceMain main = injector.getInstance(GuiceMain.class);
main.helloWorld.hello();
}
}



実行結果

Hello Google Guice!!


まず、 Guice#createInjector(Module...) メソッドで Injector のインスタンスを取得する。

引数の Module には AbstractModule を継承したクラスのインスタンスを渡す。

configure() メソッドで DI の設定を記述するが、今回の Hello World は単純なケースなので、特に設定は不要。

取得した Injector から GuiceMain クラスのインスタンスを取得すると、 @Inject アノテーションが付与された helloWorld フィールドに、勝手に HelloWorld クラスのインスタンスがインジェクションされるので、そのまま hello() メソッドを実行している。


インターフェースの実装を指定する


Speaker.java

package google.guice;

public interface Speaker {
void thankYou();
}



JapaneseSpeaker.java

package google.guice;

public class JapaneseSpeaker implements Speaker {

@Override
public void thankYou() {
System.out.println("ありがとう");
}
}



GuiceMain.java

package google.guice;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;

public class GuiceMain {

@Inject
private Speaker speaker;

public static void main(String[] args) {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override protected void configure() {
bind(Speaker.class).to(JapaneseSpeaker.class);
}
});

GuiceMain main = injector.getInstance(GuiceMain.class);
main.speaker.thankYou();
}
}



実行結果

ありがとう


DI の設定は configure() メソッドの中で記述する。

bind(Speaker.class).to(JapaneseSpeaker.class); がその設定部分。 Speaker をインジェクションするときの具体的なクラスに JapaneseSpeaker を指定している。


@ImplementedBy アノテーションで実装クラスを指定する

Speaker インターフェースに @ImplementedBy アノテーションを付与すると、上記と同じ設定ができる。


Speaker.java

package google.guice;

import com.google.inject.ImplementedBy;

@ImplementedBy(JapaneseSpeaker.class)
public interface Speaker {
void thankYou();
}


@ImplementedByconfigure() メソッドの両方で設定がされていると、 configure() メソッドで設定した内容が優先される。

つまり、 @ImplementedBy はデフォルトの実装を定義するのに利用できる。

ただし、コンパイル時に具体的な実装に依存してしまうので、注意して使ったほうが良い。


インジェクションできる場所について

以下の3つ。


  • フィールド

  • メソッド

  • コンストラクタ


フィールドインジェクション

package google.guice;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;

public class GuiceMain {

@Inject
private Speaker speaker;

public static void main(String[] args) {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override protected void configure() {
bind(Speaker.class).to(JapaneseSpeaker.class);
}
});

GuiceMain main = injector.getInstance(GuiceMain.class);
main.speaker.thankYou();
}
}


実行結果

ありがとう


フィールドに @Inject アノテーションを付与すれば、可視性が private でもインジェクションできる。


メソッドインジェクション

package google.guice;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;

public class GuiceMain {

private Speaker speaker;

@Inject
public void setSpeaker(Speaker speaker) {
System.out.println("setter injection");
this.speaker = speaker;
}

public static void main(String[] args) {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override protected void configure() {
bind(Speaker.class).to(JapaneseSpeaker.class);
}
});

GuiceMain main = injector.getInstance(GuiceMain.class);
main.speaker.thankYou();
}
}


実行結果

setter injection

ありがとう

セッターメソッドに @Inject アノテーションを付与すると、セッターメソッド経由でインジェクションされる。


コンストラクタインジェクション

package google.guice;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;

public class GuiceMain {

private Speaker speaker;

@Inject
public GuiceMain(Speaker speaker) {
System.out.println("constructer injection");
this.speaker = speaker;
}

public static void main(String[] args) {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override protected void configure() {
bind(Speaker.class).to(JapaneseSpeaker.class);
}
});

GuiceMain main = injector.getInstance(GuiceMain.class);
main.speaker.thankYou();
}
}


実行結果

constructer injection

ありがとう

コンストラクタに @Inject アノテーションを付与すると、そのコンストラクタ経由でインジェクションされる。


アノテーションでインジェクションするクラスを指定する


EnglishSpeaker.java

package google.guice;

public class EnglishSpeaker implements Speaker {

@Override
public void thankYou() {
System.out.println("Thank you.");
}
}



Japanese.java

package google.guice;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import com.google.inject.BindingAnnotation;

@BindingAnnotation
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface Japanese {
}



English.java

package google.guice;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import com.google.inject.BindingAnnotation;

@BindingAnnotation
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface English {
}



GuiceMain.java

package google.guice;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;

public class GuiceMain {

@Inject @Japanese
private Speaker japaneseSpeaker;

@Inject @English
private Speaker englishSpeaker;

public static void main(String[] args) {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override protected void configure() {
bind(Speaker.class).annotatedWith(Japanese.class).to(JapaneseSpeaker.class);
bind(Speaker.class).annotatedWith(English.class).to(EnglishSpeaker.class);
}
});

GuiceMain main = injector.getInstance(GuiceMain.class);
main.japaneseSpeaker.thankYou();
main.englishSpeaker.thankYou();
}
}



実行結果

ありがとう

Thank you.

@BindingAnnotation アノテーションを設定した自作のアノテーション(@Japanese, @English)を作り、 configure() メソッド内で annotatedWith(Class<?>) を使えば、インジェクションする具体的なクラスをアノテーションごとに指定できる。


インジェクションするインスタンスを指定する


User.java

package google.guice;

public class User {

private String name;

public User(String name) {
this.name = name;
}

public void introduce() {
System.out.println("私の名前は" + this.name + "です。");
}
}



GuiceMain.java

package google.guice;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;

public class GuiceMain {

@Inject
private User user;

public static void main(String[] args) {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override protected void configure() {
bind(User.class).toInstance(new User("佐藤"));
}
});

GuiceMain main = injector.getInstance(GuiceMain.class);
main.user.introduce();
}
}



実行結果

私の名前は佐藤です。


toInstance() メソッドにインスタンスを渡すと、そのインスタンスがインジェクションされる。


Provides メソッドでインジェクションするインスタンスを定義する


GuiceMain.java

package google.guice;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provides;

public class GuiceMain {

@Inject
private User user;

public static void main(String[] args) {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override protected void configure() {}

@Provides
private User provideUser() {
return new User("鈴木");
}
});

GuiceMain main = injector.getInstance(GuiceMain.class);
main.user.introduce();
}
}



実行結果

私の名前は鈴木です。


Module クラスに Provides メソッドを定義すると、このメソッドの戻り値がインジェクションに利用される。

Provides メソッドの定義は @Provides アノテーションを付与すればいい。


Provider クラスでインジェクションするインスタンスを定義する


UserProvider.java

package google.guice;

import com.google.inject.Provider;

public class UserProvider implements Provider<User> {

@Override
public User get() {
return new User("田中");
}
}



GuiceMain.java

package google.guice;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;

public class GuiceMain {

@Inject
private User user;

public static void main(String[] args) {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override protected void configure() {
bind(User.class).toProvider(UserProvider.class);
}
});

GuiceMain main = injector.getInstance(GuiceMain.class);
main.user.introduce();
}
}



実行結果

私の名前は田中です。


com.google.inject.Provider インターフェースを実装したクラスを作成して、 configure() メソッドの中で toProvider(Class<Provider>) に指定すると、 Provider クラスの get() メソッドが返した値がインジェクションに利用されるようになる。


@ProvidedByアノテーションで Provider クラスを指定する

インターフェースに @ProvidedBy アノテーションを付与すると、上記と同じ設定ができる。


Speaker.java

package google.guice;

import com.google.inject.ProvidedBy;

@ProvidedBy(SpeakerProvider.class)
public interface Speaker {
void thankYou();
}


@ImplementedBy と同じで、 configure() メソッドで Provider クラスが設定されている場合は、そちらが優先される。


Provider クラス自体をインジェクションする


GuiceMain.java

package google.guice;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;

public class GuiceMain {

@Inject
private Provider<User> speakerProvider;

public static void main(String[] args) {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override protected void configure() {
bind(User.class).toProvider(UserProvider.class);
}
});

GuiceMain main = injector.getInstance(GuiceMain.class);
User user = main.speakerProvider.get();
user.introduce();
}
}



実行結果

私の名前は田中です。



Provider クラスの get() メソッドでチェック例外をスローする

Provider#get() メソッドは例外をスローしない定義になっているので、そのままだと非チェック例外しかスローできない。

何らかの理由で get() メソッドでチェック例外をスローしたい場合は、 Throwing Providers という Guice の拡張機能を利用する。

Throwing Providers を使用する場合は、 guice-throwingproviders-3.0.jar をクラスパスに追加する。

pom.xml は以下。


pom.xml

<dependency>

<groupId>com.google.inject.extensions</groupId>
<artifactId>guice-throwingproviders</artifactId>
<version>3.0</version>
</dependency>

まず、 CheckedProvider<T> インターフェースを拡張した独自の Provider インターフェースを定義する。


CheckedSpeakerProvider.java

package google.guice;

import com.google.inject.throwingproviders.CheckedProvider;

public interface CheckedSpeakerProvider extends CheckedProvider<Speaker> {
@Override Speaker get() throws MyException;
}



MyException.java

package google.guice;

public class MyException extends Exception {
}


次に、 CheckedSpeakerProvider インターフェースを実装した Provider クラスを作成する。


CheckedEnglishSpeakerProvider.java

package google.guice;

public class CheckedEnglishSpeakerProvider implements CheckedSpeakerProvider {

@Override
public Speaker get() throws MyException {
return new EnglishSpeaker();
}
}


最後に、 configure() メソッドの中で Provider クラスを設定する。


GuiceMain.java

package google.guice;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.throwingproviders.ThrowingProviderBinder;

public class GuiceMain {

@Inject
private CheckedSpeakerProvider provider;

public static void main(String[] args) {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override protected void configure() {
ThrowingProviderBinder.create(binder())
.bind(CheckedSpeakerProvider.class, Speaker.class)
.to(CheckedEnglishSpeakerProvider.class);
}
});

GuiceMain main = injector.getInstance(GuiceMain.class);
try {
Speaker speaker = main.provider.get();
speaker.thankYou();
} catch (MyException e) {
e.printStackTrace();
}
}
}


ThrowingProviderBinder クラスのメソッドを使って Provider クラスの設定をしている。


インジェクションするインスタンスを生成するときのコンストラクタを指定する


GuiceMain.java

package google.guice;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;

public class GuiceMain {

@Inject
private User user;

public static void main(String[] args) {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override protected void configure() {
bind(String.class).toInstance("高橋");
try {
bind(User.class).toConstructor(User.class.getConstructor(String.class));
} catch (NoSuchMethodException | SecurityException e) {
addError(e);
}
}
});

GuiceMain main = injector.getInstance(GuiceMain.class);
main.user.introduce();
}
}



実行結果

私の名前は高橋です。


toConstructor(Constructor) メソッドで、インスタンスを生成するときのコンストラクタを指定できる。


シングルトン

Guice は、デフォルトだとインジェクションの度に新しいインスタンスを生成している。

シングルトンにしたい場合は、以下のいずれかの方法を用いる。

クラスに @Singleton アノテーションを設定する

package google.guice;

import com.google.inject.Singleton;

@Singleton
public class SingletonScope {
public int count = 1000;
}

configure() で in(Singleton.class) を指定する

Injector injector = Guice.createInjector(new AbstractModule() {

@Override protected void configure() {
bind(SingletonScope.class).in(Singleton.class);
}
});

Provide メソッドに @Singleton アノテーションを設定する

Injector injector = Guice.createInjector(new AbstractModule() {

@Override protected void configure() {
}

@Provides @Singleton
private SingletonScope provideSingleton() {
return new SingletonScope();
}
});


動作確認


SingletonScope.java

package google.guice;

import com.google.inject.Singleton;

@Singleton
public class SingletonScope {
public int count = 1000;
}



DefaultScope.java

package google.guice;

public class DefaultScope {
public int count = 100;
}



GuiceMain.java

package google.guice;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;

public class GuiceMain {

public static void main(String[] args) {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override protected void configure() {}
});

SingletonScope singletonScope = injector.getInstance(SingletonScope.class);
DefaultScope defaultScope = injector.getInstance(DefaultScope.class);

System.out.printf("singleton.count = %d\n", singletonScope.count);
System.out.printf("default.count = %d\n", defaultScope.count);

singletonScope.count = 9999;
defaultScope.count = 999;

singletonScope = injector.getInstance(SingletonScope.class);
defaultScope = injector.getInstance(DefaultScope.class);

System.out.printf("singleton.count = %d\n", singletonScope.count);
System.out.printf("default.count = %d\n", defaultScope.count);
}
}



実行結果

singleton.count = 1000

default.count = 100
singleton.count = 9999
default.count = 100


起動時にシングルトンのインスタンスを生成させる

package sample.guice;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;

public class Main {

public Main() {
System.out.println("Main Constructor.");
}

public static void main(String[] args) {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override protected void configure() {
bind(Main.class).asEagerSingleton();
}
});
}
}

通常、シングルトンのオブジェクトは、初めてインジェクションされるときにインスタンスが生成される。

起動時にあらかじめ初期化をしておきたい場合は、 asEagerSingleton()configure() の中で指定する。


依存関係が解決できないときにエラーを無視する

@Inject アノテーションの optional 属性に true を指定すると、依存関係が解決できないときにエラーを無視できる。

package google.guice;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;

public class GuiceMain {

@Inject(optional=true) @English
private Speaker speaker;

public static void main(String[] args) {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override protected void configure() {
// 設定をコメントアウトしているので、 optional=false だとエラーになる
// bind(Speaker.class).annotatedWith(English.class).to(EnglishSpeaker.class);
}
});

GuiceMain main = injector.getInstance(GuiceMain.class);
System.out.println(main.speaker);
}
}


実行結果

null


インジェクションは実行されず、デフォルト値(この場合 null) になる。


オンデマンドでインジェクションする


GuiceMain.java

package google.guice;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;

public class GuiceMain {

@Inject
private Speaker speaker;

public static void main(String[] args) {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override protected void configure() {
bind(Speaker.class).to(EnglishSpeaker.class);
}
});

GuiceMain main = new GuiceMain();
System.out.println(main.speaker);

injector.injectMembers(main);
main.speaker.thankYou();
}
}



実行結果

null

Thank you.

Injector#injectMembers(Object) メソッドを使えば、好きなタイミングで、引数に渡したインスタンスに依存関係をインジェクションできる。

実行されるインジェクションはフィールドインジェクションかメソッドインジェクションで、コンストラクタインジェクションは無視される。


コンストラクタインジェクションは無視

package google.guice;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;

public class GuiceMain {

private Speaker speaker;

@Inject
public GuiceMain(Speaker speaker) {
this.speaker = speaker;
}

public static void main(String[] args) {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override protected void configure() {
bind(Speaker.class).to(EnglishSpeaker.class);
}
});

GuiceMain main = new GuiceMain(null);
System.out.println(main.speaker);

injector.injectMembers(main);
System.out.println(main.speaker);
}
}



実行結果

null

null


static フィールドにインジェクションする


GuiceMain.java

package google.guice;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;

public class GuiceMain {

@Inject
private static Speaker speaker;

public static void main(String[] args) {
Guice.createInjector(new AbstractModule() {
@Override protected void configure() {
bind(Speaker.class).to(EnglishSpeaker.class);
requestStaticInjection(GuiceMain.class);
}
});

GuiceMain.speaker.thankYou();
}
}



実行結果

Thank you.


configure() メソッドのなかで requestStaticInjection(Class<?>) を実行すると、 static フィールドに依存関係をインジェクションできる。

ただし、 Google Guice はこの使い方を推奨していない(テストしづらいし、グローバルな参照にほかならないとかとか)。

既存の実装が static な Factory を使用している場合に、 Guice による実装に移行するときに "繋ぎ" として使用することを想定しているらしい。


インターセプター


MyInterceptor.java

package google.guice;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class MyInterceptor implements MethodInterceptor {

@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("before proceed");
Object result = invocation.proceed();
System.out.println("after proceed");

return result;
}
}



GuiceMain.java

package google.guice;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.matcher.Matchers;

public class GuiceMain {

@Inject
private Speaker speaker;

public static void main(String[] args) {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override protected void configure() {
bind(Speaker.class).to(EnglishSpeaker.class);
bindInterceptor(Matchers.any(), Matchers.any(), new MyInterceptor());
}
});

GuiceMain main = injector.getInstance(GuiceMain.class);
main.speaker.thankYou();
}
}



実行結果

before proceed

Thank you.
after proceed

まず、 MethodInterceptor インターフェースを実装した、独自のインターセプターを実装する。

次に、 configure() メソッドでインターセプターの設定を記述する。

インターセプターの設定は、 bindInterceptor(Matcher<? super Class<?>>, Matcher<? super Method>, MethodInterceptor...) を使用する。

1つ目と2つ目の Matcher はインターセプターを適用するクラスを限定するための引数で、3つ目の引数に具体的なインターセプターのインスタンスを渡す。

Matcher.any() はなんでも OK を意味するので、前述の実装だと全てのクラスの全てのメソッドに対してインターセプターが適用される。

以下、 Matcher による絞り込みの実装例。


特定のパッケージ直下にあるクラスのみ対象

bindInterceptor(Matchers.inPackage(Hoge.class.getPackage()), Matchers.any(), new MyInterceptor());

Hoge クラスのあるパッケージ直下にあるクラスが対象。


特定のパッケージ以下にあるクラスのみ対象(サブパッケージも含む)

bindInterceptor(Matchers.inSubpackage("google.guice.aop.hoge"), Matchers.any(), new MyInterceptor());

google.guice.aop.hoge 以下にあるクラス(サブパッケージも含む)が対象。


特定のパッケージ以外にあるクラスのみ対象

bindInterceptor(Matchers.not(Matchers.inPackage(Hoge.class.getPackage())), Matchers.any(), new MyInterceptor());

Matchers#not(Matcher) メソッドで引数に渡した Matcher を否定した条件が指定できる。

上記の場合は、 Hoge クラスのあるパッケージ以外のパッケージに含まれるクラスが対象になる。


特定のアノテーションが付与されたクラス(メソッド)のみ対象


AopTarget.java

package google.guice.aop;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface AopTarget {
}


bindInterceptor(Matchers.annotatedWith(AopTarget.class), Matchers.any(), new MyInterceptor());

@AopTarget アノテーションを付与したクラスのみが対象になる。

bindInterceptor(Matchers.any(), Matchers.annotatedWith(AopTarget.class), new MyInterceptor());

@AopTarget アノテーションを付与したメソッドのみが対象。


特定のクラス、およびそのサブクラスのみ対象

bindInterceptor(Matchers.subclassesOf(HogeClass.class), Matchers.any(), new MyInterceptor());

HogeClass と、それを継承したクラスのみが対象。


Matcher を自作する

AbstractMatcher というクラスがあるので、それを継承すれば任意のクラス(メソッド)にマッチする Matcher を自作できる。


MyMatcher.java

package google.guice.aop;

import com.google.inject.matcher.AbstractMatcher;

public class MyMatcher extends AbstractMatcher<Class<?>> {

@Override
public boolean matches(Class<?> clazz) {
return clazz.getSimpleName().endsWith("Impl");
}
}


bindInterceptor(new MyMatcher(), Matchers.any(), new MyInterceptor());

名前が Impl で終わるクラスが対象になる。


参考