静态代理和动态代理简单实现

静态代理:

静态代理的实现要求:真实角色,代理角色;真实角色和代理角色要实现同一个接口,代理角色要持有真实角色的引用。

  在Java中线程的设计就使用了静态代理设计模式,其中自定义线程类实现Runable接口,Thread类也实现了Runalbe接口,

  在创建子线程的时候,传入了自定义线程类的引用,再通过调用start()方法,调用自定义线程对象的run()方法。实现了线程的并发执行。

 

需求:在被代理类执行方法的时候,能够打印日志信息,比如当前的时间节点

1、业务方法,被代理类和代理类都要实现这个接口:

public interface LogService {
    void logTime();
}

2、被代理类

public class BeProxy implements LogService{
    public void logTime() {
        System.out.println("被代理类执行了~");
    }
}

3、代理类:代理角色实现被代理类需要的功能。

public class Proxy implements LogService {
    // 持有代理角色的引用
    private BeProxy beProxy;

    public Proxy() {
    }

    public Proxy(BeProxy beProxy) {
        this.beProxy = beProxy;
    }

    public void logTime() {
        System.out.println(new SimpleDateFormat("HH:mm:ss SSS").format(new Date(System.currentTimeMillis())) + " 代理对象开始执行");
        // 执行的是被代理角色的功能
        beProxy.logTime();
        System.out.println(new SimpleDateFormat("HH:mm:ss SSS").format(new Date(System.currentTimeMillis())) + " 代理对象结束执行");
    }
}

4、测试

public class Test {
    public static void main(String[] args) {
        // 被代理对象
        BeProxy beProxy = new BeProxy();

        // 代理者对象
        Proxy proxy = new Proxy(beProxy);

        proxy.logTime();
    }
}

5、结果:

00:48:21 119 代理对象开始执行
被代理类执行了~
00:48:21 121 代理对象结束执行

静态代理的问题:

上面的代码中,为了给被代理类类做日志增强,我们编写了代理类,而且准备了一个构造器接收目标对象。

代理对象构造器的参数类型是LogService,这意味着它只能接受LogService 的实现类对象,亦即我们写的代理类Proxy只能给LogService做代理,它们绑定死了!

如果现在我们系统需要全面改造,要给其他类也添加日志打印功能,就得为其他几百个接口都各自写一份代理类...

动态代理案例一(JDK)

动态代理和静态代理角色一样
态代理的代理类是动态生成的,不是我们直接写好的!
动态代理分为两大类:基于接口的动态代理,基于类的动态代理

  • 基于接口--- JDK动态代理 [我们在这里使用]
  • 基于类: cglib
  • java字节码实现 : javasist

需要了解两个类:

java.lang.reflect.Proxy; 代理,
    static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 
          返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。 

java.lang.reflect.InvocationHandler: 调用处理程序
    Object invoke(Object proxy, Method method, Object[] args) 
              在代理实例上处理方法调用并返回结果。

动态代理具体步骤:

  1. 通过实现 InvocationHandler 接口创建自己的调用处理器;
  2. 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
  3. 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
  4. 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

 

1、业务方法

public interface LogService {
    void logTime();
}

2、被代理类

public class BeProxy implements LogService{
    @Override
    public void logTime() {
        System.out.println("被代理类执行了~");
    }
}

3、处理程序类,可以自动生成代理类

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

// 处理程序类,用这个类可以自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
    // 被代理的接口
    private LogService logService;

    public void setLogService(LogService logService) {
        this.logService = logService;
    }

    // 生成得到代理类
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),logService.getClass().getInterfaces(),this);
    }

    @Override
    /**处理代理实例,并返回结果*/
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object invoke = method.invoke(logService, args);
        return invoke;
    }
}

4、测试类

public class Test {
    public static void main(String[] args) {
        // 真实角色
        BeProxy proxy = new BeProxy();
        // 代理角色【不存在】
        ProxyInvocationHandler handler = new ProxyInvocationHandler();
        // 设置要代理的对象
        handler.setLogService(proxy);
        // 生成代理类对象
        LogService logService = (LogService) handler.getProxy();
        // 执行角色方法
        logService.logTime();
    }
}

5、结果

被代理类执行了~

动态代理案例二(JDK)

需求:

需求:
    有一个service接口,调用这个接口中的方法:
        调用的方法是doSome-->在方法开始前打印系统当前时间,以及在最后打印一句话
        调用的方法是doOther-->就正常输出

 

1、在开始和结束要输出的两句话(进行一个简单封装)

 1 import java.text.SimpleDateFormat;
 2 import java.util.Date;
 3 
 4 /**
 5  * @author zhangzhixi
 6  */
 7 public class Utils {
 8     public static void currentTime() {
 9         SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
10         String showTime = sdf.format(new Date());
11         System.out.println("当前系统时间为:" + showTime);
12     }
13 
14     public static void executeTransaction(){
15         System.out.println("事务执行结束");
16     }
17 }

 2、业务接口:

1 public interface UserService {
2     void doSome();
3     void doOther();
4 }

3、业务接口的实现类:

 1 public class UserServiceImpl implements UserService {
 2     @Override
 3     public void doSome() {
 4         System.out.println("doSome方法执行!~");
 5     }
 6 
 7     @Override
 8     public void doOther() {
 9         System.out.println("doOther方法执行!~");
10     }
11 }

