一.什么是代理模式?

    客户端不直接和目标对象(实际对象)发生交互,或不直接调用目标对象,而是通过调用代理对象来间接的调用目标对象的设计模式。

二.作用(为什么要使用代理模式)

  1. 当客户端不想直接访问目标对象或访问目标对象有困难时,需要通过代理对象进行间接访问
  2. 在不改变目标对象源代码的情况下,通过代理对目标对象的功能进行增强。如添加日志、事务控制,权限控制,鉴权登录等

三.代理模式分类

1.静态代理(了解)

静态代理,代理类和i能代理某一特定的目标对象,并且对目标对象进行增强。

public interface Ticket {

    void buyTicket();
}


/**
 * 12306 直接购买
 */
public class By12306TicketImpl implements Ticket{

    @Override
    public void buyTicket() {


        System.out.println("12306 买票");

    }
}

/**
 * 通过黄牛购买
 */
public class HuangNiuTicketImpl implements Ticket {

    private Ticket ticket;

    public void setTicket(Ticket ticket) {
        this.ticket = ticket;
    }

    @Override
    public void buyTicket() {

        System.out.println("黄牛开始买票");
        if (ticket!=null){
            ticket.buyTicket();
        }
        System.out.println("黄牛买票成功");

    }
}

测试类

public class Test {

    public static void main(String[] args) {

        // 自己买票
        Ticket ticket = new By12306TicketImpl();
        ticket.buyTicket();

        // 通过黄牛买票,最终票的来源还是12306
        HuangNiuTicketImpl huangNiu = new HuangNiuTicketImpl();
        huangNiu.setTicket(ticket);
        huangNiu.buyTicket();

    }
}

2.动态代理(重要)

动态代理,代理对象可以代理很多类型的目标对象,并且对目标对象进行增强

java中动态代理有JDK和CGLIB两种方式

四、基于JDK接口的动态代理(面试必会)

1.JDK动态代理步骤:

  1. 实现InvocationHandler接口
  2. 创造代理对象
  3. 通过反射执行目标对象方法

2.注意点

应用场景:基于java jdk实现只能代理实现接口的目标对象,如果没有接口,则报错

  1. 搞清楚Proxy.newProxyInstance三个参数的意思
  2. 返回的代理对象是接口,目标对象实现的那个接口
  3. 当代理对象调用对应的接口方法时 会回调传递第三个参数 InvocationHandler 对象内的 invoke 方法通过反射执行
public class JdkProxyTest1 {
    public static void main(String[] args) {
        //12306 目标对象   因为作为了匿名内部类参数的缘故,所以用final修饰
        final TicketBy12306Impl t12306 = new TicketBy12306Impl();

        //创建一个代理对象
        //ClassLoader loader, 类加载器
        //Class<?>[] interfaces, 目标对象实现的接口,代理目标对象必须实现接口
//             t12306.getClass().getInterfaces() 获取目标对象实现的接口
//       InvocationHandler 当代理对象执行接口的方法时  会回调InvocationHandler的invoke
        Ticket proxyTicket = (Ticket) Proxy.newProxyInstance(t12306.getClass().getClassLoader(), t12306.getClass().getInterfaces(), new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("代理对象开始调用 目标对象");

                //让目标对象执行方法  并且获取返回结果
                Object result = method.invoke(t12306, args);
                System.out.println("代理对象结束调用 目标对象");
                return result;
            }
        });

        String str = proxyTicket.bubTicket();
        System.out.println("str:" + str);
    }
}

 3.封装工具类

/**
 * 专门用于产生JDK代理对象
 */
public class JdkProxy implements InvocationHandler {

    //目标对象
    private Object target;
    public Object createProxy(final Object target){
        this.target = target;
        //ClassLoader loader,指定当前目标对象使用类加载器,我们知道获取加载器的写法是固定的
        //Class<?>[ ] interfaces,目标对象实现的接口的类型,使用泛型方式确认类型,我们知道要想知道一个类是否实现某个接口,可以使用    Object.getClass().getInterfaces() ,这个方法是获取类是否实现接口,如果此对象表示一个类,则返回值是一个数组,它包含了表示该类所实现的所有接口的对象。
        //InvocationHandler h,事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理对象开始调用 目标对象");

        // 让目标对象执行方法  并且获取返回结果
        Object result = method.invoke(target,args);
        System.out.println("代理对象结束调用 目标对象");
        return result;
    }
}

五、cglib基于类的动态代理

cglib基于asm字节码生成框架,用于动态生成代理类。

重点:

  1. 应用场景:cglib可以代理类和接口,返回的代理对象是其子类
  2. cglib 直接调用生成的类的方法,而jdkproxy是通过反射,返回效率差 

1.cglib与jdk的区别(重点)?

         1. jdk实现代理,目标对象必须实现接口

             cglib是根据目标对象/目标类生成代理类的字节码文件,不要求实现接口

         2.在生产代理类的效率上,cglib效率差 因为要生成字节码文件   jdk效率高

         3.在代理方法执行效率上来说。 cglib直接执行class文件的方法,效率高。jdk通过反射实现,效率低

2.实现

public class CglibProxyTest1 {
    public static void main(String[] args) {

        //创建cglib代理对象需要工具类
        //1.引入jar包
        Enhancer enhancer = new Enhancer();

        //传递目标类 cglib的原理:生成的代理类是目标类的子类
        enhancer.setSuperclass(TicketBy12306Impl.class);

        enhancer.setCallback(new MethodInterceptor() {
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("cglib 代理开始调用 目标对象");

                //让目标对象执行
                //o 目标对象
                //object 目标对象执行方法需要的参数
                Object result = methodProxy.invokeSuper(o, objects);
                System.out.println("cglib-result:"+result);
                System.out.println("cglib 代理结束调用 目标对象");
                return null;
            }
        });

        //创建代理对象 目标对象可以不创建 cglib会自己创建目标
        TicketBy12306Impl ticket = (TicketBy12306Impl) enhancer.create();
        ticket.bubTicket();
    }
}

3.封装工具类

/**
 * cglib 创建代理对象的工具类
 */
public class CglibProxy implements MethodInterceptor {

    public Object createProxy(Class superClass){
        Enhancer enhancer = new Enhancer();

        // 传递目标类  cglib 的原理是生成的代理类是 目标类的子类
        enhancer.setSuperclass(superClass);

        enhancer.setCallback(this);

        //创建代理对象
        return enhancer.create();
    }


    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("cglib 代理开始调用 目标对象");

        // 让目标 对象执行
        // o 目标对象
        // objects 目标对象执行方法需要的惨呼
        Object result =   methodProxy.invokeSuper(o,objects);

        System.out.println("cglib-result:"+result);
        System.out.println("cglib 代理结束调用 目标对象");
        return result;
    }
}
public class CglibProxyTest2 {
    public static void main(String[] args) {

         //生成动态代理的工具类
        CglibProxy cglibProxy = new CglibProxy();

        //12306代理对象
        TicketBy12306Impl ticketBy12306Proxy = (TicketBy12306Impl) cglibProxy.createProxy(TicketBy12306Impl.class);
        ticketBy12306Proxy.bubTicket();

        System.out.println("*********************");

        //第二个代理对象
        RealSubject realSubjectProxy = (RealSubject) cglibProxy.createProxy(RealSubject.class);
        realSubjectProxy.action();
    }
}

 

posted on 2020-10-21 22:46  zitian246  阅读(404)  评论(0编辑  收藏  举报