21 动态代理详解
Spring 中使用到动态代理的地方非常多,如Aop,Transaction事务等。后面文章准备些Aop相关的内容,本章写一下动态代理相关的内容。
本文内容
- 何为代理模式?
- JDK代理详解
- cglib代理详解
何为代理模式?
来回顾下设计模式之代理模式场景和概念?
在实际生活中,我们经常见到这样的场景,如:租房中介、售票黄牛、婚介、经纪人、快递、事务代理、非侵入式日志监听等,这些都是代理模式的实际体现。代理模式的定义也非常简单,是指为其它对象提供一种代理,以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用,代理模式属于结构性设计模式。使用代理模式主要有两个目的:一是保护目标对象,二是增强目标对象。
那么java中如何创建代理呢?常见的是下面的JDK代理和cglib代理。
JDK代理详解
JDK自带的代理只能为接口创建代理类,无法为具体类创建代理,为具体类创建代理可以使用后面的Cglib。
JDK实现动态代理的核心类是java.lang.reflect.Proxy
和``java.lang.reflect.InvocationHandler`
Proxy 详解
Proxy 提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。简单过一下几个概念。
动态代理类:一个实现类,在运行时创建指定的接口列表的类
代理接口:一个由代理类实现的接口
代理实例:是代理类的实例。每个代理实例都有一个关联的调用处理程序对象,该对象实现了接口InvocationHandler 。通过其代理接口之一对代理实例的方法调用将被分派到实例的调用处理程序的invoke方法,传递代理实例、标识被调用方法的java.lang.reflect.Method对象和数组包含参数的Object类型。调用处理程序适当地处理编码的方法调用,并且它返回的结果将作为代理实例上的方法调用的结果返回。
为某些接口Foo创建代理如下
public class FooProxyCreator {
public static void main(String[] args) {
//1 定义一个 InvocationHandler
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("invocationHandler 当前调用方式是:" + method.getName());
return null;
}
};
// 2 Proxy.newProxyInstance 创建代理类实例
Object proxyInstance = Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class[]{Foo.class
}, invocationHandler);
// 3 代理类实例调用接口方法
Foo foo = (Foo) proxyInstance;
foo.hello("xxx");
System.out.println("输出代理对象的类型:" + proxyInstance.getClass());
}
}
观察下输出结果
invocationHandler 当前调用方式是:hello
输出代理对象的类型:class com.sun.proxy.$Proxy0
方法被拦截了。注意JDK生成的代理类一般类名带"com.sun.proxy.$Proxy0"字样。
源码和关键方法说明如下
public class Proxy implements java.io.Serializable {
// 1 创建代理实例
// 参数:类加载器 被代理的接口类 调用处理器
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException{...}
// 2 判断给定类是否是代理类
public static boolean isProxyClass(Class<?> cl)
// 3 获取代理实例的调用处理器
public static InvocationHandler getInvocationHandler(Object proxy)
throws IllegalArgumentException
}
InvocationHandler 详解
InvocationHandler是代理实例的调用处理程序实现的接口。每个代理实例都有一个关联的调用处理程序。**当在代理实例上调用方法时,方法调用被编码并分派到其调用处理程序的invoke方法。**记住,这一点很关键!!!
public interface InvocationHandler {
// 处理代理实例上的方法调用并返回结果。当在与其关联的代理实例上调用方法时,将在调用处理程序上调用此方法。
// proxy – 调用该方法的代理实例
// method – Method实例对应于代理实例上调用的接口方法。Method对象的声明类将是声明该方法的接口
// args – 一个对象数组,其中包含在代理实例上的方法调用中传递的参数值,如果接口方法不接受任何参数,则 null , 原始类型的参数被包装在适当的原始包装类的实例中
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
综合案例
我们使用JDK代理来实现一个接口方法调用的耗时监视器。
监视器类
/**
* 方法耗时监控器
*
* @author zfd
* @version v1.0
* @date 2022/1/25 11:43
*/
public class TimeCostMonitor implements InvocationHandler {
private Object target;
public TimeCostMonitor(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.nanoTime();
// 1 原始业务:调用目标方法
Object result = method.invoke(this.target, args);
long end = System.nanoTime();
// 2 新增统计耗时业务执行
System.out.println(this.target.getClass() + " 方法 " + method.getName() + " 耗时纳秒:" + (end - start));
return result;
}
/**
* 创建代理实例
* @param target 被代理对象,必须实现被代理接口,否则异常
* @param targetInterface 被代理接口,必须是接口否则异常
* @param <T> 接口类型
* @return 接口实现类实例
*/
public static <T> T createProxy(Object target, Class<T> targetInterface) {
if (!targetInterface.isInterface()) {
throw new IllegalStateException("targetInterface必须是接口类型");
}
if (!targetInterface.isAssignableFrom(target.getClass())) {
throw new IllegalStateException("target必须实现targetInterface接口");
}
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new TimeCostMonitor(target));
}
}
模拟数据库存储层的统计耗时
public interface Repository<T> {
void add(T t);
int delete(Long id);
}
public class OrderRepository implements Repository<String>{
@Override
public void add(String s) {
System.out.println("OrderRepository add");
}
@Override
public int delete(Long id) {
System.out.println("OrderRepository delete");
return 1;
}
}
测试程序和结果
@Test
public void createProxy() {
Repository repository = TimeCostMonitor.createProxy(new OrderRepository(), Repository.class);
repository.add("xx");
repository.delete(100L);
}
OrderRepository add
class com.crab.spring.ioc.demo18.OrderRepository 方法 add 耗时纳秒:54700
OrderRepository delete
class com.crab.spring.ioc.demo18.OrderRepository 方法 delete 耗时纳秒:19200
cglib代理详解
cglib是什么
开源地址:https://github.com/cglib/cglib/wiki
cglib是一个功能强大、高性能和高质量的代码生成库。它用于在运行时扩展Java类和实现接口。CGLIB底层使用了ASM(一个短小精悍的字节码操作框架)来操作字节码生成新的类。
本质上它是通过动态的生成一个子类去覆盖所要代理的类(非final修饰的类和方法)。
Spring已将第三方cglib jar包中所有的类集成到spring自己的jar包中,本文使用是Spring内部集成的。
在Spring 中使用 cglib代理最常用的2个类:
- Enhancer:生成动态子类以启用方法拦截。此类开始是作为 JDK 1.3 中包含的标准动态代理支持的替代品,但它允许代理扩展具体的基类,除了实现接口。动态生成的子类覆盖超类的非最终方法,并具有回调到用户定义的拦截器实现的钩子。
- MethodInterceptor:类似
InvocationHandler
,代理实例任何方法调用时候都会被MethodInterceptor
中的intercept()
方法拦截处理。
生成和使用cglib代理的基本步骤如下:
- 创建Enhancer对象
- 设置父类型,即需要给哪个类创建代理类
- 设置回调
- 获取代理对象
- 调用代理对象的方法
源码简析
简单过一下 Enhancer 源码主要方法
public class Enhancer extends AbstractClassGenerator {
// 生成一个新类并使用指定的回调(如果有)来创建一个新的对象实例。使用超类的无参数构造函数。
public Object create() {}
// 生成一个新类并使用指定的回调(如果有)来创建一个新的对象实例。
// 使用与argumentTypes 参数匹配的超类的构造函数和给定的参数。
public Object create(Class[] argumentTypes, Object[] arguments) {}
// 设置生成的类将扩展的类。为方便起见,如果提供的超类实际上是一个接口,则将使用适当的参数调用 setInterfaces。
// 非接口参数不得声明为 final,并且必须具有可访问的构造函数。
public void setSuperclass(Class superclass) {}
// 设置要实现的接口
public void setInterfaces(Class[] interfaces) {
// 设置要使用的单一回调
public void setCallback(final Callback callback) {}
//设置要使用的回调数组 必须使用 CallbackFilter 为代理类中的每个方法指定此数组的索引
public void setCallbacks(Callback[] callbacks) {}
// 设置用于将生成的类的方法映射到特定回调索引的 CallbackFilte
public void setCallbackFilter(CallbackFilter filter) {}
}
简单过一下 MethodInterceptor 方法拦截器接口
public interface MethodInterceptor extends Callback {
/**
* 代理对象方法拦截器
* @param proxy 代理实例
* @param method 被代理的方法 OrderRepository中的方法
* @param args 方法参数
* @param methodProxy 方法代理对象
* @return
* @throws Throwable
*/
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {}
}
案例1拦截所有方法(含内部互相调用)
OrderRepository 类,注意方法 test() 中通过this调用了add() 和 delete() 方法
public class OrderRepository {
public void add(String s) {
System.out.println("OrderRepository add");
}
public int delete(Long id) {
System.out.println("OrderRepository delete");
return 1;
}
public int test() {
System.out.println("OrderRepository test");
this.add("ooo");
return this.delete(100L);
}
}
使用cglib拦截测试类
/**
* @author zfd
* @version v1.0
* @date 2022/1/25 15:03
*/
public class TestCglib {
@Test
public void test_intercept_all() {
// 1 创建Enhancer对象
Enhancer enhancer = new Enhancer();
// 2 设置父类型,即需要给哪个类创建代理类
enhancer.setSuperclass(OrderRepository.class);
// 3 设置回调MethodInterceptor
enhancer.setCallback(new MethodInterceptor() {
/**
* 代理对象方法拦截器
* @param proxy 代理实例
* @param method 被代理的方法 OrderRepository中的方法
* @param args 方法参数
* @param methodProxy 方法代理对象
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println(method + " 方法调用开始");
// MethodProxy#invokeSuper 调用被代理类的方法
Object result = methodProxy.invokeSuper(proxy, args);
System.out.println(method + " 方法调用结束");
return result;
}
});
// 4 获取代理对象
Object proxy = enhancer.create();
OrderRepository repository = (OrderRepository) proxy;
// 5 调用
repository.add("xxx");
repository.delete(100L);
// 注意test的特殊调用
repository.test();
System.out.println("输出代理对象的类型:" + proxy.getClass());
}
}
观察测试结果
// 对应 repository.add("xxx");
public void com.crab.spring.ioc.demo18.cglib.OrderRepository.add(java.lang.String) 方法调用开始
OrderRepository add
public void com.crab.spring.ioc.demo18.cglib.OrderRepository.add(java.lang.String) 方法调用结束
// 对应 repository.delete(100L);
public int com.crab.spring.ioc.demo18.cglib.OrderRepository.delete(java.lang.Long) 方法调用开始
OrderRepository delete
public int com.crab.spring.ioc.demo18.cglib.OrderRepository.delete(java.lang.Long) 方法调用结束
// 对应 repository.test();
public int com.crab.spring.ioc.demo18.cglib.OrderRepository.test() 方法调用开始
OrderRepository test
public void com.crab.spring.ioc.demo18.cglib.OrderRepository.add(java.lang.String) 方法调用开始
OrderRepository add
public void com.crab.spring.ioc.demo18.cglib.OrderRepository.add(java.lang.String) 方法调用结束
public int com.crab.spring.ioc.demo18.cglib.OrderRepository.delete(java.lang.Long) 方法调用开始
OrderRepository delete
public int com.crab.spring.ioc.demo18.cglib.OrderRepository.delete(java.lang.Long) 方法调用结束
public int com.crab.spring.ioc.demo18.cglib.OrderRepository.test() 方法调用结束
输出代理对象的类型:class com.crab.spring.ioc.demo18.cglib.OrderRepository$$EnhancerByCGLIB$$72f0698b
结论:无论是外部实例对象调用方法,或是方法内部通过this调用都可以拦截。
注意一下代理对象的类型,基本带有**"$$EnhancerByCGLIB$$"**字样的说明是cglib生成的代理。
案例2不同方法使用不同拦截器
实际场景中经常会存在不同的方法使用不同的拦截器进行拦截。如 add 方法使用拦截器1拦截,其它方法delete使用拦截器2拦截。可以使用帮助类 CallbackHelper
来实现。
/**
* 拦截所有方法,不同方法不同拦截器
*/
@Test
public void test_intercept_different() {
// 1 创建Enhancer对象
Enhancer enhancer = new Enhancer();
// 2 设置父类型,即需要给哪个类创建代理类
enhancer.setSuperclass(OrderRepository.class);
// 3 设置回调MethodInterceptor
// 拦截add 统计耗时
MethodInterceptor addInterceptor = (target, method, objects, methodProxy) -> {
long starTime = System.nanoTime();
Object result = methodProxy.invokeSuper(target, objects);
long endTime = System.nanoTime();
System.out.println(method + " 耗时纳秒:" + (endTime - starTime));
return result;
};
// 拦截其它 前后输出
MethodInterceptor otherInterceptor = (target, method, objects, methodProxy) -> {
System.out.println(method + " 方法调用开始");
Object result = methodProxy.invokeSuper(target, objects);
System.out.println(method + " 方法调用结束");
return result;
};
// 使用 CallbackHelper类来获取拦截器
CallbackHelper callbackHelper = new CallbackHelper(OrderRepository.class, null) {
@Override
protected Object getCallback(Method method) {
boolean isAdd = method.getName().startsWith("add");
return isAdd ? addInterceptor : otherInterceptor ;
}
};
// 指定拦截器的过滤
enhancer.setCallbackFilter(callbackHelper);
// 指定所有拦截器
enhancer.setCallbacks(callbackHelper.getCallbacks());
// 4 获取代理对象
OrderRepository repository = (OrderRepository) enhancer.create();
// 5 调用
repository.add("xxx");
repository.delete(100L);
// 注意test的特殊调用
repository.test();
}
代码注释很清晰了,来看下输出结果。
OrderRepository add
public void com.crab.spring.ioc.demo18.cglib.OrderRepository.add(java.lang.String) 耗时纳秒:11216000
public int com.crab.spring.ioc.demo18.cglib.OrderRepository.delete(java.lang.Long) 方法调用开始
OrderRepository delete
public int com.crab.spring.ioc.demo18.cglib.OrderRepository.delete(java.lang.Long) 方法调用结束
public int com.crab.spring.ioc.demo18.cglib.OrderRepository.test() 方法调用开始
OrderRepository test
OrderRepository add
public void com.crab.spring.ioc.demo18.cglib.OrderRepository.add(java.lang.String) 耗时纳秒:13800
public int com.crab.spring.ioc.demo18.cglib.OrderRepository.delete(java.lang.Long) 方法调用开始
OrderRepository delete
public int com.crab.spring.ioc.demo18.cglib.OrderRepository.delete(java.lang.Long) 方法调用结束
public int com.crab.spring.ioc.demo18.cglib.OrderRepository.test() 方法调用结束
案例3使用cglib实现方法耗时监视器
类似上一节的耗时监视器,使用cglib实现,可以尝试下。
案例4同时代理类和多个接口
cglib可以同时代理类和多个接口,比较简单,直接看案例。
package com.crab.spring.ioc.demo18.cglib2;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* @author zfd
* @version v1.0
* @date 2022/1/25 17:25
*/
public class ProxyClassMultiInterfaces {
interface Repository1 {
void m1();
}
interface Repository2 {
void m2();
}
static class OrderRepository implements Repository1, Repository2 {
@Override
public void m1() {
}
@Override
public void m2() {
}
public void add() {
}
}
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OrderRepository.class);
enhancer.setInterfaces(new Class[]{Repository1.class, Repository2.class});
enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
System.out.println("执行 " + method);
return methodProxy.invokeSuper(o, args);
});
Object proxy = enhancer.create();
if (proxy instanceof OrderRepository) {
OrderRepository repository = (OrderRepository) proxy;
repository.m1();
repository.m2();
repository.add();
}
System.out.println("代理对象的类型:" + proxy.getClass());
System.out.println("代理对象的直接父类:" + proxy.getClass().getSuperclass());
System.out.println("代理对象的实现的接口:");
Arrays.stream(proxy.getClass().getInterfaces()).forEach(System.out::println);
}
}
测试输出
执行 public void com.crab.spring.ioc.demo18.cglib2.ProxyClassMultiInterfaces$OrderRepository.m1()
执行 public void com.crab.spring.ioc.demo18.cglib2.ProxyClassMultiInterfaces$OrderRepository.m2()
执行 public void com.crab.spring.ioc.demo18.cglib2.ProxyClassMultiInterfaces$OrderRepository.add()
代理对象的类型:class com.crab.spring.ioc.demo18.cglib2.ProxyClassMultiInterfaces$OrderRepository$$EnhancerByCGLIB$$f155d747
代理对象的直接父类:class com.crab.spring.ioc.demo18.cglib2.ProxyClassMultiInterfaces$OrderRepository
代理对象的实现的接口:
interface com.crab.spring.ioc.demo18.cglib2.ProxyClassMultiInterfaces$Repository1
interface com.crab.spring.ioc.demo18.cglib2.ProxyClassMultiInterfaces$Repository2
interface org.springframework.cglib.proxy.Factory
实现的Factory 接口是 Enhancer 添加的。
案例5自定义代理对象类名称NamingPolicy接口
观察下上一个
样例:com.crab.spring.ioc.demo18.cglib2.ProxyClassMultiInterfaces$OrderRepository$$EnhancerByCGLIB$$f155d747
格式:被代理类全路径类名 + "$$" + 使用cglib处理的简单类名 + 自定义的tag + "$$" + key的hashcode
cglib通过 NamingPolicy 接口来命名代理对象的
public interface NamingPolicy {
public String getClassName(String prefix, String source, Object key, Predicate names);
boolean equals(Object var1);
}
默认实现是 DefaultNamingPolicy
public class DefaultNamingPolicy implements NamingPolicy {
public static final DefaultNamingPolicy INSTANCE = new DefaultNamingPolicy();
private static final boolean STRESS_HASH_CODE = Boolean.getBoolean("org.springframework.cglib.test.stressHashCodes");
public DefaultNamingPolicy() {
}
public String getClassName(String prefix, String source, Object key, Predicate names) {
if (prefix == null) {
prefix = "org.springframework.cglib.empty.Object";
} else if (prefix.startsWith("java")) {
prefix = "$" + prefix;
}
// 关键的命名格式
String base = prefix + "$$" + source.substring(source.lastIndexOf(46) + 1) + this.getTag() + "$$" + Integer.toHexString(STRESS_HASH_CODE ? 0 : key.hashCode());
String attempt = base;
for(int var7 = 2; names.evaluate(attempt); attempt = base + "_" + var7++) {
}
return attempt;
}
protected String getTag() {
return "ByCGLIB";
}
如果我们需要自定义代理对象名称命名,那么实现 NamingPolicy 接口或是继承 DefaultNamingPolicy。后者这种方式Spring提供了一个实现 SpringNamingPolicy 。
public class SpringNamingPolicy extends DefaultNamingPolicy {
public static final SpringNamingPolicy INSTANCE = new SpringNamingPolicy();
// 重写了getTag
@Override
protected String getTag() {
return "BySpringCGLIB";
}
}
案例6各种Callback扩展
前面案例基本的回调拦截器都是 MethodInterceptor ,基本可以满足绝大部分场景。实际 Callback 接口还有不少有用的实现,本节快速了解下。
各个实现的功能说明如下表
接口名 | 功能 |
---|---|
NoOp | 直接放行不做拦截 |
FixedValue | 拦截并返回固定值 |
LazyLoader | 实现懒加载,第一调用方法触发回调生成方法调用对象 |
Dispatcher | 类似 LazyLoader,但每次对增强bean进行方法调用都会触发回调 |
NoOp 案例
public class NoOpTest {
static class MyRepository{
public void m() {
// 输出类实例
System.out.println(this);
}
}
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyRepository.class);
enhancer.setCallback(NoOp.INSTANCE);
Object proxy = enhancer.create();
MyRepository myRepository = (MyRepository) proxy;
System.out.println("第1次 MyRepository m()");
myRepository.m();
System.out.println("第2次 MyRepository m()");
myRepository.m();
}
}
观察输出,无任何拦截
第1次 MyRepository m()
com.crab.spring.ioc.demo18.cglib2.NoOpTest$MyRepository$$EnhancerByCGLIB$$35fbb92b@6477463f
第2次 MyRepository m()
com.crab.spring.ioc.demo18.cglib2.NoOpTest$MyRepository$$EnhancerByCGLIB$$35fbb92b@6477463f
FixedValue案例
拦截方法执行,每次返回固定值。
public class FixedValueTest {
static class MyRepository{
public String m() {
// 输出类实例
System.out.println(this);
return "oooo";
}
}
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyRepository.class);
enhancer.setCallback(new FixedValue() {
@Override
public Object loadObject() throws Exception {
return "xxxxx";
}
});
Object proxy = enhancer.create();
MyRepository myRepository = (MyRepository) proxy;
System.out.println("第1次 MyRepository m()");
System.out.println(myRepository.m());
System.out.println("第2次 MyRepository m()");
System.out.println(myRepository.m());
}
观察输出,每次返回的都是固定的"xxxxx"而不是"oooo"
第1次 MyRepository m()
xxxxx
第2次 MyRepository m()
xxxxx
LazyLoader 案例
public class LazyLoaderTest {
static class MyRepository{
public void m() {
// 输出类实例
System.out.println(this);
}
}
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyRepository.class);
enhancer.setCallback(new LazyLoader() {
@Override
public Object loadObject() throws Exception {
System.out.println("回调 LazyLoader loadObject");
return new MyRepository();
}
});
Object proxy = enhancer.create();
MyRepository myRepository = (MyRepository) proxy;
System.out.println("第1次 MyRepository m()");
myRepository.m();
System.out.println("第2次 MyRepository m()");
myRepository.m();
}
}
观察结果中,loadObject() 方法执行的次数和执行方法的对象是否是同一个,
第1次 MyRepository m()
回调 LazyLoader loadObject
com.crab.spring.ioc.demo18.cglib2.LazyLoaderTest$MyRepository@6438a396
第2次 MyRepository m()
com.crab.spring.ioc.demo18.cglib2.LazyLoaderTest$MyRepository@6438a396
结论:loadObject() 方法执行1次,执行方法的对象是同一个。
Dispatcher案例
public class DispatcherTest {
static class MyRepository{
public void m() {
// 输出类实例
System.out.println(this);
}
}
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyRepository.class);
enhancer.setCallback(new Dispatcher() {
@Override
public Object loadObject() throws Exception {
System.out.println("回调 Dispatcher loadObject");
return new MyRepository();
}
});
Object proxy = enhancer.create();
MyRepository myRepository = (MyRepository) proxy;
System.out.println("第1次 MyRepository m()");
myRepository.m();
System.out.println("第2次 MyRepository m()");
myRepository.m();
}
观察结果中,loadObject() 方法执行的次数和执行方法的对象是否是同一个
第1次 MyRepository m()
回调 Dispatcher loadObject
com.crab.spring.ioc.demo18.cglib2.DispatcherTest$MyRepository@6438a396
第2次 MyRepository m()
回调 Dispatcher loadObject
com.crab.spring.ioc.demo18.cglib2.DispatcherTest$MyRepository@e2144e4
结论:loadObject() 方法执行2次,执行方法的对象非同一个。
总结
本文详解了JDK动态代理和cglib动态代理原理和使用,提供了大量的案例,建议多实践掌握,为后续Spring Aop打下扎实基础。