前言
習慣了Spring全家桶,對spring的容器愛不釋手。使用dropwizard,看起來確實很輕,然而,真正使用的時候不得不面臨一個問題。我們不可能一個resource就能把所有的業務邏輯囊括!那么,必然就要有負責處理邏輯的代碼,有要提取的公共的代碼,要做面向接口開發等等。按照簡單的用法,Java Bean就自己new,只要通過Jersey提供的web能力發出去就好。這樣寫下來,到處都需要new,難以測試等等。目前我最care的是,每個request過來都要new一堆重復的對象,垃圾回收頻繁。寫個單例不就解決了?是的,當然要想到單例,然后發現幾乎所有的類都是設計成單例的。然后,一堆單例的代碼寫的死。這就是樣板代碼。于是,想到提取工具類,算了,不如用Dagger好了。
什么是Dagger
Dagger是Java里開源的DI框架中最火的之一,主要用在Android領域,很多特性也多針對Android開發的。因為Android開發對省電,性能之類的要求比較高,因此拋棄了反射,直接在編譯級別生成工廠。詳細學習測試:Dagger2之helloworld原理探究
Demo Source
https://github.com/Ryan-Miao/l4dropwizard
structure
.
├── pom.xml
├── readme.md
└── src└── main├── java│?? └── com│?? └── test│?? ├── HelloWorldApplication.java│?? ├── bundles│?? │?? └── ConnectivityBundle.java│?? ├── configuration│?? │?? ├── HelloWorldConfiguration.java│?? │?? └── modules│?? │?? ├── ConnectAndReadConfig.java│?? │?? └── GithubApiConfig.java│?? └── domain│?? ├── connect│?? │?? ├── FeignClientBuilder.java│?? │?? ├── GithubClient.java│?? │?? └── GithubConnector.java│?? ├── entiry│?? │?? ├── GithubUser.java│?? │?? └── Saying.java│?? ├── exception│?? │?? └── UpstreamException.java│?? ├── health│?? │?? └── TemplateHealthCheck.java│?? ├── ioc│?? │?? ├── component│?? │?? │?? └── GithubComponent.java│?? │?? └── module│?? │?? ├── ConfigurationModule.java│?? │?? ├── ConnectorModule.java│?? │?? └── ServiceModule.java│?? ├── resource│?? │?? ├── GithubResource.java│?? │?? └── HelloWorldResource.java│?? └── service│?? ├── IGithubService.java│?? └── impl│?? └── GithubService.java└── resources└── config└── dev.yml
本文基于之前的dropwizard入門演進.
添加dagger依賴
在properties
結點下新增
<dagger.verion>2.12</dagger.verion>
在dependencies下新增
<dependency><groupId>com.google.dagger</groupId><artifactId>dagger</artifactId><version>${dagger.verion}</version>
</dependency>
在build.plugins
下新增plugin
<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.6.1</version><configuration><source>${java.version}</source><target>${java.version}</target><encoding>UTF-8</encoding><annotationProcessorPaths><path><groupId>com.google.dagger</groupId><artifactId>dagger-compiler</artifactId><version>${dagger.verion}</version></path></annotationProcessorPaths></configuration>
</plugin>
在IDEA設置中,找到Build>Compiler>Annotation Processors
, 選擇Enable annotation processing
.
創建一個Component
下面創建Component,用來包容Resource類,對外提供Resource類。新建com.test.domain.ioc.component.GithubComponent
package com.test.domain.ioc.component;import com.test.domain.ioc.module.ServiceModule;
import com.test.domain.resource.GithubResource;
import dagger.Component;import javax.inject.Singleton;/**
* Created by Ryan Miao on 10/26/17.
*/
@Singleton
@Component(modules = {ServiceModule.class})
public interface GithubComponent {GithubResource gitHubResource();
}
顯然,這個GithubResource
需要注入一個service,于是聲明一個ServiceModule
, 創建com.test.domain.ioc.module.ServiceModule
package com.test.domain.ioc.module;import com.test.domain.service.IGithubService;
import com.test.domain.service.impl.GithubService;
import dagger.Module;
import dagger.Provides;import javax.inject.Singleton;/**
* Created by Ryan Miao on 10/26/17.
*/
@Module(includes = ConnectorModule.class)
public class ServiceModule {@Singleton@Providespublic IGithubService githubService(GithubService service) {return service;}
}
ServiceModule
用來提供service注入,service接著依賴connector層,新建com.test.domain.ioc.module.ConnectorModule
package com.test.domain.ioc.module;import com.test.domain.connect.FeignClientBuilder;
import dagger.Module;
import dagger.Provides;import javax.inject.Singleton;/**
* Created by Ryan Miao on 10/26/17.
*/
@Module(includes = ConfigurationModule.class)
public class ConnectorModule {@Provides@Singletonpublic FeignClientBuilder feignClientBuilder(){return new FeignClientBuilder();}}
在connecttor
層中,需要調用GlobalConfiguration的配置項,所以,單獨把配置提出來,引入。新增com.test.domain.ioc.module.ConfigurationModule
package com.test.domain.ioc.module;import com.test.configuration.HelloWorldConfiguration;
import dagger.Module;
import dagger.Provides;import javax.inject.Singleton;/**
* Created by Ryan Miao on 11/20/17.
*/
@Module
public class ConfigurationModule {private final HelloWorldConfiguration configuration;public ConfigurationModule(HelloWorldConfiguration configuration) {this.configuration = configuration;}@Provides@Singletonpublic HelloWorldConfiguration helloWorldConfiguration(){return configuration;}
}
這是依賴的最底層,我們通過手動構造函數的方式注入configuration,這樣可以在dropwizard啟動時生成module,并且得到configuration。
引入我們的Component
這時候,build一下,dagger就會自動生成我們的工廠。
mvn clean install
然后,在IDEA里的maven plugin里,右鍵,reimport。防止IDEA不認識dagger自動生成的類。dagger自動生成的類位于target/generated-sources/annotations
. 點擊刷新按鈕,刷新下maven依賴。
然后,在com.test.HelloWorldApplication
中,新增
private void registerResources(HelloWorldConfiguration configuration, Environment environment) {GithubComponent component = DaggerGithubComponent.builder().configurationModule(new ConfigurationModule(configuration)).build();environment.jersey().register(component.gitHubResource());
}
DaggerGithubComponent
要在maven install之后,dagger生成的。完整啟動類如下:
public class HelloWorldApplication extends Application<HelloWorldConfiguration> {public static void main(String[] args) throws Exception {new HelloWorldApplication().run(args);}@Overridepublic String getName() {return "hello-world";}@Overridepublic void initialize(Bootstrap<HelloWorldConfiguration> bootstrap) {bootstrap.addBundle(new ConnectivityBundle());}@Overridepublic void run(HelloWorldConfiguration configuration, Environment environment) throws Exception {final HelloWorldResource resource = new HelloWorldResource(configuration.getTemplate(),configuration.getDefaultName());final TemplateHealthCheck healthCheck =new TemplateHealthCheck(configuration.getTemplate());environment.healthChecks().register("template", healthCheck);environment.jersey().register(resource);environment.jersey().register(healthCheck);registerResources(configuration, environment);}private void registerResources(HelloWorldConfiguration configuration, Environment environment) {GithubComponent component = DaggerGithubComponent.builder().configurationModule(new ConfigurationModule(configuration)).build();environment.jersey().register(component.gitHubResource());}
}
當然,我們的Resource也要改成Inject模式
public class GithubResource {private IGithubService service;@Injectpublic GithubResource(IGithubService service) {this.service = service;}@GET@Timed@Path("/users/{username}")public GithubUser getUserProfile(@PathParam("username") final String username) {return service.getUserProfile(username);}}
啟動,運行。一切OK。以后就可以在需要注入的類的構造器上聲明@Inject
, 或者在module里@Provide
,就可以實現構造解耦。測試不要太方便.
唯有不斷學習方能改變! -- Ryan Miao