4、创建调用处理程序类

 1 package com.zhixi.invaction;
 2 
 3 import com.zhixi.service.UserService;
 4 import com.zhixi.util.Utils;
 5 
 6 import java.lang.reflect.InvocationHandler;
 7 import java.lang.reflect.Method;
 8 import java.lang.reflect.Proxy;
 9 
10 /**
11  * @author zhangzhixi
12  */
13 public class MyInvocation implements InvocationHandler {
14 
15     /**
16      * 被代理的接口
17      */
18     UserService userService;
19 
20     public void setUserService(UserService userService) {
21         this.userService = userService;
22     }
23 
24     public Object getProxy() {
25         // 生成得到代理类
26         return Proxy.newProxyInstance(this.getClass().getClassLoader(), userService.getClass().getInterfaces(), this);
27     }
28 
29 
30     /**
31      * @param proxy 代理对象
32      * @param method 代理的方法对象
33      * @param args 方法调用时参数
34      */
35     @Override
36     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
37         Object invoke = null;
38 
39         // 调用了doSome就执行这段业务
40         if ("doSome".equals(method.getName())) {
41             // 增加的业务
42             Utils.currentTime();
43 
44             /**
45              * userService: 被代理的类
46              * args: 参数
47              */
48             invoke = method.invoke(userService, args);
49 
50             // 增加的业务
51             Utils.executeTransaction();
52         } else {
53             //说明调用了doOther
54             invoke = method.invoke(userService, args);
55         }
56         return invoke;
57     }
58 }

5、测试类:

 1 public class MyTest {
 2     public static void main(String[] args) {
 3         // 创建目标对象
 4         UserService userService= new UserServiceImpl();
 5         // 代理对象【不存在】
 6         MyInvocation invocation = new MyInvocation();
 7         // 设置要代理的对象
 8         invocation.setUserService(userService);
 9         // 生成代理
10         UserService service = (UserService) invocation.getProxy();
11         // 调用对象的业务方法
12         service.doSome();
13         System.out.println("===============");
14         service.doOther();
15     }
16 }

6、测试结果:

当前系统时间为:2021年01月07日 21:56:41
doSome方法执行!~
事务执行结束
===============
doOther方法执行!~

动态代理案例三(Cglib)

  静态代理和 JDK 代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理,这就是 Cglib 代理

Cglib 代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展,有些书也将 Cglib 代理归属到动态代理。

Cglib 是一个强大的高性能的代码生成包,它可以在运行期扩展 java 类与实现 java 接口。它广泛的被许多 AOP 的框架使用,例如 Spring AOP,实现方法拦截。

在 AOP 编程中如何选择代理模式:

  • 目标对象需要实现接口,用 JDK 代理
  • 目标对象不需要实现接口,用 Cglib 代理

Cglib 包的底层是通过使用字节码处理框架 ASM 来转换字节码并生成新的类。
这里我们这边改造动态代理案例一项目,不实现接口了,直接写个三星硬盘作为目标类,然后使用Cglib来进行代理。

  • 1)在内存中动态构建子类,注意代理的类不能为 final,否则报错java.lang.IllegalArgumentException
  • 2)目标对象的方法如果为 final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法。

第一步:引入Cglib的jar包

https://files.cnblogs.com/files/zhangzhixi/%E9%9D%9EMaven%E9%A1%B9%E7%9B%AECglib%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86.zip?t=1688971967&download=true

第二步:创建目标类

/**
 * @author zhangzhixi
 * @version 1.0
 * @description 三星硬盘厂家类(目标类)
 * @date 2023-07-10 14:36
 */
public class SamsungHardDisk {
    public float sell(int amount) {
        System.out.println("目标类中,执行了sell目标方法");
        return 85.0f * amount;
    }
}

第三步:创建代理类,由代理进行加价

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * cglib动态代理工厂类
 */
public class ProxyFactory<T> implements MethodInterceptor {

    /**
     * 聚合目标对象
     */
    private T target = null;

    public ProxyFactory(T target) {
        this.target = target;
    }

    /**
     * 返回target的代理对象
     *
     * @return 代理对象
     */
    public T getProxyInstance() {
        //创建工具类
        Enhancer enhancer = new Enhancer();
        //设置父类
        enhancer.setSuperclass(target.getClass());
        //设置回调函数
        enhancer.setCallback(this);
        //返回代理对象
        return (T) enhancer.create();
    }

    /**
     * 重写intercept()方法,实现了对于目标对象的方法调用
     *
     * @param o           代理对象
     * @param method      目标对象的方法
     * @param objects     目标对象的方法参数
     * @param methodProxy 代理对象的方法
     * @return 目标对象的方法返回值
     * @throws Throwable 异常
     */
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("代理模式开始");
        Object res = method.invoke(target, objects);
        // 执行功能增强
        if (res != null) {
            // 商家手中的加价,1/个加价25
            float price = (float) res;
            price = price + (25 * (int) objects[0]);
            // 增加后的U盘价格
            res = price;
        }
        return res;
    }
}

第四步:测试

public class Client {
    public static void main(String[] args) {
        //目标对象
        SamsungHardDisk samsungHardDisk = new SamsungHardDisk();
        //代理工厂
        ProxyFactory<SamsungHardDisk> proxyFactory = new ProxyFactory<>(samsungHardDisk);
        //获取代理对象
        SamsungHardDisk proxyInstance = proxyFactory.getProxyInstance();
        // 执行目标方法
        float sell = proxyInstance.sell(2);
        System.out.println(sell);
    }
}

posted @ 2021-01-03 00:58  Java小白的搬砖路  阅读(297)  评论(0编辑  收藏  举报