Java动态代理
首先我们介绍代理模式
静态代理模式
静态代理
- 可以做到在不修改目标对象前提下,对目标功能扩展。
- 缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.
动态代理模式
使用静态代理,会有一个问题,就是当Subject
接口更改了之后,所有的Proxy类也要跟着更改。动态代理有两种实现方式,JDK内部自带的实现方法和利用cglib实现方式。静态代理的代理类是我们自己定义好的,而动态代理的代理类时根据我们的代码动态生成的。
JDK内部自带动态代理
在java的java.lang.reflect
包下提供了一个Proxy
类和一个InvocationHandler
接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。
创建一个动态代理对象步骤:
- 创建一个
InvocationHandler
对象 - 使用
Proxy
类的getProxyClass
静态方法生成一个动态代理类stuProxyClass
- 获得
stuProxyClass
中一个带InvocationHandler
参数的构造器constructor
- 通过构造器
constructor
来创建一个动态实例stuProxy
InvocationHandler stuHandler = new MyInvocationHandler<Person>(stu);
Class<?> stuProxyClass = Proxy.getProxyClass(Person.class.getClassLoader(), new Class<?>[] {Person.class});
Constructor<?> constructor = PersonProxy.getConstructor(InvocationHandler.class);
Person stuProxy = (Person) cons.newInstance(stuHandler);
上述四部可以简化为两步:
//创建一个与代理对象相关联的InvocationHandler
InvocationHandler stuHandler = new MyInvocationHandler<Person>(stu);
//创建一个代理对象stuProxy,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
Person stuProxy= (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);
下面是具体实现
public interface Person {
void giveMoney();
}
//接口的实现类
public class Student implements Person {
private String name;
public Student(String name) {
this.name = name;
}
@Override
public void giveMoney() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + "上交班费50元");
}
}
实现InvocationHanlder
方法
public class StuInvocationHandler<T> implements InvocationHandler {
//invocationHandler持有的被代理对象
T target;
public StuInvocationHandler(T target) {
this.target = target;
}
/**
* proxy:代表动态代理对象
* method:代表正在执行的方法
* args:代表调用目标方法时传入的实参
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理执行" +method.getName() + "方法");
MonitorUtil.start();
Object result = method.invoke(target, args);
MonitorUtil.finish(method.getName());
return result;
}
}
测试类:
public class ProxyTest {
public static void main(String[] args) {
Person zhangsan = new Student("张三");
InvocationHandler stuHandler = new StuInvocationHandler<Person>(zhangsan);
Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);
stuProxy.giveMoney();
}
}
JDK动态代理的原理分析
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) {
Objects.requireNonNull(h);
final Class<?> caller = System.getSecurityManager() == null
? null
: Reflection.getCallerClass();
//获得代理类的构造器
Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
return newProxyInstance(caller, cons, h);
}
private static Object newProxyInstance(Class<?> caller, // null if no SecurityManager
Constructor<?> cons,
InvocationHandler h) {
if (caller != null) {
checkNewProxyPermission(caller, cons.getDeclaringClass());
}
//通过代理类的构造器构造代理类实例,返回代理类实例对象
return cons.newInstance(new Object[]{h});
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.Person;
public final class $Proxy0 extends Proxy implements Person
{
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
/**
*注意这里是生成代理类的构造方法,方法参数为InvocationHandler类型,看到这,是不是就有点明白
*为何代理对象调用方法都是执行InvocationHandler中的invoke方法,而InvocationHandler又持有一个
*被代理对象的实例,不禁会想难道是....? 没错,就是你想的那样。
*
*super(paramInvocationHandler),是调用父类Proxy的构造方法。
*父类持有:protected InvocationHandler h;
*Proxy构造方法:
* protected Proxy(InvocationHandler h) {
* Objects.requireNonNull(h);
* this.h = h;
* }
*
*/
public $Proxy0(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
//这个静态块本来是在最后的,我把它拿到前面来,方便描述
static
{
try
{
//看看这儿静态块儿里面有什么,是不是找到了giveMoney方法。请记住giveMoney通过反射得到的名字m3,其他的先不管
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("proxy.Person").getMethod("giveMoney", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
/**
*
*这里调用代理对象的giveMoney方法,直接就调用了InvocationHandler中的invoke方法,并把m3传了进去。
*this.h.invoke(this, m3, null);这里简单,明了。
*来,再想想,代理对象持有一个InvocationHandler对象,InvocationHandler对象持有一个被代理的对象,
*再联系到InvacationHandler中的invoke方法。嗯,就是这样。
*/
public final void giveMoney()
throws
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
//注意,这里为了节省篇幅,省去了toString,hashCode、equals方法的内容。原理和giveMoney方法一毛一样。
}
CGLIB实现动态代理
CGLIB使用ASM框架直接对字节码进行操作,可以对普通的类进行代理。
CGLIB动态代理的使用
创建一个实体类
public class PersonService {
public PersonService(){
System.out.println("PersonService构造");
}
final public Integer getPerson(String code){
System.out.println("PersonService:getPerson>>" + code);
return null;
}
public void setPerson(){
System.out.println("setPerson");
}
}
创建一个实现MethodInterceptor
接口的类
public class CglibProxyIntercepter implements MethodInterceptor {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before");
Object ob = methodProxy.invokeSuper(o,objects);
System.out.println("after");
return ob;
}
}
通过Enhancer
创建代理类并执行方法
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PersonService.class);
enhancer.setCallback(new CglibProxyIntercepter());
PersonService proxy = (PersonService) enhancer.create();
proxy.setPerson();
proxy.getPerson("1");
}
CGLIB动态代理原理分析
CGLIB共生成3个类,分别为代理类的FastClass,代理类,目标类的FastClass,FastClass,这里简单说一下,就是给每个方法编号,通过编号找到方法,这样可以避免频繁使用反射导致效率比较低,也可以叫做FastClass机制。
public class Target$$EnhancerByCGLIB$$788444a0 extends Target implements Factory
{
private boolean CGLIB$BOUND;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private static final Method CGLIB$g$0$Method;
private static final MethodProxy CGLIB$g$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$f$1$Method;
private static final MethodProxy CGLIB$f$1$Proxy;
static void CGLIB$STATICHOOK1()
{
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class localClass1 = Class.forName("net.sf.cglib.test.Target$$EnhancerByCGLIB$$788444a0");
Class localClass2;
Method[] tmp60_57 = ReflectUtils.findMethods(new String[] { "g", "()V", "f", "()V" }, (localClass2 = Class.forName("net.sf.cglib.test.Target")).getDeclaredMethods());
CGLIB$g$0$Method = tmp60_57[0];
CGLIB$g$0$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "g", "CGLIB$g$0");
CGLIB$f$1$Method = tmp60_57[1];
CGLIB$f$1$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "f", "CGLIB$f$1");
}
final void CGLIB$g$0()
{
super.g();
}
public final void g()
{
MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
if (tmp4_1 == null)
{
CGLIB$BIND_CALLBACKS(this);
tmp4_1 = this.CGLIB$CALLBACK_0;
}
if (this.CGLIB$CALLBACK_0 != null) {
tmp4_1.intercept(this, CGLIB$g$0$Method, CGLIB$emptyArgs, CGLIB$g$0$Proxy);
}
else{
super.g();
}
}
}
FastClass机制分析
public class test10 {
public static void main(String[] args){
Test tt = new Test();
Test2 fc = new Test2();
int index = fc.getIndex("f()V");
fc.invoke(index, tt, null);
}
}
class Test{
public void f(){
System.out.println("f method");
}
public void g(){
System.out.println("g method");
}
}
class Test2{
public Object invoke(int index, Object o, Object[] ol){
Test t = (Test) o;
switch(index){
case 1:
t.f();
return null;
case 2:
t.g();
return null;
}
return null;
}
public int getIndex(String signature){
switch(signature.hashCode()){
case 3078479:
return 1;
case 3108270:
return 2;
}
return -1;
}
}
JDK动态代理与CGLIB动态代理的对比
- JDK动态代理是接口代理,被代理类A需要实现接口,业务代理类b也需要实现该接口并继承Proxy类,所以这种动态代理不能代理普通类,因为Java只允许单继承。
- CGLIB生成的代理类会直接继承被代理类,通过生成子类字节码来实现,比反射快一点,没有性能问题。但是由于cglib会继承被代理类,需要重写被代理方法,所以被代理类不能是final类,被代理方法不能是final。
JDK动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
-
如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
-
如果目标对象实现了接口,可以强制使用CGLIB实现AOP
-
如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
JDK动态代理和CGLIB字节码生成的区别?
- JDK动态代理只能对实现了接口的类生成代理,而不能针对类
- CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
- 因为是继承,所以该类或方法最好不要声明成final