如何在不修改子类的情况下添加对现有子类中方法的调用的控制?

epe*_*leg 6 java methods class

我有BaseClass一些方法void doSomething().

有不同的方式foSomething,他们通过实施SubClass1,SubClass2SubClass3.

现在我想添加一个Boolean active属性,BaseClass以便doSomething在实例上调用它时它将返回而不做任何事情.

我知道我可以编写的BaseClassdoSomething(),看起来是这样的:

Void doSomething(){
   if (this.getActive()) actuallyDoSomething();
}
Run Code Online (Sandbox Code Playgroud)

@Override actuallyDoSomething() 不是@Override doSomething()在子类中.但感觉不对......从某种意义上说,已经同意子类应该提供一个实现,doSomething()而他们并不知道actuallyDoSomething().

我也可以让每个子类if (!this.getActive()) return;在其实现开始时添加一个,doSomething()但这似乎也是错误的,因为它的常用功能我宁愿保持常见.

这样做的常见/最佳实践方法是什么?可以在不改变子类的情况下完成吗?

更新

Q的重点不在于设计此类功能的正确方法(这非常简单),而是关于如何在不破坏任何内容的情况下将此类功能添加到现有方案中.

active默认情况下会是真的,但是如果有人打电话,setActive(false)那么在任何所述子类的任何实例上都希望它会变为非活动状态并且连续调用.doSomething()将不会做任何事情......

Ste*_*han 3

您想要使用@AroundAspectJ 的建议并执行如下操作:

// Let subClass instances run normally...
cec.setActive(true);
letThemDoSomething("BEFORE", sbc1, sbc2, sbc3);

// Now change existing scenario...
cec.setActive(false);
letThemDoSomething("AFTER", sbc1, sbc2, sbc3);
Run Code Online (Sandbox Code Playgroud)

这将输出:

BEFORE ======
SubClass1: doSomething() called.
SubClass2: doSomething() called.
SubClass3: doSomething() called.

AFTER ======
Blocking instance<1> method: my.first.spring.aop.aspectj.SubClassN#doSomething([]) !!
Blocking instance<2> method: my.first.spring.aop.aspectj.SubClassN#doSomething([]) !!
Blocking instance<3> method: my.first.spring.aop.aspectj.SubClassN#doSomething([]) !!
Run Code Online (Sandbox Code Playgroud)

在下面几行中,我将描述如何使用注释来实现这一点。
我这里也将使用Spring。它有助于使配置更快、更容易。


1- 配置

工具和依赖项

Java 7、AspectJ 1.7.4、Spring 4.0.2

项目结构

AspectJ around 建议示例代码 - 项目结构

pom.xml

<project ...>

  <properties>
     <maven.compiler.source>1.7</maven.compiler.source>
     <maven.compiler.target>1.7</maven.compiler.target> 

     <spring.version>4.0.2.RELEASE</spring.version>
     <aspectj.version>1.7.4</aspectj.version>
  </properties>

  <dependencies>
    <!-- Spring -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <!-- AspectJ -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>${aspectj.version}</version>
    </dependency>

    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>${aspectj.version}</version>
    </dependency>
  </dependencies>

</project>
Run Code Online (Sandbox Code Playgroud)

2 - 现有代码

基类.java

public class BaseClass {
    public void doSomething() {

    }

    public void say(String msg) {
         System.out.println(msg);
    }
}    
Run Code Online (Sandbox Code Playgroud)

子类N.java

public class SubClassN extends BaseClass {
    private Integer index;

    public SubClassN(Integer index) {
        this.index = index;
    }

    @Override
    public void doSomething() {
        say("SubClass" + index + ": doSomething() called.");
    }

    public Integer getIndex() {
        return index;
    }
}
Run Code Online (Sandbox Code Playgroud)

3 - 更改现有代码(不破坏任何内容......)

这是 AspectJ 及其 @Around 建议。当调用任何方法时,我们首先要求 AsjectJ 调用特定的方法。可以位于其子类中的任何位置。doSomethingdoSomethingBaseClass

