和我一起迎接明天的太阳吧

klaus08

焦虑源于行动的匮乏

Java中动态代理

场景

有一个业务功能接口,接口中有两个业务方法,创建对应的实现类。

package com.klaus.service;

public interface MyService {
    public void service1();
    public void service2();
}
package com.klaus.service;

public class MyServiceImpl implements MyService {
    @Override
    public void service1() {
        System.out.println("业务方法一");
    }

    @Override
    public void service2() {
        System.out.println("业务方法二");
    }
}

此时业务功能正常

package com.klaus;

import com.klaus.service.MyService;
import com.klaus.service.MyServiceImpl;

public class main {
    public static void main(String[] args) {
        MyService myService = new MyServiceImpl();
        myService.service1(); //业务方法一
        myService.service2(); //业务方法二
    }
}

新增功能

此时新增要求,在每个业务执行前打印日志,业务执行后进行事务提交。


解决方法

一、源码更改

第一种很简单,直接在原业务方法上更改。这种方式看起来很简单,但却在业务类中出现了大量重复的、与业务无直接关联的冗余代码。

package com.klaus.service;

public class MyServiceImpl implements MyService {
    @Override
    public void service1() {
        System.out.println("日志信息");
        System.out.println("业务方法一");
        System.out.println("提交事务");
    }

    @Override
    public void service2() {
        System.out.println("日志信息");
        System.out.println("业务方法二");
        System.out.println("提交事务");
    }
}

二、代理类

可以新建一个第三方类,在这个类中呢,既有业务对象,又有新增业务。执行业务的时候不直接在 MyServiceImpl 中调用业务方法,而是首先在第三方类中先调用日志方法(因为会多次用到,所以可以新建工具类吧把新增的日志功能、提交事务功能编成方法加进去);然后通过第三方类中的业务对象调用调用业务方法;最后再在第三方类中调用提交事务的方法。这样就可以实现新的功能,并且不在原来的业务代码上进行改动。新建的这个第三方类称作代理类(自己什么都没有,像是一个中介)。具体示例如下

代理类

package com.klaus;

import com.klaus.service.MyServiceImpl;
import com.klaus.util.ServiceTools;

public class Test {

    MyServiceImpl service;

    public Test(MyServiceImpl service) {
        this.service = service;
    }

    public void service1(){
        ServiceTools.log();
        service.service1();
        ServiceTools.trans();
    }

    public void service2(){
        ServiceTools.log();
        service.service2();
        ServiceTools.trans();
    }
}

测试类

package com.klaus;

import com.klaus.service.MyService;
import com.klaus.service.MyServiceImpl;

public class main {
    public static void main(String[] args) {
        Test test = new Test(new MyServiceImpl());
        test.service1();
        //Thu Aug 12 11:45:42 CST 2021
        //业务方法一
        //提交事务
        System.out.println("++++++++++++++++++++");
        //++++++++++++++++++++
        test.service2();
        //Thu Aug 12 11:45:42 CST 2021
        //业务方法二
        //提交事务
    }
}

三、动态代理

上面的代码是我自己手写的,但是在 Java 中,可以基于 JDK 实现动态代理。这里代理类是MyProxy.java,或者说代理对象是 MyProxy 中的 proxy。JDK 中并没由把代理功能全放在一起实现,而是把方法执行的功能呢交给MyInvocationHandler 做的是接收传递来的方法并处理。


主要方法:

  1. Proxy.newProxyInstance()。第一个参数 loader 是目标类(要被代理的类)的类加载器。
    第二个参数 interfaces 是目标类实现的接口。
    第三个参数 h 是代理对象要执行的功能。
    前两个参数都是通过反射得到的。通过代理执行方法,会调用 h 中的 invoke() 方法。

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
    
  2. Method.invoke()。第一个参数 obj 是 要执行的方法所在的类。

    ​ 第二个参数 args 是方法的参数。

    public Object invoke(Object obj, Object... args)
    

    就是和反射一模一样的思想。反射的讲述可以参考这里

MyInvocationHandler.java

package com.klaus.handler;

import com.klaus.util.ServiceTools;

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

public class MyInvocationHandler implements InvocationHandler {

    private Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        ServiceTools.log();

        Object res = method.invoke(target, args);

        ServiceTools.trans();

        return res;
    }
}

MyProxy.java

package com.klaus.proxy;

import com.klaus.handler.MyInvocationHandler;
import com.klaus.service.MyService;
import com.klaus.service.MyServiceImpl;

import java.lang.reflect.Proxy;

public class MyProxy {
    public static void main(String[] args) {
        MyService service = new MyServiceImpl();

        MyInvocationHandler handler = new MyInvocationHandler(service);

        MyService proxy =(MyService) Proxy.newProxyInstance(service.getClass().getClassLoader(),
                service.getClass().getInterfaces(),
                handler);


        proxy.service1();
        //Thu Aug 12 11:57:34 CST 2021
        //业务方法一
        //提交事务
        System.out.println("++++++++++++++++++++++");
        //++++++++++++++++++++++
        proxy.service2();
        //Thu Aug 12 11:57:34 CST 2021
        //业务方法二
        //提交事务

    }
}

最后是我的

posted @ 2021-08-12 12:11  klaus08  阅读(42)  评论(0编辑  收藏  举报