最近在针对一个开源A/B Test系统做二次开发,这个系统使用的是google 开源的轻量级依赖注入框架Guice。
1、Guice简介
Guice是Google开发的一个轻量级,基于Java5(主要运用泛型与注释特性)的依赖注入框架(IOC)。Guice非常小而且快。Guice是一个非常干净的依赖注入框架,框架除了依赖注入功能之外,没有任何其它非相关模块功能。
2、依赖注入方式
首先要引入JAR包
com.google.inject
guice
4.0
- 字段直接注入
@Inject
UserService userService;
//注意,并不是说实现注入的service,一定是要在Module中和接口绑定,只是使用接口的方式更加优雅,
//像RedisClientTemplate这种工具性质的类,可以直接注入
@Inject
RedisClientTemplate redisClientTemplate;
- 配件文件注入
//配置文件参数读取并注入,当使用Names.bindProperties把配置文件中的内容绑定加载后,就可以直接
// @Inject @Named("redis.timeout") 注入使用了,相当的方便
Names.bindProperties(binder(), PropertyUtil.loadFile(file, getClass()));
- @Named名称指令来指定依赖注入实现(一个接口,多个实现类,这个非常实用)
@Inject
@Named("user")
UserService userService;
@Inject
@Named("worker")
UserService workerService;
- 构造器注入
@Inject
public UserApi(UserService userService)
{
this.userService = userService;
}
3、Demo
git地址 https://github.com/kzhang09/GuiceLearning.git
- pom.xml
4.0.0
guice
guice-learning
1.0-SNAPSHOT
com.google.inject
guice
4.0
org.projectlombok
lombok
1.16.10
mysql
mysql-connector-java
5.1.38
runtime
org.mybatis
mybatis
3.4.5
com.alibaba
druid
1.1.9
org.mybatis
mybatis-guice
3.10
com.intuit.autumn
autumn-service
1.0.20160714060618
com.intuit.autumn
autumn-web
1.0.20160714060618
com.intuit.autumn
autumn-api
1.0.20160714060618
javax.ws.rs
jsr311-api
1.1.1
redis.clients
jedis
3.0.1
jar
- 项目启动入口Main
import api.ApiModule;
import com.google.inject.Inject;
import com.intuit.autumn.service.ServiceManager;
import mapper.MysqlModule;
import service.UserService;
import static com.intuit.autumn.web.WebServices.getEnabledWebServices;
public class Main {
private Main() {
}
@Inject
UserService userService;
public static void main(String[] args) throws Exception {
ServiceManager serviceManager = new ServiceManager()
//加载module类,实现注入
.addModules(MysqlModule.class, ApiModule.class)
//加载http服务
.addServices(getEnabledWebServices());
//项目启动
serviceManager.start();
}
}
- Api 接口
package api;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import po.User;
import service.UserService;
import javax.annotation.Resource;
import javax.ws.rs.*;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
@Path("/api")
@Produces(APPLICATION_JSON)
@Singleton
public class UserApi
{
//两种方式注入都可以,直接注入更简单点
@Inject
UserService userService;
@Inject
RedisClientTemplate redisClientTemplate;
// @Inject
// public UserApi(UserService userService)
// {
// this.userService = userService;
// }
@GET
@Path("/users/{userId}")
@Produces(APPLICATION_JSON)
public Response getAssignment(
@PathParam("userId")
final int userId
) {
try {
System.out.println(userService.getUser());
redisClientTemplate.setex("guice_test",10,"OK");
System.out.println(redisClientTemplate.get("guice_test"));
return Response.ok(userService.getUser()).build();
} catch (Exception exception) {
throw exception;
}
}
}
api module
package api;
import com.google.inject.AbstractModule;
import service.ServiceModule;
public class ApiModule extends AbstractModule
{
@Override protected void configure()
{
install(new com.intuit.autumn.api.ApiModule());
//把下层serviceModule 注入,当然也可以在 main中直接注入 .addModules(MysqlModule.class, ApiModule.class,ServiceModule.class);
// 这种注入有个好处,当多模块开发时,可以只在需要的地方注入
install(new ServiceModule());
}
}
- Service类
package service;
import po.User;
import java.util.List;
public interface UserService
{
List getUser();
User getUserByName(String userName);
User getUserByAge(int age);
}
package service.impl;
import com.google.inject.Inject;
import mapper.UserMapper;
import po.User;
import service.UserService;
import java.util.List;
public class UserServiceImpl implements UserService
{
//注入
@Inject
UserMapper userMapper;
@Override public List getUser()
{
return userMapper.getUsers();
}
@Override public User getUserByName(String userName)
{
return userMapper.getUserByName(userName);
}
@Override public User getUserByAge(int age)
{
return userMapper.getUserByAge(age);
}
}
Service Module
package service;
import com.google.inject.AbstractModule;
import service.impl.UserServiceImpl;
public class ServiceModule extends AbstractModule
{
@Override protected void configure()
{
//service服务注入,同时绑定接口UserService 和 实现类UserServiceImpl;
//这样就可以在别的地方直接使用接口注入了;比如 @Inject UserService userService;
bind(UserService.class).to(UserServiceImpl.class);
}
}
- 集成mybatis
package mapper;
import com.google.inject.AbstractModule;
import com.google.inject.name.Names;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.mybatis.guice.MyBatisModule;
import org.mybatis.guice.datasource.druid.DruidDataSourceProvider;
import util.PropertyUtil;
public class MysqlModule extends AbstractModule
{
private String file = "mysql.properties";
@Override
protected void configure() {
//配置文件参数读取并注入,当使用Names.bindProperties把配置文件中的内容bind加载后,就可以直接
// @Inject @Named("redis.timeout") 注入使用了,相当的方便
Names.bindProperties(binder(), PropertyUtil.loadFile(file, getClass()));
//mysql数据库注入
this.install(new MyBatisModule() {
@Override
protected void initialize() {
bindDataSourceProviderType(DruidDataSourceProvider.class);
bindTransactionFactoryType(JdbcTransactionFactory.class);
// 通过属性配置完成映射,即属性的命名是遵从驼峰命名法的,数据列名遵从下划线命名
// 会自动把 列名user_name 映射成实体userName;
// 使用这个,同时使用注解,可以完全去除xml配置了
mapUnderscoreToCamelCase(true);
//类名,通常是使用下面通过包名来加载Mapper.class文件
addMapperClass(UserMapper.class);
//包名
//addMapperClasses("mapper");
//必须要有,不然会报错
environmentId("dev");
}
});
}
}
Mapper 类
package mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import po.User;
import java.util.List;
public interface UserMapper
{
@Select("SELECT * FROM user WHERE user_name = #{userName}")
User getUserByName(@Param("userName") String userName);
@Select("SELECT * FROM user WHERE age = #{arg0}")
User getUserByAge(int age);
@Select("SELECT * FROM user ")
List getUsers();
}
- 实体PO
package po;
import lombok.Data;
@Data
public class User
{
private String userName;
private int age;
}
- 集成Redis
RedisModule
package redis;
import com.google.inject.AbstractModule;
import com.google.inject.Scopes;
import com.google.inject.name.Names;
import util.PropertyUtil;
public class RedisModule extends AbstractModule
{
private String file = "redis.properties";
@Override protected void configure()
{
System.out.println("installing module: {}"+ RedisModule.class.getSimpleName());
Names.bindProperties(binder(), PropertyUtil.loadFile(file, getClass()));
//绑定redis数据源
bind(RedisDataSource.class).toProvider(RedisClientProvider.class).in(Scopes.SINGLETON);
}
}
RedisClientTemplate
package redis;
import com.google.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.*;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class RedisClientTemplate {
private static final Logger log = LoggerFactory.getLogger(RedisClientTemplate.class);
@Inject
private RedisDataSource redisDataSource;
public void disconnect() {
ShardedJedis shardedJedis = redisDataSource.getRedisClient();
shardedJedis.disconnect();
}
/**
* 设置单个值
*
* @param key
* @param value
* @return
*/
public String set(String key, String value) {
String result = null;
ShardedJedis shardedJedis = redisDataSource.getRedisClient();
if (shardedJedis == null) {
return result;
}
try {
result = shardedJedis.set(key, value);
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
redisDataSource.returnResource(shardedJedis);
}
return result;
}
/**
* 获取单个值
*
* @param key
* @return
*/
public String get(String key) {
String result = null;
ShardedJedis shardedJedis = redisDataSource.getRedisClient();
if (shardedJedis == null) {
return result;
}
try {
result = shardedJedis.get(key);
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
redisDataSource.returnResource(shardedJedis);
}
return result;
}
//......
//太长了,剩余的见git上的源码
}
RedisClientProvider
package redis;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.name.Named;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.ShardedJedisPool;
import java.util.Arrays;
import java.util.Objects;
public class RedisClientProvider implements Provider
{
@Inject
@Named("redis.database")
private Integer database;
@Inject
@Named("redis.host")
private String host;
@Inject
@Named("redis.port")
private Integer port;
@Inject
@Named("redis.password")
private String password;
@Inject
@Named("redis.pool.max.active")
private Integer maxActive;
@Inject
@Named("redis.pool.max.idle")
private Integer maxIdle;
@Inject
@Named("redis.pool.max.wait")
private Integer maxWait;
@Inject
@Named("redis.pool.min.idle")
private Integer minIdle;
@Inject
@Named("redis.timeout")
private Integer timeout;
private ShardedJedisPool getJedisPool() {
if (Objects.isNull(shardedPool)) {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(this.maxActive);
config.setMaxWaitMillis(this.maxWait);
config.setMaxIdle(this.maxIdle);
config.setMinIdle(this.minIdle);
JedisShardInfo info = new JedisShardInfo(this.host, this.port.intValue(),
this.timeout.intValue());
if (this.password != null && !this.password.isEmpty()) {
info.setPassword(password);
}
shardedPool = new ShardedJedisPool(config, Arrays.asList(new JedisShardInfo[]{info}));
} else {
return shardedPool;
}
return shardedPool;
}
@Override
public RedisDataSource get() {
return new RedisDataSource(getJedisPool());
}
private static ShardedJedisPool shardedPool;
}
RedisDataSource
package redis;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;
public class RedisDataSource
{
private static final Logger log = LoggerFactory.getLogger(RedisDataSource.class);
private ShardedJedisPool shardedJedisPool;
public RedisDataSource(ShardedJedisPool jedisPool)
{
this.shardedJedisPool = jedisPool;
}
public ShardedJedis getRedisClient() {
try {
ShardedJedis shardJedis = shardedJedisPool.getResource();
return shardJedis;
} catch (Exception e) {
log.error("getRedisClent error", e);
}
return null;
}
public void returnResource(ShardedJedis shardedJedis) {
shardedJedis.close();
}
}
- Uitl工具类
package util;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class PropertyUtil
{
/**
* 读取配置文件属性
*/
public static Properties loadFile(String prefix, Class cla) {
String fileName = prefix;
Properties prop = new Properties();
InputStream in = null;
try {
in = cla.getResource("/" + fileName).openStream();
prop.load(in);
} catch (IOException e) {
e.printStackTrace();
}
return prop;
}
}
- 配置文件
mysql.properties
#dev
#JDBC
JDBC.driverClassName=com.mysql.jdbc.Driver
JDBC.url=JDBC:mysql://x.x.x.x:3306/wasabi?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=true&zeroDateTimeBehavior=convertToNull
JDBC.username=readwrite
JDBC.password=readwrite
JDBC.initialSize=5
JDBC.maxWait=30000
JDBC.testWhileIdle=true
JDBC.testOnBorrow=true
JDBC.testOnReturn=false
JDBC.validationQuery=SELECT 1
JDBC.minEvictableIdleTimeMillis=300000
JDBC.timeBetweenEvictionRunsMillis=60000
web.properties
#http服务
application.http.enabled=true
#服务端口
application.http.port=8080
#api服务包名
application.jersey.provider.paths=api
application.jersey.pojo.enabled=true
application.jersey.wadl.enabled=false
redis.properties
redis.database=0
redis.host=x.x.x.x
redis.port=6379
redis.password=xxxx
#连接池最大连接数(使用负值表示没有限制)
redis.pool.max.active=10000
# 连接池中的最大空闲连接
redis.pool.max.idle=100
#连接池最大阻塞等待时间(使用负值表示没有限制)
redis.pool.max.wait=-1
# 连接池中的最小空闲连接
redis.pool.min.idle=0
# 连接超时时间(毫秒)
redis.timeout=0
demo结构