代理技术详解---静态代理和动态代理

什么是代理技术?

代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。比如买房子找个中介来帮你进行买卖,中介如何操作你并不关心,而你关心的是最终的结果,房子是否买卖成功,中间过程有中介来处理。这里的中介就像我们理解的代理模式一样。我们不直接进行买卖,而是通过中介买卖。我们不直接访问某个对象,而是访问其代理对象

怎样实现代理?

实现代理,我们需要三个要素,分别是共同接口,真实对象和代理对象

一.静态代理

在service层中,我们处理事务的代码一般是这样的(伪代码)

public class ServiceEg {
    public void biz1() {
        System.out.println("事务开始");
        try {
            System.out.println("增加abc");
            System.out.println("增加def");
            System.out.println("增加ghi");
            System.out.println("提交事务");
        } catch (Exception e) {
            System.out.println("回滚事务");
        }
    }

    public void biz2() {
        try {
            System.out.println("删除jkl");
            System.out.println("删除mn");
            System.out.println("删除opq");
            System.out.println("提交事务");
        } catch (Exception e) {
            System.out.println("回滚事务");
        }
    }
}

但是,我们会发现里面有好多代码都是重复的,比如事务的开始,回滚等,因此我们需要对代码进行简化,让关于事务的代码只需要写一遍即可,而不是每次执行方法时都要重复,此时就可以使用代理技术

以上述代码为例,使用静态代理实现事务的代码重用

思路:

1.提供一个代理类(Proxy)

   调用通知类的invoke方法

   获取方法对象和方法实际参数

   与目标要实现相同的接口(目的是让使用者察觉不出是代理替换了原来的目标)

2.提供一个通知类(Advice)

   实现了重复代码(事务的重复代码)

   反射调用了目标对象的方法

   把重复代码和目标方法联系在了一起

代码过程实现

UserService接口(这个是父类接口,以便代理对象替换真实对象)

public interface UserService {
    public void biz1();
    public void biz2();
}

UserServiceTarget类(实际类,是业务逻辑的实际执行者)

public class UserServiceTarget implements UserService {
    //单一职责原则
    public void biz1() {
        System.out.println("增加abc");
        System.out.println("增加def");
        System.out.println("增加ghi");
    }

    public void biz2() {
        System.out.println("删除jkl");
        System.out.println("删除mn");
        System.out.println("删除opq");
    }
}

TrancationAdvice类(通知类,用于实现可重用的逻辑,并调用实际对象的方法)

import java.lang.reflect.Method;

//advice类,其中放事务之类可重用的逻辑
public class TrancationAdvice {
    private UserServiceTarget target = new UserServiceTarget();
    //method是任意方法的对象,args代表了方法的参数数组
    //method就是biz1(),biz2()...,args就是biz1()里面的参数...
    public Object invoke(Method method, Object[] args) {
        System.out.println("事务开始");
        Object obj = null;
        try {
            //通过反射调用方法     方法.invoke(对象,参数);
            obj = method.invoke(target, args);
            System.out.println("提交事务");
        } catch (Exception e) {
            System.out.println("处理异常");
        }
        return obj;
    }
}

UserProxy类(代理类,最后调用的方法,将通知类和需要执行的目标类结合)

import java.lang.reflect.Method;

//使用接口的目的就是为了UserProxy能够替换原来的UserServiceTarget
//能够以与原来一直的方式使用Proxy类,Proxy代理(把事务重复代码和目标业务类代码结合)
public class UserProxy implements UserService {
    TrancationAdvice advice = new TrancationAdvice();
    @Override
    public void biz1() {
        advice.invoke(biz1, new Object[] {});
    }

    @Override
    public void biz2() {
        advice.invoke(biz2, new Object[] {});
    }

    static Method biz1;
    static Method biz2;
    static {
        Class<UserServiceTarget> c = UserServiceTarget.class;
        try {
            biz1 = c.getMethod("biz1");
            biz2 = c.getMethod("biz2");
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

测试(使用单元测试块进行测试)

public class TestProxy {
    @Test
    public void teat1() {
        UserProxy userProxy = new UserProxy();
        userProxy.biz1();
    }
}

测试结果

20190416195158615.png

二.动态代理

通过上面的静态代理(即我们自己写的代理),我们了解到代理技术的思想和具体实现过程的例子

但是,里面的代理类不具有通用性,只能解决事务类的可重用逻辑,但实际中,我们遇到会有更多的场景,那么就又要重新再写一个代理类,十分麻烦,所以,我们要针对这一问题进行改进,实现一个更加通用的代理类,即动态代理

什么是动态代理?

在Java中,会有一种技术,即在代码运行期间,jdk可以自己生成代理类和代理对象,而不用程序员去书写,我们只要告诉它通知是谁,接口是谁就可以

正常使用类:*.java  --->  javac  --->  *.class  --->  java  --->  加载该class到虚拟机

动态代理:  直接生成   *.class 字节码   --->   加载该class到虚拟机

如何使用动态代理?

要实现动态代理,就需要使用java自己的动态代理类,它位于java.lang.reflcet包下,主要涉及到两个类

1.Interface  InvocationHandler : 该接口中仅仅定义了一个方法 Object invoke(Object obj, Method method, Object[] args)

   参数1(obj)一般是指代理类

   参数2(method)是被代理的方法

   参数3(args)是该方法的参数数组

   通知类需要实现这个借口,并实现里面的方法

2.Proxy :该类即为动态代理类,用于创建动态代理及对象

   里面有一个方法newProxyInstance(ClassLoader loader,Class[] interfaces,  InvocationHandler h);

   参数1(loader) 是一个类加载器,获得方式是得到类对象,调用getClassLoader()方法就能得到类加载器啦

   参数2(interfaces) 是告诉代理类需要实现哪些接口,直接 new Class[] {接口1.class, 接口2.class ...} 就可以了

   参数3(h) 通知,规定代理类要执行哪些代码,也就是把通知类对象传进去就行了

代码实现

其中UserService类,UserServiceTarget类不改变

通知类  TrancationAdviceDynamic 

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

//实现InvocationHandler接口,并重写里面的方法
public class TrancationAdviceDynamic implements InvocationHandler {
    private UserServiceTarget target = new UserServiceTarget();

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("事务开始");
        Object obj = null;
        try {
            //通过反射调用方法     方法.invoke(对象,参数);
            obj = method.invoke(target, args);
            System.out.println("提交事务");
        } catch (Exception e) {
            System.out.println("处理异常");
        }
        return obj;
    }
}

测试类  TestDynamicProxy 

import org.junit.Test;
import service.TrancationAdviceDynamic;
import service.UserService;

import java.lang.reflect.Proxy;

public class TestDynamicProxy {
    @Test
    public void test1() {
        ClassLoader loader = TestDynamicProxy.class.getClassLoader();
        UserService proxy = (UserService) Proxy.newProxyInstance(loader, new Class[]{UserService.class}, new TrancationAdviceDynamic());
        proxy.biz2();
    }
}

测试结果

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N6eTIzMzM=,size_16,color_FFFFFF,t_70

到这里,静态代理和动态代理的基本知识和操作就可以啦!

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值