简单demo
定义一个usb接口
public interface USB{
void m1();
void m2();
void m3();
}
接口有2个实现类mouse fan鼠标风扇
package com.zzb.test;
public class Fan implements USB{
@Override
public void m1() {
System.out.println("fan m1");
}
@Override
public void m2() {
System.out.println("fan m2");
}
@Override
public void m3() {
System.out.println("fan m3");
}
}
package com.zzb.test;
public class Mouse implements USB{
@Override
public void m1() {
System.out.println("mouse m1");
}
@Override
public void m2() {
System.out.println("mouse m2");
}
@Override
public void m3() {
System.out.println("mouse m3");
}
}
定义一个代理类
package com.zzb.test;
public class USBProxy implements USB{
private USB target;
public USBProxy(USB target){
this.target = target;
}
@Override
public void m1() {
long starTime = System.nanoTime();
this.target.m1();
long endTime = System.nanoTime();
System.out.println(this.target.getClass() + ".m1()方法耗时(纳秒):" + (endTime - starTime));
}
@Override
public void m2() {
long starTime = System.nanoTime();
this.target.m1();
long endTime = System.nanoTime();
System.out.println(this.target.getClass() + ".m1()方法耗时(纳秒):" + (endTime - starTime));
}
@Override
public void m3() {
long starTime = System.nanoTime();
this.target.m1();
long endTime = System.nanoTime();
System.out.println(this.target.getClass() + ".m1()方法耗时(纳秒):" + (endTime - starTime));
}
}
定义一个测试类
package com.zzb.test;
public class Test {
public static void main(String[] args) {
USB usb1 = new USBProxy(new Mouse());
USB usb2 = new USBProxy(new Fan());
}
}
从代码中可以看出,访问被代理的对象mouse和fan的时候,必须经过代理对象USBProxy,这里只写了计算调用方法所需时间来进行举例。
把实现类都通过代理类进行操作,添加了代理类独有的功能,不用再去挨个修改每个实体类了,节省了大量时间和资源。
缺点:为每个接口写一个代理类,同样很花费时间,能否写个通用的代理类呢?
通用代理类
方法1:jdk动态代理
缺点:只能为接口创建代理类
jdk中为实现代理提供了支持,主要有2个类java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler
getProxyClass方法
为指定的接口创建代理类,返回代理类的Class对象
public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces)
newProxyInstance方法
创建代理类的实例对象
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
/*InvocationHandler是个接口,内含invoke方法,操作代码写在其中*/
isProxy方法
判断指定的类是否是一个代理类
public static boolean isProxyClass(Class<?> cl)
getInvocationHandler方法
获取代理对象的 InvocationHandler 对象
public static InvocationHandler getInvocationHandler(Object proxy)
throws IllegalArgumentException
方式一:先获取代理类,在创建实例。getProxyClass getConstructor
package com.zzb.test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<USB> proxyClass = (Class<USB>) Proxy.getProxyClass(USB.class.getClassLoader(), USB.class);
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("我是InvocationHandler,被调用的方法是:" + method.getName());
return null;
}
};
USB usb = proxyClass.getConstructor(InvocationHandler.class).newInstance(invocationHandler);
usb.m1();
usb.m2();
usb.m3();
}
}
方式二:直接通过newProxyInstance创建实例
package com.zzb.test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("我是InvocationHandler,被调用的方法是:" + method.getName());
return null;
}
};
USB usb = (USB) Proxy.newProxyInstance(USB.class.getClassLoader(), new Class[]{USB.class}, invocationHandler);
usb.m1();
usb.m2();
usb.m3();
}
}
综合以上,重写简单demo中的代理类
package com.zzb.test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class CommonProxy implements InvocationHandler {
private Object target;
public CommonProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long starTime = System.nanoTime();
Object result = method.invoke(this.target, args);
long endTime = System.nanoTime();
System.out.println(this.target.getClass() + ".m1()方法耗时(纳秒):" +
(endTime - starTime));
return result;
}
/**
* @author zzb
* @date 2021/4/11 22:52
* @Param: target 接口实现类
* @Param: targetInterface 接口类型
* @return
*/
public static <T> T createProxy(Object target, Class<T> targetInterface) {
if (!targetInterface.isInterface()) {
throw new IllegalStateException("targetInterface必须是接口类型!");
} else if (!targetInterface.isAssignableFrom(target.getClass())) {
throw new IllegalStateException("target必须是targetInterface接口的实现类!");
}
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new CommonProxy(target)
);
}
}
这里建议和上面的简单demo的代理类对比着看
测试类:
package com.zzb.test;
public class Test {
public static void main(String[] args) throws Exception {
USB proxy = CommonProxy.createProxy(new Fan(), USB.class);
proxy.m1();
}
}
这段的重点是createProxy方法
如果要修改执行内容,修改invoke方法
jdk动态代理只能解决接口代理,而类的代理只能用下面讲的cglib了
cglib代理
可以解决为普通的类也实现代理功能。
cglib是什么?
cglib作为一个开源项目,不属于jdk。
cglib是一个字节码生成库,用于在运行时扩展java类和实现接口,本质上是通过动态生成一个子类来覆盖索要代理的类。所要代理的类是非final修饰的类。毕竟final类不可继承。
cglib底层使用ASM,一个字节码操作框架,用来操作字节码生成新的类。
Enhancer是CGLIB中最常用的一个类,和jdk中的Proxy对比,Enhancer既能够代理普通的class,也能够代理接口。
Enhancer类代理的原理?
它创建一个被代理对象的子类并且拦截所有的方法调用,包括toString和hashCode方法。不能拦截final方法,如Object.getClass()
spring已将第三方cglib jar包中所有的类集成到spring自己的jar包中,我这里是直接使用spring的jar包。
创建一个需要被代理的类
package com.zzb.test;
public class USB {
public void m1(){
System.out.println("usb 方法1");
}
public void m2(){
System.out.println("usb 方法2");
}
public void m3(){
System.out.println("usb 方法3");
}
}
测试类,测试cglib代理
到这里就不是一个java工程了,是个web工程,jar包,另外pom.xml中别忘了添加spring依赖,毕竟spring中有着cglib的jar包,而这里写的是cglib代理程序
package com.zzb.test;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) throws Exception {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(USB.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
/**
* 代理对象方法拦截器
* @Param: o 代理对象(被代理类的实例)
* @Param: method 被代理类的方法
* @Param: objects 调用方法传的参数
* @Param: methodProxy 方法代理对象
* @return
*/
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("调用方法:"+method);
Object result = methodProxy.invokeSuper(o, objects);
return result;
}
});
USB proxy = (USB) enhancer.create();
proxy.m1();
proxy.m2();
proxy.m3();
}
}
这里用到了ENhancer类和MethodInterceptor接口
spring中的@configuration注解就是采用的cglib代理
举个例子
package com.zzb.test;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author zhangzhibin
* @create 2021-04-12 0:06
*/
@Configuration
public class TestSpring {
public static class Class1{}
public static class Class2{
private Class1 Class1;
public Class2(Class1 Class1) {
this.Class1 = Class1;
}
}
public static class Class3{
private Class1 Class1;
public Class3(Class1 Class1) {
this.Class1 = Class1;
}
}
@Bean
public Class1 getClass1(){
return new Class1();
}
@Bean
public Class2 getClass2(){
Class1 Class1 = this.getClass1();
return new Class2(Class1);
}
@Bean
public Class3 getClass3(){
Class1 Class1 = this.getClass1();
return new Class3(Class1);
}
}
这里Configuration能够保证,class2和class3的bean获取的class1的bean是同一个bean。
添加多个拦截器,分别拦截不同的方法,根据方法名判断使用不同的方法
package com.zzb.test;
import org.springframework.cglib.proxy.*;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) throws Exception {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(USB.class);
Callback[] callbacks = {
new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("第一个拦截器");
Object result = methodProxy.invokeSuper(o, objects);
return result;
}
},
new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("第二个拦截器");
Object result = methodProxy.invokeSuper(o, objects);
return result;
}
}
};
enhancer.setCallbackFilter(new CallbackFilter() {
@Override
public int accept(Method method) {
String methodName = method.getName();
/*
如果方法名字以1结尾,就返回0,使用第一个拦截器
*/
return methodName.endsWith("1") ? 0 : 1;
}
});
enhancer.setCallbackFilter(new CallbackFilter() {
@Override
public int accept(Method method) {
String methodName = method.getName();
/*
如果方法名字以2结尾,就返回1,使用第二个拦截器
*/
return methodName.endsWith("2") ? 1 : 0;
}
});
enhancer.setCallbacks(callbacks);
USB proxy = (USB) enhancer.create();
proxy.m1();
proxy.m2();
}
}
下面是创建通用代理类,CommonProxy,可以和jdk动态代理的通用代理类对比着来看。
package com.zzb.test;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CommonProxy implements MethodInterceptor {
private Object target;
public CommonProxy(Object target) {
this.target = target;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
long starTime = System.nanoTime();
Object result = method.invoke(target, objects);
long endTime = System.nanoTime();
System.out.println(method + ",耗时(纳秒):" + (endTime - starTime));
return result;
}
public static <T> T createProxy(T target) {
CommonProxy commonProxy = new CommonProxy(target);
Enhancer enhancer = new Enhancer();
enhancer.setCallback(commonProxy);
enhancer.setSuperclass(target.getClass());
return (T) enhancer.create();
}
}
测试类
package com.zzb.test;
public class Test {
public static void main(String[] args) throws Exception {
USB proxy = CommonProxy.createProxy(new USB());
proxy.m1();
proxy.m2();
}
}
总结
CGLIB和Java动态代理的区别
- Java动态代理只能够对接口进行代理,不能对普通的类进行代理
- Java动态代理使用Java原生的反射API进行操作,在生成类上比较高效;CGLIB使用ASM框架直接对字节码进行操作,在类的执行过程中比较高效
本文介绍Java动态代理和CGLIB代理的基本概念及应用,对比两种代理方式的特点,并提供示例代码,帮助理解如何为接口和普通类创建代理。
668

被折叠的 条评论
为什么被折叠?



