Aspject加载时织入示例
问题场景
最近的一个使用DDD风格建模的项目中,遇到这样的一个场景:
- 领域类的抽象类层次,作为Hibernate实体类使用,由于其包含了一些业务逻辑,需要Spring依赖注入的支持,故使用了@Configurable注解+AspectJ编译时织入的方式
- 具体领域类,一般没有业务逻辑,但是类数量较多,故使用Javassist进行自动的类生成
运行的时候出现问题了,使用Javassist生成的具体领域类,无法注入依赖,尽管其父类上有@Configurable注解。经过跟踪,发现子类也必须进行织入才能正常的进行依赖注入,那么,如何在生成字节码的时候,同时织入AOP逻辑呢?
AspectJ字节码织入工具类
其实AspectJ已经提供了加载时织入的API,下面这个工具类,即可用于织入任何指定的切面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
import java.lang.instrument.IllegalClassFormatException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.aspectj.weaver.loadtime.Aj; import org.aspectj.weaver.loadtime.DefaultWeavingContext; import org.aspectj.weaver.loadtime.definition.Definition; import org.aspectj.weaver.tools.WeavingAdaptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Weave byte codes at load time. * * @author Andrica Silviu * @author WangZhen */ public class AspectJBytecodeWeaver { private static final Logger LOGGER = LoggerFactory.getLogger( AspectJBytecodeWeaver.class ); private class DynamicAspectWeavingContext extends DefaultWeavingContext { public DynamicAspectWeavingContext( ClassLoader loader ) { super( loader ); } public List<Definition> getDefinitions( ClassLoader loader, WeavingAdaptor adaptor ) { List<Definition> definitions = new ArrayList<Definition>(); Definition d = new Definition(); for ( String aspectName : aspectNames ) { d.getAspectClassNames().add( aspectName ); } d.appendWeaverOptions( "-Xjoinpoints:synchronization" ); if ( LOGGER.isDebugEnabled() ) { d.appendWeaverOptions( "-showWeaveInfo" ); } definitions.add( d ); return definitions; } } private final Map<ClassLoader, Aj> mapFromClassLoaderToAj; private final String[] aspectNames; public AspectJBytecodeWeaver( String[] aspectName ) { mapFromClassLoaderToAj = new ConcurrentHashMap<ClassLoader, Aj>(); this.aspectNames = aspectName; } /** * Weaving delegation * * @param loader * the defining class loader * @param className * the name of class beeing loaded * @param bytes * the bytecode before weaving * @return the weaved bytecode */ public byte[] weave( ClassLoader loader, String className, byte[] bytes ) throws IllegalClassFormatException { if ( className.replace( "/", "." ).equals( aspectNames ) ) { return null; } Aj aj = getAj( loader ); if ( LOGGER.isDebugEnabled() ) { LOGGER.debug( "Weaving class : {} with aspect(s) :{}", className, Arrays.deepToString( aspectNames ) ); } byte[] b = aj.preProcess( className, bytes, loader ); return b; } private Aj getAj( ClassLoader loader ) { if ( mapFromClassLoaderToAj.containsKey( loader ) ) { return mapFromClassLoaderToAj.get( loader ); } Aj aj = new Aj( new DynamicAspectWeavingContext( loader ) ); mapFromClassLoaderToAj.put( loader, aj ); return aj; } } |
该工具类的一个实例,专门用于织入Spring aspects:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class SpringAspectJ { private static final String ASPECT_BEAN_CONFIG = "org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect"; private static final String ASPECT_SCHEDULING = "org.springframework.scheduling.aspectj.AnnotationAsyncExecutionAspect"; private static final String ASPECT_TRANSACTION = "org.springframework.transaction.aspectj.AnnotationTransactionAspect"; private static final String ASPECT_CACHE = "org.springframework.cache.aspectj.AnnotationCacheAspect"; public static final AspectJBytecodeWeaver WEAVER = new AspectJBytecodeWeaver( new String[] { ASPECT_BEAN_CONFIG, ASPECT_SCHEDULING, ASPECT_TRANSACTION, ASPECT_CACHE } ); } |
在生成字节码时织入Spring-aspects
下面的例子中,首先生成了类的基本骨架,然后调用AspectJBytecodeWeaver进行织入。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
ClassPool cp = ClassPool.getDefault(); ClassPath clsPath = new ClassClassPath( this.getClass() ); cp.insertClassPath( clsPath ); CtClass cc = cp.makeClass( getClassName() ); if ( StringUtils.isNotBlank( getSuperClassName() ) ) { cc.setSuperclass( cp.get( getSuperClassName() ) ); } ClassFile cf = cc.getClassFile(); ConstPool constPool = cf.getConstPool(); if ( isWeaveSpringAspects() ) { ClassLoader loader = Thread.currentThread().getContextClassLoader(); try { //织入Spring切面 byte[] b = SpringAspectJ.WEAVER.weave( loader, getClassName(), cc.toBytecode() ); cc.detach(); cc = cp.makeClass( new ByteArrayInputStream( b ) ); } catch ( IOException e ) { throw new RuntimeException( e.getMessage(), e ); } } Class<?> c = cc.toClass(); cc.detach(); return c; |
Leave a Reply