JDK
只能针对接口代理
public class JdkProxy {
interface Foo{
void foo();
}
static class Target implements Foo{
@Override
public void foo() {
System.out.println("foo ...");
}
}
public static void main(String[] args) {
//目标对象
Target target = new Target();
//用来加载运行期间代理生成的源代码
ClassLoader classLoader = JdkProxy.class.getClassLoader();
Foo fooProxy = (Foo)Proxy.newProxyInstance(classLoader, new Class[]{Foo.class}, (proxy, method, args1) -> {
System.out.println("before...");
// 目标.方法(参数)
//方法.invoke(目标,参数)
Object res = method.invoke(target, args1);
System.out.println("after...");
//代理返回目标方法执行的结果
return res;
});
fooProxy.foo();
}
}
说明:
- 代理对象和目标对象是平级的关系,不能相互强转,即使目标对象被final修饰,也不影响代理对象的创建和执行
cglib
- 基于子父继承关系生成代理
- 代理类是子类型,目标是父类型
- 目标不能是final 类 ,并且里面的方法也不能被增强
- 执行原始方法 通过cglib代理有三种方式
- 通过反射 需要 目标对象
- 内部不是用反射 需要 目标对象
Spring默认使用的就是这种
- 内部不是用反射 不需要 目标对象 需要代理对象
- 最后两种没用反射的调用的效率高于使用反射调用的方式
- 下面通过代码演示,关键信息都通过注释方式展现出来了
public class cglibProxy {
static class Target{
public void foo(){
System.out.println("target foo ...");
}
}
public static void main(String[] args) {
/*
四个参数
1.Object p 代表代理类自己
2.Method method 代理类中执行的方法
3.Object[] args 方法执行时的实际参数
4.MethodProxy methodProxy 方法对象 避免反射来调用目标方法 内部没有用反射
*/
Target target = new Target();
//因为cglib代理和jdk代理不一样,jdk代理只能代理接口
//所以我们定义的目标对象和代理对象之间时 平级(兄弟)关系,在这种情况下,代理对象不能转为目标对象
//但是cglib代理和jdk代理不同,cglib代理 可以理解为 父子继承式 的代理,所以这个代理对象可以强转为父
//final修饰类不能被继承,修饰方法不可被重写
//此时这个代理相当于一个 子类,如果被代理的父类使用final修饰,那么就会报错~~~
//IllegalArgumentException: Cannot subclass final class com.itheima.demo.cglibProxy$Target
//如果我们在父类的方法上添加final来修饰方法,那么这个代理方法就会失效,仅仅会调用父类的方法,不能达到增强的效果
Target tarProxy = (Target) Enhancer.create(Target.class, (MethodInterceptor) (p, method, args2, methodProxy) -> {
System.out.println("before...");
Object res = method.invoke(target,args2);
// Object res = methodProxy.invoke(target, args2); //内部没用反射 依赖目标 Spring用的就是这种方式
// Object res = methodProxy.invokeSuper(p, args2); //内部没用反射 依赖代理
System.out.println("after...");
return res;
});
tarProxy.foo();
}
}
代理类 普通类区别
普通类:先写Java源代码,—> Java字节码 —> 类加载 使用
代理类:没有源码,在运行期间 直接生成代理类的源码,但是需要加载后才能运行
那怎么加载呢? 通过 classLoader类加载器