静态代理与动态代理

一、代理

代理这词并不陌生,如我们买车票,我们只需要准备好钱和你要去哪儿。接着直接到购票平台选择目的地并付钱,购票平台就会帮我们与服务提供商进行交接帮我们够买好车票。这里的购票平台就充当了代理角色。
又或者朋友圈的微商。他们通过厂家拿货然后在朋友圈进行推广。我们买的时候只需要找他们购买就行不需要直接跟厂家进行交涉。这里的微商也充当了代理的角色。

而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修饰,负责不能代理

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值