013 Enhancer创建动态代理

  在上一章使用了Enhancer,没有仔细说明代理的问题,在这一章进行详细的说明。

  原本想使用cf包的,后来发现使用framework包下的包也没有问题,程序中就一直使用framework中的类。

一:概述

1.说明

  CGLIB是一个强大、高性能的字节码生成库,它用于在运行时扩展Java类和实现接口;

  本质上它是通过动态的生成一个子类去覆盖所要代理的类(非final修饰的类和方法)。

  Enhancer是一个非常重要的类,它允许为非接口类型创建一个JAVA代理,Enhancer动态的创建给定类的子类并且拦截代理类的所有的方法,和JDK动态代理不一样的是不管是接口还是类它都能正常工作。

2.回调接口

  net.sf.cglib.proxy.Callback接口:在cglib包中是一个很关键的接口,所有被net.sf.cglib.proxy.Enhancer类调用的回调(callback)接口都要继承这个接口
  net.sf.cglib.proxy.MethodInterceptor接口:   是通用的回调(callback)类型,他经常被AOP用来实现拦截(intercept)方法的调用;

 3.关于MethodInterceptor接口的源代码

  是Callback的子接口,所以,实现这个接口的类可以用于回调。

package org.aopalliance.intercept;

@FunctionalInterface
public interface MethodInterceptor extends Interceptor {
    Object invoke(MethodInvocation var1) throws Throwable;
}

二:示例

1.结构

  

2.对象

package com.jun.web.enhancer;

public class HelloWorld {
    public String say(boolean say) throws Exception {
        System.out.println("Hello Student");
        if(!say) {
            throw new Exception("回答错误!");
        }
        return "回答正确!";
    }
}

3.回调

