关于Java中的代理
1. 代理模式
1.1 定义
代理(Proxy)模式标准定义:为其他对象提供一种代理以控制对这个对象的访问。
1.2 解释
代理模式可以理解找一个人做我的代理人,可以帮我代理一些事情,但是实际上他只是一个中间人,最后执行的人还是我
1.3 类图
1.4 优缺点
- 优点 : 1. 看起来很直观 2.编译期就已经初始化了,指定了调用顺序,效率高
- 缺点 : 定义的代理类太多,维护困难
1.5 代码实现
Subject 类
public interface Subject {
public void dosomething();
}
RealSubject 类
public class RealSubject implements Subject {
@Override
public void dosomething() {
System.out.println("dosomething...");
}
}
Proxy 类
public class proxy implements Subject{
private Subject subject;
public proxy(Subject subject){
this.subject = subject;
}
@Override
public void dosomething() {
System.out.println("你好我是subject的代理....");
subject.dosomething();
System.out.println("你看着是我做的,其实是老板做的...");
}
}
client 类
public class client {
public static void main(String[] args) {
proxy p = new proxy(new RealSubject());
p.dosomething();
}
}
2.静态代理
静态代理也就是上面的代理模式中实现的例子。但是静态代理必须要保证被代理对象已经实例化,才可以被代理
3.JDK 动态代理
3.1 定义
java动态代理机制以巧妙的方式实现了代理模式的设计理念。
3.2 解释
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
}
每一个动态代理的类必须实现InvocationHandler,InvocationHandler类是每次调用代理的方法都会触发,并将 代理类,请求的方法,方法参数传入,可以通过反射去执行方法
3.1 优缺点
- 优点:1.仅需要一个动态代理类 2.在运行期才会去调用,灵活方便
- 缺点:运行期创建效率相对较低,可读性稍差
3.2 代码实现
Subject 类
public interface Subject {
public void dosomething();
public String sayHello(String username);
}
RealSubject类
public class RealSubject implements Subject {
@Override
public void dosomething() {
System.out.println("dosomething...");
}
@Override
public String sayHello(String username) {
return "hello , " + username ;
}
}
DynamicProxy类
/**
* 1. 必须实现<code>InvocationHandler</code><br/>
* 2. 在调用方式都会进入到<code>DynamicProxy</code>类中
*/
public class DynamicProxy implements InvocationHandler {
private Object proxyObject;
public DynamicProxy(Object proxyObject){
this.proxyObject = proxyObject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(proxyObject,args);
}
}
client调用
public class client {
public static void main(String[] args) {
Subject realSubject = new RealSubject();
Subject subject = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(),realSubject.getClass().getInterfaces(),new DynamicProxy(realSubject));
String result = subject.sayHello("shanyepifu");
System.out.println(result);
subject.dosomething();
}
}
4. 通过动态代理和Socket简单实现RPC
服务端代码(暴露服务):
public class RPCExporter {
public void export(Object inter, int port) throws ServiceUnBindException, PortllegalIIException, IOException,
ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
// 判断服务是否注册
if (inter == null) {
throw new ServiceUnBindException("服务未绑定");
}
// 判断端口号是否合法
if (!(Integer.MIN_VALUE < port && port < Integer.MAX_VALUE)) {
throw new PortllegalIIException("端口号不合法");
}
// 在指定的端口上绑定Socket服务
ServerSocket serverSocket = new ServerSocket(port);
while (true) {
// socket开始监听端口
Socket socket = serverSocket.accept();
// 获取对象输入、输出流
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
// 读取传过来的方法信息
String methodName = ois.readUTF();
Class<?>[] parameterTypes = (Class<?>[]) ois.readObject();
Object[] arguments = (Object[]) ois.readObject();
// 通过反射执行方法
Method method = inter.getClass().getMethod(methodName, parameterTypes);
Object result = method.invoke(inter, arguments);
// 将结果返回
oos.writeObject(result);
oos.flush();
// 关闭流
oos.close();
ois.close();
socket.close();
}
}
}
客户端代码(消费者):
public class RPCRefer {
public <T> T refer(Class<T> targetInter, InetAddress inetAddress, int port) throws Exception {
// 代理类实现
T target = (T) Proxy.newProxyInstance(targetInter.getClassLoader(), new Class<?>[]{targetInter}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Socket socket = new Socket(inetAddress, port);
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
oos.writeUTF(method.getName());
oos.writeObject(method.getParameterTypes());
oos.writeObject(args);
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
Object result = ois.readObject();
if (result instanceof Throwable) {
throw (Throwable) result;
}
return result;
}
});
return target;
}
}
完整代码已经托管到码云 : 点击查看
5. cglib动态代理
5.1 定义
CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。
5.2 cglib 和 jdk 动态代理的优缺点(摘录)
(1)Proxy 毕竟是通过反射实现的,必须在效率上付出代价:有实验数据表明,调用反射比一般的函数开销至少要大 10 倍。而且,从程序实现上可以看出,对 proxy class 的所有方法调用都要通过使用反射的 invoke 方法。因此,对于性能关键的应用,使用 proxy class 是需要精心考虑的,以避免反射成为整个应用的瓶颈。 (2)CGLib封装了asm,可以再运行期动态生成新的class。ASM 能够通过改造既有类,直接生成需要的代码。增强的代码是硬编码在新生成的类文件内部的,没有反射带来性能上的付出。同时,ASM 与 Proxy 编程不同,不需要为增强代码而新定义一个接口,生成的代码可以覆盖原来的类,或者是原始类的子类。 (3)CGLib在不同频次的调用性能会发生变化,体现为调用频次越高、性能越好。 (4)不同版本的JDK中JDK Proxy的性能也不尽相同,越高版本的JDK(JDK7及以上),性能提升越明显。
5.3 代码实现
Subject类
public interface Subject {
public void dosomething();
}
RealSubject类
public class RealSubject implements Subject {
@Override
public void dosomething() {
System.out.println("dosomething...");
}
}
CglibProxy 类
public class CglibProxy implements MethodInterceptor {
private Object target;
private Enhancer enhancer = new Enhancer();
public Object getInstance(Class target){
// 设置从字节码创建的类
enhancer.setSuperclass(target);
enhancer.setCallback(this);
// 创建字节码类
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
return methodProxy.invokeSuper(o , objects);
}
}
client 类
public class client {
public static void main(String[] args) {
Subject cp = (Subject) new CglibProxy().getInstance(RealSubject.class);
cp.dosomething();
}
}