这种特殊的方法称为changeExistingScenario. 它可以有任何名称。这里重要的是其上的注释。

关于 @Around 值的一句话:

执行(* my.first.spring.aop.aspectj.BaseClass.doSomething(..))

该表达式仅指示我们要拦截的方法签名模式。
它会拦截 BaseClass 或子类中的任何 doSomething 方法,无论有多少参数、返回类型和访问修饰符。

有关更多详细信息,请参阅:http://guptavikas.wordpress.com/2010/04/15/aspectj-pointcut-expressions/

更改现有代码.java

@Aspect // Mark ChangeExistingCode as the class for modifying the code 
@Component
public class ChangeExistingCode {
    private boolean active;

    public void setActive(boolean active) {
        this.active = active;
    }

    /**
     *
     * This method will be called by AspectJ anytime a `doSomething` method is called.
     *
     * This will give us a chance to decide whether the `doSomething` method should
     * be called or not.
     *
     */
    @Around("execution(* my.first.spring.aop.aspectj.BaseClass.doSomething(..))")
    public void changeExistingScenario(ProceedingJoinPoint joinPoint) throws Throwable {
        // Is active ?
        if (active) { // Yes, let doSomething() run as usual
            joinPoint.proceed();
        } else {// No, block doSomething() invokation
            Signature s = joinPoint.getSignature();

            System.out.format( //
                    "Blocking instance<%d> method: %s#%s(%s) !!\n", //
                    ((SubClassN)joinPoint.getTarget()).getIndex(), //
                    s.getDeclaringTypeName(), //
                    s.getName(), //
                    Arrays.toString(joinPoint.getArgs()) //
                    );
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

4-让所有的魔法出现......

主程序.java

@Configuration // Mark the Main class as the class where Spring will find its configuration
@ComponentScan // Ask Spring to look for other components within the Main class package
@EnableAspectJAutoProxy // Let Spring auto configure AspectJ aspects for us...
public class Main {

    private static int subClassCounter;

    public static void main(String[] args) {
        subClassCounter=0;

        GenericApplicationContext  context = new AnnotationConfigApplicationContext(Main.class);

        SubClassN sbc1 = context.getBean(SubClassN.class);
        SubClassN sbc2 = context.getBean(SubClassN.class);
        SubClassN sbc3 = context.getBean(SubClassN.class);

        ChangeExistingCode cec = context.getBean(ChangeExistingCode.class);

        // Let subClass instances run normally...
        cec.setActive(true);
        letThemDoSomething("BEFORE", sbc1, sbc2, sbc3);

        // Now change existing scenario...
        cec.setActive(false);
        letThemDoSomething("AFTER", sbc1, sbc2, sbc3);

        context.close();
    }

    private static void letThemDoSomething(String prefix, SubClassN... existingClasses) {
        System.out.format("%s ======\n", prefix);
        for (SubClassN subClassInstance : existingClasses) {
            subClassInstance.doSomething();
        }
        System.out.println();
    }

    @Bean // Tell Spring to use this method for creating SubClassN instances
    @Scope(BeanDefinition.SCOPE_PROTOTYPE) // Scope prototype force creation of multiple instances
    private static SubClassN buildSubClassN() {
        subClassCounter++;
        return new SubClassN(subClassCounter);
    }
}
Run Code Online (Sandbox Code Playgroud)

输出

BEFORE ======
SubClass1: doSomething() called.
SubClass2: doSomething() called.
SubClass3: doSomething() called.

AFTER ======
Blocking instance<1> method: my.first.spring.aop.aspectj.SubClassN#doSomething([]) !!
Blocking instance<2> method: my.first.spring.aop.aspectj.SubClassN#doSomething([]) !!
Blocking instance<3> method: my.first.spring.aop.aspectj.SubClassN#doSomething([]) !!
Run Code Online (Sandbox Code Playgroud)

5- 参考文献