相信大家脱口而出的就是JDK、CGLIB这两种最熟悉的代理模式,其实代理模式除了这两种之外,还有三种代理模式.下面就一一列举.
准备一个基础的接口和类作为演示的对象.
// 接口
public interface CatAPI{
void skin(String color);
}
// 目标对象
public class blackCat implements CatAPI{
@Override
public void skin(String color) {
System.out.println("我是一只" + color + "的小猫咪");
}
}
一、JDK代理
public static class JdkProxy implements InvocationHandler{
// 目标对象
private static Object target;
public static Object getProxy(Object obj) throws Exception {
target = obj;
// 获取类加载器
ClassLoader classLoader = obj.getClass().getClassLoader();
// 获取目标对象实现的接口
Class>[] interfaces = obj.getClass().getInterfaces();
return Proxy.newProxyInstance(classLoader, interfaces,new JdkProxy());
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 增强语句
System.out.println("我是一只聪明的小猫咪");
// 执行原方法
return method.invoke(target,args);
}
}
public static void main(String[] args) throws Exception {
BlackCat blackCat = new BlackCat();
CatAPI proxy = (CatAPI)JdkProxy.getProxy(blackCat);
proxy.skin("黑色");
}
测试结果
二、CGLIB代理
依赖
cglib cglib3.3.0 compile
public static class CGLIBProxy implements MethodInterceptor {
private static Object target;
public static Object getProxy(Object obj){
target = obj;
return Enhancer.create(obj.getClass(), new CGLIBProxy());
}
@Override
public Object intercept(Object p, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 参数说明:
// 1、p 代理对象
// 2、method 目标方法
// 3、args 方法参数
// 4、methodProxy 代理方法 :使用methodProxy的好处是内部不会反射
System.out.println("我是一只聪明的小猫咪");
// return methodProxy.invokeSuper(p,args); // 内部没有反射,需要代理对象
return method.invoke(target,args); // 内部没有反射,需要目标对象
}
}
public static void main(String[] args) throws Exception {
BlackCat blackCat = new BlackCat();
CatAPI proxy = (CatAPI)CGLIBProxy.getProxy(blackCat);
proxy.skin("黑色");
}
测试结果
三、 ASM代理方式
依赖
asm asm-all3.3.1
public static class ASMProxy extends ClassLoader {
public static T getProxy(Class clazz) throws Exception {
ClassReader classReader = new ClassReader(clazz.getName());
ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS);
classReader.accept(new ClassVisitor(ASM5, classWriter) {
@Override
public MethodVisitor visitMethod(int access, final String name, String descriptor, String signature, String[] exceptions) {
// 方法过滤
if (!"skin".equals(name))
return super.visitMethod(access, name, descriptor, signature, exceptions);
final MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);
return new AdviceAdapter(ASM5, methodVisitor, access, name, descriptor) {
@Override
protected void onMethodEnter() {
// 执行指令;获取静态属性
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
// 加载常量 load constant
methodVisitor.visitLdcInsn(name + " 你被代理了,By ASM!");
// 调用方法
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
super.onMethodEnter();
}
};
}
}, ClassReader.EXPAND_FRAMES);
byte[] bytes = classWriter.toByteArray();
return (T) new ASMProxy().defineClass(clazz.getName(), bytes, 0, bytes.length).newInstance();
}
}
public static void main(String[] args) throws Exception {
CatAPI proxy = (CatAPI) ASMProxy.getProxy(BlackCat.class);
proxy.skin("黑色");
}
测试结果
四、 Byte-Buddy代理方式
依赖
net.bytebuddy byte-buddy1.10.13
private static CatAPI createByteBuddyDynamicProxy() throws Exception {
return (CatAPI) new ByteBuddy().subclass(BlackCat.class)
.implement(CatAPI.class)
.method(ElementMatchers.named("skin"))
.intercept(MethodDelegation.to(new SingerAgentInterceptor()))
.make()
.load(Thread.currentThread().getContextClassLoader())
.getLoaded()
.getDeclaredConstructor()
.newInstance();
}
public static class SingerAgentInterceptor {
public Object interceptor(@This Object proxy,
@Origin Method method,
@SuperMethod Method superMethod,
@AllArguments Object[] args) throws Exception {
System.out.println("我是一只聪明的小猫咪 ");
Object ret = superMethod.invoke(proxy, args);
return ret;
}
}
public static void main(String[] args) throws Exception {
CatAPI proxy = createByteBuddyDynamicProxy();
proxy.skin("黑色");
System.out.println(proxy.toString());
}
测试结果
五、Javassist代理方式
依赖
org.javassist javassist3.21.0-GA
public static class JavassistProxy extends ClassLoader {
public static T getProxy(Class clazz) throws Exception {
ClassPool pool = ClassPool.getDefault();
// 获取类
CtClass ctClass = pool.get(clazz.getName());
// 获取方法
CtMethod ctMethod = ctClass.getDeclaredMethod("skin");
// 方法前加强
ctMethod.insertBefore("{System.out.println("" + ctMethod.getName() + " 你被代理了,By Javassist");}");
byte[] bytes = ctClass.toBytecode();
return (T) new JavassistProxy().defineClass(clazz.getName(), bytes, 0, bytes.length).newInstance();
}
}
public static void main(String[] args) throws Exception {
CatAPI proxy = (CatAPI) JavassistProxy.getProxy(BlackCat.class);
proxy.skin("黑色");
}
测试结果
以上列举了五种代理方式,对于ASM、Byte-Buddy和Javassist一层一层封装,使用起来也越来越简单,后续会有文章对这三种方式进行一个详细的讲解,这里只做一个例子展示.



