我们在前文中已经介绍了SpringAOP的切面实现和创建动态代理的过程,那么动态代理是如何工作的呢?本文主要介绍Cglib动态代理的案例和SpringAOP实现的原理。
要了解动态代理是如何工作的,首先需要了解
什么是代理?
代理模式(Proxy pattern): 为另一个对象提供一个替身或占位符以控制对这个对象的访问
举个简单的例子:
我(client)如果要买(doOperation)房,可以找中介(proxy)买房,中介直接和卖方(target)买房。中介和卖方都实现买卖(doOperation)的操作。中介就是代理(proxy)。
什么是动态代理?
动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术。
在生成代理对象的过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法。可以理解为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作。
Cglib是一个强大的、高性能的代码生成包,它广泛被许多AOP框架使用,为他们提供方法的拦截。
这里我们写一个使用cglib的简单例子。@pdai
引入cglib的依赖包
tech-pdai-spring-demos tech.pdai 1.0-SNAPSHOT 4.0.0 007-spring-framework-demo-aop-proxy-cglib 8 8 cglib cglib 3.3.0
User
package tech.pdai.springframework.entity; /** * @author pdai */ public class User { /** * user's name. */ private String name; /** * user's age. */ private int age; /** * init. * * @param name name * @param age age */ public User(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
即目标类, 对被代理的类中的方法进行增强
package tech.pdai.springframework.service; import java.util.Collections; import java.util.List; import tech.pdai.springframework.entity.User; /** * @author pdai */ public class UserServiceImpl { /** * find user list. * * @return user list */ public ListfindUserList() { return Collections.singletonList(new User("pdai", 18)); } /** * add user */ public void addUser() { // do something } }
cglib代理类,需要实现MethodInterceptor接口,并指定代理目标类target
package tech.pdai.springframework.proxy; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * This class is for proxy demo. * * @author pdai */ public class UserLogProxy implements MethodInterceptor { /** * 业务类对象,供代理方法中进行真正的业务方法调用 */ private Object target; public Object getUserLogProxy(Object target) { //给业务对象赋值 this.target = target; //创建加强器,用来创建动态代理类 Enhancer enhancer = new Enhancer(); //为加强器指定要代理的业务类(即:为下面生成的代理类指定父类) enhancer.setSuperclass(this.target.getClass()); //设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦 enhancer.setCallback(this); // 创建动态代理类对象并返回 return enhancer.create(); } // 实现回调方法 @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // log - before method System.out.println("[before] execute method: " + method.getName()); // call method Object result = proxy.invokeSuper(obj, args); // log - after method System.out.println("[after] execute method: " + method.getName() + ", return value: " + result); return null; } }
启动类中指定代理目标并执行。
package tech.pdai.springframework; import tech.pdai.springframework.proxy.UserLogProxy; import tech.pdai.springframework.service.UserServiceImpl; /** * Cglib proxy demo. * * @author pdai */ public class ProxyDemo { /** * main interface. * * @param args args */ public static void main(String[] args) { // proxy UserServiceImpl userService = (UserServiceImpl) new UserLogProxy().getUserLogProxy(new UserServiceImpl()); // call methods userService.findUserList(); userService.addUser(); } }
我们启动上述类main 函数,执行的结果如下:
[before] execute method: findUserList [after] execute method: findUserList, return value: [User{name='pdai', age=18}] [before] execute method: addUser [after] execute method: addUser, return value: null
我们把上述Demo的主要流程画出来,你便能很快理解
更多细节:
SpringAOP封装了cglib,通过其进行动态代理的创建。
我们看下CglibAopProxy的getProxy方法
@Override public Object getProxy() { return getProxy(null); } @Override public Object getProxy(@Nullable ClassLoader classLoader) { if (logger.isTraceEnabled()) { logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource()); } try { Class> rootClass = this.advised.getTargetClass(); Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy"); // 上面流程图中的目标类 Class> proxySuperClass = rootClass; if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) { proxySuperClass = rootClass.getSuperclass(); Class>[] additionalInterfaces = rootClass.getInterfaces(); for (Class> additionalInterface : additionalInterfaces) { this.advised.addInterface(additionalInterface); } } // Validate the class, writing log messages as necessary. validateClassIfNecessary(proxySuperClass, classLoader); // 重点看这里,就是上图的enhancer,设置各种参数来构建 Enhancer enhancer = createEnhancer(); if (classLoader != null) { enhancer.setClassLoader(classLoader); if (classLoader instanceof SmartClassLoader && ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) { enhancer.setUseCache(false); } } enhancer.setSuperclass(proxySuperClass); enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised)); enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader)); // 设置callback回调接口,即方法的增强点 Callback[] callbacks = getCallbacks(rootClass); Class>[] types = new Class>[callbacks.length]; for (int x = 0; x < types.length; x++) { types[x] = callbacks[x].getClass(); } // 上节说到的filter enhancer.setCallbackFilter(new ProxyCallbackFilter( this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset)); enhancer.setCallbackTypes(types); // 重点:创建proxy和其实例 return createProxyClassAndInstance(enhancer, callbacks); } catch (CodeGenerationException | IllegalArgumentException ex) { throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() + ": Common causes of this problem include using a final class or a non-visible class", ex); } catch (Throwable ex) { // TargetSource.getTarget() failed throw new AopConfigException("Unexpected AOP exception", ex); } }
获取callback的方法如下,提几个理解的要点吧,具体读者在学习的时候建议把我的例子跑一下,然后打一个断点进行理解。
rootClass advised exposeProxy DynamicAdvisedInterceptor targetInterceptor
private Callback[] getCallbacks(Class> rootClass) throws Exception { // Parameters used for optimization choices... boolean exposeProxy = this.advised.isExposeProxy(); boolean isFrozen = this.advised.isFrozen(); boolean isStatic = this.advised.getTargetSource().isStatic(); // Choose an "aop" interceptor (used for AOP calls). Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised); // Choose a "straight to target" interceptor. (used for calls that are // unadvised but can return this). May be required to expose the proxy. Callback targetInterceptor; if (exposeProxy) { targetInterceptor = (isStatic ? new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) : new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource())); } else { targetInterceptor = (isStatic ? new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) : new DynamicUnadvisedInterceptor(this.advised.getTargetSource())); } // Choose a "direct to target" dispatcher (used for // unadvised calls to static targets that cannot return this). Callback targetDispatcher = (isStatic ? new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp()); Callback[] mainCallbacks = new Callback[] { aopInterceptor, // targetInterceptor, // invoke target without considering advice, if optimized new SerializableNoOp(), // no override for methods mapped to this targetDispatcher, this.advisedDispatcher, new EqualsInterceptor(this.advised), new HashCodeInterceptor(this.advised) }; Callback[] callbacks; // If the target is a static one and the advice chain is frozen, // then we can make some optimizations by sending the AOP calls // direct to the target using the fixed chain for that method. if (isStatic && isFrozen) { Method[] methods = rootClass.getMethods(); Callback[] fixedCallbacks = new Callback[methods.length]; this.fixedInterceptorMap = CollectionUtils.newHashMap(methods.length); // TODO: small memory optimization here (can skip creation for methods with no advice) for (int x = 0; x < methods.length; x++) { Method method = methods[x]; List
可以结合调试,方便理解
https://github.com/realpdai/tech-pdai-spring-demos