代理模式--静态代理and动态代理

35 篇文章 0 订阅

1. 代理模式

代理模式:代理模式是Java中一种常用的设计模式,主要就是提供一个代理对象对外提供一个方法,然后用过代理对象去访问目标表对象;

通俗理解:
实际就是创建一个与目标对象(实际要操作的对象)相同类型的代理对象proxy,使用这个proxy对象去访问目标对象完成操作,在这个proxy对象中还可以有其他的功能去丰富目标对象的功能。服务器最后调用的就是这个proxy对象,这样就完成了代理增强。

代理模式最主要的就是在不改变原来代码(就是目标对象)的情况下实现功能的增强

1.1 静态代理

1.1.1 静态代理理解

静态代理:相当于是自己写了一个代理类,在调用的时候调用的是代理类,代理类中的处理还是原生的处理逻辑,不过在前后添加上需要增强功能的代码。

缺点:需要为每一个被代理的对象都创建一个代理类。

1.1.2 静态代理实现
  1. 创建接口
public interface PetService {
    Pet add(String username, String password);
}
  1. 实现目标对象
public class PetServiceImpl implements PetService {
    public Pet add(String username, String password) {
        System.out.println("目标对象:petService执行");
        return petDao.add(username,password);
    }
}
  1. 实现代理对象
//代理对象
public class ProxyPetService implements PetService {
//    代理对象自己是不做事的,是交给目标对象做,相当于就是创建个目标对象,然后通过目标对象去完成方法,所以需要引入目标对象
    private PetService petService;
    //通过构造器去完成目标对象的创建
    public ProxyPetService(PetService petService) {
        this.petService = petService;
    }

    @Override
    public Pet add(String username, String password) {
        //可以加功能啥的了,就是要实现的方法,为了方便,就写一句输出就行
        System.out.println("代理增强功能1....");
        Pet add = petService.add(username, password);
        //增加代理功能
        System.out.println("代理增强功能2....");
        return add;
    }
}
  1. 测试代码
@Test
    public void testJingTai(){
        //要实现静态代理,就要将代理对象和目标对象进行关联起来,相当于就是要将目标对象放到代理对象中去
        PetService petService = new PetServiceImpl();//创建目标对象
        PetService proxyPetService = new ProxyPetService(petService);//创建代理对象
        //外部调用目标对象去完成增强功能呢
        Pet xiaowang = proxyPetService.add("xiaowang", "123456");
    }

注意:目标对象和代理对象要实现用一个接口,目标对象完成自己的事情,代理对象调用目标对象,不仅可以完成目标对象的事情,自己还可以再加一些功能上去

1.2 动态代理

动态代理模式最大的优势就是不用自己去写一个代理对象,它的代理对象会在java文件编译的时候,通过Java反射(javac的过程)去创建的。所以减少了程序员的工作。动态代理的实现有两种方式,现在来介绍一下

1.2.1 JDK动态代理

注意:JDK动态代理的目标对象必须有接口实现

jdk实现动态代理理解:
主要是通过Java反射包里面的类和接口去完成的。反射包里面有三个类:InvocationHandler、Method、Prixy

  1. 创建接口
public interface PetDao {
    Pet add(String username, String password);
}
  1. 目标对象
public class PetDaoImpl implements PetDao {
    @Override
    public Pet add(String username, String password) {
        System.out.println("petDao执行");
        return new Pet(username,password);
    }
}

  1. 代理对象执行器invoke
    要通过jdk反射来完成的话,要先去将代理对象的执行器invoke方法给构造好,所以需要实现反射包里面的InvovationHandler接口来完成了,相当于就是通过代理对象的执行器来完成
public class PetDaoInvoinvokecation implements InvocationHandler {

    //在执行器中还是需要有目标对象呀
    private Object obj;

    public PetDaoInvoinvokecation(Object obj) {
        this.obj = obj;
    }

    //invoke参数解释:proxy:代理对象,Method:代理对象的方法,args:代理对象所需的参数
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //动态代理模式不需要自己去写代理对象,而是在程序运行编译的时候就会创建的
        //真正的操作是由目标对象来完成的,所以通过method方法去完成执行器,里面参数就是目标对象以及目标对象要操作的方法的参数
        System.out.println("jdk增强之前..");
        Object invoke = method.invoke(obj, args);
        System.out.println("jdk增强之后..");
        return invoke;
    }
}

注意:是反射包( java.lang.reflect )里面的InvovationHandler接口哈

  1. 测试类
 @Test
    public void testJDK(){
        PetDao petDao = new PetDaoImpl();//目标对象
        // 代理对象执行器
        PetDaoInvoinvokecation petDaoInvoinvokecation = new PetDaoInvoinvokecation(petDao);
        /*
        代理对象,通过反射完成Proxy.newProxyInstance()
         newProxyInstance参数:
         类加载器:(代理对象的类型,也就是目标对象的)、
         目标对象所实现的接口:(返回是一个数组,因为一个类可以实现多个接口嘛)、
         执行器:
         */
        //代理对象的类型,是目标对象所实现的接口类型之一,就相当于静态代理的时候一样,目标对象和代理对象都是实现的同一个接口
        PetDao o = (PetDao) Proxy.newProxyInstance(petDao.getClass().getClassLoader(), petDao.getClass().getInterfaces(), petDaoInvoinvokecation);
        Pet xiaowang = o.add("xiaowang", "123456");//代理对象执行方法
        System.out.println(xiaowang);
    }

