`
birdmen
  • 浏览: 38515 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

Spring Aop 之 CGLIB浅析

阅读更多
在改造某旧系统时,遇到的问题及解决方式如下所述:
方式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.");  
            }  
        }  
    }   



分享到:
评论

相关推荐

    spring_aop_cglib的实现方式

    在Spring AOP(面向切面编程)中,CGLIB是一个重要的动态代理库,它用于在运行时创建子类以实现对目标对象的代理。CGLIB是针对那些不支持接口代理(例如Java中的final类)的情况而设计的。下面我们将深入探讨Spring ...

    springAop.rar_AOP java_cglib_spring aop

    CGLIB是一个强大的高性能的代码生成库,它在许多AOP的实现中被用到,比如Spring AOP。CGLIB使用字节码技术为一个类创建子类,并在子类中拦截方法调用,从而实现代理。CGLIB生成的子类是动态的,因此在编译时并不存在...

    spring aop jar 包

    Spring AOP(Aspect Oriented Programming,面向切面编程)是Spring框架的重要组成部分,它提供了一种在不修改源代码的情况下,对程序进行功能增强的技术。这个"spring aop jar 包"包含了实现这一功能所需的类和接口,...

    AOP使用CGLIB实现AOP功能

    Spring AOP实现方法之一:CGLIB 实现AOP功能

    死磕Spring之AOP篇 - Spring AOP两种代理对象的拦截处理(csdn)————程序.pdf

    在 Spring 中,AOP 的实现主要依赖于代理模式,有两种代理方式:JDK 动态代理和 CGLIB 动态代理。 JDK 动态代理是基于接口的,它要求被代理的目标对象必须实现至少一个接口。Spring 使用 `java.lang.reflect.Proxy`...

    AOP、CGLIB

    在Java领域,Spring框架是AOP应用最广泛的平台之一。AOP的主要目标是解决业务逻辑中的横切关注点,比如日志记录、事务管理、权限控制等,这些关注点通常会分散在应用程序的各个角落,AOP则提供了一种将它们集中处理...

    Spring AOP 16道面试题及答案.docx

    Spring AOP,全称为Aspect Oriented Programming,是面向切面编程的一种编程范式,它是对传统的面向对象编程(OOP)的一种补充。在OOP中,核心是对象,而在AOP中,核心则是切面。切面是关注点的模块化,即程序中的...

    简单spring aop 例子

    1. **代理**:Spring AOP支持两种类型的代理:JDK动态代理和CGLIB代理。JDK代理用于实现了接口的类,而CGLIB代理则用于没有接口或不希望使用接口的类。代理对象在调用目标方法时会执行切面逻辑。 2. **织入**:织入...

    Spring Aop四个依赖的Jar包

    总的来说,Spring AOP通过这四个关键的Jar包,结合AspectJ的强大功能和CGLIB的代理机制,为开发者提供了强大的面向切面编程支持,使得我们可以在不侵入原有业务代码的情况下,实现跨切面的关注点管理。

    Spring AOP实现机制

    Spring AOP(面向切面编程)是Spring框架的核心特性之一,它允许程序员在不修改源代码的情况下,通过“切面”来插入额外的业务逻辑,如日志、事务管理等。AOP的引入极大地提高了代码的可复用性和可维护性。 ### 1. ...

    Spring AOP完整例子

    Spring AOP(面向切面编程)是Spring框架的核心特性之一,它允许开发者在不修改源代码的情况下,通过插入切面来增强或改变程序的行为。在本教程中,我们将深入探讨Spring AOP的不同使用方法,包括定义切点、通知类型...

    spring-aop.jar各个版本

    spring-aop-1.1.1.jar spring-aop-1.2.6.jar spring-aop-1.2.9.jar spring-aop-2.0.2.jar spring-aop-2.0.6.jar spring-aop-2.0.7.jar spring-aop-2.0.8.jar spring-aop-2.0.jar spring-aop-2.5.1.jar spring-aop-...

    spring aop 五个依赖jar

    在Spring AOP中,如果目标类没有实现接口,Spring会使用CGLIB创建一个代理对象来实现切面功能。CGLIB-nodep是无依赖版本,不包含ASM库,但通常Spring会自行处理ASM的依赖。 5. **aopalliance-1.0.jar**:AOP ...

    spring aop依赖jar包

    Spring AOP,全称Aspect-Oriented Programming(面向切面编程),是Spring框架的重要组成部分,它为Java应用程序提供了声明式的企业级服务,如事务管理、性能监控等。在Spring AOP中,我们可以通过定义切面(Aspect...

    Spring AOP面向方面编程原理:AOP概念

    Spring框架作为Java开发领域的领头羊之一,提供了强大的AOP支持。本文旨在深入探讨Spring AOP的核心概念及其原理。 #### 二、AOP基本概念 AOP是一种编程范式,其目的是提高模块化程度,特别是将那些对很多类都具有...

    spring之AOP(动态代理)

    在Spring中,AOP主要通过两种动态代理技术实现:JDK动态代理和CGLIB动态代理。 首先,让我们详细了解一下JDK动态代理。JDK动态代理基于Java的接口实现,它适用于目标对象实现了至少一个接口的情况。在运行时,JDK...

    AOP-CGLIB学习-实现简单的注解权限系统

    标题中的"AOP-CGLIB学习-实现简单的注解权限系统"指的是使用Spring的面向切面编程(AOP)特性,结合CGLIB库来创建一个基于注解的权限控制系统。在这个系统中,通过CGLIB动态代理技术,可以对带有特定注解的方法进行...

    cglib及其依赖包

    在Spring框架中,CGLib被用作AOP代理的一个重要组件,特别是在没有实现接口的类上创建代理对象时,Spring会默认使用CGLib。 CGLib的工作原理是基于ASM库,ASM是一个字节码操作和分析框架,它可以用来动态生成类或...

    反射实现 AOP 动态代理模式(Spring AOP 的实现原理)

    如果被代理的类没有实现接口,Spring AOP会采用CGLIB来生成代理对象。CGLIB(Code Generation Library)是一个开源的代码生成库,它允许运行时在内存中动态生成类和对象。 在Spring AOP中,我们通常使用@Aspect注解...

    Spring-AOP-JDK动态代理

    在Java编程领域,Spring框架是应用最广泛的轻量级开源框架之一,它提供了一系列强大的功能,包括依赖注入、面向切面编程(AOP)等。本篇将详细讲解Spring中的AOP实现,特别是JDK动态代理的应用。 首先,我们要了解...

Global site tag (gtag.js) - Google Analytics