文章目录
定义
JDK动态代理
原理
是根据类加载器和接口创建代理类(此代理类是接口的实现类,所以必须使用接口 面向接口生成代理,位于java.lang.reflect包下)
实现方式
- 通过实现
InvocationHandler
接口创建自己的调用处理器IvocationHandler handler = new InvocationHandlerImpl(…);
- 通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类
Class clazz = Proxy.getProxyClass(classLoader,new Class[]{…})
- 通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型
Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
- 通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入
Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
demo
//接口
public interface IUserDao {
public void save();
}
//目标业务类
public class UserDao implements IUserDao {
@Override
public void save() {
System.out.println("已经保存数据...");
}
}
//代理实现
public class InvocationHandlerImpl implements InvocationHandler {
private Object target;// 这其实业务实现类对象,用来调用具体的业务方法
// 通过构造函数传入目标对象
public InvocationHandlerImpl(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
System.out.println("调用开始处理");
result = method.invoke(target, args);
System.out.println("调用结束处理");
return result;
}
public static void main(String[] args) {
// 被代理对象
IUserDao userDao = new UserDao();
InvocationHandlerImpl invocationHandlerImpl = new InvocationHandlerImpl(userDao);
ClassLoader loader = userDao.getClass().getClassLoader();
Class<?>[] interfaces = userDao.getClass().getInterfaces();
// 主要装载器、一组接口及调用处理动态代理实例
IUserDao newProxyInstance = (IUserDao) Proxy.newProxyInstance(loader, interfaces, invocationHandlerImpl);
newProxyInstance.save();
}
}
缺点
jdk动态代理,必须是面向接口,目标业务类必须实现接口
疑问
为什么目标业务类必须实现接口?
JDK动态代理的原理是根据定义好的规则,用传入的接口创建一个新类,这就是为什么采用动态代理时为什么只能用接口引用指向代理,而不能用传入的类引用执行动态类。
cglib采用的是用创建一个继承实现类的子类,用asm库动态修改子类的代码来实现的,所以可以用传入的类引用执行代理类
CGLIB动态代理
原理
利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。 并不要求委托类必须实现接口。
demo
public class CglibProxy implements MethodInterceptor {
private Object targetObject;
// 创建代理对象这里的目标类型为Object,则可以接受任意一种参数作为被代理类,实现了动态代理
public Object getInstance(Object target) {
// 设置需要创建子类的类
this.targetObject = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("开启事物");
Object result = proxy.invoke(targetObject, args);
System.out.println("关闭事物");
// 返回代理对象
return result;
}
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
UserDao userDao = (UserDao) cglibProxy.getInstance(new UserDao());
userDao.save();
}
}
扩展
JDK动态代理和cglib区别
java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
JDK动态代理只能对实现了接口的类生成代理,而不能针对类 。
CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法 。因为是继承,所以该类或方法最好不要声明成final ,final可以阻止继承和多态。
动态代理在spring的应用
todo
spring 如何选择使用 JDK动态代理还是 cglib
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换