I have some BaseClass
with some method void doSomething()
.
There are different ways to foSomething
and they are implemented by SubClass1
, SubClass2
and SubClass3
.
Now I want to add a Boolean active
property to the BaseClass
so that when doSomething
is called on an instance it will just return without doing anything.
I know I can code the BaseClass
to have doSomething()
that looks something like:
Void doSomething(){
if (this.getActive()) actuallyDoSomething();
}
And then @Override actuallyDoSomething()
instead of @Override doSomething()
in the subclasses.
but it feels wrong... in the sense that It has already been agreed that the subclasses should provide an implementation for doSomething()
and they are not aware of actuallyDoSomething()
.
I also can have each sub class add an if (!this.getActive()) return;
at the beginning of its implementation of doSomething()
but this also seems wrong as its common functionality that I would prefer to keep common.
What is the common/best practice way to do this? Can it be done without changing the sub classes?
Update
The focus of the Q is not about the right way to design such functionality (which is quite simple), but on how such functionality can be added to an existing scenario without breaking anything.
active
would be true by default, but it is desired that on any instance of any the said sub classes if someone would call setActive(false)
then it will become inactive and consecutive calls to .doSomething()
will not do anything...
Therefore, you cannot override two methods that exist in the same class, you can just overload them.
When a method in a subclass has the same name, same parameters or signature, and same return type(or sub-type) as a method in its super-class, then the method in the subclass is said to override the method in the super-class. Method overriding is one of the way by which java achieve Run Time Polymorphism.
If a method cannot be inherited, then it cannot be overridden. A subclass within the same package as the instance's superclass can override any superclass method that is not declared private or final. A subclass in a different package can only override the non-final methods declared public or protected.
We can only use those access specifiers in subclasses that provide larger access than the access specifier of the superclass. For example, Suppose, a method myClass() in the superclass is declared protected . Then, the same method myClass() in the subclass can be either public or protected , but not private .
You want to use @Around
advice from AspectJ and do something like this:
// 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);
This will output:
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([]) !!
In the following lines, I'll describe how to make this happen with the annotation.
I will also use Spring here. It helps making the configuration quickier and easier.
Tools and dependencies
Java 7, AspectJ 1.7.4, Spring 4.0.2
Project structure
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>
BaseClass.java
public class BaseClass {
public void doSomething() {
}
public void say(String msg) {
System.out.println(msg);
}
}
SubClassN.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;
}
}
Here comes AspectJ and its @Around advice. We'll first ask AsjectJ to call a particular method when any doSomething
method is invoked. doSomething
can be anywhere in the BaseClass
or in any of its child class.
This particular method is called changeExistingScenario
. It can have any name. The important here is the annotation placed on it.
A word about the @Around value:
execution(* my.first.spring.aop.aspectj.BaseClass.doSomething(..))
This expression simply indicates the method signature pattern we want to intercept.
It will intercept any doSomething method in BaseClass or child class no matter
how many parameters, return type and access modifier.
For more details see: http://guptavikas.wordpress.com/2010/04/15/aspectj-pointcut-expressions/
ChangeExistingCode.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()) //
);
}
}
}
Main.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);
}
}
Output
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([]) !!
Download full code: http://www.filedropper.com/advicearoundsample
AspectJ official site
- @Around javadoc
- @Aspect javadoc
Spring official site
- @Bean javadoc
- @Component javadoc
- @ComponentScan javadoc
- @Configuration javadoc
- @EnableAspectJAutoProxy javadoc
Other useful resources that helped writing this answer
- AspectJ Pointcut Expressions
- Mkyong - Spring AOP + AspectJ annotation example
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With