一、代理
代理这词并不陌生,如我们买车票,我们只需要准备好钱和你要去哪儿。接着直接到购票平台选择目的地并付钱,购票平台就会帮我们与服务提供商进行交接帮我们够买好车票。这里的购票平台就充当了代理角色。
又或者朋友圈的微商。他们通过厂家拿货然后在朋友圈进行推广。我们买的时候只需要找他们购买就行不需要直接跟厂家进行交涉。这里的微商也充当了代理的角色。
而java中的代理就跟上面提到的类似,将真实业务与代理分开。用户只关心真实业务的内容并不关心代理怎么做的。java中代理又分为静态代理与动态代理。
接下来用一个例子来看看静态代理与动态代理的区别。真实业务就以随机生成一个随机数来将线程暂停随机秒来代表。而后来我们想知道这个真实业务到底耗费了多长时间,这时候通过改真实业务的代码是不合适的,所以就以代理解决。
二、静态代理
静态代理是在程序运行之前就已经创建好了代理类。静态代理一般是对某一个接口进行代理。
1.先创建一个真实业务的接口
public interface IRealService {
//真实业务主体
public void doSomeThing();
}
2.实现真实业务
public class RealServiceImpl implements IRealService {
@Override
public void doSomeThing() {
try {
//用随机随眠来代表模拟业务执行过程
Thread.sleep((int)(Math.random()*10000));
System.out.println("真实业务已经完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
3.接着我们想知道到底执行了多久,所以创建一个用来记录时间。
public class TimeManager {
Date start;
Date end;
/**
* 用来记录真实业务开始的时间
*/
public void start() {
start=new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
System.out.println("开始时间:"+sdf.format(start));
}
/**
* 用来记录真实业务结束的时间
*/
public void end() {
end=new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
System.out.println("结束时间:"+sdf.format(end));
total();
}
/**
* 用来计算耗费了多少时间
*/
public void total() {
Long total=end.getTime()-start.getTime();
Long day=total/(1000 * 24 * 60 * 60);
Long hour=total/(1000 * 60 * 60);
Long min=total/(1000* 60);
Long second=total/(1000);
System.out.println(day+"天"+hour+"时"+min+"分"+second+"秒");
}
}
4.接着就要为真实业务创建代理来测试真实业务到底运行了多长时间。所以创建一个代理类,这个代理类实现真实业务的接口IRealService
public class RealServiceImplProxy implements IRealService {
//用于记录时间的工具
private TimeManager time;
//真实业务主体 也就是那个随机暂停的那个类
private IRealService realService;
public RealServiceImplProxy(TimeManager time, IRealService realService) {
this.time = time;
this.realService = realService;
}
@Override
public void doSomeThing() {
//开始记录起始时间
time.start();
//调用真实业务
realService.doSomeThing();
//记录结束时间
time.end();
}
}
5.测试
@Test
public void testStaticProxy() throws Exception {
//创建真实业务主体
IRealService realService = new RealServiceImpl();
//创建时间记录管理器
TimeManager time=new TimeManager();
//创建代理类 并赋予真实业务主体和时间管理器
IRealService realServiceProxy=new RealServiceImplProxy(time,realService);
//开始做事 用代理类做事
realServiceProxy.doSomeThing();
}
结果:
```
开始时间:2018-10-14 08:16:06
真实业务已经完成
结束时间:2018-10-14 08:16:12
0天0时0分5秒
```
根据结果就可以发现代理已经帮我们记录了执行的时间,而我们的真实业务主体还是只有随机暂停那句代码并没有改动。这就是静态代理。
通过代码可以看出真实业务主体代码并没改变,时间记录功能也加上了。但是通过代码可以看出这个代理指只对当前的业务有效果。如果我们有多个真实业务主体,每个业务主体都要记录时间那就要每个业务主体都创建一个代理类。所以这就是静态代理的缺点,产生太多代理类。
三、动态代理
动态代理是在程序运行时动态的生成代理对象。动态代理有两种实现方式。一种是基于jdk通过反射实现,一种是基于cglib实现。
3.1、基于jdk的动态代理
1.先创建一个类实现InvocationHandler接口。重写invoke方法对真实业务加代理。真实业务及时间记录器和上面的一个不需要改动。
public class JDKProxyHandler implements InvocationHandler {
//用于记录时间的工具
private TimeManager time;
//目标对象即真实业务主体 因为是动态的 所以可以对多种真实业务进行代理 所以用Object声明
private Object target;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//开始记录时间
time.start();
//通过反射做真实业务 target为真实业务对象 args为我们调用的方法的参数 因为我们写的doSomeThing并不需要传参所以args为null
//result为方法执行后的返回值
Object result = method.invoke(target, args);
//记录结束时间
time.end();
//将真实业务的返回值返回
return result;
}
}
2.测试
@Test
public void testJDKProxy() throws Exception {
//创建真实业务主体
IRealService realService = new RealServiceImpl();
//创建时间记录管理器
TimeManager time=new TimeManager();
//真实业务的类加载器
ClassLoader loader = realService.getClass().getClassLoader();
//真实业务的接口
Class<?>[] interfaces = realService.getClass().getInterfaces();
//自己写的handler
JDKProxyHandler handler = new JDKProxyHandler(time, realService);
//通过Proxy.newProxyInstance获取动态生成的代理类
IRealService realServiceProxy = (IRealService) Proxy.newProxyInstance(loader, interfaces, handler);
//通过代理类做事
realServiceProxy.doSomeThing();
}
结果:
```
开始时间:2018-10-14 08:37:51
真实业务已经完成
结束时间:2018-10-14 08:37:52
0天0时0分1秒
```
通过结果可以看出结果和静态代理一样,都没改动真实业务主体。但是动态代理可以对多个业务代理。可以再创一个真实业务进行测试。在多个业务需要代理的情况下使用动态代理不需要再多创建代理类。解决了静态代理代理类过多的情况。但是动态代理的对象必须要有接口,否在无法代理。
3.2、基于cglib的动态代理
原理:动态的给目标对象创建一个子类,子类重写了父类的方法。也就是我们的真实业务主体方法里只有随机暂停的代码,cglib为这个真实业务创建了一个子类,在子类的方法里面增加了记录时间的功能,并调用了父类的随机暂停的方法。
1.需要导入cglib的jar包
2.创建一个类实现MethodInterceptor接口,实现intercept方法。真实业务及时间记录器和上面的一个不需要改动。
public class CglibProxyHandler implements MethodInterceptor {
//用于记录时间的工具
private TimeManager time;
public CglibProxyHandler(TimeManager time) {
this.time = time;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//开始记录时间
time.start();
//因为cglib是动态的为真实业务创建一个子类 所以执行真实业务方法要调用父类的方法 o为真实业务对象 objects为方法的参数 result为返回值
Object result = methodProxy.invokeSuper(o, objects);
//记录结束时间
time.end();
//将真实业务的返回值返回
//将方法执行后的返回值返回
return result;
}
}
3.测试
@Test
public void testCglibProxy() throws Exception {
//创建真实业务主体
IRealService realService = new RealServiceImpl();
//创建时间记录管理器
TimeManager time=new TimeManager();
//创建Enhancer用于获取代理对象
Enhancer enhancer=new Enhancer();
//设置目标对象 也就是真实业务主体对象
enhancer.setSuperclass(realService.getClass());
//设置handler
enhancer.setCallback(new CglibProxyHandler(time));
//获取代理对象
IRealService realServiceProxy = (IRealService) enhancer.create();
//通过代理对象做事
realServiceProxy.doSomeThing();
}
结果
```
开始时间:2018-10-14 09:00:50
真实业务已经完成
结束时间:2018-10-14 09:00:56
0天0时0分5秒
```
通过结果可以发现结果和jdk动态代理一样,可以为多种业务进行代理。也同样解决了代理类过多的情况。但是cglib跟jdk动态代理不一样的是cglib可以不用接口就可以进行代理,但是cglib的动态代理目标对象不能用final修饰,负责不能代理。
1661

被折叠的 条评论
为什么被折叠?



