咱们在前文中曾经介绍了SpringAOP的切面实现和创立动静代理的过程,那么动静代理是如何工作的呢?本文次要介绍Cglib动静代理的案例和SpringAOP实现的原理。@pdai
Spring框架系列(11) - Spring AOP实现原理详解之Cglib代理实现
引入
动静代理要解决什么问题?
- 什么是代理?
- 什么是动静代理?
- 什么是Cglib? SpringAOP和Cglib是什么关系?
Cglib代理的案例
- pom包依赖
- 定义实体
- 被代理的类
- cglib代理
- 应用代理
- 简略测试
- Cglib代理的流程
- SpringAOP中Cglib代理的实现
- 示例源码
- 更多文章
引入
咱们在前文中曾经介绍了SpringAOP的切面实现和创立动静代理的过程,那么动静代理是如何工作的呢?本文次要介绍Cglib动静代理的案例和SpringAOP实现的原理。
要理解动静代理是如何工作的,首先须要理解
- 什么是代理模式?
- 什么是动静代理?
- 什么是Cglib?
- SpringAOP和Cglib是什么关系?
动静代理要解决什么问题?
什么是代理?
代理模式(Proxy pattern): 为另一个对象提供一个替身或占位符以管制对这个对象的拜访
举个简略的例子:
我(client)如果要买(doOperation)房,能够找中介(proxy)买房,中介间接和卖方(target)买房。中介和卖方都实现交易(doOperation)的操作。中介就是代理(proxy)。
什么是动静代理?
动静代理就是,在程序运行期,创立指标对象的代理对象,并对指标对象中的办法进行功能性加强的一种技术。
在生成代理对象的过程中,指标对象不变,代理对象中的办法是指标对象办法的加强办法。能够了解为运行期间,对象中办法的动静拦挡,在拦挡办法的前后执行性能操作。
什么是Cglib? SpringAOP和Cglib是什么关系?
Cglib是一个弱小的、高性能的代码生成包,它宽泛被许多AOP框架应用,为他们提供办法的拦挡。
- 最顶层是字节码,字节码相干的常识请参考 JVM根底 - 类字节码详解
- ASM是操作字节码的工具
- cglib基于ASM字节码工具操作字节码(即动静生成代理,对办法进行加强)
- SpringAOP基于cglib进行封装,实现cglib形式的动静代理
Cglib代理的案例
这里咱们写一个应用cglib的简略例子。@pdai
pom包依赖
引入cglib的依赖包
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>tech-pdai-spring-demos</artifactId> <groupId>tech.pdai</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>007-spring-framework-demo-aop-proxy-cglib</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <!-- https://mvnrepository.com/artifact/cglib/cglib --> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency> </dependencies></project>
定义实体
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 List<User> findUserList() { return Collections.singletonList(new User("pdai", 18)); } /** * add user */ public void addUser() { // do something }}
cglib代理
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
Cglib代理的流程
咱们把上述Demo的次要流程画进去,你便能很快了解
更多细节:
- 在上图中,咱们能够通过在Enhancer中配置更多的参数来管制代理的行为,比方如果只心愿加强这个类中的一个办法(而不是所有办法),那就减少callbackFilter来对指标类中办法进行过滤;Enhancer能够有更多的参数类配置其行为,不过咱们在学习上述次要的流程就够了。
- final办法为什么不能被代理?很显然final办法没法被子类笼罩,当然不能代理了。
- Mockito为什么不能mock静态方法?因为mockito也是基于cglib动静代理来实现的,static办法也不能被子类笼罩,所以显然不能mock。但PowerMock能够mock静态方法,因为它间接在bytecode上工作,更多能够看Mockito单元测试。(pdai: 通了没?是不是so easy...)
SpringAOP中Cglib代理的实现
SpringAOP封装了cglib,通过其进行动静代理的创立。
咱们看下CglibAopProxy的getProxy办法
@Overridepublic Object getProxy() { return getProxy(null);}@Overridepublic 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
: 蕴含上文中咱们获取到的advisor增强器的汇合exposeProxy
: 在xml配置文件中配置的,背景就是如果在事务A中应用了代理,事务A调用了指标类的的办法a,在办法a中又调用指标类的办法b,办法a,b同时都是要被加强的办法,如果不配置exposeProxy属性,办法b的加强将会生效,如果配置exposeProxy,办法b在办法a的执行中也会被加强了DynamicAdvisedInterceptor
: 拦截器将advised(蕴含上文中咱们获取到的advisor增强器)构建配置的AOP的callback(第一个callback)targetInterceptor
: xml配置的optimize属性应用的(第二个callback)- 最初连同其它5个默认的Interceptor 返回作为cglib的拦截器链,之后通过CallbackFilter的accpet办法返回的索引从这个汇合中返回对应的拦挡增强器执行加强操作。
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<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, rootClass); fixedCallbacks[x] = new FixedChainStaticTargetInterceptor( chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass()); this.fixedInterceptorMap.put(method, x); } // Now copy both the callbacks from mainCallbacks // and fixedCallbacks into the callbacks array. callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length]; System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length); System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length); this.fixedInterceptorOffset = mainCallbacks.length; } else { callbacks = mainCallbacks; } return callbacks;}
能够联合调试,不便了解
示例源码
https://github.com/realpdai/t...
更多文章
首先, 从Spring框架的整体架构和组成对整体框架有个认知。
Spring根底 - Spring和Spring框架组成
- Spring是什么?它是怎么诞生的?有哪些次要的组件和外围性能呢? 本文通过这几个问题帮忙你构筑Spring和Spring Framework的整体认知。
其次,通过案例引出Spring的外围(IoC和AOP),同时对IoC和AOP进行案例应用剖析。
Spring根底 - Spring简略例子引入Spring的外围
- 上文中咱们简略介绍了Spring和Spring Framework的组件,那么这些Spring Framework组件是如何配合工作的呢?本文次要承接上文,向你展现Spring Framework组件的典型利用场景和基于这个场景设计出的简略案例,并以此引出Spring的外围要点,比方IOC和AOP等;在此基础上还引入了不同的配置形式, 如XML,Java配置和注解形式的差别。
Spring根底 - Spring外围之管制反转(IOC)
- 在Spring根底 - Spring简略例子引入Spring的外围中向你展现了IoC的根底含意,同时以此发散了一些IoC相干知识点; 本节将在此基础上进一步解读IOC的含意以及IOC的应用形式
Spring根底 - Spring外围之面向切面编程(AOP)
- 在Spring根底 - Spring简略例子引入Spring的外围中向你展现了AOP的根底含意,同时以此发散了一些AOP相干知识点; 本节将在此基础上进一步解读AOP的含意以及AOP的应用形式。
基于Spring框架和IOC,AOP的根底,为构建下层web利用,须要进一步学习SpringMVC。
Spring根底 - SpringMVC申请流程和案例
- 前文咱们介绍了Spring框架和Spring框架中最为重要的两个技术点(IOC和AOP),那咱们如何更好的构建下层的利用呢(比方web 利用),这便是SpringMVC;Spring MVC是Spring在Spring Container Core和AOP等技术根底上,遵循上述Web MVC的标准推出的web开发框架,目标是为了简化Java栈的web开发。 本文次要介绍SpringMVC的申请流程和根底案例的编写和运行。
Spring进阶 - IoC,AOP以及SpringMVC的源码剖析
Spring进阶 - Spring IOC实现原理详解之IOC体系结构设计
- 在对IoC有了初步的认知后,咱们开始对IOC的实现原理进行深刻了解。本文将帮忙你站在设计者的角度去看IOC最顶层的结构设计
Spring进阶 - Spring IOC实现原理详解之IOC初始化流程
- 上文,咱们看了IOC设计要点和设计构造;紧接着这篇,咱们能够看下源码的实现了:Spring如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中的
Spring进阶 - Spring IOC实现原理详解之Bean实例化(生命周期,循环依赖等)
- 上文,咱们看了IOC设计要点和设计构造;以及Spring如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中的;容器中寄存的是Bean的定义即BeanDefinition放到beanDefinitionMap中,实质上是一个
ConcurrentHashMap<String, Object>
;并且BeanDefinition接口中蕴含了这个类的Class信息以及是否是单例等。那么如何从BeanDefinition中实例化Bean对象呢,这是本文次要钻研的内容?
- 上文,咱们看了IOC设计要点和设计构造;以及Spring如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中的;容器中寄存的是Bean的定义即BeanDefinition放到beanDefinitionMap中,实质上是一个
Spring进阶 - Spring AOP实现原理详解之切面实现
- 前文,咱们剖析了Spring IOC的初始化过程和Bean的生命周期等,而Spring AOP也是基于IOC的Bean加载来实现的。本文次要介绍Spring AOP原理解析的切面实现过程(将切面类的所有切面办法依据应用的注解生成对应Advice,并将Advice连同切入点匹配器和切面类等信息一并封装到Advisor,为后续交给代理加强实现做筹备的过程)。
Spring进阶 - Spring AOP实现原理详解之AOP代理
- 上文咱们介绍了Spring AOP原理解析的切面实现过程(将切面类的所有切面办法依据应用的注解生成对应Advice,并将Advice连同切入点匹配器和切面类等信息一并封装到Advisor)。本文在此基础上持续介绍,代理(cglib代理和JDK代理)的实现过程。
Spring进阶 - Spring AOP实现原理详解之Cglib代理实现
- 咱们在前文中曾经介绍了SpringAOP的切面实现和创立动静代理的过程,那么动静代理是如何工作的呢?本文次要介绍Cglib动静代理的案例和SpringAOP实现的原理。
Spring进阶 - Spring AOP实现原理详解之JDK代理实现
- 上文咱们学习了SpringAOP Cglib动静代理的实现,本文次要是SpringAOP JDK动静代理的案例和实现局部。
Spring进阶 - SpringMVC实现原理之DispatcherServlet初始化的过程
- 前文咱们有了IOC的源码根底以及SpringMVC的根底,咱们便能够进一步深刻了解SpringMVC次要实现原理,蕴含DispatcherServlet的初始化过程和DispatcherServlet解决申请的过程的源码解析。本文是第一篇:DispatcherServlet的初始化过程的源码解析。
Spring进阶 - SpringMVC实现原理之DispatcherServlet解决申请的过程
- 前文咱们有了IOC的源码根底以及SpringMVC的根底,咱们便能够进一步深刻了解SpringMVC次要实现原理,蕴含DispatcherServlet的初始化过程和DispatcherServlet解决申请的过程的源码解析。本文是第二篇:DispatcherServlet解决申请的过程的源码解析。