Spring AOP proxy代理生成

来源:互联网 发布:mac os x 安装软件 编辑:程序博客网 时间:2024/05/17 23:25

1、Spring AOP几个概念阐述

1.通知(Advice):

通知定义了切面是什么以及何时使用。描述了切面要完成的工作和何时需要执行这个工作。

2.连接点(Joinpoint):

程序能够应用通知的一个“时机”,这些“时机”就是连接点,例如方法被调用时、异常被抛出时等等。

3.切入点(Pointcut)

通知定义了切面要发生的“故事”和时间,那么切入点就定义了“故事”发生的地点,例如某个类或方法的名称,Spring中允许我们方便的用正则表达式来指定

4.切面(Aspect)

通知和切入点共同组成了切面:时间、地点和要发生的“故事”

5.引入(Introduction)

引入允许我们向现有的类添加新的方法和属性(Spring提供了一个方法注入的功能)

6.目标(Target)

即被通知的对象,如果没有AOP,那么它的逻辑将要交叉别的事务逻辑,有了AOP之后它可以只关注自己要做的事(AOP让他做爱做的事)

7.代理(proxy)

应用通知的对象,详细内容参见设计模式里面的代理模式

8.织入(Weaving)

把切面应用到目标对象来创建新的代理对象的过程,织入一般发生在如下几个时机:

(1)编译时:当一个类文件被编译时进行织入,这需要特殊的编译器才可以做的到,例如AspectJ的织入编译器

(2)类加载时:使用特殊的ClassLoader在目标类被加载到程序之前增强类的字节代码

(3)运行时:切面在运行的某个时刻被织入,SpringAOP就是以这种方式织入切面的,原理应该是使用了JDK的动态代理技术

9.通知器(Advisor)

定义应该使用哪个通知(advice)并在哪个关注点(pointcut)使用它,也就是通过Advisor把advice以及pointcut结合起来。

在spring中一个典型的advisor就是DefaultPointcutAdvisor,如下代码所示:

