java之动态代理

java之动态代理

代理模式

代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.

  • logging when a method starts and stops
  • perform extra checks on arguments
  • mocking the behavior of the original class
  • implement lazy access to costly resources

静态代理

  • 代理类和真实类都继承同一个接口

  • 代理类持有真实类的实例

动态代理

jdk

1.代理对象,不需要实现接口 (目标对象一定要实现接口)

2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)

Screen Shot 2020-09-04 at 3.40.34 PM

Proxy.getProxyClass()这个方法的本质就是:以Class造Class。

代理Class创建对象是,需要传入InvocationHandler, 每次调用代理对象的方法最终都会调用InvocationHandler的invoke方法。

InvocationHandler持有目标对象。

java.lang.reflect.Proxy

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
  • Classloader loader: 指定当前目标对象使用类加载器
  • Class<?>[] interface: 目标对象实现的接口的类型,使用泛型方式确认类型
  • InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
/**
* 创建动态代理对象
* 动态代理不需要实现接口,但是需要指定接口类型
*/
public class ProxyFactory{

//维护一个目标对象
private Object target;
public ProxyFactory(Object target){
this.target=target;
}

//给目标对象生成代理对象
public Object getProxyInstance(){
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("开始事务2");
//执行目标对象方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务2");
return returnValue;
}
}
);
}

}

CGlib

Code Generation Library

静态代理和动态代理模式都是要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理

静态代理和动态代理模式都是要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理

  • JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现.
  • Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)
  • Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.
  • cglib动态代理生成的代理类是被代理者的子类,并且会重写父类的所有方法,要求该父类必须有空的构造方法,否则会报错:Superclass has no null constructors but no arguments were given,还有,private和final修饰的方法不会被子类重写。
/**
* Cglib子类代理工厂
* 对UserDao在内存中动态构建一个子类对象
*/
public class ProxyFactory implements MethodInterceptor{
//维护目标对象
private Object target;

public ProxyFactory(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 obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("开始事务...");

//执行目标对象的方法
Object returnValue = method.invoke(target, args);

System.out.println("提交事务...");

return returnValue;
}
}
public class PersonService {
public String sayHello(String name) {
return "Hello " + name;
}

public Integer lengthOfName(String name) {
return name.length();
}
}

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PersonService.class);
enhancer.setCallback((FixedValue) () -> "Hello Tom!"); //The FixedValue is a callback interface that simply returns the value from the proxied method.
PersonService proxy = (PersonService) enhancer.create();

String res = proxy.sayHello(null);

assertEquals("Hello Tom!", res);
//*[MethodInterceptor](http://cglib.sourceforge.net/apidocs/net/sf/cglib/proxy/MethodInterceptor.html)* interface to intercept all calls to the proxy and decide if want to make a specific call or execute a method from a superclass:

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PersonService.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
if (method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {
return "Hello Tom!";
} else {
return proxy.invokeSuper(obj, args);
}
});

PersonService proxy = (PersonService) enhancer.create();

Hibernate uses cglib for generation of dynamic proxies. For example, it will not return full object stored in a database but it will return an instrumented version of stored class that lazily loads values from the database on demand.

Popular mocking frameworks, like Mockito, use cglib for mocking methods. The mock is an instrumented class where methods are replaced by empty implementations.

Spring AOP, jdk dynamic proxy for class implments interface, cglib for sub-class proxy.

对比

1、CGLib所创建的动态代理对象在实际运行时候的性能要比JDK动态代理高不少,有研究表明,大概要高10倍;

2、但是CGLib在创建对象的时候所花费的时间却比JDK动态代理要多很多,有研究表明,大概有8倍的差距;

https://zhuanlan.zhihu.com/p/67041662


https://zhuanlan.zhihu.com/p/70098824

https://www.zhihu.com/question/20794107/answer/658139129

https://www.zhihu.com/question/40536038/answer/658146278

https://www.baeldung.com/cglib