参考文章:https://www.cnblogs.com/Pjson/p/8783284.html
JDK动态代理步骤
1.创建一个实现InvocationHandler接口的类,它必须实现invoke()方法
2. 创建被代理的类及接口
3. 调用Proxy的静态方法,创建一个代理类
4.通过代理调用方法
根据以上步骤
1,创建一个工厂类,思路:通过该工厂类将一个目标对象生成目标代理类,这里通过proxy对象的newProxyInstance的方法来生成代理对象
JDK中生成代理对象的AP代理类所在包:java.lang.reflect.Proxy
JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:
ClassLoader loader,:指定当前目标对象使用类加载器,获取加载器的方法是固定的
Class<?>[] interfaces,:目标对象实现的接口的类型,使用泛型方式确认类型
InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
package com.skindow.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* Created by Administrator on 2019/7/30.
*/
public class MyJDKProxyFactory {
/**
* 维护一个目标对象
*/
private Object target;
public MyJDKProxyFactory (Object target)
{
this.target = target;
}
/**Proxy.newProxyInstance注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:
ClassLoader loader,:指定当前目标对象使用类加载器,获取加载器的方法是固定的
Class<?>[] interfaces,:目标对象实现的接口的类型,使用泛型方式确认类型
InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
* @return 通过目标对象创建代理对象
*/
public Object getProxy(){
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("开始记录时间");
long begin = System.currentTimeMillis();
Object invoke = method.invoke(target, args);
long end = System.currentTimeMillis();
System.out.println(target.getClass().getName() + " 总耗时:" + (end - begin ) + "ms");
return invoke;
}
});
}
}
2,因为jdk动态代理是通过接口来调用的,所以这里我们新建一个接口类
package com.skindow.forInterface;
/**
* Created by Administrator on 2019/7/30.
*/
public interface For {
public void doSomething();
}
3,新建类来实现该接口类
package com.skindow.forInterface;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Administrator on 2019/7/30.
*/
public class ForImpl_forTest_1 implements For{
private Integer size;
public ForImpl_forTest_1(Integer size)
{
this.size = size;
}
@Override
public void doSomething() {
List<Integer> list = new ArrayList<>();
for (Integer i = 0; i < size; i++)
{
list.add(i);
}
}
}
package com.skindow.forInterface;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Administrator on 2019/7/30.
*/
public class ForImpl_forTest_2 implements For{
private Integer size;
public ForImpl_forTest_2(Integer size)
{
this.size = size;
}
@Override
public void doSomething() {
List<Integer> list = new ArrayList<>(size);
for (Integer i = 0; i < size; i++)
{
list.add(i);
}
}
}
4,测试
package com.skindow.mainTest;
import com.skindow.forInterface.For;
import com.skindow.forInterface.ForImpl_forTest_1;
import com.skindow.forInterface.ForImpl_forTest_2;
import com.skindow.proxy.MyJDKProxyFactory;
/**
* Created by Administrator on 2019/7/30.
*/
public class ForTestMain {
public static void main(String[] args)
{
Integer size = 1000000;
// 目标对象
For for_1 = new ForImpl_forTest_1(size);
// 给目标对象创建代理对象
for_1 =(For) new MyJDKProxyFactory(for_1).getProxy();
//通过代理对象执行目标对象的方法
for_1.doSomething();
// 目标对象
For for_2 = new ForImpl_forTest_2(size);
// 给目标对象创建代理对象
for_1 =(For) new MyJDKProxyFactory(for_2).getProxy();
//通过代理对象执行目标对象的方法
for_1.doSomething();
}
}
测试结果如下
总结如下
- 通过控制台的打印可以看到有参和无参的性能差异,相差接近90ms 这里需要着重讲一下
- 在测试的时候发生了以下错误,错误原因是我并没有用接口去调用而是使用实现类去调用,结果导致了以下错误。切忌一定要用接口去调用
错误代码如下
package com.skindow.mainTest;
import com.skindow.forInterface.ForImpl_forTest_1;
import com.skindow.proxy.MyJDKProxyFactory;
/**
* Created by Administrator on 2019/7/30.
*/
public class ForTestMain {
public static void main(String[] args)
{
// 目标对象
ForImpl_forTest_1 forTest_1 = new ForImpl_forTest_1(Integer.MAX_VALUE);
// 给目标对象创建代理对象
forTest_1 =(ForImpl_forTest_1) new MyJDKProxyFactory(forTest_1).getProxy();
//通过代理对象执行目标对象的方法
forTest_1.doSomething();
}
}
cglib动态代理
- Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.
-
JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现.
-
Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring
AOP和synaop,为他们提供方法的interception(拦截) -
Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.
1,创建一个工厂类,通过这个工厂类将目标对象来生成对应的代理类,该工厂类必须实现MethodInterceptor接口
package com.skindow.proxy;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* Created by Administrator on 2019/7/30.
*/
public class MyCglibProxyFactory implements MethodInterceptor {
private Object target;
public MyCglibProxyFactory (Object target)
{
this.target = target;
}
//给目标对象创建一个代理对象
public Object getProxyInstance(){
//1.工具类
Enhancer en = new Enhancer();
//2.设置父类
en.setSuperclass(target.getClass());
//3.设置回调函数
en.setCallback(this);
//4.创建子类(代理对象)
return en.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("开始记录时间");
long begin = System.currentTimeMillis();
//执行目标对象的方法
Object returnValue = method.invoke(target, objects);
long end = System.currentTimeMillis();
System.out.println(target.getClass().getName() + " 总耗时:" + (end - begin ) + "ms");
return returnValue;
}
}
2,测试
ForImpl_forTest_1 forTest1 = new ForImpl_forTest_1(size);
forTest1 = (ForImpl_forTest_1)new MyCglibProxyFactory(forTest1).getProxyInstance();
forTest1.doSomething();
3,测试结果
总结
-
相比较jdk动态代理,不用接口去调用实现类的方法,而直接以实现类去调用
-
在期间遇到了一个错误
Exception in thread “main” java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given
解决方案是在ForImpl_forTest_1类种添加无参的构造函数如下
-
**从以下控制台打印的日志能看出cglib比jdk动态代理的性能更高