java动态代理--proxy&cglib

大纲

  1. 代理
  2. proxy
  3. cglib
  4. 小结

 

一、代理

为什么要用代理?其实就是希望不修改对象的情况下,增强对象。

静态代理:

  • 静态代理模式,需要代理类和目标类实现同一接口,代理类的方法调用目标类的方法,调用方法的前后实现需要增强的逻辑。
  • 静态代理有一个问题就是,每个代理类和目标类一一对应,需要代理的类多的情况,需要大量的代理类,难以维护。

动态代理:

  • 动态代理就是运行时动态生成的类,并不是在编译时期。
  • 动态代理有两种不同的方式,一种是jdk反射包下的的Prxoy,一种是cglib。

 

二、Proxy

Proxy生成代理对象需要目标对象实现一至少一个接口。

Proxy通过反射实现动态代理。

生成代理对象需要调用Proxy中newProxyInstance方法。

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

loader-目标对象的classloader

interfaces-目标对象实现的接口

InvocationHandler-处理器,当目标对象接口中的方法被调用时处理器中invoke方法会被调用从而实现动态代理

 

在看下InvocationHandler

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

proxy-代理对象
method-被调用的方法
args-调用方法时传入的参数
invoke返回值为代理方法的返回值

测试:

接口:

public interface Speak {
    String say(String content);
}

目标类:


import lombok.Data;

@Data public class Person implements Speak{ private String name; private int age; public String say(String content){ System.out.println("hi"+name+age+"content:"+content); return "say return"; } }

代理工厂:

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


public class ProxyFactory{
    //维护一个目标对象
    private Object target;
    public ProxyFactory(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 {
                        System.out.println("before method:"+method.getName());
                        //执行目标对象方法,返回return值。
                        Object returnValue = method.invoke(target, args);
                        System.out.println("end method:"+method.getName());
                        return returnValue;
                    }
                });
    }
}

代理对象在代理方法调用前后打了一行字。

public static void main(String[] args) {
        Person p = new Person();
        p.setAge(11);
        p.setName("xx");
        ProxyFactory factory = new ProxyFactory(p);
        Object proxyInstance = factory.getProxyInstance();
        Speak speak = (Speak) proxyInstance;
        String returnValue = speak.say("haha");
        System.out.println("returnValue:"+returnValue);
    }

 

三、cglib

静态代理和都必须实现接口,而cglib没有这个限制,cglib通过字节码操作动态生成子类,因此目标类不能被final修饰。

与proxy类似的我们也需要复写一个处理器

public interface MethodInterceptor extends Callback {
    Object  (Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable;
}

proxy-代理对象
method-被调用的方法
methodProxy-代理方法(具体本人不是特别清楚)
intercept返回值为代理方法的返回值

 

测试:

重写Person不需要实现接口

import lombok.Data;

@Data
public class Person{
    private String name;
    private int age;
    public String say(String content){
        System.out.println("hi"+name+age+"content:"+content);
        return "say return";
    }
}

代理工厂:

import org.assertj.core.internal.cglib.proxy.Enhancer;
import org.assertj.core.internal.cglib.proxy.MethodInterceptor;
import org.assertj.core.internal.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CgProxyFactory<T> {
    //维护目标对象
    private T target;
    public CgProxyFactory(T target) {
        this.target = target;
    }

    //获取代理
    public T getProxyInstance() {
        Enhancer en = new Enhancer();
        en.setSuperclass(this.target.getClass());
        //设置拦截器
        en.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("before method:"+method.getName());
                Object returnValue = method.invoke(target, objects);
                System.out.println("end method:"+method.getName());
                return returnValue;
            }
        });
        return (T) en.create();
    }
}

 

和之前的代理对象一样,在代理方法调用前后打了一行字。

public static void main(String[] args) {
        Person p = new Person();
        p.setAge(11);
        p.setName("xx");
        CgProxyFactory<Person> factory = new CgProxyFactory(p);
        Person proxyInstance = factory.getProxyInstance();
        String returnValue = proxyInstance.say("cg");
        System.out.println("returnValue:" + returnValue);
    }

 

四、小结:

  1. Proxy需要代理类实现接口,底层为反射。
  2. Cglib代理对象不能被final修饰,底层是字节码操作。
  3. spring会根据目标类是否实现接口的情况,切换动态代理的模式,也可以通过配置强制使用cglib。

 

上一篇:Java 动态代理


下一篇:java – Spring – 不能强制使用CGLIB代理作为Classcast异常