基本需求:
1.用netty搭建一个http协议请求,并能接受到POST和GET的请求。
2.模拟spring的DispatcherServlet,业务层的分发操作
小编是使用gradle进行搭建项目的,开发工具的eclipse,不多说,直接上代码!
最后有源码下载地址!!!!(重要事情说三遍)
最后有源码下载地址!!!!(重要事情说三遍)
最后有源码下载地址!!!!(重要事情说三遍)
用netty搭建一个http协议请求,并能接受到POST和GET的请求。
利用gradle添加要用到的依赖包
compile group: 'com.google.protobuf', name: 'protobuf-java', version: '3.5.1'
compile group: 'io.netty', name: 'netty-all', version: '4.1.25.Final'
compile group: 'org.springframework', name: 'spring-context', version: '5.0.7.RELEASE'
compile group: 'org.springframework', name: 'spring-core', version: '5.0.7.RELEASE'
compile group: 'org.springframework', name: 'spring-beans', version: '5.0.7.RELEASE'
compile group: 'org.springframework', name: 'spring-context-support', version: '5.0.7.RELEASE'
compile group: 'org.codehaus.groovy', name: 'groovy-all', version: '2.4.15'
compile group: 'com.google.guava', name: 'guava', version: '25.1-jre'
compile group: 'com.alibaba', name: 'fastjson', version: '1.2.58'
compile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.25'
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.7'
compile group: 'commons-lang', name: 'commons-lang', version: '2.6'
compile group: 'commons-net', name: 'commons-net', version: '3.6'
compileOnly group: 'org.projectlombok', name: 'lombok', version: '1.18.0'
利用spring boot的启动方式:
/**
* Netty http 启动类
*
* @bk https://home.cnblogs.com/u/huanuan/
* @简书 https://www.jianshu.com/u/d29cc7d7ca49
* @Author 六月星辰
* @Date 2020年1月10日
*/
@Slf4j
public class HttpServerApp {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
context.start();
HttpServer bean = context.getBean(HttpServer.class);
// 安排独立的一条线程进行启动http服务,
new Thread(new NettyHttpServer(bean)).start();
;
}
}
springBean加载和相关配置的初始化
/**
* 服务配置
*
* @bk https://home.cnblogs.com/u/huanuan/
* @简书 https://www.jianshu.com/u/d29cc7d7ca49
* @Author 六月星辰
* @Date 2020年1月10日
*/
@Configuration
@PropertySource("classpath:/httpserver.properties")
@ImportResource("classpath:/quartz.xml")
public class BeanConfig {
@Autowired
Environment env;
@Autowired
void init() {
// 系统配置 设置
String ip = env.getProperty("ip");
int port = env.getProperty("port", int.class);
}
@Bean
HandlerDispatcher dispatcher() {
// 注册业务处理器
HandlerDispatcher dispatcher = new HandlerDispatcher();
Set<Class> clazzs = ClassPathScanner.scan("liu.yue.xin.chen.com.server", false, true, false, null);
dispatcher.initHandler(clazzs);
return dispatcher;
}
@Bean
HttpServer httpServer() {
HttpServer httpServer = new HttpServer(env.getProperty("port", int.class));
return httpServer;
}
}
Netty 初始化Http服务
/**
* netty HttpServer
*
* @bk https://home.cnblogs.com/u/huanuan/
* @简书 https://www.jianshu.com/u/d29cc7d7ca49
* @Author 六月星辰
* @Date 2020年1月11日
*/
public class HttpServer {
/** 服务器的端口 **/
private int port;
public HttpServer(int port) {
this.port = port;
}
public void start() throws Exception {
ServerBootstrap bootstrap = new ServerBootstrap();
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup work = new NioEventLoopGroup();
bootstrap.group(boss, work).channel(NioServerSocketChannel.class).childHandler(new HttpServerInitializer());
ChannelFuture f = bootstrap.bind(new InetSocketAddress(port)).sync();
System.err.println(" http服务器 启动 端口 : " + port);
f.channel().closeFuture().sync();
}
}
Netty的Http服务初始化配置
/**
* http 初始化器
*
* @bk https://home.cnblogs.com/u/huanuan/
* @简书 https://www.jianshu.com/u/d29cc7d7ca49
* @Author 六月星辰
* @Date 2020年1月11日
*/
public class HttpServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// TODO Auto-generated method stub
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new HttpServerCodec());// http 编解码
/**
* 下面的是 http 消息聚合器 将http的get和post请求解析为统一的FullHttpRequest或者FullHttpResponse
*
*/
pipeline.addLast("httpAggregator", new HttpObjectAggregator(1024 * 1024)); // http 消息聚合器 1024*1024为接收的最大contentlength
pipeline.addLast(new HttpServerExpectContinueHandler());
pipeline.addLast(new HttpRequestHandler());// 请求处理器
}
}
http消息处理器
/**
* http消息处理器
*
* @bk https://home.cnblogs.com/u/huanuan/
* @简书 https://www.jianshu.com/u/d29cc7d7ca49
* @Author 六月星辰
* @Date 2020年1月11日
*/
public class HttpRequestHandler extends SimpleChannelInboundHandler<HttpObject> {
private HttpHeaders headers;
private HttpRequest request;
private FullHttpRequest fullRequest;
private static final String FAVICON_ICO = "/favicon.ico";
private static final AsciiString CONTENT_TYPE = AsciiString.of("Content-Type");
private static final AsciiString CONTENT_LENGTH = AsciiString.of("Content-Length");
private static final AsciiString CONNECTION = AsciiString.of("Connection");
private static final AsciiString KEEP_ALIVE = AsciiString.of("keep-alive");
private static final AsciiString ACCESS_CONTROL_ALLOW_ORIGIN = AsciiString.of("Access-Control-Allow-Origin");
private static final AsciiString ACCESS_CONTROL_ALLOW_HEADERS = AsciiString.of("Access-Control-Allow-Headers");
private static final AsciiString ACCESS_CONTROL_ALLOW_METHODS = AsciiString.of("Access-Control-Allow-Methods");
private static final AsciiString ACCESS_CONTROL_ALLOW_CREDENTIALS = AsciiString.of("Access-Control-Allow-Credentials");
@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
// TODO Auto-generated method stub
if (msg instanceof HttpRequest) {
request = (HttpRequest) msg;
headers = request.headers();
String uri = request.uri();
if (uri.equals(FAVICON_ICO)) {
return;
}
HttpMethod method = request.method();// 获取请求的方式
if (method.equals(HttpMethod.GET)) {
System.err.println("收到GET的请求! 请求地址 = " + uri);
QueryStringDecoder queryDecoder = new QueryStringDecoder(uri, Charsets.toCharset(CharEncoding.UTF_8));
Map<String, List<String>> uriAttributes = queryDecoder.parameters();
// 此处仅打印请求参数(你可以根据业务需求自定义处理)
for (Map.Entry<String, List<String>> attr : uriAttributes.entrySet()) {
for (String attrVal : attr.getValue()) {
System.err.println("Key = " + attr.getKey() + " values = " + attrVal);
}
}
String path = queryDecoder.path();
System.err.println("请求的路径 = " + path);
// TODO 这里进行分发操作,这里就不编写分发了
getDispense(ctx, path, uriAttributes);
JsonResult jsonResult = new JsonResult();
JSONSerializer jsonSerializer = new JSONSerializer();
responsePush(ctx, jsonSerializer.serialize(jsonResult));
} else if (method.equals(HttpMethod.POST) || method.equals(HttpMethod.OPTIONS)) {
System.err.println("收到POST或者OPTIONS的请求! 请求地址 = " + uri);
// POST请求,由于你需要从消息体中获取数据,因此有必要把msg转换成FullHttpRequest
fullRequest = (FullHttpRequest) msg;
// 分发操作
postDispense(ctx, uri);
} else if (method.equals(HttpMethod.HEAD)) {
System.err.println("收到HEAD请求! 请求地址 = " + uri);
} else {
System.err.println("收到其他方式的请求! 请求的地址 = " + uri);
}
}
}
/**
* GET请求,请求分发操作
*
* @param ctx
* 请求的用户
* @param path
* 请求的地址
* @param uriAttributes
* 请求的参数
* @bk https://home.cnblogs.com/u/huanuan/
* @Author 六月星辰
* @Date 2020年1月13日
*/
private void getDispense(ChannelHandlerContext ctx, String path, Map<String, List<String>> uriAttributes) {
// TODO 请求的分发操作
System.err.println("暂时不进行分发操作!");
}
/**
* post请求,请求进行分发操作
*
* @param ctx
* 用户的链接
* @param uri
* 用户请求的地址
* @bk https://home.cnblogs.com/u/huanuan/
* @Author 六月星辰
* @Date 2020年1月13日
*/
private void postDispense(ChannelHandlerContext ctx, String url) {
// TODO Auto-generated method stub
String typeStr2 = headers.get("Accept").toString();
String[] list = typeStr2.split(";");
String contentType = list[0];
switch (contentType) {
case "application/json":
byte[] content = null;
String jsonStr = fullRequest.content().toString(Charsets.toCharset(CharEncoding.UTF_8));
JSONObject params = JSON.parseObject(jsonStr);
try {
Object response = HandlerDispatcher.dispatcher(liu.yue.xin.chen.com.handler.dispatcher.HttpMethod.POST, url, params);
if (response == null) {
JsonResult jsonResult = new JsonResult();
JSONSerializer jsonSerializer = new JSONSerializer();
content = jsonSerializer.serialize(jsonResult);
} else {
JsonResult jsonResult = new JsonResult(response);
JSONSerializer jsonSerializer = new JSONSerializer();
content = jsonSerializer.serialize(jsonResult);
}
responsePush(ctx, content);
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
if (e.getTargetException() instanceof LogicException) {
// 如果是主动抛出异常
LogicException e1 = (LogicException) e.getTargetException();
JsonResult jsonResult = new JsonResult();
jsonResult.setState(e1.getCode());
JSONSerializer jsonSerializer = new JSONSerializer();
content = jsonSerializer.serialize(jsonResult);
responsePush(ctx, content);
} else {
// 非主动抛出异常
JsonResult jsonResult = new JsonResult();
jsonResult.setState(-1);
JSONSerializer jsonSerializer = new JSONSerializer();
content = jsonSerializer.serialize(jsonResult);
responsePush(ctx, content);
}
} catch (Exception e) {
JsonResult jsonResult = new JsonResult();
JSONSerializer jsonSerializer = new JSONSerializer();
content = jsonSerializer.serialize(jsonResult);
responsePush(ctx, content);
}
break;
case "application/x-www-form-urlencoded":
System.err.println("接受到application/x-www-form-urlencoded类型的请求!暂时不进行处理操作!");
break;
case "multipart/form-data":
System.err.println("接受到multipart/form-data类型的请求!暂时不进行处理操作!");
break;
default:
System.err.println("服务器不进行其他请求方式的业务处理!");
break;
}
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
// TODO Auto-generated method stub
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// TODO Auto-generated method stub
System.err.println("链接异常! ");
cause.printStackTrace();
ctx.close();
}
/**
* 响应信息
*
* @param ctx
* 响应的玩家 </br>
* @param content
* 响应的数据</br>
* @bk https://home.cnblogs.com/u/huanuan/
* @Author 六月星辰
* @Date 2020年1月11日
*/
private void responsePush(ChannelHandlerContext ctx, byte[] content) {
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(content));
// 允许跨域访问
response.headers().set(CONTENT_TYPE, "application/json;charset=UTF-8");
response.headers().set(ACCESS_CONTROL_ALLOW_ORIGIN, "*");
response.headers().set(ACCESS_CONTROL_ALLOW_HEADERS, "*");// 允许headers自定义
response.headers().set(ACCESS_CONTROL_ALLOW_METHODS, "GET, POST, PUT,DELETE");
response.headers().set(ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
response.headers().set(CONNECTION, KEEP_ALIVE);
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
}
模拟spring的DispatcherServlet,业务层的分发操作
1.利用反射注册业务层
2.根据不同的请求方式,分发到不同业务层
/**
* 模拟spring 业务层分发
*
* @bk https://home.cnblogs.com/u/huanuan/
* @简书 https://www.jianshu.com/u/d29cc7d7ca49
* @Author 六月星辰
* @Date 2020年1月11日
*/
@Slf4j
public class HandlerDispatcher implements Dispatcher {
/**
* POST方式 业务内存
*/
private static Map<String, HanderServer> POST_HANDER = Maps.newConcurrentMap();
/**
* GET方式业务内存
*/
private static Map<String, HanderServer> GET_HANDER = Maps.newConcurrentMap();
@Override
public void initHandler(Set<Class> clazzs) {
// 业务处理器初始化
if (CollectionUtils.isEmpty(clazzs)) {
System.exit(0);// 处理器异常关闭处理器
return;
}
try {
for (Class clazz : clazzs) {
Class[] interfaces = clazz.getInterfaces();
boolean isHandler = false;
for (Class class1 : interfaces) {
if (class1.getSimpleName().equals("HttpServerHandle")) {
isHandler = true;
}
}
if (isHandler) {
System.err.println("类 : " + clazz + " 实现了 HttpServerHandle 接口 进行初始化业务处理器 -------------");
} else {
System.err.println("类 : " + clazz + " 未实现 HttpServerHandle 接口 ");
continue;
}
// 获取业务层的 指定的注解
HttpParams hParams = (HttpParams) clazz.getAnnotation(HttpParams.class);
if (null != hParams) {
String requestMethod = hParams.method();// 请求的方式 !
if (StringUtils.isEmpty(requestMethod)) {
continue;
}
Method[] declaredMethods = clazz.getDeclaredMethods();
// Method[] methods = clazz.getMethods();
// POST方式处理器内存存储
switch (requestMethod) {
case HttpMethod.POST:// POST方式处理器内存存储
for (Method method : declaredMethods) {
if (method.getName().equals("hanlde")) {
Class paramType = method.getParameterTypes()[0];
if (paramType.getSimpleName().equals("Object")) {
continue;
}
POST_HANDER.put(hParams.url(), new HanderServer(clazz.newInstance(), method));
}
}
break;
case HttpMethod.GET:// GET方式处理器内存存储
for (Method method : declaredMethods) {
if (method.getName().equals("hanlde")) {
Class paramType = method.getParameterTypes()[0];
if (paramType.getSimpleName().equals("Object")) {
continue;
}
GET_HANDER.put(hParams.url(), new HanderServer(clazz.newInstance(), method));
}
}
break;
default:
break;
}
}
}
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
}
public static Object dispatcher(String method, String url, JSONObject params) throws Exception {
// TODO Auto-generated method stub
switch (method) {
case HttpMethod.POST:
HanderServer postHander = POST_HANDER.get(url);
if (postHander == null) {
return null;
}
Object[] argsValues = new Object[1];
JSONObject response = new JSONObject();
argsValues[0] = params;// 请求参数
// argsValues[1] = response;
Object invoke = null;
invoke = postHander.method.invoke(postHander.o, argsValues);
return invoke;
case HttpMethod.GET:
HanderServer getHander = GET_HANDER.get(url);
if (getHander == null) {
return null;
}
Object[] getArgsValues = new Object[1];
getArgsValues[0] = params;
Object getInvoke = null;
getInvoke = getHander.method.invoke(getHander.o, getArgsValues);
return getInvoke;
default:
break;
}
return null;
}
}
小编把剩下有关的类都贴出来!就不11说明了!
/**
* http静态资源
*
* @bk https://home.cnblogs.com/u/huanuan/
* @简书 https://www.jianshu.com/u/d29cc7d7ca49
* @Author 六月星辰
* @Date 2020年1月13日
*/
public class HttpMethod {
/**
* postd的请求方式
*/
public final static String POST = "POST";
/**
* get的请求方式
*/
public final static String GET = "GET";
}
/**
* 业务层 请求内存存储类
*
* @bk https://home.cnblogs.com/u/huanuan/
* @简书 https://www.jianshu.com/u/d29cc7d7ca49
* @Author 六月星辰
* @Date 2020年1月13日
*/
public class HanderServer {
/**执行命令对象*/
public Object o;
/**执行命令方法*/
public Method method;
/**
* 业务层 业务处理
* @param o 请求的业务类
* @param method 请求的方法
* @throws NoSuchMethodException
*/
public HanderServer(Object o, Method method) throws NoSuchMethodException {
super();
this.o = o;
this.method = method;
}
}
public interface Dispatcher {
/**
* 初始化处理器
*
* @param clazzs
* @bk https://home.cnblogs.com/u/huanuan/
* @Author 六月星辰
* @Date 2020年1月11日
*/
void initHandler(Set<Class> clazzs);
/**
* 业务层的调用
* @param method 请求的方式:POST、GET
* @param url 请求的地址
* @param params 请求参数
* @throws Exception
* @bk https://home.cnblogs.com/u/huanuan/
* @Author 六月星辰
* @Date 2020年1月11日
* @return 业务层的业务处理结果
*/
// Object dispatcher(String method,String url,JSONObject params) throws Exception;
}
/**
* HTTP 请求标签
*
* @bk https://home.cnblogs.com/u/huanuan/
* @简书 https://www.jianshu.com/u/d29cc7d7ca49
* @Author 六月星辰
* @Date 2020年1月11日
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface HttpParams {
/**
* 请求的方式
*
* @bk https://home.cnblogs.com/u/huanuan/
* @Author 六月星辰
* @Date 2020年1月11日
* @return
*/
String method();
/**
* 请求的地址
*
* @bk https://home.cnblogs.com/u/huanuan/
* @Author 六月星辰
* @Date 2020年1月11日
* @return
*/
String url();
}
/**
* 自定义逻辑异常
*
* @bk https://home.cnblogs.com/u/huanuan/
* @简书 https://www.jianshu.com/u/d29cc7d7ca49
* @Author 六月星辰
* @Date 2020年1月13日
*/
public class LogicException extends RuntimeException {
/**
* 异常码
*/
private final int code;
/**
* 异常信息
*/
private final String resultInfo;
/**
* <p>
* Title:
* </p>
*
* <p>
* Description:异常构造器
* </p>
*
* @param code
*/
public LogicException(int code) {
this.code = code;
this.resultInfo = "";
}
/**
* <p>
* Title:
* </p>
*
* <p>
* Description:异常构造器
* </p>
*
* @param code
*/
public LogicException(int code, String resultInfo) {
this.code = code;
this.resultInfo = resultInfo;
}
public int getCode() {
return code;
}
public String getResultInfo() {
return resultInfo;
}
}
/**
* json数据封装
*
* @bk https://home.cnblogs.com/u/huanuan/
* @简书 https://www.jianshu.com/u/d29cc7d7ca49
* @Author 六月星辰
* @Date 2020年1月13日
*/
public class JsonResult {
public static final int SUCCESS = 200;
public static final int ERROR = 201;
/**
* 返回是否成功的状态,200表示成功, 201或其他值 表示失败
*/
private int state = SUCCESS;
/**
* 成功时候,返回的JSON数据
*/
private Object data = null;
/**
* 是错误时候的错误消息
*/
private String message = null;
/**
* 服务器的时间
*/
private long sysTime = System.currentTimeMillis();
public JsonResult() {
}
public JsonResult(int state, Object data, String message) {
this.state = state;
this.data = data;
this.message = message;
}
public JsonResult(Throwable e) {
state = ERROR;
data = null;
message = e.getMessage();
}
public JsonResult(Object data) {
state = SUCCESS;
this.data = data;
message = "";
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public long getSysTime() {
return sysTime;
}
public void setSysTime(long sysTime) {
this.sysTime = sysTime;
}
@Override
public String toString() {
return "JsonResult [state=" + state + ", data=" + data + ", message=" + message + ",sysTime=" + sysTime + "]";
}
}
/**
* json序列化
*
* @bk https://home.cnblogs.com/u/huanuan/
* @简书 https://www.jianshu.com/u/d29cc7d7ca49
* @Author 六月星辰
* @Date 2020年1月11日
*/
public class JSONSerializer implements Serializer {
@Override
public byte[] serialize(Object object) {
// TODO Auto-generated method stub
return JSON.toJSONBytes(object);
}
@Override
public <T> T deserialize(Class<T> clazz, byte[] bytes) {
// TODO Auto-generated method stub
return JSON.parseObject(bytes, clazz);
}
}
/**
* 序列化接口
*
* @bk https://home.cnblogs.com/u/huanuan/
* @简书 https://www.jianshu.com/u/d29cc7d7ca49
* @Author 六月星辰
* @Date 2020年1月11日
*/
public interface Serializer {
/**
* java 对象转换成二进制
*
* @param object
* @return
* @bk https://home.cnblogs.com/u/huanuan/
* @Author 六月星辰
* @Date 2020年1月11日
*/
byte[] serialize(Object object);
/**
* 二进制转换成 java 对象
*
* @param clazz
* @param bytes
* @return
* @bk https://home.cnblogs.com/u/huanuan/
* @Author 六月星辰
* @Date 2020年1月11日
*/
<T> T deserialize(Class<T> clazz, byte[] bytes);
}
/**
* http服务 独立线程启动
*
* @bk https://home.cnblogs.com/u/huanuan/
* @简书 https://www.jianshu.com/u/d29cc7d7ca49
* @Author 六月星辰
* @Date 2020年1月11日
*/
@Slf4j
public class NettyHttpServer implements Runnable {
private HttpServer httpServer;
public NettyHttpServer(HttpServer httpServer) {
// TODO Auto-generated constructor stub
this.httpServer = httpServer;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
this.httpServer.start();
} catch (Exception e) {
// TODO: handle exception
System.err.println("启动http服务异常! ");
e.printStackTrace();
System.exit(0);
}
}
}
/**
* 业务层处理接口
*
* @bk https://home.cnblogs.com/u/huanuan/
* @简书 https://www.jianshu.com/u/d29cc7d7ca49
* @Author 六月星辰
* @Date 2020年1月11日
*/
public interface HttpServerHandle {
/**
* 业务逻辑
* @param params 请求参数
* @bk https://home.cnblogs.com/u/huanuan/
* @Author 六月星辰
* @Date 2020年1月11日
* @return
*/
Object hanlde(JSONObject params);
}
/**
* Http POST请求测试接口
*
* @bk https://home.cnblogs.com/u/huanuan/
* @简书 https://www.jianshu.com/u/d29cc7d7ca49
* @Author 六月星辰
* @Date 2020年1月11日
*/
@HttpParams(method = HttpMethod.POST, url = "")
public class TestPostServer implements HttpServerHandle {
@Override
public Object hanlde(JSONObject params) {
// TODO Auto-generated method stub
return null;
}
}
/**
* HTTP GET测试接口
*
* @bk https://home.cnblogs.com/u/huanuan/
* @简书 https://www.jianshu.com/u/d29cc7d7ca49
* @Author 六月星辰
* @Date 2020年1月11日
*/
@HttpParams(method = HttpMethod.GET, url = "")
public class TestGetServer implements HttpServerHandle {
@Override
public Object hanlde(JSONObject params) {
// TODO Auto-generated method stub
return null;
}
}
/**
* 包扫描器
*
* @bk https://home.cnblogs.com/u/huanuan/
* @简书 https://www.jianshu.com/u/d29cc7d7ca49
* @Author 六月星辰
* @Date 2020年1月13日
*/
public class ClassPathScanner {
private static final Logger logger = LoggerFactory.getLogger(ClassPathScanner.class);
/**
* 扫描包
*
* @param basePackage
* 基础包
* @param recursive
* 是否递归搜索子包
* @param excludeInner
* 是否排除内部类 true->是 false->否
* @param checkInOrEx
* 过滤规则适用情况 true—>搜索符合规则的 false->排除符合规则的
* @param classFilterStrs
* List<java.lang.String> 自定义过滤规则,如果是null或者空,即全部符合不过滤
* @return Set
*/
public static Set<Class> scan(String basePackage, boolean recursive, boolean excludeInner, boolean checkInOrEx, List<String> classFilterStrs) {
Set<Class> classes = new LinkedHashSet<Class>();
String packageName = basePackage;
List<Pattern> classFilters = toClassFilters(classFilterStrs);
if (packageName.endsWith(".")) {
packageName = packageName.substring(0, packageName.lastIndexOf('.'));
}
String package2Path = packageName.replace('.', '/');
Enumeration<URL> dirs;
try {
dirs = Thread.currentThread().getContextClassLoader().getResources(package2Path);
while (dirs.hasMoreElements()) {
URL url = dirs.nextElement();
String protocol = url.getProtocol();
if ("file".equals(protocol)) {
logger.debug("扫描file类型的class文件....");
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
doScanPackageClassesByFile(classes, packageName, filePath, recursive, excludeInner, checkInOrEx, classFilters);
} else if ("jar".equals(protocol)) {
logger.debug("扫描jar文件中的类....");
doScanPackageClassesByJar(packageName, url, recursive, classes, excludeInner, checkInOrEx, classFilters);
}
}
} catch (IOException e) {
logger.error("IOException error:", e);
}
return classes;
}
/**
* 以jar的方式扫描包下的所有Class文件
*/
private static void doScanPackageClassesByJar(String basePackage, URL url, final boolean recursive, Set<Class> classes, boolean excludeInner, boolean checkInOrEx, List<Pattern> classFilters) {
String packageName = basePackage;
String package2Path = packageName.replace('.', '/');
JarFile jar;
try {
jar = ((JarURLConnection) url.openConnection()).getJarFile();
Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
String name = entry.getName();
if (!name.startsWith(package2Path) || entry.isDirectory()) {
continue;
}
// 判断是否递归搜索子包
if (!recursive && name.lastIndexOf('/') != package2Path.length()) {
continue;
}
// 判断是否过滤 inner class
if (excludeInner && name.indexOf('$') != -1) {
logger.debug("exclude inner class with name:" + name);
continue;
}
String classSimpleName = name.substring(name.lastIndexOf('/') + 1);
// 判定是否符合过滤条件
if (filterClassName(classSimpleName, checkInOrEx, classFilters)) {
String className = name.replace('/', '.');
className = className.substring(0, className.length() - 6);
try {
classes.add(Thread.currentThread().getContextClassLoader().loadClass(className));
} catch (ClassNotFoundException e) {
logger.error("Class.forName error:", e);
}
}
}
} catch (IOException e) {
logger.error("IOException error:", e);
}
}
/**
* 以文件的方式扫描包下的所有Class文件
*
* @param packageName
* @param packagePath
* @param recursive
* @param classes
*/
private static void doScanPackageClassesByFile(Set<Class> classes, String packageName, String packagePath, boolean recursive, final boolean excludeInner, final boolean checkInOrEx, final List<Pattern> classFilters) {
File dir = new File(packagePath);
if (!dir.exists() || !dir.isDirectory()) {
return;
}
final boolean fileRecursive = recursive;
File[] dirfiles = dir.listFiles(new FileFilter() {
// 自定义文件过滤规则
public boolean accept(File file) {
if (file.isDirectory()) {
return fileRecursive;
}
String filename = file.getName();
if (excludeInner && filename.indexOf('$') != -1) {
logger.debug("exclude inner class with name:" + filename);
return false;
}
return filterClassName(filename, checkInOrEx, classFilters);
}
});
for (File file : dirfiles) {
if (file.isDirectory()) {
doScanPackageClassesByFile(classes, packageName + "." + file.getName(), file.getAbsolutePath(), recursive, excludeInner, checkInOrEx, classFilters);
} else {
String className = file.getName().substring(0, file.getName().length() - 6);
try {
classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));
} catch (ClassNotFoundException e) {
logger.error("IOException error:", e);
}
}
}
}
/**
* 根据过滤规则判断类名
*/
private static boolean filterClassName(String className, boolean checkInOrEx, List<Pattern> classFilters) {
if (!className.endsWith(".class")) {
return false;
}
if (null == classFilters || classFilters.isEmpty()) {
return true;
}
String tmpName = className.substring(0, className.length() - 6);
boolean flag = false;
for (Pattern p : classFilters) {
if (p.matcher(tmpName).find()) {
flag = true;
break;
}
}
return (checkInOrEx && flag) || (!checkInOrEx && !flag);
}
/**
* @param pClassFilters
* the classFilters to set
*/
private static List<Pattern> toClassFilters(List<String> pClassFilters) {
List<Pattern> classFilters = new ArrayList<Pattern>();
if (pClassFilters != null) {
for (String s : pClassFilters) {
String reg = "^" + s.replace("*", ".*") + "$";
Pattern p = Pattern.compile(reg);
classFilters.add(p);
}
}
return classFilters;
}
}
小编自己写的源码下载地址:https://github.com/zhuhuanuan/nettyHttpServer
感觉小编写的可以,记得给小编点一个赞哇 ~~