├── .gitignore ├── README.md ├── pom.xml └── src ├── main ├── java │ └── api │ │ └── mengkang │ │ └── net │ │ ├── Config.java │ │ ├── ErrorCode.java │ │ ├── Request.java │ │ ├── RequestHandler.java │ │ ├── Response.java │ │ ├── api │ │ ├── BaseController.java │ │ └── UserController.java │ │ ├── dao │ │ └── UserDao.java │ │ ├── entity │ │ └── User.java │ │ ├── model │ │ └── UserModel.java │ │ ├── netty │ │ ├── HttpServer.java │ │ ├── HttpServerHandler.java │ │ └── HttpServerInitializer.java │ │ └── utils │ │ └── mysql │ │ ├── DMLTypes.java │ │ ├── DbFiled.java │ │ ├── JdbcPool.java │ │ ├── MySelect.java │ │ └── Mysql.java └── resources │ ├── api.properties │ ├── read.db.properties │ └── write.db.properties └── test └── java └── TestUserInfo.java /.gitignore: -------------------------------------------------------------------------------- 1 | /demo.iml 2 | /target 3 | /.idea 4 | /netty-http-demo.iml 5 | # Created by .ignore support plugin (hsz.mobi) 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # netty-http-demo 2 | 直播 https://segmentfault.com/l/1500000009988553 3 | 详细讲解 https://segmentfault.com/a/1190000010333464 4 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | mengkang.net 8 | demo 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | io.netty 14 | netty-all 15 | 5.0.0.Alpha2 16 | 17 | 18 | junit 19 | junit 20 | 4.7 21 | test 22 | 23 | 24 | com.alibaba 25 | fastjson 26 | 1.2.27 27 | 28 | 29 | ch.qos.logback 30 | logback-classic 31 | 1.1.3 32 | 33 | 34 | 35 | mysql 36 | mysql-connector-java 37 | 5.1.18 38 | 39 | 40 | commons-dbcp 41 | commons-dbcp 42 | 1.4 43 | 44 | 45 | commons-pool 46 | commons-pool 47 | 1.6 48 | 49 | 50 | 51 | 52 | 53 | 54 | org.apache.maven.plugins 55 | maven-compiler-plugin 56 | 2.3.2 57 | 58 | 1.8 59 | 1.8 60 | UTF-8 61 | 62 | 63 | 64 | org.apache.maven.plugins 65 | maven-dependency-plugin 66 | 2.1 67 | 68 | 69 | copy-dependencies 70 | compile 71 | 72 | copy-dependencies 73 | 74 | 75 | ${project.build.directory}/lib 76 | false 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /src/main/java/api/mengkang/net/Config.java: -------------------------------------------------------------------------------- 1 | package api.mengkang.net; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStreamReader; 5 | import java.util.Properties; 6 | 7 | /** 8 | * @author zhoumengkang 9 | */ 10 | public class Config { 11 | 12 | private static Properties PROPERTIES = new Properties(); 13 | private static final String CONFIG_NAME = "/api.properties"; 14 | private static Config instance; 15 | 16 | private Config() { 17 | try { 18 | PROPERTIES.load(new InputStreamReader(getClass().getResourceAsStream(CONFIG_NAME), "UTF-8")); 19 | } catch (IOException e) { 20 | e.printStackTrace(); 21 | } 22 | } 23 | 24 | private synchronized static Config getInstance() { 25 | if (null == instance) { 26 | instance = new Config(); 27 | } 28 | return instance; 29 | } 30 | 31 | public static int getInt(String str) { 32 | try { 33 | if (null == instance) { 34 | getInstance(); 35 | } 36 | return Integer.parseInt(PROPERTIES.getProperty(str)); 37 | } catch (Exception e) { 38 | e.printStackTrace(); 39 | return 0; 40 | } 41 | } 42 | 43 | public static long getLong(String str) { 44 | try { 45 | if (null == instance) { 46 | getInstance(); 47 | } 48 | return Long.parseLong(PROPERTIES.getProperty(str)); 49 | } catch (Exception e) { 50 | e.printStackTrace(); 51 | return 0; 52 | } 53 | } 54 | 55 | public static double getDouble(String str) { 56 | try { 57 | if (null == instance) { 58 | getInstance(); 59 | } 60 | return Double.parseDouble(PROPERTIES.getProperty(str)); 61 | 62 | } catch (Exception e) { 63 | e.printStackTrace(); 64 | return 0; 65 | } 66 | } 67 | 68 | public static String getString(String str) { 69 | try { 70 | if (null == instance) { 71 | getInstance(); 72 | } 73 | return PROPERTIES.getProperty(str); 74 | } catch (Exception e) { 75 | e.printStackTrace(); 76 | return ""; 77 | } 78 | } 79 | 80 | public static boolean getBoolean(String str) { 81 | try { 82 | if (null == instance) { 83 | getInstance(); 84 | } 85 | return Boolean.parseBoolean(PROPERTIES.getProperty(str)); 86 | } catch (Exception e) { 87 | e.printStackTrace(); 88 | return false; 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/api/mengkang/net/ErrorCode.java: -------------------------------------------------------------------------------- 1 | package api.mengkang.net; 2 | 3 | import java.util.HashMap; 4 | 5 | /** 6 | * @author zhoumengkang 7 | * @date 17/7/26. 8 | */ 9 | public class ErrorCode { 10 | 11 | public static final int SYSTEM_ERROR = 1000; 12 | public static final int METHOD_CAN_NOT_BE_NULL = 1001; 13 | public static final int PARAMETER_NOT_FOUND = 1002; 14 | public static final int PARAMETER_FORMAT_ERROR = 1003; 15 | 16 | public static final HashMap ERROR_MESSAGES = new HashMap<>(); 17 | 18 | static { 19 | ERROR_MESSAGES.put(METHOD_CAN_NOT_BE_NULL,"method 不能为空"); 20 | ERROR_MESSAGES.put(SYSTEM_ERROR,"系统异常"); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/api/mengkang/net/Request.java: -------------------------------------------------------------------------------- 1 | package api.mengkang.net; 2 | 3 | import java.util.HashMap; 4 | import java.util.List; 5 | import java.util.Map; 6 | 7 | import io.netty.handler.codec.http.HttpMethod; 8 | 9 | /** 10 | * @author zhoumengkang 11 | * @date 17/7/26. 12 | */ 13 | public class Request { 14 | private String ip; 15 | private HttpMethod method; 16 | private Map> parameters = new HashMap>(); // get 和 post 的键值对都存储在这里 17 | private String postBody = null; // post 请求时的非键值对内容 18 | 19 | public String getIp() { 20 | return ip; 21 | } 22 | 23 | public void setIp(String ip) { 24 | this.ip = ip; 25 | } 26 | 27 | public HttpMethod getMethod() { 28 | return method; 29 | } 30 | 31 | public void setMethod(HttpMethod method) { 32 | this.method = method; 33 | } 34 | 35 | public Map> getParameters() { 36 | return parameters; 37 | } 38 | 39 | public void setParameters(Map> parameters) { 40 | this.parameters = parameters; 41 | } 42 | 43 | public String getPostBody() { 44 | return postBody; 45 | } 46 | 47 | public void setPostBody(String postBody) { 48 | this.postBody = postBody; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/api/mengkang/net/RequestHandler.java: -------------------------------------------------------------------------------- 1 | package api.mengkang.net; 2 | 3 | import java.lang.reflect.Constructor; 4 | import java.lang.reflect.InvocationTargetException; 5 | import java.lang.reflect.Method; 6 | import java.net.InetSocketAddress; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import com.alibaba.fastjson.JSON; 11 | 12 | import io.netty.channel.ChannelHandlerContext; 13 | import io.netty.handler.codec.http.HttpMethod; 14 | import io.netty.handler.codec.http.HttpRequest; 15 | import io.netty.handler.codec.http.QueryStringDecoder; 16 | import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder; 17 | import io.netty.handler.codec.http.multipart.InterfaceHttpData; 18 | import io.netty.handler.codec.http.multipart.MixedAttribute; 19 | import io.netty.util.CharsetUtil; 20 | 21 | /** 22 | * @author zhoumengkang 23 | * @date 17/7/25. 24 | */ 25 | public class RequestHandler { 26 | 27 | /** 28 | * 我们约定的请求的 method 参数 来查找控制 29 | */ 30 | private static final String REQUEST_METHOD = "method"; 31 | 32 | private static Request requestFetch(ChannelHandlerContext ctx, Object msg){ 33 | 34 | Request request = new Request(); 35 | 36 | HttpRequest req = (HttpRequest)msg; 37 | String uri = req.uri(); 38 | 39 | // ip 40 | String clientIP = (String) req.headers().get("X-Forwarded-For"); 41 | if (clientIP == null) { 42 | InetSocketAddress remoteSocket = (InetSocketAddress) ctx.channel().remoteAddress(); 43 | clientIP = remoteSocket.getAddress().getHostAddress(); 44 | } 45 | request.setIp(clientIP); 46 | 47 | // method 48 | request.setMethod(req.method()); 49 | 50 | // get 51 | QueryStringDecoder queryStringDecoder = new QueryStringDecoder(uri); 52 | if (queryStringDecoder.parameters().size() > 0) { 53 | request.getParameters().putAll(queryStringDecoder.parameters()); 54 | } 55 | 56 | // post 57 | if (req.method().equals(HttpMethod.POST)) { 58 | HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(req); 59 | try { 60 | List postList = decoder.getBodyHttpDatas(); 61 | for (InterfaceHttpData data : postList) { 62 | List values = new ArrayList<>(); 63 | MixedAttribute value = (MixedAttribute) data; 64 | value.setCharset(CharsetUtil.UTF_8); 65 | values.add(value.getValue()); 66 | request.getParameters().put(data.getName(), values); 67 | } 68 | } catch (Exception e) { 69 | e.printStackTrace(); 70 | } 71 | } 72 | 73 | return request; 74 | } 75 | 76 | public static byte[] response(ChannelHandlerContext ctx, Object msg) { 77 | 78 | Request request = requestFetch(ctx,msg); 79 | 80 | if (!request.getParameters().containsKey(REQUEST_METHOD)) { 81 | return responseError(ErrorCode.METHOD_CAN_NOT_BE_NULL); 82 | } 83 | 84 | String method = request.getParameters().get(REQUEST_METHOD).get(0); 85 | String[] classAndMethodArray = method.split("\\."); 86 | 87 | if (classAndMethodArray.length < 2) { 88 | return responseError(ErrorCode.METHOD_CAN_NOT_BE_NULL); 89 | } 90 | 91 | String clazz = getApiController(classAndMethodArray[0]); 92 | String function = classAndMethodArray[1]; 93 | 94 | Object obj = invoke(clazz, function,request); 95 | 96 | return encode(new Response(obj)); 97 | } 98 | 99 | private static byte[] encode(Object object) { 100 | String data = JSON.toJSONString(object); 101 | return data.getBytes(); 102 | } 103 | 104 | /** 105 | * 返回错误信息 106 | * 107 | * @return 108 | */ 109 | private static byte[] responseError(int errorCode) { 110 | Response response = new Response(errorCode); 111 | return encode(response); 112 | } 113 | 114 | private static String getApiController(String method) { 115 | char[] tmp = method.toCharArray(); 116 | tmp[0] -= 32; 117 | return String.valueOf(tmp); 118 | } 119 | 120 | private static Object invoke(String clazz, String function,Request request) { 121 | Class classname; 122 | Object classObject; 123 | Constructor constructor; 124 | Method methodName; 125 | Object result = null; 126 | 127 | try { 128 | classname = Class.forName("api.mengkang.net.api." + clazz + "Controller"); 129 | constructor = classname.getConstructor(Request.class); 130 | classObject = constructor.newInstance(request); 131 | methodName = classname.getMethod(function); 132 | result = methodName.invoke(classObject); 133 | } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) { 134 | e.printStackTrace(); 135 | return responseError(ErrorCode.SYSTEM_ERROR); 136 | } 137 | 138 | return result; 139 | } 140 | 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/api/mengkang/net/Response.java: -------------------------------------------------------------------------------- 1 | package api.mengkang.net; 2 | 3 | /** 4 | * @author zhoumengkang 5 | * @date 17/7/26. 6 | */ 7 | public class Response { 8 | /** 9 | * 成功时 0 ,如果大于 0 则表示则显示error_msg 10 | */ 11 | private int errorCode; 12 | private String errorMsg; 13 | private Object data; 14 | 15 | public int getErrorCode() { 16 | return errorCode; 17 | } 18 | 19 | public void setErrorCode(int errorCode) { 20 | this.errorCode = errorCode; 21 | } 22 | 23 | public String getErrorMsg() { 24 | return errorMsg; 25 | } 26 | 27 | public void setErrorMsg(String errorMsg) { 28 | this.errorMsg = errorMsg; 29 | } 30 | 31 | public Object getData() { 32 | return data; 33 | } 34 | 35 | public void setData(Object data) { 36 | this.data = data; 37 | } 38 | 39 | public Response(int errorCode) { 40 | this.errorCode = errorCode; 41 | if (ErrorCode.ERROR_MESSAGES.containsKey(errorCode)){ 42 | this.errorMsg = ErrorCode.ERROR_MESSAGES.get(errorCode); 43 | } 44 | } 45 | 46 | public Response(int errorCode, String errorMsg) { 47 | this.errorCode = errorCode; 48 | this.errorMsg = errorMsg; 49 | } 50 | 51 | public Response(Object data) { 52 | this.data = data; 53 | } 54 | 55 | public Response() { 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/api/mengkang/net/api/BaseController.java: -------------------------------------------------------------------------------- 1 | package api.mengkang.net.api; 2 | 3 | import api.mengkang.net.ErrorCode; 4 | import api.mengkang.net.Request; 5 | import api.mengkang.net.Response; 6 | 7 | /** 8 | * @author zhoumengkang 9 | * @date 17/7/26. 10 | */ 11 | public class BaseController { 12 | Request request; 13 | 14 | public BaseController(Request request) { 15 | this.request = request; 16 | } 17 | 18 | protected Response responseError(int errorCode) { 19 | return new Response(errorCode); 20 | } 21 | 22 | protected Response parameterNotFound(String parameterName){ 23 | return new Response(ErrorCode.PARAMETER_NOT_FOUND,"参数: " + parameterName + " 不可缺省"); 24 | } 25 | 26 | protected Response parameterFormatError(String parameterName){ 27 | return new Response(ErrorCode.PARAMETER_FORMAT_ERROR,"参数: " + parameterName + " 格式错误"); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/api/mengkang/net/api/UserController.java: -------------------------------------------------------------------------------- 1 | package api.mengkang.net.api; 2 | 3 | import api.mengkang.net.Request; 4 | import api.mengkang.net.entity.User; 5 | import api.mengkang.net.model.UserModel; 6 | 7 | /** 8 | * @author zhoumengkang 9 | * @date 17/7/25. 10 | */ 11 | public class UserController extends BaseController { 12 | 13 | public UserController(Request request) { 14 | super(request); 15 | } 16 | 17 | public Object get(){ 18 | 19 | int uid; 20 | 21 | if (!request.getParameters().containsKey("id")){ 22 | return parameterNotFound("uid"); 23 | } 24 | 25 | try{ 26 | uid = Integer.parseInt(request.getParameters().get("id").get(0)); 27 | }catch (NumberFormatException e){ 28 | return parameterFormatError("uid"); 29 | } 30 | 31 | User user = UserModel.getUserInfoById(uid); 32 | 33 | if (user == null) { 34 | return parameterFormatError("uid"); 35 | } 36 | 37 | return user; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/api/mengkang/net/dao/UserDao.java: -------------------------------------------------------------------------------- 1 | package api.mengkang.net.dao; 2 | 3 | import api.mengkang.net.entity.User; 4 | import api.mengkang.net.utils.mysql.MySelect; 5 | import api.mengkang.net.utils.mysql.Mysql; 6 | 7 | /** 8 | * @author zhoumengkang 9 | * @date 17/7/26. 10 | */ 11 | public class UserDao { 12 | public static User getById(int id){ 13 | String sql = "select id,age,sex,username from `user` where id=?"; 14 | MySelect mySelect = new MySelect<>(new User()); 15 | return mySelect.get(sql,id); 16 | } 17 | 18 | public static int add(User user){ 19 | return Mysql.insert("insert into user (age,sex,username) values (?,?,?)", 20 | user.getAge(),user.getSex(),user.getUsername() 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/api/mengkang/net/entity/User.java: -------------------------------------------------------------------------------- 1 | package api.mengkang.net.entity; 2 | 3 | /** 4 | * @author zhoumengkang 5 | * @date 17/7/26. 6 | */ 7 | public class User { 8 | private Integer id; 9 | private Integer age; 10 | private Integer sex; 11 | private String username; 12 | 13 | public User() { 14 | } 15 | 16 | public User(Integer id, Integer age, Integer sex, String username) { 17 | this.id = id; 18 | this.age = age; 19 | this.sex = sex; 20 | this.username = username; 21 | } 22 | 23 | public Integer getId() { 24 | return id; 25 | } 26 | 27 | public void setId(Integer id) { 28 | this.id = id; 29 | } 30 | 31 | public Integer getAge() { 32 | return age; 33 | } 34 | 35 | public void setAge(Integer age) { 36 | this.age = age; 37 | } 38 | 39 | public Integer getSex() { 40 | return sex; 41 | } 42 | 43 | public void setSex(Integer sex) { 44 | this.sex = sex; 45 | } 46 | 47 | public String getUsername() { 48 | return username; 49 | } 50 | 51 | public void setUsername(String username) { 52 | this.username = username; 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | return "User{" + 58 | "id=" + id + 59 | ", age=" + age + 60 | ", sex=" + sex + 61 | ", username='" + username + '\'' + 62 | '}'; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/api/mengkang/net/model/UserModel.java: -------------------------------------------------------------------------------- 1 | package api.mengkang.net.model; 2 | 3 | import api.mengkang.net.dao.UserDao; 4 | import api.mengkang.net.entity.User; 5 | 6 | /** 7 | * @author zhoumengkang 8 | * @date 17/7/26. 9 | */ 10 | public class UserModel { 11 | public static User getUserInfoById(int uid){ 12 | return UserDao.getById(uid); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/api/mengkang/net/netty/HttpServer.java: -------------------------------------------------------------------------------- 1 | package api.mengkang.net.netty; 2 | 3 | import api.mengkang.net.Config; 4 | import io.netty.bootstrap.ServerBootstrap; 5 | import io.netty.channel.Channel; 6 | import io.netty.channel.EventLoopGroup; 7 | import io.netty.channel.nio.NioEventLoopGroup; 8 | import io.netty.channel.socket.nio.NioServerSocketChannel; 9 | import io.netty.handler.logging.LogLevel; 10 | import io.netty.handler.logging.LoggingHandler; 11 | import io.netty.handler.ssl.SslContext; 12 | 13 | /** 14 | * @author zhoumengkang 15 | */ 16 | public final class HttpServer { 17 | 18 | private static final int PORT = Config.getInt("server.port"); 19 | 20 | public static void main(String[] args) throws Exception { 21 | final SslContext sslCtx = null; 22 | 23 | EventLoopGroup bossGroup = new NioEventLoopGroup(1); 24 | EventLoopGroup workerGroup = new NioEventLoopGroup(); 25 | try { 26 | ServerBootstrap b = new ServerBootstrap(); 27 | b.group(bossGroup, workerGroup) 28 | .channel(NioServerSocketChannel.class) 29 | .handler(new LoggingHandler(LogLevel.INFO)) 30 | .childHandler(new HttpServerInitializer(sslCtx)); 31 | 32 | Channel ch = b.bind(PORT).sync().channel(); 33 | ch.closeFuture().sync(); 34 | } finally { 35 | bossGroup.shutdownGracefully(); 36 | workerGroup.shutdownGracefully(); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/api/mengkang/net/netty/HttpServerHandler.java: -------------------------------------------------------------------------------- 1 | package api.mengkang.net.netty; 2 | 3 | import api.mengkang.net.RequestHandler; 4 | import io.netty.buffer.Unpooled; 5 | import io.netty.channel.ChannelFutureListener; 6 | import io.netty.channel.ChannelHandlerAdapter; 7 | import io.netty.channel.ChannelHandlerContext; 8 | import io.netty.handler.codec.AsciiString; 9 | import io.netty.handler.codec.http.DefaultFullHttpResponse; 10 | import io.netty.handler.codec.http.FullHttpResponse; 11 | import io.netty.handler.codec.http.HttpHeaderUtil; 12 | import io.netty.handler.codec.http.HttpHeaderValues; 13 | import io.netty.handler.codec.http.HttpRequest; 14 | 15 | import static io.netty.handler.codec.http.HttpHeaderNames.CONNECTION; 16 | import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH; 17 | import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE; 18 | import static io.netty.handler.codec.http.HttpResponseStatus.CONTINUE; 19 | import static io.netty.handler.codec.http.HttpResponseStatus.OK; 20 | import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; 21 | 22 | 23 | /** 24 | * @author zhoumengkang 25 | */ 26 | public class HttpServerHandler extends ChannelHandlerAdapter { 27 | 28 | @Override 29 | public void channelReadComplete(ChannelHandlerContext ctx) { 30 | ctx.flush(); 31 | } 32 | 33 | @Override 34 | public void channelRead(ChannelHandlerContext ctx, Object msg) { 35 | if (msg instanceof HttpRequest) { 36 | HttpRequest req = (HttpRequest) msg; 37 | 38 | if (HttpHeaderUtil.is100ContinueExpected(req)) { 39 | ctx.write(new DefaultFullHttpResponse(HTTP_1_1, CONTINUE)); 40 | } 41 | boolean keepAlive = HttpHeaderUtil.isKeepAlive(req); 42 | FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer( 43 | RequestHandler.response(ctx,msg))); 44 | // 返回 json 格式的数据 45 | response.headers().set(CONTENT_TYPE,new AsciiString("application/json; charset=utf-8")); 46 | response.headers().setInt(CONTENT_LENGTH, response.content().readableBytes()); 47 | 48 | if (!keepAlive) { 49 | ctx.write(response).addListener(ChannelFutureListener.CLOSE); 50 | } else { 51 | response.headers().set(CONNECTION, HttpHeaderValues.KEEP_ALIVE); 52 | ctx.write(response); 53 | } 54 | } 55 | } 56 | 57 | @Override 58 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 59 | cause.printStackTrace(); 60 | ctx.close(); 61 | } 62 | } -------------------------------------------------------------------------------- /src/main/java/api/mengkang/net/netty/HttpServerInitializer.java: -------------------------------------------------------------------------------- 1 | package api.mengkang.net.netty; 2 | 3 | import io.netty.channel.ChannelInitializer; 4 | import io.netty.channel.ChannelPipeline; 5 | import io.netty.channel.socket.SocketChannel; 6 | import io.netty.handler.codec.http.HttpContentCompressor; 7 | import io.netty.handler.codec.http.HttpObjectAggregator; 8 | import io.netty.handler.codec.http.HttpServerCodec; 9 | import io.netty.handler.ssl.SslContext; 10 | 11 | 12 | /** 13 | * @author zhoumengkang 14 | */ 15 | public class HttpServerInitializer extends ChannelInitializer { 16 | 17 | private final SslContext sslCtx; 18 | 19 | public HttpServerInitializer(SslContext sslCtx) { 20 | this.sslCtx = sslCtx; 21 | } 22 | 23 | @Override 24 | public void initChannel(SocketChannel ch) throws Exception { 25 | ChannelPipeline pipeline = ch.pipeline(); 26 | if (sslCtx != null) { 27 | pipeline.addLast(sslCtx.newHandler(ch.alloc())); 28 | } 29 | pipeline.addLast(new HttpServerCodec()); 30 | pipeline.addLast(new HttpObjectAggregator(65536)); 31 | // 增加 gzip 压缩 32 | pipeline.addLast(new HttpContentCompressor(1)); 33 | pipeline.addLast(new HttpServerHandler()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/api/mengkang/net/utils/mysql/DMLTypes.java: -------------------------------------------------------------------------------- 1 | package api.mengkang.net.utils.mysql; 2 | 3 | 4 | public enum DMLTypes { 5 | INSERT, 6 | SELECT, 7 | UPDATE, 8 | DELETE, 9 | REPLACE, 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/api/mengkang/net/utils/mysql/DbFiled.java: -------------------------------------------------------------------------------- 1 | package api.mengkang.net.utils.mysql; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | @Documented 10 | @Retention(RetentionPolicy.RUNTIME) 11 | @Target({ElementType.FIELD}) 12 | public @interface DbFiled { 13 | String value(); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/api/mengkang/net/utils/mysql/JdbcPool.java: -------------------------------------------------------------------------------- 1 | package api.mengkang.net.utils.mysql; 2 | 3 | import java.io.InputStreamReader; 4 | import java.sql.Connection; 5 | import java.sql.ResultSet; 6 | import java.sql.SQLException; 7 | import java.sql.Statement; 8 | import java.util.Properties; 9 | 10 | import javax.sql.DataSource; 11 | 12 | import org.apache.commons.dbcp.BasicDataSourceFactory; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | 16 | public class JdbcPool { 17 | 18 | private static final Logger logger = LoggerFactory.getLogger(JdbcPool.class); 19 | 20 | /** 21 | * 在 java 中,编写数据库连接池需实现 java.sql.DataSource 接口 22 | * DBCP 连接池就是 java.sql.DataSource 接口的一个具体实现 23 | */ 24 | private static DataSource writeDataSource = null; 25 | private static DataSource readDataSource = null; 26 | 27 | static { 28 | try { 29 | Properties writeProp = new Properties(); 30 | writeProp.load(new InputStreamReader(JdbcPool.class.getResourceAsStream("/write.db.properties"), "UTF-8")); 31 | writeDataSource = BasicDataSourceFactory.createDataSource(writeProp); 32 | 33 | Properties readProp = new Properties(); 34 | readProp.load(new InputStreamReader(JdbcPool.class.getResourceAsStream("/read.db.properties"), "UTF-8")); 35 | readDataSource = BasicDataSourceFactory.createDataSource(readProp); 36 | 37 | } catch (Exception e) { 38 | logger.error("load db properties error",e); 39 | throw new ExceptionInInitializerError(e); 40 | } 41 | } 42 | 43 | public static Connection getWriteConnection() throws SQLException { 44 | return writeDataSource.getConnection(); 45 | } 46 | 47 | public static Connection getReadConnection() throws SQLException { 48 | return readDataSource.getConnection(); 49 | } 50 | 51 | public static void release(Connection conn, Statement st, ResultSet rs) { 52 | if (rs != null) { 53 | try { 54 | rs.close(); 55 | } catch (Exception e) { 56 | e.printStackTrace(); 57 | } 58 | rs = null; 59 | } 60 | 61 | if (st != null) { 62 | try { 63 | st.close(); 64 | } catch (Exception e) { 65 | e.printStackTrace(); 66 | } 67 | } 68 | 69 | if (conn != null) { 70 | try { 71 | conn.close(); 72 | } catch (Exception e) { 73 | e.printStackTrace(); 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/api/mengkang/net/utils/mysql/MySelect.java: -------------------------------------------------------------------------------- 1 | package api.mengkang.net.utils.mysql; 2 | 3 | import java.lang.reflect.Field; 4 | import java.sql.Connection; 5 | import java.sql.Date; 6 | import java.sql.PreparedStatement; 7 | import java.sql.ResultSet; 8 | import java.sql.SQLException; 9 | import java.util.ArrayList; 10 | import java.util.HashMap; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | public class MySelect extends Mysql { 18 | 19 | private final Logger logger = LoggerFactory.getLogger(this.getClass()); 20 | 21 | private Class clazz; 22 | 23 | private Map fieldMap = new HashMap<>(); 24 | 25 | public MySelect(A bean) { 26 | this.clazz = bean.getClass(); 27 | fieldMapInit(); 28 | } 29 | 30 | private void fieldMapInit() { 31 | Field[] fields = clazz.getDeclaredFields(); 32 | 33 | for (Field field : fields) { 34 | if (field.isAnnotationPresent(DbFiled.class)) { 35 | fieldMap.put(field.getAnnotation(DbFiled.class).value(), field); 36 | } else { 37 | fieldMap.put(field.getName(), field); 38 | } 39 | } 40 | } 41 | 42 | // todo select * xxx 43 | private String[] parseSelectFields(String sql) { 44 | sql = sql.toLowerCase(); 45 | 46 | String[] fieldArray = sql.substring(sql.indexOf("select") + 6, sql.indexOf("from")).split(","); 47 | int length = fieldArray.length; 48 | String[] fields = new String[length]; 49 | 50 | for (int i = 0; i < length; i++) { 51 | fields[i] = fieldArray[i].trim().replace("`", ""); 52 | } 53 | 54 | return fields; 55 | } 56 | 57 | @SuppressWarnings("unchecked") 58 | public A resultSet(String[] selectFields,ResultSet resultSet){ 59 | 60 | A bean = null; 61 | 62 | try{ 63 | bean = (A) Class.forName(clazz.getName()).newInstance(); 64 | 65 | for (int i = 0; i < selectFields.length; i++) { 66 | int j = i + 1; 67 | Field field = fieldMap.get(selectFields[i]); 68 | field.setAccessible(true); 69 | Class fieldClass = field.getType(); 70 | if (fieldClass == String.class) { 71 | field.set(bean, resultSet.getString(j)); 72 | } else if (fieldClass == int.class || fieldClass == Integer.class) { 73 | field.set(bean, resultSet.getInt(j)); 74 | } else if (fieldClass == float.class || fieldClass == Float.class) { 75 | field.set(bean, resultSet.getFloat(j)); 76 | } else if (fieldClass == double.class || fieldClass == Double.class) { 77 | field.set(bean, resultSet.getDouble(j)); 78 | } else if (fieldClass == long.class || fieldClass == Long.class) { 79 | field.set(bean, resultSet.getLong(j)); 80 | } else if (fieldClass == Date.class) { 81 | field.set(bean, resultSet.getDate(j)); 82 | } 83 | } 84 | }catch (SQLException e){ 85 | logger.error("resultSet parse error", e); 86 | } catch (IllegalAccessException | ClassNotFoundException | InstantiationException e) { 87 | e.printStackTrace(); 88 | } 89 | 90 | return bean; 91 | } 92 | 93 | public A get(String sql, Object... params) { 94 | 95 | grammarCheck(sql, DMLTypes.SELECT); 96 | int paramSize = getParameterNum(sql, params); 97 | 98 | Connection conn = null; 99 | PreparedStatement statement = null; 100 | ResultSet resultSet = null; 101 | 102 | try { 103 | conn = JdbcPool.getReadConnection(); 104 | statement = conn.prepareStatement(sql); 105 | 106 | if (paramSize > 0) { 107 | statement = bindParameters(statement, params); 108 | } 109 | 110 | resultSet = statement.executeQuery(); 111 | if (resultSet.next()){ 112 | String[] selectFields = parseSelectFields(sql); 113 | return resultSet(selectFields,resultSet); 114 | } 115 | } catch (SQLException e) { 116 | logger.error("sql error", e); 117 | } finally { 118 | JdbcPool.release(conn, statement, resultSet); 119 | } 120 | return null; 121 | } 122 | 123 | public List list(String sql, Object... params) { 124 | List beanList = new ArrayList<>(); 125 | 126 | grammarCheck(sql, DMLTypes.SELECT); 127 | int paramSize = getParameterNum(sql, params); 128 | 129 | Connection conn = null; 130 | PreparedStatement statement = null; 131 | ResultSet resultSet = null; 132 | try { 133 | conn = JdbcPool.getReadConnection(); 134 | statement = conn.prepareStatement(sql); 135 | 136 | if (paramSize > 0) { 137 | statement = bindParameters(statement, params); 138 | } 139 | 140 | resultSet = statement.executeQuery(); 141 | String[] selectFields = parseSelectFields(sql); 142 | while (resultSet.next()) { 143 | beanList.add(resultSet(selectFields,resultSet)); 144 | } 145 | } catch (Exception e) { 146 | logger.error("sql error", e); 147 | } finally { 148 | JdbcPool.release(conn, statement, resultSet); 149 | } 150 | 151 | return beanList; 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/main/java/api/mengkang/net/utils/mysql/Mysql.java: -------------------------------------------------------------------------------- 1 | package api.mengkang.net.utils.mysql; 2 | 3 | import java.sql.Connection; 4 | import java.sql.Date; 5 | import java.sql.PreparedStatement; 6 | import java.sql.ResultSet; 7 | import java.sql.SQLException; 8 | import java.sql.Statement; 9 | 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | /** 14 | * @author zhoumengkang 15 | */ 16 | 17 | public class Mysql { 18 | 19 | private static final Logger logger = LoggerFactory.getLogger(Mysql.class); 20 | 21 | static void grammarCheck(String sql,DMLTypes sqlType){ 22 | 23 | String dmlType = sqlType.toString().toLowerCase(); 24 | 25 | try { 26 | if (!sql.toLowerCase().startsWith(dmlType)) { 27 | throw new Exception(dmlType + " statement needed"); 28 | } 29 | 30 | if(!dmlType.equals(DMLTypes.INSERT.toString().toLowerCase()) && sql.toLowerCase().indexOf("where") < 1){ 31 | throw new Exception("where statement needed"); 32 | } 33 | } catch (Exception e) { 34 | logger.error(e.getMessage()); 35 | } 36 | } 37 | 38 | static int getParameterNum(String sql, Object... params) { 39 | int paramSize = sql.length() - sql.replaceAll("\\?", "").length(); 40 | 41 | try { 42 | if (paramSize != params.length) { 43 | throw new Exception("parameter's num error"); 44 | } 45 | } catch (Exception e) { 46 | logger.error(e.getMessage()); 47 | } 48 | 49 | return paramSize; 50 | } 51 | 52 | /** 53 | * bind parameters 54 | * 55 | * @param statement 56 | * @param params 57 | * @return 58 | * @throws SQLException 59 | */ 60 | static PreparedStatement bindParameters(PreparedStatement statement, Object... params) throws SQLException { 61 | 62 | int paramSize = params.length; 63 | 64 | for (int i = 1; i <= paramSize; i++) { 65 | Object param = params[i - 1]; 66 | if (param instanceof Integer) { 67 | statement.setInt(i, (Integer) param); 68 | } else if (param instanceof String) { 69 | statement.setString(i, (String) param); 70 | } else if (param instanceof Float) { 71 | statement.setFloat(i, (Float) param); 72 | } else if (param instanceof Long) { 73 | statement.setLong(i, (Long) param); 74 | } else if (param instanceof Double) { 75 | statement.setDouble(i, (Double) param); 76 | } else if (param instanceof Date) { 77 | statement.setDate(i, (Date) param); 78 | } else { 79 | statement.setObject(i, param); 80 | } 81 | } 82 | 83 | return statement; 84 | } 85 | 86 | /** 87 | * 插入操作 88 | * 89 | * @param sql 90 | * @param params 91 | * @return 92 | */ 93 | public static int insert(String sql, Object... params) { 94 | 95 | grammarCheck(sql,DMLTypes.INSERT); 96 | int paramSize = getParameterNum(sql,params); 97 | 98 | Connection conn = null; 99 | PreparedStatement statement = null; 100 | ResultSet rs = null; 101 | int id = 0; 102 | 103 | try { 104 | conn = JdbcPool.getWriteConnection(); 105 | statement = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); 106 | 107 | if (paramSize > 0) { 108 | statement = bindParameters(statement, params); 109 | } 110 | 111 | statement.executeUpdate(); 112 | rs = statement.getGeneratedKeys(); 113 | if (rs.next()) { 114 | id = rs.getInt(1); 115 | } 116 | } catch (SQLException e) { 117 | logger.error("sql error",e); 118 | } finally { 119 | JdbcPool.release(conn, statement, rs); 120 | } 121 | 122 | return id; 123 | } 124 | 125 | /** 126 | * 删除操作 127 | * 128 | * @param sql 129 | * @param params 130 | * @return 131 | */ 132 | public static int delete(String sql, Object... params) { 133 | return update(sql, params); 134 | } 135 | 136 | /** 137 | * 更新操作 138 | * 139 | * @param sql 140 | * @param params 141 | * @return 142 | */ 143 | public static int update(String sql, Object... params) { 144 | 145 | if (!sql.toLowerCase().startsWith(DMLTypes.DELETE.toString().toLowerCase()) 146 | && !sql.toLowerCase().startsWith(DMLTypes.UPDATE.toString().toLowerCase()) 147 | && !sql.toLowerCase().startsWith(DMLTypes.REPLACE.toString().toLowerCase())) { 148 | try { 149 | throw new Exception("update statement needed"); 150 | } catch (Exception e) { 151 | e.printStackTrace(); 152 | } 153 | } 154 | 155 | int paramSize = getParameterNum(sql,params); 156 | 157 | Connection conn = null; 158 | PreparedStatement statement = null; 159 | int row = 0; 160 | 161 | try { 162 | conn = JdbcPool.getWriteConnection(); 163 | statement = conn.prepareStatement(sql); 164 | 165 | if (paramSize > 0) { 166 | statement = bindParameters(statement, params); 167 | } 168 | 169 | row = statement.executeUpdate(); 170 | } catch (SQLException e) { 171 | logger.error("sql error",e); 172 | } finally { 173 | JdbcPool.release(conn, statement, null); 174 | } 175 | 176 | return row; 177 | } 178 | 179 | public static String getValue(String sql, Object... params) { 180 | grammarCheck(sql,DMLTypes.SELECT); 181 | int paramSize = getParameterNum(sql,params); 182 | 183 | Connection conn = null; 184 | PreparedStatement statement = null; 185 | ResultSet rs = null; 186 | String res = null; 187 | 188 | try { 189 | conn = JdbcPool.getReadConnection(); 190 | statement = conn.prepareStatement(sql); 191 | 192 | if (paramSize > 0) { 193 | statement = bindParameters(statement, params); 194 | } 195 | 196 | rs = statement.executeQuery(); 197 | if (rs.next()) { 198 | res = rs.getString(1); 199 | } 200 | } catch (SQLException e) { 201 | logger.error("sql error", e); 202 | } finally { 203 | JdbcPool.release(conn, statement, rs); 204 | } 205 | 206 | return res; 207 | } 208 | 209 | public static int getIntValue(String sql, Object... params) { 210 | 211 | grammarCheck(sql,DMLTypes.SELECT); 212 | int paramSize = getParameterNum(sql,params); 213 | 214 | Connection conn = null; 215 | PreparedStatement statement = null; 216 | ResultSet rs = null; 217 | int res = 0; 218 | 219 | try { 220 | conn = JdbcPool.getReadConnection(); 221 | statement = conn.prepareStatement(sql); 222 | 223 | if (paramSize > 0) { 224 | statement = bindParameters(statement, params); 225 | } 226 | 227 | rs = statement.executeQuery(); 228 | if (rs.next()) { 229 | res = rs.getInt(1); 230 | } 231 | } catch (SQLException e) { 232 | logger.error("sql error",e); 233 | } finally { 234 | JdbcPool.release(conn, statement, rs); 235 | } 236 | 237 | return res; 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /src/main/resources/api.properties: -------------------------------------------------------------------------------- 1 | server.port=9009 -------------------------------------------------------------------------------- /src/main/resources/read.db.properties: -------------------------------------------------------------------------------- 1 | #连接设置 2 | driverClassName=com.mysql.jdbc.Driver 3 | url=jdbc:mysql://127.0.0.1:3306/segmentfault 4 | username=root 5 | password=zmkzmk 6 | 7 | #初始化连接 8 | initialSize=2 9 | 10 | #最大连接数量 11 | maxActive=10 12 | 13 | #最大空闲连接 14 | maxIdle=5 15 | 16 | #最小空闲连接 17 | minIdle=2 18 | 19 | #超时等待时间以毫秒为单位 20 | maxWait=10000 21 | 22 | 23 | #JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;] 24 | #注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。 25 | connectionProperties=useUnicode=true;characterEncoding=utf8 26 | 27 | #指定由连接池所创建的连接的自动提交(auto-commit)状态。 28 | defaultAutoCommit=true 29 | 30 | #driver default 指定由连接池所创建的连接的只读(read-only)状态。 31 | #如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix) 32 | defaultReadOnly=true 33 | 34 | #driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。 35 | #可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE 36 | defaultTransactionIsolation=READ_UNCOMMITTED -------------------------------------------------------------------------------- /src/main/resources/write.db.properties: -------------------------------------------------------------------------------- 1 | #连接设置 2 | driverClassName=com.mysql.jdbc.Driver 3 | url=jdbc:mysql://127.0.0.1:3306/segmentfault 4 | username=root 5 | password=zmkzmk 6 | 7 | #初始化连接 8 | initialSize=2 9 | 10 | #最大连接数量 11 | maxActive=10 12 | 13 | #最大空闲连接 14 | maxIdle=5 15 | 16 | #最小空闲连接 17 | minIdle=2 18 | 19 | #超时等待时间以毫秒为单位 20 | maxWait=10000 21 | 22 | 23 | #JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;] 24 | #注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。 25 | #connectionProperties=useUnicode=true;characterEncoding=utf8mb4 26 | connectionProperties=useUnicode=true;characterEncoding=utf8 27 | 28 | #指定由连接池所创建的连接的自动提交(auto-commit)状态。 29 | defaultAutoCommit=true 30 | 31 | #driver default 指定由连接池所创建的连接的只读(read-only)状态。 32 | #如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix) 33 | defaultReadOnly=false 34 | 35 | #driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。 36 | #可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE 37 | defaultTransactionIsolation=READ_UNCOMMITTED -------------------------------------------------------------------------------- /src/test/java/TestUserInfo.java: -------------------------------------------------------------------------------- 1 | import api.mengkang.net.Request; 2 | import api.mengkang.net.api.UserController; 3 | import org.junit.Test; 4 | 5 | /** 6 | * @author zhoumengkang 7 | * @date 17/7/25. 8 | */ 9 | public class TestUserInfo { 10 | 11 | @Test 12 | public void getUserInfo(){ 13 | Request request = null; 14 | UserController userController = new UserController(request); 15 | Object res = userController.get(); 16 | System.out.println(res); 17 | } 18 | } 19 | --------------------------------------------------------------------------------