前言
上篇實作了rmi遠端跨JVM調用service,但是感覺很雞肋吧!不能每個接口都單獨注冊一個RMI路徑呀,太累了。是以這篇呢,會将rmi和spring內建起來,通過rmi實作遠端反射調用spring容器中的其他接口。
準備工作
修改pom檔案
<!-- spring支援 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.30</version>
</dependency>
要實作的效果
消費者通過遠端rmi協定調用提供者内部的UserService接口(提供者隻暴露RmiService服務)
代碼實作
通用接口
- 建立RmiService接口
package com.zyu.service;
import java.io.Serializable;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.Map;
/**
* rmi遠端服務接口,可以遠端調用提供者的内部方法
*/
public interface RmiService extends Remote, Serializable {
final String REMOTE_URL = "rmi://127.0.0.1:9080/RmiService";
final int port = 9080;
/**
* 遠端調用的方法資訊
* @param info 對象,方法,參數
* @return
*/
Object remoteInvoke(Map<String,Object> info) throws RemoteException;
}
- 建立UserService接口
package com.zyu.service;
public interface UserService {
String echoUser(String username);
}
Provider端實作
- 實作RmiService接口
package com.zyu.service;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.Map;
public class RmiServiceImpl extends UnicastRemoteObject implements RmiService {
protected RmiServiceImpl() throws RemoteException {
}
public Object remoteInvoke(Map<String, Object> info) throws RemoteException {
return null;
}
}
- 實作UserService接口
package com.zyu.service;
public class UserServiceImpl implements UserService {
public String echoUser(String username) {
return "hello," + username;
}
}
- 反射工具類
package com.zyu.utils;
import org.springframework.context.ApplicationContext;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Map;
/**
* 反射工具類
*/
public class InvokeUtils {
/**
* 根據傳遞的方法資訊,反射調用spring容器中的方法
*
* @param info
* @param app
* @return
*/
public static Object call(Map<String, Object> info, ApplicationContext app) {
String targetName = (String) info.get("target");
String methodName = (String) info.get("method");
Object[] args = (Object[]) info.get("args");
Class<?>[] argTypes = new Class[args.length];
int i = 0;
for (Object arg : args) {
argTypes[i++] = arg.getClass();
}
return call(app.getBean(targetName), methodName,argTypes,args);
}
/**
* java反射
*
* @param target 調用的目标對象
* @param methodName 方法名
* @param argTypes 方法參數清單類型
* @param args 方法參數清單
* @return
*/
public static Object call(Object target, String methodName, Class<?>[] argTypes, Object[] args) {
try {
Method method = target.getClass().getMethod(methodName, argTypes);
return method.invoke(target, args);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
}
- 服務暴露
package com.zyu;
import com.alibaba.fastjson.JSON;
import com.zyu.service.RmiService;
import com.zyu.service.RmiServiceImpl;
import com.zyu.service.UserService;
import com.zyu.service.UserServiceImpl;
import com.zyu.utils.InvokeUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
import java.net.MalformedURLException;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.util.Map;
/**
* 服務提供者,通過rmi協定暴露spring中相關的服務
*/
public class Provider {
public static void main(String[] args) throws IOException {
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Config.class);
app.start();
System.out.println("spring容器啟動成功");
initRmiProtocol(app);
System.in.read();
}
/**
* 開放rmi調用協定
* @param app
*/
public static void initRmiProtocol(final AnnotationConfigApplicationContext app) throws RemoteException {
RmiService rmiService = new RmiServiceImpl() {
@Override
public Object remoteInvoke(Map<String, Object> info) throws RemoteException {
Object result = InvokeUtils.call(info, app);
System.out.println("方法調用成功,傳回值:"+ JSON.toJSONString(result));
return result;
}
};
try {
//綁定端口
LocateRegistry.createRegistry(RmiService.port);
//綁定url
Naming.bind(RmiService.REMOTE_URL,rmiService);
} catch (RemoteException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (AlreadyBoundException e) {
e.printStackTrace();
}
System.out.println("開放rmi協定。。。");
}
@Configuration
static class Config{
@Bean
public UserService userService(){
return new UserServiceImpl();
}
}
}
Consumer端實作
兩種調用方式:
- 自己組裝反射的調用資訊
- 使用靜态代理,封裝反射資訊
package com.zyu;
import com.alibaba.fastjson.JSON;
import com.zyu.service.RmiService;
import com.zyu.service.UserService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Map;
public class Consumer {
public static void main(String[] args) {
//啟動spring容器
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Config.class);
app.start();
//擷取遠端調用服務
RmiService rmiService = app.getBean(RmiService.class);
//調用方式1:手動組裝要調用的資訊
Map<String, Object> info = new HashMap<String, Object>();
info.put("target", "userService");
info.put("method", "echoUser");
info.put("args", new Object[]{"zyufocus"});
try {
Object ret = rmiService.remoteInvoke(info);
System.out.println(JSON.toJSONString(ret));
} catch (RemoteException e) {
e.printStackTrace();
}
//調用方式2:靜态代理封裝
UserService userService = getService(rmiService);
String result = userService.echoUser("zyufocus2");
System.out.println("static proxy remote invoke,result:" + result);
}
//使用靜态代理,自動封裝需要反射的參數資訊
private static UserService getService(final RmiService rmiService) {
return new UserService() {
public String echoUser(String username) {
//組裝要調用的資訊
Map<String, Object> info = new HashMap<String, Object>();
info.put("target", "userService");
info.put("method", "echoUser");
info.put("args", new Object[]{username});
try {
return (String) rmiService.remoteInvoke(info);
} catch (RemoteException e) {
e.printStackTrace();
}
return null;
}
};
}
static class Config {
@Bean
public RmiService rmiService() {
RmiService rmiService = null;
try {
rmiService = (RmiService) Naming.lookup(RmiService.REMOTE_URL);
} catch (NotBoundException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
return rmiService;
}
}
}
測試
- 啟動Provider
-
啟動Consumer
消費者
提供者
結束語
感覺有點意思,開心
學無止境,諸君共勉