反射 静态代理 动态代理 (JDK代理和CGLib 代理 )
动态代理
常用的方式是JDK 代理(基于反射)CGlib代理(基于ASM)
反射
指程序在运行期间,可以访问、检测或修改其本身状态或行为的一种能力,使用反射,可以任意调用一个对象的
属性和方法
静态代理
概念:代理对象和实际对象都继承了同一个接口,在代理对象中指向的是实际对象的实例,这样对外暴露的是代理对象而真正调用的是 Real Object.
基于接口实现
代码实现
/**
* 需要静态代理的接口
*/
public interface Subject {
public void inrtroduce(String name);
}
/**
* 被代理的真实对象
*/
public class RealSubject implements Subject {
@Override
public String inrtroduce(String name) {
System.out.println("My name is :"+name);
return name;
}
}
/**
* 代理类
*/
public class InvocationHandlerIml implements Subject {
//代理的真实对象 基本已经写死
private RealSubject subject;
public InvocationHandlerIml(RealSubject subject) {
this.subject = subject;
}
@Override
public void inrtroduce(String name) {
System.out.println("调用前的业务");
subject.inrtroduce(name);
System.out.println("调用后的业务");
}
}
/**
* 测试
*/
public class Test {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
InvocationHandlerIml invocationHandlerIml = new InvocationHandlerIml(realSubject);
invocationHandlerIml.inrtroduce("帅哥");
}
}
这种静态代理的方式存在一个最大的问题就是对目标方法的调用逻辑写死在代理类方法里面,也就是每一个被代理类的方法都是不同的,需要创建同样个
数的代理类才能实现对不同被代理类的调用。为了解决这个问题,动态代理应运而生
JDK动态代理
动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术。在生成代理对象的过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法。可以理解为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作。
步骤
通过实现增强器接口,重写invoke 方法就可以了
/**
* 需要动态代理的接口
*/
public interface Subject {
public String inrtroduce(String name);
}
/**
* 被代理的真实对象
*/
public class RealSubject implements Subject {
@Override
public String inrtroduce(String name) {
System.out.println("My name is :"+name);
return name;
}
}
/**
* 代理类
*/
public class InvocationHandlerIml implements InvocationHandler {
//代理的真实对象 接口
private Subject subject;
//开始调度
public Object newproxy(Subject targetObject){
subject=targetObject;
ClassLoader classLoader = subject.getClass().getClassLoader();
Class<?>[] interfaces = subject.getClass().getInterfaces();
return Proxy.newProxyInstance(classLoader,interfaces,this);
}
//增强当前的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("调用前的业务");
System.out.println("method"+method);
Object result = method.invoke(subject, args);
System.out.println("调用后的业务");
return result;
}
}
/**
* 测试
*/
public class Test {
public static void main(String[] args) {
Subject realSubject = new RealSubject();
InvocationHandlerIml invocationHandlerIml = new InvocationHandlerIml();
Subject subject = (Subject)invocationHandlerIml.newproxy(realSubject);
subject.inrtroduce("帅哥");
}
}
CGLib 代理
GLib
采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑,来完成动态代理的实现。实现方式实现 MethodInterceptor 接口,重写 intercept 方法,通过 Enhancer 类的回调方法来实现。
//被代理对象
public class UserDao {
public void addUser(){
System.out.println("添加用户");
}
}
/**
* CGlib动态代理类
*/
public class CGLibProxy implements MethodInterceptor {
// CGlib需要代理的目标对象
private Object targetObject;
public Object createProxyObject(Object obj) {
this.targetObject = obj;
// 通过CGLIB动态代理获取代理对象的过程
Enhancer enhancer = new Enhancer();
// 设置enhancer对象的父类
enhancer.setSuperclass(obj.getClass());
// 设置enhancer的回调对象
enhancer.setCallback(this);
// 设置enhancer对象的父类
Object proxyObj = enhancer.create();
return proxyObj;
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object obj = null;
System.out.println("调用前的业务");
obj = method.invoke(targetObject, args);
System.out.println("调用后的业务");
return obj;
}
}
/**
* 测试
*/
public class Test {
public static void main(String[] args) {
// RealSubject realSubject = new RealSubject();
// InvocationHandlerIml invocationHandlerIml = new InvocationHandlerIml(realSubject);
// invocationHandlerIml.inrtroduce("帅哥");
UserDao userDao = new UserDao();
CGLibProxy proxy = new CGLibProxy();
UserDao proxyObject = (UserDao) proxy.createProxyObject(userDao);
proxyObject.addUser();
}
}
JDK代理和CGLib 代理区别
- JDK动态代理只能针对接口实现类生成代理实例,而不能针对类;也就是说它是面向接口的
- CGLIB是针对类实现代理,主要是对指定的类生成一个子类,并覆盖其中方法实现增强,但是因为采用的是继承,
- JDK 代理是java 语言自带的,无需加载第三方类去实现,CGLIB
是一个基于
ASM实现的- 使用动态代理的对象必须实现一个或多个接口
- 使用cglib代理的对象则无需实现接口,达到代理类无侵入。
小总结
- 静态代理实现较简单,只要代理对象对目标对象进行包装,即可实现增强功能,但静态代理只能为一个目标对象服务,如果目标对象过多,则会产生很多代理类,会很冗余
- JDK动态代理需要目标对象实现业务接口,代理类只需实现InvocationHandler接口。
- 静态代理在编译时产生class字节码文件,可以直接使用,效率高。
- JDK动态代理必须实现InvocationHandler接口,通过反射代理方法,比较消耗系统性能,但可以减少代理类的数量,使用更灵活。
- JDK 动态代理是基于接口设计实现的,如果没有接口,会抛异常。
- cglib代理无需实现接口,通过生成类字节码实现代理,比反射稍快,不存在性能问题,但cglib会继承目标对象,需要重写方法