最近看到一篇关于讲代理的文章,接触到了一些jdk动态代理,然后写一篇文章加深下记忆。
主要讲静态代理和动态代理,通俗一点就是在原对象的基础上增加原对象的功能,比如说:在原对象的调用方法前后进行日志记录、事务操作等。Spring AOP就是用了代理模式,后续有机会看看这部分源码。
1.RPC(Remote Procedure Call)
—[ 远程过程调用 ] 它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。
接触到的一种概念,好像没啥关系。
2. 静态代理
在程序运行前就已经存在的编写好的代理类
大概就四个步骤:
- 定义接口 业务接口
- 实现接口 被代理类实现接口
- 再次实现接口 代理类实现接口
- 客户端调用
从上面的步骤主要看出来要写三个类 业务类、被代理类 、代理类,客户端调用的类我们暂且不管,下面来看代码的实现:
2.1 业务接口 也可称被代理的接口
主要就是定义业务要实现的功能
package com.zhb.jdk.proxy;
public interface IUserService {
void add(String name);
}
2.2 被代理类
通常这个地方就是我们实现接口的类
package com.zhb.jdk.proxy;
public class UserServiceImpl implements IUserService {
@Override
public void add(String name) {
System.out.println("向数据库中插入名为: "+name+" 的用户");
}
}
2.3 代理类
这个类就是我们需要添加的代理类,一般代理类命名都会加上proxy,可以由此来判断是否是代理类。
它主要就是两个部分,第一部分就是构造函数传入被代理类
,然后在实现业务接口
,重写业务接口的方法,并且在重写方法里,调用被代理类
的方法。然后在调用被代理类
的方法的前后你就可以进行自己的业务操作。
package com.zhb.jdk.proxy;
public class UserServiceProxy implements IUserService {
// 被代理对象
private IUserService target;
// 通过构造方法传入被代理对象
public UserServiceProxy(IUserService target) {
this.target = target;
}
@Override
public void add(String name) {
System.out.println("准备向数据库中插入数据");
target.add(name);
System.out.println("插入数据库成功");
}
}
2.4 客户端调用
使用代理的话,主要就是两个部分,第一:就是先实例化
被代理类
,然后在是实例化代理类
时,将被代理类
传入,最后调用代理类
的方法,执行。
package com.zhb.jdk.proxy;
public class StaticProxyTest {
public static void main(String[] args) {
IUserService target = new UserServiceImpl();
UserServiceProxy proxy = new UserServiceProxy(target);
proxy.add("陈粒");
}
}
静态代理就这样实现了,很简单的,也容易理解的。
3. 动态代理
为啥要动态代理,因为静态代理有缺点,1. 两次实现业务接口,接口 增加方法,两个类都需要修改;2. 代理对象只服务于一种类型的对象,如果要服务多类型的对象。比如上面的例子,只是对用户的业务功能(IUserService)进行代理,如果是商品(IItemService)的业务功能那就无法代理,需要去编写商品服务的代理类。
所以有了动态代理 ,所谓动态代理是指:在程序运行期间根据需要动态创建代理类及其实例来完成具体的功能。
动态代理主要分为JDK动态代理
和cglib动态代理
两大类,本文主要对JDK动态代理
进行探讨。
主要步骤有以下:
- 创建被代理的接口和它的实现类
- 创建
InvocationHandler接口
的实现类,在invoke方法
中实现代理逻辑; - 通过Proxy的静态方法
newProxyInstance( ClassLoaderloader, Class[] interfaces, InvocationHandler h)
创建一个代理对象 - 使用代理对象。
3.1 被代理的接口和它的实现类
还是使用上面静态代理的两个
3.2 创建InvocationHandler接口
的实现类
这个实现类也是构造函数传入
被代理接口的实现类
,然后重写InvocationHandler接口
的invoke方法
,然后在method.invoke方法
前后去完成自己需要加入的操作,记录日志呀、事务操作等;看到这里应该就懂了,这个方法是特定的,不管你被代理的业务接口有多少个方法。
package com.zhb.jdk.dynamicProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
//被代理对象,Object类型
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("准备向数据库中插入数据");
Object returnvalue = method.invoke(target, args);
System.out.println("插入数据库成功");
return returnvalue;
}
}
3.3 客户端调用
然后这块主要理解
invoke方法
的传入的参数
- 第一个参数是指定代理类的类加载器(我们传入当前测试类的类加载器)
- 第二个参数是代理类需要实现的接口(我们传入被代理类实现的接口,这样生成的代理类和被代理类就实现了相同的接口)
- 第三个参数是invocation handler,用来处理方法的调用。这里传入我们自己实现的handler
package com.zhb.jdk.dynamicProxy;
import java.lang.reflect.Proxy;
public class DynamicProxyTest {
public static void main(String[] args) {
IUserService target = new UserServiceImpl();
MyInvocationHandler handler = new MyInvocationHandler(target);
IUserService proxyObject = (IUserService) Proxy.newProxyInstance(DynamicProxyTest.class.getClassLoader(),
target.getClass().getInterfaces(), handler);
proxyObject.add("陈粒");
}
}
实际上,动态代理的代理对象是在内存中的,是JDK根据我们传入的参数生成好的。那动态代理的代理类和代理对象是怎么产生的呢?
这块就需要去查阅jdk的源码了。下次再会!
记住胖子不是一口吃成的!加油!
网友评论