1.需求
(1):在程序中,对象A和对象B无法直接交互时。
 (2):在程序中,功能需要增强时。
 (3):在程序中,目标需要被保护时
代理模式中有一个非常重要的特点:对于客户端程序来说,使用代理对象时就像在使用目标对象一样。
2.分类
在代码形式上,代理模式分为动态代理和静态代理两种,两种原理相同,静态代理工作都是由我们完成,动态代理工
作则是自动完成,下面介绍两种动态代理。
 
2.1 jdk动态代理
JDK动态代理技术:只能代理接口。
以一个例子来说明
 
创建业务类接口
package com.hkd.service;
public interface OrderService {
    /**
     * 生成订单
     */
    void generate();
    /**
     * 删除订单
     */
    void delete();
}
 
业务类接口实现类(目标类)
package com.hkd.service.impl;
import com.hkd.service.OrderService;
public class OrderServiceImpl implements OrderService {
    @Override
    public void generate() {
        try {
            Thread.sleep(1024);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("正在生成订单.....");
    }
    @Override
    public void delete() {
        try {
            Thread.sleep(1002);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("正在删除订单.....");
    }
}
 
其中sleep用来模拟执行时间
 
需求
需要统计每个业务的执行时间
解决
我们可以使用代理的方式
 
客户端代码
package com.hkd.client;
import com.hkd.invocationHandler.TimerInvocationHandler;
import com.hkd.service.OrderService;
import com.hkd.service.impl.OrderServiceImpl;
import java.lang.reflect.Proxy;
public class Client {
    public static void main(String[] args) {
//        创建目标类
        OrderService orderService = new OrderServiceImpl();
//        创建代理对象
        OrderService proxyInstance = (OrderService) Proxy.newProxyInstance(orderService.getClass().getClassLoader(), orderService.getClass().getInterfaces(), new TimerInvocationHandler(orderService));
//        执行目标方法
        proxyInstance.generate();
        proxyInstance.delete();
    }
}
 
重点创建代理对象这一行代码
1,该行做了什么?
	答:第一件事:在内存中生成了代理类的字节码
		   第二件事:创建代理对象
2.三个参数的作用分别是什么?
	答:第一个参数:类加载器。在内存中生成了字节码,要想执行这个字节码,也是需要先把这个字节码加载到
	内存当中的。所以要指定使用哪个类加载器加载。
	第二个参数:接口类型。代理类和目标类实现相同的接口,所以要通过这个参数告诉JDK动态代理生成的类要实
	现哪些接口。
	第三个参数:调用处理器。这是一个JDK动态代理规定的接口,接口全名:java.lang.reflect.InvocationHandler。
	显	然这是一个回调接口,也就是说调用这个接口中方法的程序已经写好了,就差这个接口的实现类了。
 
第三个参数对应的类
package com.hkd.invocationHandler;
import com.hkd.service.OrderService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class TimerInvocationHandler implements InvocationHandler {
    Object target;
    public TimerInvocationHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//        目标执行之前增强
        long begin = System.currentTimeMillis();
//        调用目标对象目标方法
        OrderService invoke = (OrderService) method.invoke(target, args);
//        目标执行之后执行
        long end = System.currentTimeMillis();
        System.out.println("一共耗时"+ (end - begin) +"毫秒");
        return invoke;
    }
}
 
invoke 方法也有三个参数下面一一解释
 第一个参数:Object proxy。代理对象。设计这个参数只是为了后期的方便,如果想在invoke方法中使用代理对象
 的话,尽管通过这个参数来使用。
第二个参数:Method method。目标方法。
第三个参数:Object[] args。目标方法调用时要传的参数。
问题:这个invoke方法什么时候执行?
当代理对象调用代理方法时,invoke就会执行。
 
上面client执行结果如下
 
2.2 CGLIB动态代理
CGLIB动态代理技术:CGLIB(Code Generation Library)是一个开源项目。是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。它既可以代理接口,又可以代理类,底层是通过继承的方式实现的。性能比JDK动态代理要好。(底层有一个小而快的字节码处理框架ASM。)
使用
1.引入依赖
<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib</artifactId>
  <version>3.3.0</version>
</dependency>
 
2.目标类
package com.hkd.service;
public class UserService {
    public void login(){
        System.out.println("正在登录....");
    }
    public void logout(){
        System.out.println("正在退出....");
    }
}
 
3.客户端代码
package com.hkd.client;
import com.hkd.methodinterceptor.TimerMethodInterceptor;
import com.hkd.service.UserService;
import net.sf.cglib.proxy.Enhancer;
public class Client2 {
    public static void main(String[] args) {
//        创建字节码增强器
        Enhancer enhancer = new Enhancer();
//        告诉cglib要继承哪个类
        enhancer.setSuperclass(UserService.class);
//        设置回调接口
        enhancer.setCallback(new TimerMethodInterceptor());
//        生成源码,编译class,加载到JVM,并创建代理对象
        UserService userService = (UserService) enhancer.create();
//        执行代理方法
        userService.login();
        userService.logout();
    }
}
 
和JDK动态代理原理差不多,在CGLIB中需要提供的不是InvocationHandler,而是:net.sf.cglib.proxy.MethodInterceptor
 编写MethodInterceptor接口实现类:
package com.hkd.methodinterceptor;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class TimerMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object target, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//        增强前
        long begin = System.currentTimeMillis();
//        执行目标方法
        Object invokeSuper = methodProxy.invokeSuper(target, objects);
//        增强后
        long end = System.currentTimeMillis();
        System.out.println("一共消耗"+ (end - begin) +"毫秒");
        return invokeSuper;
    }
}
 
MethodInterceptor接口中有一个方法intercept(),该方法有4个参数:
第一个参数:目标对象
第二个参数:目标方法
第三个参数:目标方法调用时的实参
第四个参数:代理方法
 
对于高版本的JDK,如果使用CGLIB,需要在启动项中添加两个启动参数:
 –add-opens java.base/java.lang=ALL-UNNAMED
 –add-opens java.base/sun.net.util=ALL-UNNAMED

 运行结果
 



















