静态代理

  静态代理在不修改目标对象的情况下,通过代理对象,对目标对象的功能进行增强。下面举个例子。已有ApiService和实现类ApiServiceImpl,现在想对业务功能扩展:统计保存的时间、记录查询的日志,但是不能改原来的方法。于是新增代理类ApiServiceProxy。

package demo.proxy;

public interface ApiService {
    
    void save(String arg1, String arg2);
    
    String query(String arg1);
    
}
package demo.proxy;

public class ApiServiceImpl implements ApiService {
    
    @Override
    public void save(String arg1, String arg2) {
        String args = arg1 + arg2;
        System.out.println("执行保存操作,参数:" + args);
    }

    @Override
    public String query(String arg1) {
        System.out.println("执行查询操作,参数:" + arg1);
        return "success";
    }
}
package demo.proxy;

public class ApiServiceProxy implements ApiService {

    private ApiService apiService;
    
    public ApiServiceProxy(ApiService apiService){
        this.apiService = apiService;
    }
    
    @Override
    public void save(String arg1, String arg2) {
        long t1 = System.currentTimeMillis();
        apiService.save(arg1, arg2);
        long t2 = System.currentTimeMillis();
        System.out.println("save方法执行毫秒数:" + (t2 - t1));
    }

    @Override
    public String query(String arg1) {
        String result = apiService.query(arg1);
        System.out.println("记录安审日志");
        return result;
    }
}
package demo.proxy;

public class Client1 {

    public static void main(String[] args) {
        ApiServiceImpl apiServiceImpl = new ApiServiceImpl();
        ApiServiceProxy proxy = new ApiServiceProxy(apiServiceImpl);
        proxy.save("arg1", "arg2");
        proxy.query("arg1");
    }
}

  通过静态代理,能够实现要求:扩展功能,不改原来的业务代码,实现业务增强。但是倘若不仅仅ApiServiceImpl需要记录日志和统计时间,其他的XXXServiceImpl也要扩展业务,程序里就有大量的代理类存在,如果Service发生改变,对应的Proxy也要变,因为Proxy实现了Service。究其根本,是因为静态代理是在编译期,就确定了代理类与被代理类的关系,如果能在运行期,动态的生成代理类,那就解决了大量代理类存在的问题,也解决了当Service改变Proxy代码也随之改变的问题。

JDK代理

  JDK代理能够解决静态代理的痛点,避免大量的代理类。

  在JDK代理中,代理对象不需要实现接口,但是目标对象一定要实现接口。其原理是通过反射机制,动态的生成目标对象的代理对象。下面来代码示例。

package demo.proxy2;

public interface FunctionService {
    
    String query(String arg1);
}
package demo.proxy2;

public class FunctionServiceImpl implements FunctionService {

    @Override
    public String query(String arg1) {
        System.out.println("执行query业务方法,参数:" + arg1);
        return "success";
    }
}
package demo.proxy2;

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

public class LogProxyFactory {
    
    private Object target;
    
    public LogProxyFactory(Object target){
        this.target = target;
    }
    
    public Object getProxyInstance(){
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args)
                            throws Throwable {
                        Object result = method.invoke(target, args);
                        System.out.println("记录日志,执行了" + method.getName() + "方法");
                        return result;
                    }
                });
    }
}
package demo.proxy2;

public class Client2 {
    public static void main(String[] args) {
        FunctionServiceImpl functionServiceImpl = new FunctionServiceImpl();
        FunctionService func = (FunctionService) new LogProxyFactory(functionServiceImpl).getProxyInstance();
//这里只能用FunctionService来接收,而不能用实现类接收,因为创建的代理对象和实现类的对象,二者是兄弟关系,就好比ArrayList不能转LinkedList。
        func.query("sa");
    }
}

  为什么JDK代理需要目标类实现接口呢?JDK代理的实现原理是什么呢?

  首先,我们来复盘一下,JDK代理都经历了哪些步骤。

    1.创建一个真实业务对象FunctionServiceImpl和LogProxyFactory对象。

    2.把FunctionServiceImpl对象交给LogProxyFactory对象。

    3.调用LogProxyFactory对象的getProxyInstance方法,拿到代理对象。在这个步骤中,通过Proxy类的newProxyInstance方法,我们拿到了代理对象。从表面上看,newProxyInstance方法的第一个参数,是ClassLoader,这个参数的实际意义是:生成代理对象使用的ClassLoader,一般我们就传入被代理对象的ClassLoader,我们创建代理对象,需要代理类加载,类加载需要类加载器。第二个参数,是Class<?>[],这个参数的实际意义是:被代理对象实现的接口,因为Java可以实现多个接口,因此这里是数组格式。讲到这里先停一下,为什么要传这2个参数?因为通过ClassLoader和代理类接口数组,就已经可以利用反射机制,动态的生成代理对象了。因为已知被代理类实现的接口信息,就能知道代理类需要实现的方法。换句话说,如果没有第三个参数,Proxy类的newProxyInstance方法创建出来的代理对象就是一个空壳。

class $ProxyN implements Interface1, Interface2{
    public void foo1(){
        //接口1定义的方法
    }
    public void foo2(){
        //接口2定义的方法
    }
}

    接着说第三个参数InvocationHandler。这是一个接口,里面就一个方法invoke(Object proxy, Method method, Object[] args),假设已经动态创建了代理对象,在调用代理对象实现的接口方法时,这个代理对象究竟做什么活,就是由invoke来控制的。假设在LogProxyFactory类中的InvocationHandler匿名类里,我们不用method.invoke(target, args);,而仅仅是System.out.println一句话,那么调用任何代理对象实现的接口方法,都仅仅是System.out.println一句话。method.invoke(target, args)是指:当我们调用代理对象实现的接口里面的某个方法时,实际调用target对象的该方法。

    4.把代理对象强制转型为FunctionService,并调用FunctionService接口中的方法,成功实现业务增强。


不用这个账号了
1 声望0 粉丝