经过了前面的九节
(千难万阻)我们终于拿到了想要的结果,但是这就结束了吗?作为一个合格的程序员,一定要学会对需求说不!
,呃,这也没喝酒呀,怎么就吐真言了呢?作为一个合格的程序员,一定要学会对不好的代码说不!
。
比如我们服务端收到请求以后,反射调用方法时生成对象的时候需要依赖相关的配置(META-INF/services
),每次调用都会生成一个新的对象(clz.newINSTANCE()
) ,作为一个有追求的程序员。这怎么能忍!
spring的作用之一不就是管理bean的生命周期么?说干就干!
父pom.xml
声明 spring 的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.5.5</version>
</dependency>
rpc-portocol
模块引入声明的 spring 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
spring的环境有了,需要我们把需要托管给spring的bean交给spring,这里我们给Dog
添加对应的注解@Service
:
package com.info.provider.service.impl;
import com.info.api.Animal;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class Dog implements Animal {
@Override
public String run() {
String msg = "小狗跑起来一蹦一跳...";
log.info(msg);
return msg;
}
@Override
public String speak() {
String msg = "小狗叫起来汪汪汪...";
log.info(msg);
return msg;
}
}
然后提供一个 bean
的管理类SpringBeanManager.java
package com.info.protocol;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class SpringBeanManager implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringBeanManager.applicationContext = applicationContext;
}
public static <T> T getBean(Class<T> clazz) {
return applicationContext.getBean(clazz);
}
}
这样我们的bean
就托管给spring
了,我们需要bean
的时候也可以找spring
去获取。
修改服务端反射调用的逻辑CustomServerHandler.java
的 invoke
方法
// 调用目标方法
private Object invoke(Request request) {
final String clzName = request.getClassName();
try {
final Class<?> clz = Class.forName(clzName);
final Object instance = SpringBeanManager.getBean(clz);
final Method method = clz.getDeclaredMethod(request.getMethodName(), request.getParameterTypes());
return method.invoke(instance, request.getParams());
} catch (ClassNotFoundException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
这时候provider
的启动类代码可以调整为springboot
的启动方式并指定扫描的包路径ProviderApplication.java
package com.info.provider;
import com.info.protocol.netty.server.NettyServer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan(basePackages = "com.info")
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
new NettyServer("localhost", 9000).startServer();
}
}
变异 --> 打包 --> 启动项目发现会有日志冲突的问题,因为spring-boot-starter
中已经有了日志的依赖,因此可以去除我们之前的日志相关依赖。去除相关pom中以下依赖
<!-- 日志实现 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</dependency>
去除后再次运行项目,一切OK!搞定!
至此,我们一个简单的rpc
框架的雏形已经完成了,完成一个框架的从头到尾的开发,我相信你可以收货不少东西,但是你以为这就结束了?
- 作为一款优秀的
rpc
框架,只能单机环境玩,这怎么能忍! - 既然要效率,传输数据量大的时候真的不考虑压缩嘛!
- 突然发生网络抖动,要不要重试!
- 服务提供者挂了,请求还发过来怎么办!
......
正餐才刚刚开始!
PS:简单版 rpc
框架所有代码已经上传至gitee (click me!)
(简单版 rpc 实现 tag v1.o),需要的自取。
系列文章传送门如下:
手写RPC(一) 絮絮叨叨
手写RPC(二) 碎碎念
手写RPC(三) 基础结构搭建
手写RPC(四) 核心模块网络协议模块编写 ---- netty服务端
手写RPC(五) 核心模块网络协议模块编写 ---- 自定义协议
手写RPC(六) 核心模块网络协议模块编写 ---- 实现编解码器
手写RPC(七) 核心模块网络协议模块编写 ---- 实现客户端
手写RPC(八) provider、consumer 实现
手写RPC(九) 测试
关于 LengthFieldBasedFrameDecoder 不得不说的事