动态代理

74 阅读5分钟

基于JDK的动态代理

基于jdk的动态代理需要代理类和被代理类都实现同一个接口才行。

接口:


public interface star {
    String sing(String name);
    void dance();
}

被代理类:

public class Bigstar implements star{
    private String name;


    @Override
    public String sing(String name) {
        System.out.println(this.name + "正在唱"+ name);
        return "谢谢";
    }

    @Override
    public void dance() {
        System.out.println(this.name + "正在跳舞");
    }

    public Bigstar(String name) {
        this.name = name;
    }
}

生成代理类的工具类:

//使用了反射相关的包,所以代理的本质是反射
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class proxyUtil {
    public static star createProxy(Bigstar bigstar){
        //创建代理实例返回的对象是一个Object对象,使用时要类型转换,同时要传入三个参数才行
//        public static Object newProxyInstance(ClassLoader loader, 指定类加载器
//                Class<?>[] interfaces, 被代理类实现的接口,指定生成的代理的接口,传入的是数组
//                InvocationHandler h)指定生成的代理对象要做什么
//        一个是代理类的类加载器,
//                一个是实现的所有接口数组,
//                一个调用处理器的实现类.

        star starproxy = (star) Proxy.newProxyInstance(proxyUtil.class.getClassLoader(), new Class[]{star.class},
                new InvocationHandler() {
                    @Override//是一种回调方法
                    //在调用的时候会自动传参。参数:代理类,方法,参数列表
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //代理的功能在这里写

                        if(method.getName().equals("sing")){
                            System.out.println("唱歌收费100");
                        }else if (method.getName().equals("dance")){
                            System.out.println("场地费1000");
                        }
                        method.invoke(bigstar,args);

                        return null;
                    }
                });
        return starproxy;
    }
}

测试类

public class test {
    public static void main(String[] args) {
        Bigstar bigstar = new Bigstar("A");

        star proxystar = proxyUtil.createProxy(bigstar);

        proxystar.dance();
        proxystar.sing("asd");
    }
}

结果:
场地费1000
A正在跳舞
唱歌收费100
A正在唱asd

cglib动态代理

cglib动态代理不同于jdk动态代理,他不需要代理对象实现接口就能实现方法。他是基于重新来实现方法的代理的。

cglib的实现要导入包: 导入包:

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

被代理类:

public class UserServiceImpl {


    public void say() {
        System.out.println("hello");
        say2();
    }


    public void say2() {
        System.out.println("hello2");
        finalMethod();

    }

    public final void finalMethod() {
        System.out.println("final method");
    }

    public static void staticMethod() {
        System.out.println("static method");
    }

}

功能增强的实现类:

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

import java.lang.reflect.Method;

public class methodInterceptor implements MethodInterceptor {

    private Object target;

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

    @Override
    //参数:代理的对象,方法,参数,方法代理
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("cglib 方法调用前的增强");
        // 调用代理类FastClass对象
        Object result =  methodProxy.invokeSuper(o, objects);
//        Object result = methodProxy.invoke(target, objects);
        System.out.println("cglib 方法调用后的增强");
        return result;

    }
}

测试类:

import net.sf.cglib.proxy.Enhancer;

public class cgtest {
    public static void main(String[] args) {
        UserServiceImpl target = new UserServiceImpl();
        //enhancer类,类似于jdk代理中的Proxy类
        Enhancer enhancer = new Enhancer();
        //设置代理类,基于继承所以是设置父类
        enhancer.setSuperclass(UserServiceImpl.class);
        // 设置enhancer的回调对象,使用哪种代理的内容实现,也就是用哪种增强方法
        enhancer.setCallback(new methodInterceptor(target));
        // 创建代理对象
        UserServiceImpl userService = (UserServiceImpl)enhancer.create();
        // 通过代理对象调用目标方法
        userService.say();
        userService.finalMethod();
        UserServiceImpl.staticMethod();


    }

}


结果:
cglib 方法调用前的增强
hello
cglib 方法调用前的增强
hello2
final method
cglib 方法调用后的增强
cglib 方法调用后的增强
final method
static method

Process finished with exit code 0

从结果中看出,普通的方法被成功代理了,而final修饰的和static修饰的都没有被重写代理。而普通方法say()在内部调用say2()也被代理增强了。

静态代理

public class sProxy {
    public static void main(String[] args) {
        //创建代售点对象
        ProxyPoint proxyPoint = new ProxyPoint();
        //调用方法进行买票
        proxyPoint.sell();
    }
}

interface SellTickets {
    /**
     * 卖票方法
     */
    void sell();
}

class TrainStation implements SellTickets {

    @Override
    public void sell() {
        System.out.println("火车站卖票......");
    }
}

class ProxyPoint implements SellTickets {

    private TrainStation station = new TrainStation();

    @Override
    public void sell() {
        System.out.println("代售点收取一些费用......");
        station.sell();
    }
}

比较

静态代理和动态代理

静态代理:从上面我们可以看到,使用静态代理的话每要实现一个代理方法的话就要创建一个代理类实现,不够灵活。当需要代理的对象有很多时,就需要创建很多的代理类,降低了程序可维护性。所以有了动态代理。
静态代理是静态的,代理类由自己手动创建,在编译期就已经生成,并且代理的目标类是确定的,也就是由代码写死的,一旦运行就不能改变。

动态代理:动态代理的实现从上面中看出它的实现不需要指定方法就能执行,创建代理类后调用方法就能实现。所以比动态代理更加灵活。
与静态代理不同,动态代理的代理类运行期才生成,其代理的目标类也会根据传入的参数的改变而改变,也就是说,同一份代码,可以代理多个类(当然生成的代理类也不同),减少了代码的冗余

jdk和cglib

1、Jdk动态代理:利用拦截器(必须实现InvocationHandler接口)加上反射机制生成一个代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理

2、 Cglib动态代理:利用ASM框架,对代理对象类生成的class文件加载进来,通过修改其字节码生成子类来进行代理

所以:

  • 如果想要实现JDK动态代理那么代理类必须实现接口,否则不能使用;
  • 如果想要使用CGlib动态代理,那么代理类不能使用final修饰类和方法;

具体的应用就在spring中。spring AOP的实现就基于动态代理。 1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理
2、如果目标对象实现了接口,也可以强制使用CGLIB
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换