浏览 2794 次
锁定老帖子 主题:Spring Aop 之 CGLIB浅析
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2010-04-25
最后修改:2010-05-24
方式1。BusinessAction直接重写execute的情况 BusinessAction类没有实现任何接口,并且没有默认构造函数的情况下,通过构造函数注入时,目前的Spring是无法实现AOP切面拦截的。此时,若要被AOP切面拦截,需要默认的构造函数,具体问题及分析见后面分析。 BusinessAction重写execute方法,可以被AOP拦截。 BusinessAction.java public class BusinessAction extends Action{ private String name; public BusinessAction (){} public BusinessAction (String name){ this.name = name; } @Override @LogonRequridAnnotation("这是Aspect的pointcut") @Privilege(userType=PrivilegeType.Admin,message="需管理员权限!") public ActionForward execute(ActionMapping mapping,ActionForm actionform, HttpServletRequest request,HttpServletResponse response) throws Exception { //代码略 } } 注解AspectJ Bean LogonAspectInterceptor.java如下 @Aspect public class LogonAspectInterceptor implements Ordered { private int order = 1; public int getOrder() { return this.order; } public void setOrder(int order) { this.order = order; } @Pointcut("@annotation (LogonRequridAnnotation)") public void pointCutMethod() { } @Around("pointCutMethod()") public Object doAround(ProceedingJoinPoint pjp) throws Throwable { doSomething(); return (ActionForward)pjp.proceed(); } } appliaction.xml片段 <aop:aspectj-autoproxy proxy-target-class="true"/> <bean name="/BusinessActionPath" class="com.xxx.core.struts.action.BusinessAction" scope="prototype"> <!-- AspectJ Bean --> <bean id="logonRequired" class="com.megaeyes.msp.plugins.commons.interceptor.LogonAspectInterceptor"> <property name="order" value="100"></property> </bean> <!--自动代理 --> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" /> @LogonRequridAnnotation与@Privilege,此处略过。注解加自动代理,精简了很多XML配置,简单的权限管理也采用注解方式。 此时,BusinessAction重写了execute方法,AOP拦截一切正常。 方式2。BusinessAction继承业务父类BaseAction BaseAction.java如下 public abstract class BaseAction extends Action { public BaseAction () {} @Override public final ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { try { doSomeThing(); return doExecute(mapping, form, request, response); } catch (Exception ex) { ex.printStackTrace(); log.error(ex); return doException(ex, mapping, form, request, response); } } public abstract ActionForward doException(Exception ex, ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { return mapping.findForward("ErrorsPage"); } public abstract ActionForward doExecute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception; } BusinessAction.java继承此BaseAction,实现doExecute方法,代码如下: public class BusinessAction extends BaseAction{ @Override @LogonRequridAnnotation("这是Aspect的pointcut") @Privilege(userType=PrivilegeType.Admin,message="需管理员权限!") public ActionForward doExecute(ActionMapping mapping,ActionForm actionform, HttpServletRequest request,HttpServletResponse response) throws Exception { //代码略 } } appliaction.xml的配置无变化。 BusinessAction的doExecute方法能被AOP 切面拦截,此时,如果去掉 BaseAction.execute方法的final关键字,那么AOP将无法拦截BusinessAction的doExecute方法。 开始分析 我们将LOG的级别调整为DEBUG级别,会看到很多细节,对比SPRING的Cglib2AopProxy类,可以通过日志查找到一些端倪。 1模式下,为何需要无参构造函数? 因为其通过有参的构造函数注入了属性,而不是set/get. 首先来看Cglib2AopProxy.java getProxy的完整代码段 public Object getProxy(ClassLoader classLoader) { if (logger.isDebugEnabled()) { logger.debug("Creating CGLIB2 proxy: target source is " + 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 (AopUtils.isCglibProxyClass(rootClass)) { proxySuperClass = rootClass.getSuperclass(); Class[] additionalInterfaces = rootClass.getInterfaces(); for (int i = 0; i < additionalInterfaces.length; i++) { Class additionalInterface = additionalInterfaces[i]; this.advised.addInterface(additionalInterface); } } // Validate the class, writing log messages as necessary. validateClassIfNecessary(proxySuperClass); // 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; } catch (CodeGenerationException ex) { throw new AopConfigException("Could not generate CGLIB subclass of class [" + this.advised.getTargetClass() + "]: " + "Common causes of this problem include using a final class or a non-visible class", ex); } catch (IllegalArgumentException ex) { throw new AopConfigException("Could not generate CGLIB subclass of class [" + this.advised.getTargetClass() + "]: " + "Common causes of this problem include using a final class or a non-visible class", ex); } catch (Exception ex) { // TargetSource.getTarget() failed throw new AopConfigException("Unexpected AOP exception", ex); } } 其次来看Cglib2AopProxy类的setConstructorArguments方法 public void setConstructorArguments(Object[] constructorArgs, Class[] constructorArgTypes) { if (constructorArgs == null || constructorArgTypes == null) { throw new IllegalArgumentException("Both 'constructorArgs' and 'constructorArgTypes' need to be specified"); } if (constructorArgs.length != constructorArgTypes.length) { throw new IllegalArgumentException("Number of 'constructorArgs' (" + constructorArgs.length + ") must match number of 'constructorArgTypes' (" + constructorArgTypes.length + ")"); } this.constructorArgs = constructorArgs; this.constructorArgTypes = constructorArgTypes; } 最后是Cglib2AopProxy类的getProxy方法产生proay对象的片段 // 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(); } getProxy方法调用时,并没有调用setConstructorArguments方法,Spring通过CGLIB生成代理类对象时,没有将目标对象的构造函数的参数及其类型进行设定,导致了CGLIB在生成代理类对象时,会使用默认的构造函数生成,而如果TargetClass没有默认构造函数,异常信息Could not generate CGLIB subclass of class云云就会打印出来了。 2。BaseAction的execute为什么要加final关键字 目标对象targetClass没有变,依然是BusinessAction。很显然,此时若去掉final,拦截BaseAction的execute方法是顺利成章的事,如同方式1中拦截BusinessAction一样,因为他们都override execute方法。但是BusinessAction无法被拦截。 由于BusinessAction的execute为final,此时在Cglib2Proxy.getProxy方法中校验BaseAction的superClass时会产生警告::WARN [org.springframework.aop.framework.Cglib2AopProxy] - Unable to proxy method [public final org.apache.struts.action.ActionForward com.xxx.core.struts.action.BaseAction.execute(org.apache.struts.action.ActionMapping,org.apache.struts.action.ActionForm,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) throws java.lang.Exception] because it is final: All calls to this method via a proxy will be routed directly to the proxy. 但是没关系,继承父类的子类的方法拦截正常。 Cglib2Proxy.getProxy中校验代码块 // Validate the class, writing log messages as necessary. validateClassIfNecessary(proxySuperClass); validateClassIfNecessary代码块 private void validateClassIfNecessary(Class proxySuperClass) { if (logger.isWarnEnabled()) { synchronized (validatedClasses) { if (!validatedClasses.containsKey(proxySuperClass)) { doValidateClass(proxySuperClass); validatedClasses.put(proxySuperClass, Boolean.TRUE); } } } } 警告来源:doValidateClass private void doValidateClass(Class proxySuperClass) { Method[] methods = proxySuperClass.getMethods(); for (int i = 0; i < methods.length; i++) { Method method = methods[i]; if (!Object.class.equals(method.getDeclaringClass()) && Modifier.isFinal(method.getModifiers())) { logger.warn("Unable to proxy method [" + method + "] because it is final: " + "All calls to this method via a proxy will be routed directly to the proxy."); } } } 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |