# 什么是动态代理
动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术。在生成代理对象的过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法。可以理解为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作。
# SpringAOP中使用的动态代理技术-JDK动态代理和CGLIB动态代理
什么情况使用jdk哪种情况使用CGLIB
- 如果要代理的对象有接口,则使用jdk动态代理--创建接口实现类代理对象,增强类的方法
- 如果要代理的对象没有接口,则使用CGLIB--创建子类的代理对象。增强类的方法
# jdk动态代理示例
准备一个接口
public interface UserDao {
public int add(int a,int b);
}
接口的实现类
public class UserDaoImpl implements UserDao {
@Override
public int add(int a, int b) {
System.out.println(a);
return a + b;
}
}
代理类
public class MyProxy implements InvocationHandler {
//要代理的对象
private Object object;
public Object getObject() {
return object;
}
public void setObject(Object object) {
this.object = object;
}
//通过Proxy获取动态代理对象
public Object getProxyInstance(){
/**
* 获取类加载器
* 获取对应接口
* this就是invocationHandler
*/
return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理前");
Object invoke = method.invoke(object, args);
System.out.println("代理后");
return invoke;
}
}
测试
public class Test {
public static void main(String[] args) {
UserDaoImpl userDao=new UserDaoImpl();
MyProxy myProxy=new MyProxy();
myProxy.setObject(userDao);
UserDao proxyInstance = (UserDao) myProxy.getProxyInstance();
System.out.println(proxyInstance.add(1,2));
}
}
# JDK动态代理详解
JDK的动态代理是基于Proxy类底层通过反射实现的。JDK动态代理是有JK提供的工具类Proxy实现的。动态代理类是在运行时生成指定接口的代理类,每个代理实例都有一个关联的调用处理程序的对象,此对象实现了invocationHandler,最终的业务逻辑是是在invocationHandler实现类的invoke方法.
Proxy类中核心的方法就是 通过newProxyInstance来生成动态代理对象
# 使用步骤
使用 JDK 动态代理的 5 大步骤如下
- 为动态代理类实现
InvocationHandler
接口并重写invoke
方法,即为动态代理类关联一个自定义的handler
。 - 通过
Proxy.getProxyClass
获得动态代理类。 - 通过反射机制获得代理类的构造方法,方法签名为
getConstructor(InvocationHandler.class)
。 - 通过构造函数获得代理对象,并将自定义的
InvocationHandler
实例对象作为参数传入。 - 通过代理对象调用目标方法。
Java 的 Proxy
类提供了一个 Proxy.newProxyInstance()
方法,封装了上面的第 2~4 步的工作(详情见下 Proxy
类的源码注释),故步骤可精简为 3 步
- 为动态代理类实现
InvocationHandler
接口并重写invoke
方法,即为动态代理类关联一个自定义的handler
。 - 使用
Proxy.newProxyInstance()
方法获得代理对象 - 通过代理对象调用目标方法。
通过源码可以看到newProxyInstance需要三个参数 类加载器 接口 和 InvocationHandler
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
// 这里就是获取处接口的所有方法 复制出来了一份等待处理
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
//得到了代理类的Class
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//传入构造对象拿到代理类的构造器
//从这里我们可以猜出这个代理类一个有一个构造方法是传入InvocationHandler进行初始化
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
//通过反射传入之前我们定义的那个HelloInvocationHandle,进行构造器实例化代理对象
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
InvocationHandler是一个接口,里面只有一个invoke方法。这个方法主要就是用来做业务上的增强的
//proxy-被代理的实例 method-要增强的方法 args-要增强的方法所需要的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
# Spring AOP和AspectJ是什么关系
- 首先AspectJ是什么? AspectJ是一个java实现的AOP框架,它能够对java代码进行AOP编译(一般在编译期进行),让java代码具有AspectJ的AOP功能(当然需要特殊的编译器)
动态织入和静态织入:
- 动态织入的方式是在运行时动态将要增强的代码织入到目标类中,这样往往是通过动态代理技术完成,如Java JDK的动态代理(Proxy,底层通过反射实现)或者CGLIB的动态代理(底层通过继承实现),SpringAOP采用的就是基于运行时增强的代理技术
- AspectJ采用的就是静态织入的方式。AspectJ只要采用的是编译器织入,这个期间使用AspectJ的acj编译器(类似javac)把aspect类编译成class字节码后,在Java目标类编译时织入,即先编译aspect类在编译目标类
# Spring AOP and AspectJ AOP 有什么区别?AOP 有哪些实现方式?
AOP实现的关键在于代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以SpringAOP为代表
- AspectJ是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。
- SpringAOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特点的切点做了增强处理,并回调原对象的方法