diff --git a/README.md b/README.md
index 5d3eb06..7df844b 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,69 @@
# seven-rpc
-基于Spring+Netty框架构建的简版RPC个人框架
+
+#### 介绍
+
+```
+ 用7这个数字,是因为我儿子是七夕生的。开源的rpc框架有很多,公司的、个人的,优秀的很多。那为什么我又要写个呢?
+因为spring、springboot、因为分布式服务协调器zk、因为nio,或者说是因为netty。看了spring的源码,也clone了项目
+调了很长一段时间,总想上手一个"框架"来练练手,期间也手写过简易版的mybaits、spring-mvc框架,很不过瘾。用netty
+也编码了一段时间,总感觉少了点什么,于是乎,便决定利用平时空闲之余,串联下几大技术,自己也写一个rpc框架,切身感受
+一下站在巨人肩膀上撸代码的兴奋和快感!
+
+ 首先一个rpc框架基本该有的功能都得有吧,其次模块得鲜明、代码思路得清晰,让人一看就懂:“哦吼,原来
+Spring的BeanPostProcessor可以用的这么嗨皮啊;原来反射写起来也可以让人爱不释手啊;原来动态代理的时机可以结合bean的
+生命周期来玩啊;原来接口类型的bean注入时也是可以往ioc容器里注册beandefinition的啊;原来FactoryBean是这样用的啊;
+原来netty的ByteBuf居然这么好用的啊;原来消息编·解码器这么写就可以解决TCP的拆包或粘包的问题啊;原来netty api用起来
+是如此轻松嗨皮的啊;原来用zk搭建一个rpc服务的注册与发现中心只需一个工具类和为数不多行的代码就可以轻松办到的啊...etc”
+
+```
+
+#### 软件架构
+
+![Seven-Rpc架构图-简](https://gitee.com/appleyk/seven-rpc/raw/master/src/main/resources/static/images/%E6%9E%B6%E6%9E%841.png)
+
+![Seven-Rpc架构图-详](https://gitee.com/appleyk/seven-rpc/raw/master/src/main/resources/static/images/%E6%9E%B6%E6%9E%842.jpg)
+
+
+
+#### 安装`使用教程
+
+1. git clone https://gitee.com/appleyk/seven-rpc
+2. idea导入父pom,配置maven,并下载相关依赖
+3. idea编译项目,检查是否报错
+4. 运行rpc-use-case模块中对应的案例启动类即可
+5. 具体使用可参考博客:https://blog.csdn.net/Appleyk/article/details/117392129
+
+#### 模块说明
+
+1. rpc-common : 定义通用工具类和netty消息编`解码器
+2. rpc-core : 定义核心业务模型和公共自定义注解re
+3. rpc-registry: 服务注册与发现中心模块,主要是基于zookeeper curator框架实现的
+4. rpc-client : rpc-netty客户端模块,主要对接口进行动态代理,并封装请求,与rpc-netty服务端建立连接并进行通信
+5. rpc-server : rpc-netty服务端模块,主要对接口实现模块中的可用服务进行一个注册,信息写入到zk中,同时启动一个netty服务,对客户端连接进行监听
+6. rpc-use-case : rpc功能测试用例模块,其又分为3个模块
+ 6.1. rpc-demo-api: 定义公共接口
+ 6.2. rpc-demo-consumer: 服务消费方,主要用来测试rpc服务能力的,依赖rpc-demo-api模块
+ 6.3. rpc-demo-provider: 服务提供方,用来满足rpc接口功能实现的,依赖rpc-demo-api模块
+
+#### 参与贡献
+
+1. Fork 本仓库
+2. 新建 Feat_xxx 分支
+3. 提交代码
+4. 新建 Pull Request
+5. 如果你提交的request对项目有用,我会merge的
+
+
+#### 番外
+
+1. Spring·Bean全生命周期(简图)
+
+![Spring·Bean完整生命周期](https://gitee.com/appleyk/seven-rpc/raw/master/src/main/resources/static/images/Spring%C2%B7Bean%E5%AE%8C%E6%95%B4%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.png)
+
+2. ZK注册的服务和API节点
+
+![ZK服务节点](https://gitee.com/appleyk/seven-rpc/raw/master/src/main/resources/static/images/ZK%E6%B3%A8%E5%86%8C%E7%9A%84%E6%9C%8D%E5%8A%A1%E5%92%8CAPI%E8%8A%82%E7%82%B9.jpg)
+
+3. Netty Reactor模型
+
+![Netty Reactor设计模型](https://gitee.com/appleyk/seven-rpc/raw/master/src/main/resources/static/images/netty%C2%B7reactor.png)
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..ced066b
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,164 @@
+
+
+
+ 4.0.0
+ com.appleyk
+ seven-rpc
+ pom
+ 0.1.1-SNAPSHOT
+ 手写RPC框架·父工程
+
+ rpc-common
+ rpc-registry
+ rpc-client
+ rpc-server
+ rpc-core
+ rpc-use-case
+
+
+
+
+ 1.8
+ 1.8
+ UTF-8
+ UTF-8
+ 1.8
+ true
+
+
+ 2.4.0
+ 5.3.1
+ 2.11.0
+ 4.1.42.Final
+ 1.18.6
+ 8.0.22
+ 5.1.0
+ 1.1.6
+ 3.2
+
+
+
+
+
+
+
+
+
+
+ org.springframework
+ spring-context
+ ${spring.framework.version}
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+ ${springboot.version}
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ ${springboot.version}
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+ ${springboot.version}
+
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ ${springboot.version}
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+ ${springboot.version}
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ ${springboot.version}
+ test
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+ ${springboot.version}
+
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${jackson.version}
+
+
+
+
+ io.netty
+ netty-all
+ ${netty.version}
+
+
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+
+
+
+
+ mysql
+ mysql-connector-java
+ ${mysql8.version}
+
+
+
+
+ org.apache.curator
+ curator-framework
+ ${curator.version}
+
+
+
+
+ com.dyuproject.protostuff
+ protostuff-core
+ ${protostuff.version}
+
+
+
+ com.dyuproject.protostuff
+ protostuff-runtime
+ ${protostuff.version}
+
+
+
+
+ org.objenesis
+ objenesis
+ ${objenesis.version}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/rpc-client/pom.xml b/rpc-client/pom.xml
new file mode 100644
index 0000000..0cb0c59
--- /dev/null
+++ b/rpc-client/pom.xml
@@ -0,0 +1,48 @@
+
+
+
+
+ seven-rpc
+ com.appleyk
+ 0.1.1-SNAPSHOT
+
+
+ 4.0.0
+ rpc-client
+ rpc客户端,实现代理转发、连接(客户端也可以做服务注册)
+
+
+
+ com.appleyk
+ rpc-common
+ ${project.version}
+
+
+ com.appleyk
+ rpc-registry-api
+ ${project.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
+
+
+
+ ${artifactId}
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.3
+
+ 1.8
+ 1.8
+
+
+
+
+
+
\ No newline at end of file
diff --git a/rpc-client/src/main/java/com/appleyk/rpc/client/RpcClient.java b/rpc-client/src/main/java/com/appleyk/rpc/client/RpcClient.java
new file mode 100644
index 0000000..1e20882
--- /dev/null
+++ b/rpc-client/src/main/java/com/appleyk/rpc/client/RpcClient.java
@@ -0,0 +1,103 @@
+package com.appleyk.rpc.client;
+
+import com.appleyk.rpc.common.util.*;
+import com.appleyk.rpc.core.model.result.RpcRequest;
+import com.appleyk.rpc.core.model.result.RpcResponse;
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.*;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioSocketChannel;
+
+import java.net.InetAddress;
+import java.util.Date;
+
+/**
+ *
Rpc客户端 (负责构建netty客户端启动类,向rpc服务端发送请求)
+ * Rpc客户端通道处理器,主要负责和服务端建立通信,发送请求然后接收服务端发回来的响应对象并进行处理
+ * @author appleyk
+ * @version V.0.1.1
+ * @blob https://blog.csdn.net/appleyk
+ * @github https://github.com/kobeyk
+ * @date created on 16:19 2021/5/19
+ */
+public class RpcClient extends SimpleChannelInboundHandler {
+
+ private String ip;
+ private int port;
+ private RpcResponse response;
+
+ public RpcClient(String ip, int port) {
+ this.ip = ip;
+ this.port = port;
+ }
+
+ public RpcClient(int port) {
+ this.ip = "127.0.0.1";
+ this.port = port;
+ }
+
+ /**
+ * 发送(API)请求
+ * @param request rpc封装的请求对象
+ * @return rpc封装的请求响应对象
+ */
+ public RpcResponse send(RpcRequest request){
+ /*默认cpu核数*2*/
+ EventLoopGroup worker = new NioEventLoopGroup();
+ try{
+ Bootstrap client = new Bootstrap();
+ client.group(worker)
+ .channel(NioSocketChannel.class)
+ .handler(new RpcClientChannelInitializer(this));
+ /*等待异步执行完,拿到异步结果(变异步为同步)*/
+ ChannelFuture channelFuture = client.connect(ip, port).sync();
+ System.out.println("Rpc-Client,已与服务器建立连接,接下来准备发送数据...");
+ /*获取客户端和服务端的通道*/
+ Channel channel = channelFuture.channel();
+ /*往通道里发送数据 -- RpcRequest对象*/
+ channel.writeAndFlush(request);
+ channel.closeFuture().sync();
+ }catch (InterruptedException e){
+ response.setE(e);
+ response.setDate(new Date());
+ }finally {
+ System.out.println("#响应的结果:"+ JsonUtils.parserJson(response));
+ /*关闭线程池及清理内存空间,只发一次请求,不管失败还是异常,结果return之前,都要把线程池给关闭了*/
+ worker.shutdownGracefully();
+ return response;
+ }
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ Channel serverChannel = ctx.channel();
+ LoggerUtils.error("服务端:{}发送来的响应处理异常,{}\n",serverChannel.remoteAddress(),cause.getMessage());
+ /*关闭通道处理器上下文*/
+ ctx.close();
+ }
+
+ /**接收解码器的结果,并对响应做出处理 -- 读取*/
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, RpcResponse response) throws Exception {
+ System.out.printf("接收来自服务端:{%s}的响应,时间:%s\n",ctx.channel().remoteAddress(), DateUtils.dateNow2Str());
+ this.response = response;
+ /*如果响应成功返回,则断开与server端的通道*/
+ ctx.close();
+ }
+
+ /**简单测试*/
+ public static void main(String[] args) throws Exception{
+ InetAddress addr = InetAddress.getLocalHost();
+ String ip = addr.getHostAddress();
+ RpcClient client = new RpcClient(GeneralUtils.isEmpty(ip) ? "127.0.0.1":ip,8077);
+ RpcRequest request = RpcRequest.builder()
+ .requestId(IdUtils.getId())
+ .interfaceName("com.appleyk.rpc.api.CacheService")
+ .type("mongodb")
+ .methodName("save")
+ .parameterTypes(new Class[]{String.class, Object.class})
+ .parameters(new Object[]{"name", "Appleyk"})
+ .build();
+ client.send(request);
+ }
+}
diff --git a/rpc-client/src/main/java/com/appleyk/rpc/client/RpcClientChannelInitializer.java b/rpc-client/src/main/java/com/appleyk/rpc/client/RpcClientChannelInitializer.java
new file mode 100644
index 0000000..0f4d3c0
--- /dev/null
+++ b/rpc-client/src/main/java/com/appleyk/rpc/client/RpcClientChannelInitializer.java
@@ -0,0 +1,34 @@
+package com.appleyk.rpc.client;
+
+import com.appleyk.rpc.common.codec.RpcDecoder;
+import com.appleyk.rpc.common.codec.RpcEncoder;
+import com.appleyk.rpc.core.model.result.RpcRequest;
+import com.appleyk.rpc.core.model.result.RpcResponse;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.socket.SocketChannel;
+
+/**
+ * Rpc客户端通道初始化器,用来给通信管道设置各种处理器的
+ *
+ * @author appleyk
+ * @version V.0.1.1
+ * @blob https://blog.csdn.net/appleyk
+ * @date created on 下午9:37 2021/5/22
+ */
+public class RpcClientChannelInitializer extends ChannelInitializer {
+
+ private final RpcClient client;
+
+ public RpcClientChannelInitializer(RpcClient client) {
+ this.client = client;
+ }
+
+ @Override
+ protected void initChannel(SocketChannel sc) throws Exception {
+ ChannelPipeline pipeline = sc.pipeline();
+ pipeline.addLast(new RpcEncoder(RpcRequest.class));
+ pipeline.addLast(new RpcDecoder(RpcResponse.class));
+ pipeline.addLast(client);
+ }
+}
diff --git a/rpc-client/src/main/java/com/appleyk/rpc/client/annotion/EnableSeven.java b/rpc-client/src/main/java/com/appleyk/rpc/client/annotion/EnableSeven.java
new file mode 100644
index 0000000..a51ac17
--- /dev/null
+++ b/rpc-client/src/main/java/com/appleyk/rpc/client/annotion/EnableSeven.java
@@ -0,0 +1,26 @@
+package com.appleyk.rpc.client.annotion;
+
+import com.appleyk.rpc.client.registrar.RpcServiceBeanDefinitionRegistrar;
+import org.springframework.context.annotation.Import;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Rpc接口包扫描·注解
+ *
+ * @author appleyk
+ * @version V.0.1.1
+ * @blob https://blog.csdn.net/appleyk
+ * @github https://github.com/kobeyk
+ * @date created on 17:35 2021/5/19
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Import(RpcServiceBeanDefinitionRegistrar.class) // 在这个bean定义注册类里,实现接口的动态代理
+public @interface EnableSeven {
+ /*你需要扫描的beans所在的根包*/
+ String scanBasePackages() default "";
+}
diff --git a/rpc-client/src/main/java/com/appleyk/rpc/client/annotion/RpcAutowired.java b/rpc-client/src/main/java/com/appleyk/rpc/client/annotion/RpcAutowired.java
new file mode 100644
index 0000000..ec8a63e
--- /dev/null
+++ b/rpc-client/src/main/java/com/appleyk/rpc/client/annotion/RpcAutowired.java
@@ -0,0 +1,22 @@
+package com.appleyk.rpc.client.annotion;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Rpc 自动注入 注解
+ *
+ * @author appleyk
+ * @version V.0.1.1
+ * @blob https://blog.csdn.net/appleyk
+ * @date created on 下午10:23 2021/5/20
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface RpcAutowired {
+ String value() default ""; // 注入的bean名称,比如cache可以是mongodb或redis实现
+ String version() default ""; // 注入的bean(服务、接口)的版本
+ String group() default ""; // 注入bean(服务、接口)的组
+}
diff --git a/rpc-client/src/main/java/com/appleyk/rpc/client/annotion/meta/RpcServiceAnnotationMetaData.java b/rpc-client/src/main/java/com/appleyk/rpc/client/annotion/meta/RpcServiceAnnotationMetaData.java
new file mode 100644
index 0000000..bd7eefa
--- /dev/null
+++ b/rpc-client/src/main/java/com/appleyk/rpc/client/annotion/meta/RpcServiceAnnotationMetaData.java
@@ -0,0 +1,24 @@
+package com.appleyk.rpc.client.annotion.meta;
+
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * rpc service bean 注入元数据
+ *
+ * @author appleyk
+ * @version V.0.1.1
+ * @blob https://blog.csdn.net/appleyk
+ * @github https://github.com/kobeyk
+ * @date created on 14:19 2021/5/26
+ */
+@Data
+@Builder
+public class RpcServiceAnnotationMetaData {
+ /**bean的类型*/
+ private String type;
+ /**bean的版本*/
+ private String version;
+ /**bean的(服务)组*/
+ private String group;
+}
diff --git a/rpc-client/src/main/java/com/appleyk/rpc/client/aop/RpcAop.java b/rpc-client/src/main/java/com/appleyk/rpc/client/aop/RpcAop.java
new file mode 100644
index 0000000..c9a122b
--- /dev/null
+++ b/rpc-client/src/main/java/com/appleyk/rpc/client/aop/RpcAop.java
@@ -0,0 +1,58 @@
+package com.appleyk.rpc.client.aop;
+
+import com.appleyk.rpc.common.result.HttpResult;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.Signature;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StopWatch;
+
+/**
+ * AOP
+ *
+ * @author appleyk
+ * @version V.0.1.1
+ * @blob https://blog.csdn.net/appleyk
+ * @github https://github.com/kobeyk
+ * @date created on 16:23 2021/5/19
+ */
+@Component
+@Aspect // 标记这个类是切面
+public class RpcAop {
+
+ /**
+ * 定义切点
+ * 第一个星号,表示返回方法任返回意类型
+ * 第一个..*,表示匹配appleyk包下面的任意个包
+ * 第二个星号,表示匹配appleyk下面的任意包
+ * 第三个星号,表示匹配controller包下面,以Controller结尾的任意前缀匹配
+ * 第四个星号,表示匹配XXXController类下面的任意方法
+ * 最后括号里面的两个..,表示任意参数
+ */
+ @Pointcut("execution(* com.appleyk.rpc..*.controller.*Controller.*(..))")
+ public void rpcPointcut(){}
+
+ @Around("rpcPointcut()")
+ public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
+ /*获取切入点签名(一个类的一个方法就是一个切点)*/
+ Signature signature = joinPoint.getSignature();
+ /*获取切入点签名的类型,就是方法所在的类*/
+ Class> declaringType = signature.getDeclaringType();
+ System.out.printf("AOP - {%s}.方法{%s}执行前\n",declaringType,signature.getName());
+ StopWatch stopWatch = new StopWatch();
+ stopWatch.start();
+ Object proceed = joinPoint.proceed();
+ stopWatch.stop();
+ System.out.printf("AOP - {%s}.方法{%s}执行耗时统计:{%d}ms\n",declaringType,signature.getName(),
+ stopWatch.getTotalTimeMillis());
+
+ if (declaringType.getName().endsWith("AopController")) {
+ return HttpResult.ok(String.format("方法{%s-%s}执行耗时统计:{%d}ms",declaringType,signature.getName(),
+ stopWatch.getTotalTimeMillis()));
+ }
+
+ return proceed;
+ }
+}
diff --git a/rpc-client/src/main/java/com/appleyk/rpc/client/autoconfigure/RpcClientAutoConfigure.java b/rpc-client/src/main/java/com/appleyk/rpc/client/autoconfigure/RpcClientAutoConfigure.java
new file mode 100644
index 0000000..ddc86bc
--- /dev/null
+++ b/rpc-client/src/main/java/com/appleyk/rpc/client/autoconfigure/RpcClientAutoConfigure.java
@@ -0,0 +1,69 @@
+package com.appleyk.rpc.client.autoconfigure;
+
+import com.appleyk.rpc.client.annotion.RpcAutowired;
+import com.appleyk.rpc.client.annotion.meta.RpcServiceAnnotationMetaData;
+import com.appleyk.rpc.client.proxy.servicebean.RpcServiceBeanProxy;
+import org.springframework.aop.support.AopUtils;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.util.ReflectionUtils;
+
+/**
+ * 自动装配rpc-client包中的bean
+ *
+ * @author appleyk
+ * @version V.0.1.1
+ * @blob https://blog.csdn.net/appleyk
+ * @github https://github.com/kobeyk
+ * @date created on 17:13 2021/5/19
+ */
+@Configuration
+@ComponentScan("com.appleyk.rpc.client")
+public class RpcClientAutoConfigure {
+ public RpcClientAutoConfigure() {
+ System.out.println("=== RPC-CLIENT complete automatic assembly !===");
+ }
+
+ @Bean
+ public BeanPostProcessor beanPostProcessor(){
+ return new BeanPostProcessor() {
+
+ /**在bean完成初始化之前(注意,这时候bean已经完成实例化、且已经状态属性了)*/
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+ Class> objClz;
+ /*先判断下bean是不是Spring生成的代理对象*/
+ if (AopUtils.isAopProxy(bean)){
+ /*获取目标对象*/
+ objClz = AopUtils.getTargetClass(bean);
+ }else {
+ /*如果不是proxy对象,那就获取bean的class,主要用于下面的反射*/
+ objClz = bean.getClass();
+ }
+ try{
+ ReflectionUtils.doWithLocalFields(objClz,field -> {
+ RpcAutowired rpcAutowired = field.getAnnotation(RpcAutowired.class);
+ /*如果标注了这个注解,则表示bean注入的方式可以多样化*/
+ if (rpcAutowired != null){
+ field.setAccessible(true);
+ Class> type = field.getType();
+ RpcServiceAnnotationMetaData metaData = RpcServiceAnnotationMetaData.builder()
+ .type(rpcAutowired.value())
+ .group(rpcAutowired.group())
+ .version(rpcAutowired.version()).build();
+ field.set(bean, RpcServiceBeanProxy.getObject(type,metaData));
+ }
+ });
+ }catch (Exception e){
+ throw new BeanCreationException(beanName, e);
+ }
+
+ return bean;
+ }
+ };
+ }
+}
diff --git a/rpc-client/src/main/java/com/appleyk/rpc/client/proxy/RpcServiceInvocationHandler.java b/rpc-client/src/main/java/com/appleyk/rpc/client/proxy/RpcServiceInvocationHandler.java
new file mode 100644
index 0000000..b2cdd66
--- /dev/null
+++ b/rpc-client/src/main/java/com/appleyk/rpc/client/proxy/RpcServiceInvocationHandler.java
@@ -0,0 +1,101 @@
+package com.appleyk.rpc.client.proxy;
+
+
+import com.appleyk.rpc.client.RpcClient;
+import com.appleyk.rpc.client.annotion.meta.RpcServiceAnnotationMetaData;
+import com.appleyk.rpc.client.util.SpringContextUtils;
+import com.appleyk.rpc.common.util.GeneralUtils;
+import com.appleyk.rpc.common.util.IdUtils;
+import com.appleyk.rpc.common.util.LoggerUtils;
+import com.appleyk.rpc.core.model.result.RpcRequest;
+import com.appleyk.rpc.core.model.result.RpcResponse;
+import com.appleyk.rpc.registry.ServiceDiscovery;
+import org.springframework.context.ApplicationContext;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+
+/**
+ * 动态代理,真正实现被代理"对象"的方法调用的处理器
+ *
+ * @author appleyk
+ * @version V.0.1.1
+ * @blob https://blog.csdn.net/appleyk
+ * @date created on 下午9:30 2021/5/19
+ */
+public class RpcServiceInvocationHandler implements InvocationHandler{
+
+ /*服务发现对象,从ioc容器里取*/
+ private ServiceDiscovery serviceDiscovery;
+
+ private RpcServiceAnnotationMetaData metaData;
+
+ public RpcServiceInvocationHandler(){
+ }
+
+ public RpcServiceInvocationHandler(RpcServiceAnnotationMetaData metaData){
+ this.metaData = metaData;
+ }
+
+ public RpcServiceInvocationHandler(ApplicationContext context){
+ ServiceDiscovery discovery = context.getBean(ServiceDiscovery.class);
+ if (discovery==null){
+ throw new RuntimeException("serviceRegistry bean not found !");
+ }
+ this.serviceDiscovery = discovery;
+ }
+
+ /**
+ * 整个RPC服务消费方·最最最重要的地方了,也是接口"实例"被代理后,就具有了"本地调用"的奥秘所在
+ * @param proxy 代理对象
+ * @param method 方法
+ * @param args 参数
+ * @return 方法执行结果
+ */
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ /*如果是当前类的实例自己调用自己的invoke方法的话,那就直接返回*/
+ if (method.getDeclaringClass().equals(this)){
+ return method.invoke(proxy,args);
+ }
+
+ if (serviceDiscovery == null){
+ this.serviceDiscovery = (ServiceDiscovery) SpringContextUtils.getBean(ServiceDiscovery.class);
+ }
+
+ /*1、获取方法所在类(其实是接口)的名称(这里是全限定名,即包名+类名)*/
+ String apiName= method.getDeclaringClass().getName();
+ String serverAddress = serviceDiscovery.discover(apiName);
+ /*2、验证服务地址合法性*/
+ if (GeneralUtils.isEmpty(serverAddress)|| serverAddress.split(":").length != 2){
+ LoggerUtils.error("未发现{}对应有可用的服务!,请稍后再试",apiName);
+ return null;
+ }
+ /*3、获取接口类型*/
+ String apiType = metaData != null ? metaData.getType() : "";
+ /*4、分割服务地址,分别获取ip和port*/
+ String[] host = serverAddress.split(":");
+ String ip = host[0];
+ int port = Integer.parseInt(host[1]);
+ /*5、基于ip和port,构建client(netty)*/
+ RpcClient client = new RpcClient(ip,port);
+ /*6、基于被代理的对象的信息构建请求请求对象*/
+ long requestId = IdUtils.getId();
+ RpcRequest request = RpcRequest.builder()
+ .requestId(requestId)
+ .interfaceName(apiName)
+ .methodName(method.getName())
+ .type(apiType) // 这个很关键,因为接口可能有多个实现类,ZK上一个节点就是一个服务,区别接口实现类的唯一标识就是这个type
+ .parameterTypes(method.getParameterTypes())
+ .parameters(args)
+ .build();
+ /*7、发送请求(与netty服务端建立连接,并往双方的通信通道里传输数据,然后服务端解码处理后返回响应结果)*/
+ RpcResponse response = client.send(request);
+ /*8、验证请求ID是不是合法的,就好比,你要点一杯咖啡,服务员却给你来了一杯冰红茶*/
+ if (requestId != response.getRequestId()){
+ throw new RuntimeException("请求与响应的requestId不对应,无法确定结果是不是合法!");
+ }
+ /*9、被代理的对象是接口类型的bean,其无法实例化,更谈不上有返回值了,所以只能从rpc调用的结果里取出返回值并返回出去*/
+ return response.getResult();
+ }
+}
diff --git a/rpc-client/src/main/java/com/appleyk/rpc/client/proxy/factorybean/RpcServiceFactoryBean.java b/rpc-client/src/main/java/com/appleyk/rpc/client/proxy/factorybean/RpcServiceFactoryBean.java
new file mode 100644
index 0000000..19220a9
--- /dev/null
+++ b/rpc-client/src/main/java/com/appleyk/rpc/client/proxy/factorybean/RpcServiceFactoryBean.java
@@ -0,0 +1,65 @@
+package com.appleyk.rpc.client.proxy.factorybean;
+
+import com.appleyk.rpc.client.proxy.RpcServiceInvocationHandler;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+import java.lang.reflect.Proxy;
+
+/**
+ *
+ *
+ * 第一种代理方式: 通过FactoryBean的方式,代理时机,在注册bean定义的时候,"篡改"接口的class
+ *
+ * 个性化定制rpc service bean
+ * (动态代理接口,使得客户端可以在本地直接使用接口调用远程的方法,让用户完全感觉不出来!!)
+ *
+ * 这种使用方式有个局限性,就是如果接口有多个实现,
+ *
+ *
+ * @author appleyk
+ * @version V.0.1.1
+ * @blob https://blog.csdn.net/appleyk
+ * @date created on 下午9:21 2021/5/19
+ */
+public class RpcServiceFactoryBean implements FactoryBean,ApplicationContextAware {
+
+ private final Class targetClass;
+ private ApplicationContext context;
+
+ public RpcServiceFactoryBean(Class targetClass) {
+ this.targetClass = targetClass;
+ }
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+ this.context = applicationContext;
+ }
+
+ /**
+ * 使用jdk动态代理创建一个对象,并返回
+ * @return 代理对象
+ */
+ @Override
+ public T getObject() {
+ Object o = Proxy.newProxyInstance(
+ targetClass.getClassLoader(),
+ new Class>[]{targetClass},
+ /*把spring ioc容器带过去*/
+ new RpcServiceInvocationHandler(context));
+ return (T) o;
+ }
+
+ @Override
+ public Class getObjectType() {
+ return targetClass;
+ }
+
+ @Override
+ public boolean isSingleton() {
+ return true;
+ }
+
+}
diff --git a/rpc-client/src/main/java/com/appleyk/rpc/client/proxy/servicebean/RpcServiceBeanProxy.java b/rpc-client/src/main/java/com/appleyk/rpc/client/proxy/servicebean/RpcServiceBeanProxy.java
new file mode 100644
index 0000000..c8d1b94
--- /dev/null
+++ b/rpc-client/src/main/java/com/appleyk/rpc/client/proxy/servicebean/RpcServiceBeanProxy.java
@@ -0,0 +1,41 @@
+package com.appleyk.rpc.client.proxy.servicebean;
+
+import com.appleyk.rpc.client.annotion.meta.RpcServiceAnnotationMetaData;
+import com.appleyk.rpc.client.proxy.RpcServiceInvocationHandler;
+
+import java.lang.reflect.Proxy;
+
+/**
+ *
+ * 第二种代理方法:使用jdk自带的Proxy类来创建接口类型bean的代理对象
+ * 代理时机:借助我们自定义的注入注解@RpcAutowired,复写bean后置处理器(实现BeanPostProcessor接口)
+ * 的postProcessBeforeInitialization方法,找到带有@RpcAutowired的field,然后对field进行动态代理
+ *
+ *
+ * @author appleyk
+ * @version V.0.1.1
+ * @blob https://blog.csdn.net/appleyk
+ * @github https://github.com/kobeyk
+ * @date created on 14:01 2021/5/26
+ */
+public class RpcServiceBeanProxy {
+
+ public static Object getObject(Class> targetClass){
+ Object o = Proxy.newProxyInstance(
+ targetClass.getClassLoader(),
+ new Class>[]{targetClass},
+ new RpcServiceInvocationHandler());
+ return o;
+ }
+
+ public static Object getObject(Class> targetClass, RpcServiceAnnotationMetaData metaData){
+ Object o = Proxy.newProxyInstance(
+ targetClass.getClassLoader(),
+ new Class>[]{targetClass},
+ /*把bean的注解元数据带过去*/
+ new RpcServiceInvocationHandler(metaData));
+ return o;
+ }
+
+
+}
diff --git a/rpc-client/src/main/java/com/appleyk/rpc/client/registrar/InstantiationAware.java b/rpc-client/src/main/java/com/appleyk/rpc/client/registrar/InstantiationAware.java
new file mode 100644
index 0000000..27d1266
--- /dev/null
+++ b/rpc-client/src/main/java/com/appleyk/rpc/client/registrar/InstantiationAware.java
@@ -0,0 +1,71 @@
+package com.appleyk.rpc.client.registrar;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.PropertyValues;
+import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
+import org.springframework.stereotype.Component;
+
+/**
+ * bean实例化/初始化前后感知类增强器,这个类在这个框架里可以忽略,是基于Spring的测试类,就是玩的
+ *
+ * @author appleyk
+ * @version V.0.1.1
+ * @blob https://blog.csdn.net/appleyk
+ * @github https://github.com/kobeyk
+ * @date created on 15:50 2021/5/21
+ */
+@Component
+public class InstantiationAware implements InstantiationAwareBeanPostProcessor {
+
+ /**
+ * bean实例化之前(增强器,这个地方其实也可以对beanClass做代理)
+ * @param beanClass bean类类型
+ * @param beanName bean名称
+ * @return 增强后的对象(如果不空,就是"偷天换日"了,成功代理了源bean)
+ */
+ @Override
+ public Object postProcessBeforeInstantiation(Class> beanClass, String beanName) throws BeansException {
+ if ("cacheService".equals(beanName)){
+ System.out.printf("InstantiationAware#postProcessBeforeInstantiation,bean{%s}实例化之前\n",beanName);
+ }
+ return null; // 空的话,继续交给spring容器对原有bean进行实例化
+ }
+
+ /**
+ * bean完成实例化后的增强器
+ * @param bean bean对象
+ * @param beanName bean名称
+ */
+ @Override
+ public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
+ if ("cacheService".equals(beanName)){
+ System.out.printf("InstantiationAware#postProcessBeforeInstantiation,bean{%s}实例化之后\n",beanName);
+ }
+ return true;
+ }
+
+ /**
+ * 对属性值进行修改,如果postProcessAfterInstantiation方法返回false,
+ * 该方法不会被调用。可以在该方法内对属性值进行修改
+ */
+ @Override
+ public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
+ return null;
+ }
+
+ /**
+ * 在Bean的自定义初始化方法执行完成之前执行
+ */
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+ return bean;
+ }
+
+ /**
+ * 在Bean的自定义初始化方法执行完成之后执行
+ */
+ @Override
+ public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+ return bean;
+ }
+}
diff --git a/rpc-client/src/main/java/com/appleyk/rpc/client/registrar/RpcServiceBeanDefinitionRegistrar.java b/rpc-client/src/main/java/com/appleyk/rpc/client/registrar/RpcServiceBeanDefinitionRegistrar.java
new file mode 100644
index 0000000..0bffe61
--- /dev/null
+++ b/rpc-client/src/main/java/com/appleyk/rpc/client/registrar/RpcServiceBeanDefinitionRegistrar.java
@@ -0,0 +1,62 @@
+package com.appleyk.rpc.client.registrar;
+
+import com.appleyk.rpc.client.annotion.EnableSeven;
+import com.appleyk.rpc.client.proxy.factorybean.RpcServiceFactoryBean;
+import com.appleyk.rpc.client.sanner.RpcServiceBeanPathScanner;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.beans.factory.support.GenericBeanDefinition;
+import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
+import org.springframework.core.type.AnnotationMetadata;
+import org.springframework.core.type.classreading.MetadataReader;
+import org.springframework.core.type.classreading.MetadataReaderFactory;
+import org.springframework.core.type.filter.TypeFilter;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ *
+ * 动态注册BeanDefinition到ioc容器:定制化 rpc service bean definition
+ * 里面很关键的一点就是:利用我们自定义的bean包扫描类,对候选bean定义进行条件放行,
+ * 比如对接口类型的bean定义进行放行,同时,对放行后的bean定义进行修改,
+ * 将其原有的接口类型指向为一个FactoryBean类型,完成bean的华丽转身!!!
+ *
+ *
+ * @author appleyk
+ * @version V.0.1.1
+ * @blob https://blog.csdn.net/appleyk
+ * @github https://github.com/kobeyk
+ * @date created on 17:39 2021/5/19
+ */
+public class RpcServiceBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
+ /*代理类*/
+ private static Class> targetClass = RpcServiceFactoryBean.class;
+ private static final String BASE_PACKAGE = "scanBasePackages";
+ @Override
+ public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
+ Map attributes = metadata.getAnnotationAttributes(EnableSeven.class.getName());
+ String basePackage = attributes.get(BASE_PACKAGE).toString();
+ RpcServiceBeanPathScanner beanPathScanner = new RpcServiceBeanPathScanner(registry);
+ beanPathScanner.addIncludeFilter(new TypeFilter() {
+ @Override
+ public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
+ /*管他三七二十一,条件放开*/
+ return true;
+ }
+ });
+ /*扫描公共接口包,获取其下的所有的接口bean定义*/
+ Set beanDefinitionHolders = beanPathScanner.doScan(basePackage);
+ for (BeanDefinitionHolder definitionHolder : beanDefinitionHolders) {
+ GenericBeanDefinition bd = (GenericBeanDefinition) definitionHolder.getBeanDefinition();
+ /*拿到service(接口)的类名*/
+ String sourceClass = bd.getBeanClassName();
+ System.out.printf("在bean{%s}还没有实例化之前,先对bean的class进行替换(狸猫换太子)\n",sourceClass);
+ /*在service (接口) 还没有开始bean的实例化之前,"篡改"它的类型*/
+ bd.setBeanClass(targetClass);
+ /*类型都改了,下面顺带给targetClass类的有参构造器传参数,将bd的源类传进去,方便后续代理用*/
+ bd.getConstructorArgumentValues().addIndexedArgumentValue(0,sourceClass);
+ }
+ }
+}
diff --git a/rpc-client/src/main/java/com/appleyk/rpc/client/sanner/RpcServiceBeanPathScanner.java b/rpc-client/src/main/java/com/appleyk/rpc/client/sanner/RpcServiceBeanPathScanner.java
new file mode 100644
index 0000000..52eae86
--- /dev/null
+++ b/rpc-client/src/main/java/com/appleyk/rpc/client/sanner/RpcServiceBeanPathScanner.java
@@ -0,0 +1,56 @@
+package com.appleyk.rpc.client.sanner;
+
+import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
+import org.springframework.core.type.classreading.MetadataReader;
+import org.springframework.core.type.filter.TypeFilter;
+
+import java.io.IOException;
+import java.util.Set;
+
+/**
+ * 自定义rpc service bean definition 扫描器(主要放开接口不能被作为bd的条件)
+ *
+ * @author appleyk
+ * @version V.0.1.1
+ * @blob https://blog.csdn.net/appleyk
+ * @date created on 下午7:57 2021/5/19
+ */
+public class RpcServiceBeanPathScanner extends ClassPathBeanDefinitionScanner {
+
+ public RpcServiceBeanPathScanner(BeanDefinitionRegistry registry) {
+ super(registry);
+ }
+
+ @Override
+ public Set doScan(String... basePackages) {
+ return super.doScan(basePackages);
+ }
+
+ @Override
+ public void addIncludeFilter(TypeFilter includeFilter) {
+ super.addIncludeFilter(includeFilter);
+ }
+
+ /**
+ * 重写父类方法,让接口类型的bean通过候选组件"扫描"
+ */
+ @Override
+ protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
+ boolean bInterface = metadataReader.getClassMetadata().isInterface();
+ System.out.printf("{%s}是否是接口:%b\n",metadataReader.getClassMetadata().getClassName(),bInterface);
+ return bInterface;
+ }
+
+ /**
+ * 重写父类方法,让接口类型的bean通过候选组件"扫描"
+ */
+ @Override
+ protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
+ boolean bInterface = beanDefinition.getMetadata().isInterface();
+ System.out.printf("{%s}是否是接口:%b\n",beanDefinition.getMetadata().getClassName(),bInterface);
+ return bInterface;
+ }
+}
diff --git a/rpc-client/src/main/java/com/appleyk/rpc/client/util/SpringContextUtils.java b/rpc-client/src/main/java/com/appleyk/rpc/client/util/SpringContextUtils.java
new file mode 100644
index 0000000..61c9c46
--- /dev/null
+++ b/rpc-client/src/main/java/com/appleyk/rpc/client/util/SpringContextUtils.java
@@ -0,0 +1,35 @@
+package com.appleyk.rpc.client.util;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.stereotype.Component;
+
+/**
+ *
+ * Spring 上下文工具类(简单版):利用Spring(观察者模式)上下文监听器,
+ * 对上下文刷新完事件进行监听,以回调onApplicationEvent方法顺便拿到全局的Spring的context对象
+ *
+ *
+ * @author appleyk
+ * @version V.0.1.1
+ * @blob https://blog.csdn.net/appleyk
+ * @github https://github.com/kobeyk
+ * @date created on 14:05 2021/5/26
+ */
+@Component
+public class SpringContextUtils implements ApplicationListener {
+
+ public static ApplicationContext context;
+
+ private SpringContextUtils(){}
+
+ @Override
+ public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
+ context = contextRefreshedEvent.getApplicationContext();
+ }
+
+ public static Object getBean(Class> beanClz){
+ return context.getBean(beanClz);
+ }
+}
diff --git a/rpc-client/src/main/resources/META-INF/spring.factories b/rpc-client/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..17aa5f1
--- /dev/null
+++ b/rpc-client/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+# Rpc Client 自动装配bean配置类
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.appleyk.rpc.client.autoconfigure.RpcClientAutoConfigure
\ No newline at end of file
diff --git a/rpc-common/pom.xml b/rpc-common/pom.xml
new file mode 100644
index 0000000..12b9984
--- /dev/null
+++ b/rpc-common/pom.xml
@@ -0,0 +1,68 @@
+
+
+
+
+ seven-rpc
+ com.appleyk
+ 0.1.1-SNAPSHOT
+
+
+ 4.0.0
+ rpc-common
+ 提供统一的消息编解码器及一些常用的工具类..etc
+
+
+
+ io.netty
+ netty-all
+
+
+ com.appleyk
+ rpc-core
+ ${project.version}
+
+
+ org.slf4j
+ slf4j-api
+ 1.7.26
+
+
+ org.projectlombok
+ lombok
+
+
+ com.dyuproject.protostuff
+ protostuff-core
+
+
+ com.dyuproject.protostuff
+ protostuff-runtime
+
+
+ org.objenesis
+ objenesis
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+
+
+ ${artifactId}
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.3
+
+ 1.8
+ 1.8
+
+
+
+
+
+
\ No newline at end of file
diff --git a/rpc-common/src/main/java/com/appleyk/rpc/common/codec/RpcDecoder.java b/rpc-common/src/main/java/com/appleyk/rpc/common/codec/RpcDecoder.java
new file mode 100644
index 0000000..fde1035
--- /dev/null
+++ b/rpc-common/src/main/java/com/appleyk/rpc/common/codec/RpcDecoder.java
@@ -0,0 +1,61 @@
+package com.appleyk.rpc.common.codec;
+
+import com.appleyk.rpc.common.util.PbfSerializationUtils;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.ByteToMessageDecoder;
+
+import java.util.List;
+
+/**
+ * 消息解码器(字节流转消息)
+ * @author appleyk
+ * @version V.0.1.1
+ * @blob https://blog.csdn.net/appleyk
+ * @github https://github.com/kobeyk
+ * @date created on 16:43 2021/5/18
+ */
+public class RpcDecoder extends ByteToMessageDecoder {
+
+ /**字节流对应的源对象的类型*/
+ private Class> sourceClass;
+
+ public RpcDecoder(Class> sourceClass) {
+ this.sourceClass = sourceClass;
+ }
+
+ @Override
+ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List