package com.jun.web.enhancer;

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class HelloWorldInterceptor implements MethodInterceptor {
    /**
     *
     * 方法描述 当对基于代理的方法回调时,在调用原方法之前会调用该方法
     * 拦截对目标方法的调用
     *
     * @param obj 代理对象
     * @param method 拦截的方法
     * @param args 拦截的方法的参数
     * @param proxy 代理
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        Object result = null;
        try {
            //前置通知
            before();
            result = proxy.invokeSuper(obj, args);
            //后置通知
            after();
        } catch (Exception e) {
            //异常通知
            exception();
        } finally {
            //方法返回前通知
            beforeReturning();
        }

        return result;
    }

    private void before() {
        System.out.println("before method invoke...");
    }
    private void after() {
        System.out.println("after method invoke...");
    }
    private void exception() {
        System.out.println("exception method invoke...");
    }
    private void beforeReturning() {
        System.out.println("beforeReturning method invoke...");
    }
}

4.代理

package com.jun.web.enhancer;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

//创建Enhancer增强代理类的回调ProxyFactory类
public class ProxyFactory  {

    //要代理的真实对象
    private Object obj;

    public Object createProxy(Object target) {
        Enhancer enhancer = new Enhancer();
        //设置代理目标
        enhancer.setSuperclass(target.getClass());
        //设置单一回调对象,在调用中拦截对目标方法的调用
        enhancer.setCallback(new HelloWorldInterceptor());
        //设置类加载器
        enhancer.setClassLoader(target.getClass().getClassLoader());

        return enhancer.create();
    }

}

5.测试

package com.jun.web.enhancer;

public class EnhancerTest {
    public static void main(String[] args) throws Exception {
        //将要被代理的对象
        HelloWorld hello = new HelloWorld();
        //代理
        ProxyFactory proxy = new ProxyFactory();
        //
        HelloWorld world = (HelloWorld)proxy.createProxy(hello);
        String result = world.say(false);
        System.out.println(result);
    }
}

6.效果

Connected to the target VM, address: '127.0.0.1:60279', transport: 'socket'
before method invoke...
Hello Student
exception method invoke...
beforeReturning method invoke...
null
Disconnected from the target VM, address: '127.0.0.1:60279', transport: 'socket'

三:其他回调

1.FixedValue

  net.sf.cglib.proxy.FixedValue:为提高性能,FixedValue回调对强制某一特定方法返回固定值。

package com.jun.web.enhancer;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.FixedValue;

public class FixedValueTest {
    public static void main(String[] args) throws Exception {
        HelloWorld hello = new HelloWorld();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(hello.getClass());
        //设置单一回调对象,在调用中拦截对目标方法的调用
        enhancer.setCallback(new FixedValue() {
            @Override
            public Object loadObject() throws Exception {
                return "FixedValue";
            }
        });
        //设置类加载器
        enhancer.setClassLoader(hello.getClass().getClassLoader());

        HelloWorld world = (HelloWorld)enhancer.create();
        String result = world.say(false);
        System.out.println(result);
    }
}

2.效果

Connected to the target VM, address: '127.0.0.1:60379', transport: 'socket'
Disconnected from the target VM, address: '127.0.0.1:60379', transport: 'socket'
FixedValue

Process finished with exit code 0

3.NoOp

  net.sf.cglib.proxy.NoOp:NoOp回调把对方法调用直接委派到这个方法在父类中的实现(也可以理解成真实对象直接调用方法);

package com.jun.web.enhancer;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.NoOp;

public class NoopTest {
    public static void main(String[] args) throws Exception {
        HelloWorld hello = new HelloWorld();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(hello.getClass());
        enhancer.setCallback(NoOp.INSTANCE);
        enhancer.setClassLoader(hello.getClass().getClassLoader());
        HelloWorld obj = (HelloWorld)enhancer.create();
        System.out.println(obj.say(true));
    }
}

4.效果

Disconnected from the target VM, address: '127.0.0.1:60418', transport: 'socket'
Hello Student
回答正确!

Process finished with exit code 0

5.其他的回调

  net.sf.cglib.proxy.LazyLoader:当实际的对象需要延迟装载时,可以使用LazyLoader回调。一旦实际对象被装载,它将被每一个调用代理对象的方法使用;
  net.sf.cglib.proxy.Dispatcher:Dispathcer回调和LazyLoader回调有相同的特点,不同的是,当代理方法被调用时,装载对象的方法也总要被调用;
  net.sf.cglib.proxy.ProxyRefDispatcher:ProxyRefDispatcher回调和Dispatcher一样,不同的是,它可以把代理对象作为装载对象方法的一个参数传递;

四:CallbackFilter回调

1.说明

  net.sf.cglib.proxy.CallbackFilter允许我们在方法层设置回调(callback),根据我们对方法处理的需求设置不同的回调;如下有一个类Hello,里面有两个方法save和update,save方法需要做前置和后置处理,但是update方法不需要:

2.Hello

package com.jun.web.enhancer;

public class Hello {
    public String save() {
        System.out.println("save...");
        return "save";
    }
    public String update() {
        System.out.println("update...");
        return "update";
    }
}

3.

package com.jun.web.enhancer;




import org.springframework.cglib.proxy.CallbackFilter;

import java.lang.reflect.Method;

public class CallBackFilterTest implements CallbackFilter {
    /**
     * 方法返回的值是和callback回调接口数组一一对应的数组下标
     */
    @Override
    public int accept(Method method) {
        String name = method.getName();
        if("save".equals(name)) {
            return 0;
        }
        return 1;
    }
}

4.测试

package com.jun.web.enhancer;


import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.NoOp;

public class CallbackFilterMainTest {
    public static void main(String[] args) throws Exception {
        Hello hello = new Hello();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(hello.getClass());
        enhancer.setCallbackFilter(new CallBackFilterTest());
        //创建各个目标代理方法的回调,回调的顺序要与过滤器索引一致
        Callback[] callbacks = new Callback[] {new HelloWorldInterceptor(), NoOp.INSTANCE};
        //设置单一回调对象,在调用中拦截对目标方法的调用
        enhancer.setCallbacks(callbacks);
        Hello obj = (Hello)enhancer.create();

        System.out.println(obj.update());
        System.out.println("=============");
        System.out.println(obj.save());
    }
}

5.效果

Connected to the target VM, address: '127.0.0.1:60873', transport: 'socket'
update...
update
=============
before method invoke...
Disconnected from the target VM, address: '127.0.0.1:60873', transport: 'socket'
save...
after method invoke...
beforeReturning method invoke...
save

Process finished with exit code 0
原文地址:https://www.cnblogs.com/juncaoit/p/11401259.html