一、使用JDK原生动态代理
实现有2种,1.基于Java反射机制 2.基于native方法。
Java动态代理是基于接口的,如果对象没有实现接口则选择用CGLIB方式实现动态代理。
实现步骤:
1.首先实现一个InvocationHandler,方法调用会被转发到该类的invoke()方法。
2.然后在需要使用真实对象的时候,通过JDK动态代理获取代理对象。
目录
接口:SayHello.java
package com.sid.proxy.jdk;
public interface SayHello {
String sayHello();
}
真实的处理:SayHelloImp.java
package com.sid.proxy.jdk;
/**
* 真实的处理
* */
public class SayHelloImp implements SayHello {
public String sayHello() {
System.out.println("hello word");
return "done";
}
}
代理类:HelloHandler.java
package com.sid.proxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 这是代理类,代理了SayHello接口的所有实现类
* */
//1.首先实现一个InvocationHandler接口,方法调用会被转发到该类的invoke()方法。
public class HelloHandler implements InvocationHandler {
SayHello sayHello;
public HelloHandler(SayHello sayHello) {
super();
this.sayHello = sayHello;
}
/**
* 所有的流程控制都在这里面处理
* 调用真实的SayHello接口的实现类的所有方法,都会先进这个invoke方法,
* 由method.invoke去真正的调用SayHello的方法
*
* 入参proxy是代理类,mehtod表示调用真实类的哪个方法,args表示调用真实类的方法的入参
* */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("前置方法");
// obj是调用真实类方法的返回值
Object obj = method.invoke(sayHello, args);
System.out.println(obj);
System.out.println("后续方法");
return obj;
}
}
Main方法调用
package com.sid.proxy;
import com.sid.proxy.jdk.HelloHandler;
import com.sid.proxy.jdk.SayHello;
import com.sid.proxy.jdk.SayHelloImp;
import java.lang.reflect.Proxy;
public class MainClass {
public static void main(String[] args) {
//2.然后在需要使用SayHello的时候,通过JDK动态代理获取SayHello的代理对象。
//newProxyInstance()会返回一个实现了指定接口的代理对象,对该对象的所有方法调用都会转发给InvocationHandler.invoke()方法
SayHello proxy = (SayHello) Proxy.newProxyInstance(
ClassLoader.getSystemClassLoader(),//1. 类加载器
new Class[]{SayHello.class},//2. 代理需要实现的接口,可以有多个
new HelloHandler(new SayHelloImp()) // 3. 方法调用的实际处理者,代理对象的方法调用都会转发到这里
);
proxy.sayHello();
}
}
运行结果
二、使用CGLIB动态代理
基于ASM的字节码生成库,它允许在运行时对字节码进行修改和动态生成。
CGLIB动态代理的原理就是用Enhancer生成一个被代理类的子类,并且设置好callback到proxy, 当被代理类的每个方法被调用就会转为调用实现了MethodInterceptor接口的proxy代理类的intercept() 函数。
优点:1.CGLIB通过继承方式实现代理。2.效率比JDK的代理要高。
缺点:CGLIB只能代理所有非final的类、方法。
实现步骤
1.首先实现一个MethodInterceptor接口,方法调用会被转发到该类的intercept()方法。
2.创建Enhancer对象,给enhancer设置父类(就是被代理的目标类),给enhancer设置callback(callback就是第一步实现的MethodInterceptor,就是拦截到目标类的方法后要做的代理逻辑),enhancer.create创建代理类。
设置callback的时候可以设置一组callback,即是有多个不同的MethodInterceptor实现,然后设置回调过滤器CallbackFilter来指定不同的真实方法使用不同的代理逻辑
CGLIB包对callback接口有一些简单实现:
1.FixedValue固定值,MethodInterceptor需要实现FixedValue接口。这会忽略被代理目标类调用的方法的返回值,使用固定值来替换。
2.NoOp.INSTANCE,直接调用真实被代理类的方法,在代理中对该方法不做任何逻辑处理。
3.LazyLoader,继承了Callback接口,有一个loadObject的方法,LazyLoader只有在第一次调用时会执行loadObject获取对象。
4.Dispatcher,继承了Callback接口,有一个loadObject的方法,每次调用时都触发loadObject方法。
目录
需要引包
pom.xml
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>
真实类SayHelloImp.java
public class SayHelloImp {
public String sayHello1() {
System.out.println("hello word 1");
return "done";
}
public String sayHello2() {
System.out.println("你好! 2");
return "完成";
}
public void sayHello3() {
System.out.println("我会被代理,但是callbackFilter里面设置的处理sayHello3方法是不做任何代理逻辑,直接调用真实方法. 3");
}
public final void sayHello4() {
System.out.println("我是final方法,用cglib方式代理的时候,我不会被代理. 4");
}
}
代理处理逻辑 SayHelloMethodInterceptor1.java 和SayHelloMethodInterceptor2.java
public class SayHelloMethodInterceptor1 implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("SayHelloMethodInterceptor1 do. param:"+ Arrays.toString(args) );
System.out.println("before...");
//通过调用MethodProxy.invokeSuper()方法,将调用转发给原始对象
Object obj = methodProxy.invokeSuper(o, args);
System.out.println(obj);
System.out.println("after...");
return obj;
}
}
public class SayHelloMethodInterceptor2 implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("SayHelloMethodInterceptor do.param:"+ Arrays.toString(args) );
System.out.println("前置方法");
//通过调用MethodProxy.invokeSuper()方法,将调用转发给原始对象
Object obj = methodProxy.invokeSuper(o, args);
System.out.println(obj);
System.out.println("后续方法");
return obj;
}
}
设置不同方法用不同代理逻辑CallbackFilterImpl.java
public class CallbackFilterImpl implements CallbackFilter {
// 这里设置真实对象不同的方法被调用,使用不同的MethodInterceptor代理逻辑来吹了
@Override
public int accept(Method method) {
String methodName = method.getName();
if ("sayHello1".equals(methodName)) {
return 0; // sayHello1()方法使用callbacks[0]对象拦截。
} else if ("sayHello2".equals(methodName)) {
return 1; // sayHello2()方法使用callbacks[1]对象拦截。
}else if ("sayHello3".equals(methodName)) {
return 2; // sayHello3()方法使用callbacks[2]对象拦截。
}
return 0;
}
}
Main方法调用
public class MainClassCglib {
public static void main(String[] args) {
// 2. 然后在需要使用真实对象的时候,通过CGLIB动态代理获取代理对象。
//通过CGLIB的Enhancer来指定要代理的目标对象
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(SayHelloImp.class);
//指定实际处理代理逻辑的对象 这样只设置了一个MethodInterceptor,真实类所有的方法都被同一个MethodInterceptor处理
//enhancer.setCallback(new SayHelloMethodInterceptor1());
// 回调实例数组
Callback[] callbacks = new Callback[] { new SayHelloMethodInterceptor1(), new SayHelloMethodInterceptor1(), NoOp.INSTANCE };
enhancer.setCallbacks(callbacks);
enhancer.setCallbackFilter(new CallbackFilterImpl());
//通过调用create()方法得到代理对象
//对这个对象所有非final方法的调用都会转发给MethodInterceptor.intercept()方法
SayHelloImp star = (SayHelloImp)enhancer.create();
star.sayHello1();
star.sayHello2();
star.sayHello3();
star.sayHello4();
}
}
结果
三、JDK动态代理和Gglib动态代理的区别
1.JDK动态代理是实现了被代理对象的接口,Cglib是继承了被代理对象。
2.JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。
3.JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。
FastClass机制的原理简单来说就是:为代理类和被代理类各生成一个Class,这个Class会为代理类或被代理类的方法分配一个index(int类型)。 这个index当做一个入参,FastClass就可以直接定位要调用的方法直接进行调用