jdk动态模式原理:
主要是由反射包( java.lang.reflect )里面的InvocationHandler、Method、Proxy三个类来完成的。
①通过实现InvocationHandler接口去创建代理对象的执行器(重写invoke方法)
invoke方法:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { }
invoke方法里面就是通过method去执行具体的代理对象的方法

②然后通过Proxy的newProxyInstance方法去获取代理对象
newProxyInstance参数:类加载器:(代理对象的类型,也就是目标对象的)、 目标对象所实现的接口:(返回是一个数组,因为一个类可以实现多个接口嘛)、 执行器:
③通过②里面的代理对象去调用目标对象的方法

注意代码中的注释!!

4.2.2 CJLIB动态代理

cglib代理模式是生成一个子类去完成的,并且不需要去实现接口

  1. 导入依赖
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>
  1. 创建目标对象
@Repository //将此类定义成持久层,然后将其放入spring中,里面可以加value参数的,加了就是这个持久层的id,默认是类名首字母是小写
//@Component //这个注解就是直接将此类放入spring中,不会标记此类的类型,一般用于普通类要放入spring的
public class PetDaoImplCGLIB{
    public Pet add(String username, String password) {
        System.out.println("目标对象 petDao执行");
        return new Pet(username,password);
    }
}
  1. MethodInterceptor拦截器
    cglib的实现最重要的就是去实现这个MethodInterceptor接口来完成,就像jdk时候的InvocationHandler一样
//拦截器去完成,记得是cglib包下面的哈
public class PetDaoImplCGLIBIntercepter implements MethodInterceptor {
    /*
        intercept参数介绍:
        Object:目标对象
         Method:目标对象的方法
         Object:目标对象方法的参数
         MethodProxy:代理方法
      */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("cglib代理增强1.....");
        //调用目标对象执行方法
        Object o1 = methodProxy.invokeSuper(o, objects);
        System.out.println("cglib代理增强2.....");
        return o1;
    }
}

4. 测试
cglib的原理就是在运行的时候,cglib会自动去生成一个子类,在子类里面去完成增强操作(就是拦截器里面),这里我们来验证cglib就用保存它的子类来查验,也就是将它自动生成的类放在一个指定路径下去看。
在这里插入图片描述
编写cglib的测试类

 @Test
    public void testCGLIB(){
        //设置生成动态代理类的路径
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"E:\\xiaowang\\kuangjia\\spring\\cglib存放位置");
        //创建Enhancer对象,就相当于jdk中的proxy类一样的,作用就是去执行方法的啦~
        Enhancer enhancer = new Enhancer();
        //设置代理类的目标对象
        enhancer.setSuperclass(PetDaoImplCGLIB.class);
        //创建拦截器
        PetDaoImplCGLIBIntercepter petDaoImplCGLIBIntercepter = new PetDaoImplCGLIBIntercepter();
        //设置回调函数 设置增强对象
        enhancer.setCallback(petDaoImplCGLIBIntercepter);
        //创建代理对象,类型是目标对象类型哈
        PetDaoImplCGLIB o = (PetDaoImplCGLIB) enhancer.create();
        //执行方法
        Pet zhangsan = o.add("zhangsan", "123456");
        System.out.println(zhangsan);
    }

执行结果:
在这里插入图片描述
也执行出来了呀
在这里插入图片描述
注意:我们可以将生成的类反编译一下,就可以看到生成的类继承了我们的目标类,所以可以说明,cglib的原理其实就是cglib在底层继承了目标类然后去实现增强的
在这里插入图片描述

cglib动态代理原理:
①创建拦截器:继承MethodInterceptor的 intercepter的类,在拦截器中重写intercerpt( )方法,就是增强+目标对象的方法调用,返回拦截器

②在测试这边创建一个类似proxy的子类对象enhancer,然后设置这个代理对象的类型(setSuperclass(目标对象的类型.class完成))

③创建一个拦截器,enhancer通过去设置回调函数(setCallback(拦截器))

④创建代理对象enhancer.create(),代理对象的类型要和目标对象一致哈,然后通过代理对象去完成方法的调用

1.2.3 JDK和CGLIB区别
  1. JDK代理是基于有接口的情况下,而CGLIB没有接口也可以实现
  2. jdk是通过java的反射包的三个类(InvocationHandler、Method、Proxy)来完成的,而cglib是只针对类来实现的,原理就是对指定的目标类生成一个子类,并覆盖目标类的方法实现增强。
  3. cglib底层是采用了ASM字节码生成框架,使用字节码技术生成代理类,比使用java的反射效率要高一点
  4. cglib动态代理采用的是继承后重写,所以不能对final修饰的类进行代理,

1.3 静态动态代理区别

静态代理就是自己需要手动去写一个代理对象,实现目标对象所实现的接口,而动态代理却简化了这一步,不需要自己去写代理对象,而是在java编译的时候,通过反射完成了对象的创建。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值