2

这是我的用例:

我需要在给定类的每个方法之前和之后做一些通用操作,这基于方法的参数。例如:

void process(Processable object) {
    LOGGER.log(object.getDesc());
    object.process();
}

class BaseClass {

    String method1(Object o){ //o may or may not be Processable(add process logic only in former case)
        if(o intstanceof Prcessable){
            LOGGER.log(object.getDesc());
            object.process();
        }
        //method logic
    }
}

BaseClass有很多方法,我知道将来也会将相同的功能添加到几个类似的类中。可能会出现以下情况吗?

@MarkForProcessing
String method1(@Process Object o){ 
    //method logic
}

PS:可以使用AspectJ/guice吗?也想知道如何从头开始实现这一点以便理解。

编辑:忘了提,我尝试过的。(不完整或工作)

public @interface MarkForProcessing {
    String getMetadata();
}


    final public class Handler {
        public boolean process(Object instance) throws Exception {
            Class<?> clazz = instance.getClass();
            for(Method m : clazz.getDeclaredMethods()) {
                if(m.isAnnotationPresent(LocalSource.class)) {
                    LocalSource annotation = m.getAnnotation(MarkForProcessing.class);

                    Class<?> returnType = m.getReturnType();
                    Class<?>[] inputParamTypes = m.getParameterTypes();
                    Class<?> inputType = null;

                    // We are interested in just 1st param
                    if(inputParamTypes.length != 0) {
                        inputType = inputParamTypes[0];
                    }
                   // But all i have access to here is just the types, I need access to the method param.
                }
                return false;
            }
            return false;
        }
4

1 回答 1

3

是的,这是可以做到的。是的,您可以使用 AspectJ。不,Guice 只会与这个问题无关。

传统的方面方法创建一个代理,它基本上是您给它的类的子类(例如 BaseClass 的子类),但该子类是在运行时创建的。子类代表所有方法的包装类。但是,在创建这个新子类时,您可以指定一些额外的行为,以在对包装类的调用之前或之后(或两者)添加。换句话说,如果您有:

public class Foo() {

  public void doFoo() {...}

}

然后动态代理将是在运行时创建的 Foo 的子类,如下所示:

public class Foo$Proxy {

  public void doFoo() {
    //Custom pre-invocation code
    super.doFoo();
    //Custom post-invocation code
  }

}

实际上,创建动态代理是一个被称为字节码操作的神奇过程。如果您想自己执行此操作,可以使用cglibasm等工具。或者您可以使用JDK 动态代理。JDK 代理的主要缺点是它们只能包装接口。

像AspectJ这样的AOP 工具在原始字节码操作之上提供了一个抽象来执行上述操作(您可以通过字节码操作做很多事情,在方法之前和之后添加行为是所有方面都允许的)。通常,他们定义“方面”,这些类具有称为“建议”的特殊方法以及定义何时应用该建议的“切入点”。换句话说,您可能有:

@Aspect
public class FooAspect {

    @Around("@annotation(MarkForProcessing)")
    public void doProcessing(final ProceedingJoinPoint joinPoint) throws Throwable 
    {
        //Do some before processing
        joinPoint.proceed(); //Invokes the underlying method
        //Do some after processing
    }

}

aspect 是 FooAspect,advice 是 doProcessing,切入点是“@annotation(MarkForProcessing)”,它匹配所有使用 @MarkForProcessing 注释的方法。值得指出的是 ProceedingJoinPoint 将引用实际参数值(与 不同java.lang.reflect.Method

最后一步实际上是将你的方面应用到你的类的一个实例上。通常,这可以通过容器(例如 Guice 或 Spring)来完成。大多数容器都有某种方式了解方面的集合以及何时将它们应用于由该容器构造的类。您也可以以编程方式执行此操作。例如,使用 AspectJ,您可以:

AspectJProxyFactory factory = new AspectJProxyFactory(baseClassInstance); 
factory.addAspect(FooAspect.class);
BaseClass proxy = factory.getProxy();

最后但同样重要的是,有一些 AOP 实现使用编译时“编织”,这是在应用方面的类文件上运行的第二个编译步骤。换句话说,您不必执行上述操作或使用容器,切面将被注入到类文件本身中。

于 2013-10-29T18:53:28.750 回答