AOP的实现,JDK动态代理和CGLIB动态代理
一、jdk动态代理实现
1.接口
代码如下(示例):
package com.bjtu;
public interface StudentDao {
public int add(int a, int b);
public String update(String id);
}
2.接口实现类
代码如下(示例):
package com.bjtu;
public class StudentDaoImpl implements StudentDao {
@Override
public int add(int a, int b) {
System.out.println("执行方法------------");
return a+b;
}
@Override
public String update(String id) {
return id;
}
}
3.代理类
代码如下(示例):
package com.bjtu;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
public class JDKProxy {
public static void main(String[] args) {
Class[] interfaces = {StudentDao.class};
/*可用匿名内部类
Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
});*/
StudentDaoImpl stu = new StudentDaoImpl();
StudentDao stuDao = (StudentDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new StudentProxy(stu));
int result = stuDao.add(1,2);
System.out.println("result: " + result);
}
static class StudentProxy implements InvocationHandler {
public Object obj;
public StudentProxy(Object obj){
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法之前执行:" + method.getName()+"传递的参数"+ Arrays.toString(args));
Object res = method.invoke(obj,args);
System.out.println("方法之后执行-------" + res);
return res;
}
}
}
执行结果如下:
二、CGILIB动态代理实现
1.业务类
代码如下(示例):
package com.bjtu;
public class Service {
public Service() {
System.out.println("=======构造方法========");
}
//final修饰,无法增强
final public void finalMethod() {
System.out.println("=========finalMethod========");
}
public String testMethod(String name) {
System.out.println("========testMethod==========");
return "name = " + name;
}
}
拦截器
package com.bjtu;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public String intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("========插入前置通知=======");
//通过代理类调用父类中的方法
String name = (String) methodProxy.invokeSuper(o, objects);
System.out.println("========后置通知======");
return name;
}
}
测试类
package com.bjtu;
import org.springframework.cglib.proxy.Enhancer;
public class Client {
public static void main(String[] args) {
//通过cglib动态代理获取代理对象的过程
Enhancer enhancer = new Enhancer();
//设置enhancer对象的父类
enhancer.setSuperclass(Service.class);
//设置enhancer的回调对象,拦截处理器
enhancer.setCallback(new MyMethodInterceptor());
//创建代理对象
Service proxy = (Service) enhancer.create();
//通过代理对象调用方法
String obj = proxy.testMethod("张三");
System.out.println(obj);
}
}
执行结果如下:
三、两者区别
Jdk动态代理:
必须有接口。创建接口实现类的代理对象,通过代理对象来增强其中的方法。
使用Proxy类,调用newProxyInstance方法,三个参数:类加载器,接口,实例(InvocationHandler的实现类)
为什么jdk动态代理必须要有接口?
因为动态代理底层是生成字节码文件,通过修改字节码文件,从而达到动态代理的效果。字节码文件是二进制的我们看不懂,需要反编译。
问题来了,Java底层生成的$Proxy长这样。
jdk动态代理帮我们增强方法,生成代理类对象,字节码中的代理类 $Proxy 默认是继承接口Proxy的,而java是单继承的,所以必须有接口。
cglib是通过生成被代理对象的子类的方式,增强方法的,也就是拿到被代理对象的一个模板,对其进行增强。也就不需要接口了。
四、AOP使用的是哪种动态代理方式?
AOP在选择动态代理方式的逻辑如图中所示:
源码判断时候,if( || ||)三个条件。第一个判断isOptimize,默认为假;第二个判断proxyTargetClass,默认为假;第三个判断,是否为接口。
因此,当被增强类继承接口时,都是用jdk动态代理。
当被增强类不继承接口时,需要判断proxyTargetClass,不指定的话,默认为false。
指定为true才会走CGLIB动态代理。