public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor implements Serializable {private Pointcut pointcut = Pointcut.TRUE;/** * Create a DefaultPointcutAdvisor, specifying Pointcut and Advice. * @param pointcut the Pointcut targeting the Advice * @param advice the Advice to run when Pointcut matches */public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {this.pointcut = pointcut;setAdvice(advice);}

它的两个属性就是pointcut以及advice,这里pointcut的实现是一个典型的懒汉单例模式实现,看其代码如下:

class TruePointcut implements Pointcut, Serializable {public static final TruePointcut INSTANCE = new TruePointcut();/** * Enforce Singleton pattern. */private TruePointcut() {}

使用static变量确保其只会被初始化一次,同时将类的构造函数设置为私有也保证了在当前的TruePointcut当中。

在pointcut当中有两个方法分别是classfilter和methodMatcher,这两个对象实现也是类似的单例模式。单例模式确保了资源的单一性,在这里使用单例模式有点作为util使用的含义。

2、AOPProxy代理对象的建立

在spring的AOP实现原理中一个重要的设计模式就是代理模式,那么代理模式里面最重要的一个点就是proxy,这里就阐述下这个proxy的建立过程。

在这个继承关系中,最底层的三个proxy类都有共同的三个基类: ProxyConfig、AdvisedSupport、ProxyCreatorSupport。

ProxyConfig:可以看做是一个数据基类,它为最底层的三个子类提供配置属性。

AdvisedSupport:封装了AOP对通知及通知器的相关操作,对于不同的aop代理对象的生成是一样的,对于具体的aop对象的创建就交给子类去操作。

ProxyCreatorSupport:是子类创建aop对象的辅助类。

 

AOP proxy创建时序图

从时序图可以看出首先是初始化advisorchain,就是从defaultListableBeanFactory中获取advisor的配置信息,并将它加入到一个List当中。主要代码逻辑如下:

for (String name : this.interceptorNames) {if (logger.isTraceEnabled()) {logger.trace("Configuring advisor or advice '" + name + "'");}if (name.endsWith(GLOBAL_SUFFIX)) {if (!(this.beanFactory instanceof ListableBeanFactory)) {throw new AopConfigException("Can only use global advisors or interceptors with a ListableBeanFactory");}addGlobalAdvisor((ListableBeanFactory) this.beanFactory,name.substring(0, name.length() - GLOBAL_SUFFIX.length()));}else {// If we get here, we need to add a named interceptor.// We must check if it's a singleton or prototype.Object advice;if (this.singleton || this.beanFactory.isSingleton(name)) {// Add the real Advisor/Advice to the chain.advice = this.beanFactory.getBean(name);}else {// It's a prototype Advice or Advisor: replace with a prototype.// Avoid unnecessary creation of prototype bean just for advisor chain initialization.advice = new PrototypePlaceholderAdvisor(name);}addAdvisorOnChainCreation(advice, name);}}

遍历interceptorNames,再依据name去ioc容器中查找到相应对象并且添加。如果非全局切面,那么就查找单个对应切面的类,并且判断是否是单例,如果是单例就直接从容器中获取,如果不是单例就额外处理。最后添加到advisor的chain当中。

接下来就是生成单例的代理对象:

private synchronized ObjectgetSingletonInstance() {                   if(this.singletonInstance == null) {                            this.targetSource= freshTargetSource();                            if(this.autodetectInterfaces && getProxiedInterfaces().length == 0&& !isProxyTargetClass()) {                                     //Rely on AOP infrastructure to tell us what interfaces to proxy.                                     ClasstargetClass = getTargetClass();                                     if(targetClass == null) {                                               thrownew FactoryBeanNotInitializedException("Cannot determine target class forproxy");                                     }                                     setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass,this.proxyClassLoader));                            }                            //Initialize the shared singleton instance.                            super.setFrozen(this.freezeProxy);                            this.singletonInstance= getProxy(createAopProxy());                   }                   returnthis.singletonInstance;         }

首先根据aop框架获取代理的接口,并设置代理接口,之后就是获取代理类。从上面的时序图也可以看到到了aopproxy的创建。

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {Class targetClass = config.getTargetClass();if (targetClass == null) {throw new AopConfigException("TargetSource cannot determine target class: " +"Either an interface or a target is required for proxy creation.");}if (targetClass.isInterface()) {return new JdkDynamicAopProxy(config);}if (!cglibAvailable) {throw new AopConfigException("Cannot proxy target class because CGLIB2 is not available. " +"Add CGLIB to the class path or specify proxy interfaces.");}return CglibProxyFactory.createCglibProxy(config);}else {return new JdkDynamicAopProxy(config);}}

创建首先判断是代理对象是否是接口,是接口的就使用jdk动态创建,其余的就采用cglib动态创建。其实最终就是创建一个Cglib2AopProxy或JdkDynamicAopProxy。也就是说着一步就是创建一个aopproxy的对象,接下来就是依据这个对象去生成我们需要的代理类。就是getproxy的过程。

JDK生成proxy对象的过程

public Object getProxy(ClassLoader classLoader) {if (logger.isDebugEnabled()) {logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());}Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);}

首先使用上面一步初始化设置的JdkDynamicAopProxy时设置的advised来加载proxy的接口,并依据这个实际接口和classload来实例化一个实际对象,具体的实现是Proxy.newProxyInstance(classLoader, proxiedInterfaces, this).在jdk的代理生成对象时,只需要传递一个InvocationHandler给到proxy,它的方法被调用时就会触发InvocationHandler的invoke方法,这个invoke方法的重点逻辑如下:

List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);if (chain.isEmpty()) {retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);}else {invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);retVal = invocation.proceed();}

invocation.proceed();就会将chain里面的切面执行到。

CGLIB的proxy创建:

// Configure CGLIB 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.setStrategy(new UndeclaredThrowableStrategy(UndeclaredThrowableException.class));enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));enhancer.setInterceptDuringConstruction(false);Callback[] callbacks = getCallbacks(rootClass);enhancer.setCallbacks(callbacks);enhancer.setCallbackFilter(new ProxyCallbackFilter(this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));Class[] types = new Class[callbacks.length];for (int x = 0; x < types.length; x++) {types[x] = callbacks[x].getClass();}enhancer.setCallbackTypes(types);// Generate the proxy class and create a proxy instance.Object proxy;if (this.constructorArgs != null) {proxy = enhancer.create(this.constructorArgTypes, this.constructorArgs);}else {proxy = enhancer.create();}return proxy;

就是使用enhance来设置Callbacks,当method被触发时。这些回调会被触发执行。所以他们的基本原理是相同的,只是jdk的是包装在里面进行invoke回调,而cglib的是显式的设置回调。

3、AOPProxy的设计模式

在aopproxy的设计中上面已经说到主要采用了代理模式。代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。代理模式的uml图如下:

在aopproxy中所谓的proxy就是aopproxy所生成的各类系统的注入对象,而request就是对象定义的切点,在这个切点被调用的时候首先会被aopproxy所生成的一个代理对象所捕获,先需要执行aopproxy在配置中所定义的advice,例如dobefore,doafter或dothrows等等。而这个执行过程对于客户端调用来说是透明的。

 

代理模式的优点:

能够协调调用者和被调用者,在一定程度上降低了系统的耦合度。

 

代理模式的缺点

由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。

实现代理模式需要额外的工作,有些代理模式的实现非常复杂。









0 0
原创粉丝点击