Spring 提供了两种方式来生成代理对象: JDKProxy 和 Cglib,具体使用哪种方式生成由 AopProxyFactory 根据 AdvisedSupport 对象的配置来决定。默认的策略是如果目标类是接口,则使用 JDK 动态代理技术,否则使用 Cglib 来生成代理。
JDK 动态接口代理
前提声明一个接口TargetInterface,Target类实现这个接口,并实现接口方法,而后TargetProxy也实现这个接口,并实例化一个Target类—target,然后在实现的接口方法中调用target的方法,并在调用其书写执行前后的语句。
具体代码
接口类
public interface TargetInteface {
void method1();
void method2();
int method3(Integer i);
}
实现类
public class Target implements TargetInteface {
@Override
public void method1() {
System.out.println(" Target method1 running ...");
}
@Override
public void method2() {
System.out.println("Target method2 running ...");
}
@Override
public int method3(Integer i) {
System.out.println("Target method3 running ...");
return i;
}
}
代理类
public class TargetProxy implements TargetInteface {
private Target target =new Target();
@Override
public void method1() {
System.out.println("执行方法前...");
target.method1();
System.out.println("执行方法后...");
}
@Override
public void method2() {
System.out.println("执行方法前...");
target.method2();
System.out.println("执行方法后...");
}
@Override
public int method3(Integer i) {
System.out.println("执行方法前...");
int method3 = target.method3(i);
System.out.println("执行方法后...");
return method3;
}
}
测试类
public class TargetUser {
public static void main(String[] args) {
TargetInteface target = new TargetProxy();
target.method1();
System.out.println("-----------------------------");
target.method2();
System.out.println("-----------------------------");
System.out.println(target.method3(3));
}
}
结果输出
总结
静态代理非常简单,但是它的缺陷也是显然的,因为静态代理的代理关系在 IDE 编译时就确定了,如果接口改变了,不仅实现类要改变,代理类也要改变,代理类和接口之间的耦合非常严重。所以在现实实践中很少用到
JDK 动态代理主要涉及到 java.lang.reflect 包中的两个类: Proxy 和 InvocationHandler 。
InvocationHandler 是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动 态将横切逻辑和业务逻辑编制在一起。Proxy 利用 InvocationHandler 动态创建一个符合某一接口的实 例,生成目标类的代理对象。
实例代码
主要修改的代理类和测试类
代理类
public class TargetProxy {
public static <T> Object getTarget(T t) {
return Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// proxy就是目标对象t,method就是调用目标对象中方法,args就是调用目标对象中方法的参数。
//比如说:代理对象.method1(),这时proxy就是目标类,method1就是method,args就是method1方法参数。
System.out.println("执行方法前...");
Object invoke = method.invoke(t, args);
System.out.println("执行方法后...");
return invoke;
}
});
}
}
测试类
public class TargetUser {
public static void main(String[] args) {
TargetInteface target = (TargetInteface) TargetProxy.getTarget(new Target());
target.method1();
System.out.println("-----------------------------");
target.method2();
System.out.println("-----------------------------");
System.out.println(target.method3(3));
}
}
测试结果
总结
利用JDK实现动态代理,本质就是利用 Java 的反射机制在程序运行期动态的创建接口的实现类,并生产代理对象而已
涉及两个类InvocationHandler 和 Proxy
缺点:JDK 动态代理这种方式只能代理通过接口定义业务方法的类,这是其缺陷
CGLib 全称为 Code Generation Library ,是一个强大的高性能,高质量的代码生成类库,可以 在运行期扩展 Java 类与实现 Java 接口, CGLib 封装了 asm ,可以在运行期动态生成新的 class 。 和 JDK 动态代理相比较: JDK 创建代理有一个限制,就是只能为接口创建代理实例,而对于没有通 过接口定义业务方法的类,则可以通过 CGLib 创建动态代理。
实例代码
首先,CGLib 代理的类不需要实现接口,代理类使用Enhancer生成代理对象,并指定目标类以及代理的行为
public class TargetProxy {
public static <T> Object getProxy(T t) {
Enhancer en = new Enhancer(); //帮我们生成代理对象
en.setSuperclass(t.getClass());//设置要代理的目标类
en.setCallback(new MethodInterceptor() {//代理要做什么
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("执行方法前。。。");
//调用原有方法
Object invoke = methodProxy.invokeSuper(object, args);
// Object invoke = method.invoke(t, args);// 作用等同与上面。
System.out.println("执行方法后。。。");
return invoke;
}
});
return en.create();
}
}
测试
public class TargetUser {
public static void main(String[] args) {
Target target = (Target) TargetProxy.getProxy(new Target());
System.out.println(target.getClass().getName());
target.method1();
}
}
结果输出
总结
不足:无法对 final 类、或者 final 方法进行代理