Yes, you can do this via AspectJ.
Here is a self-consistent, stand-alone code sample. You can just copy and paste the classes and the aspect and run it. I am not using original Hibernate classes, but some dummy replacements because I am not a Hibernate user and I also want to make this a general purpose answer:
Dummy, hibernate-like interfaces and classes:
package de.scrum_master.app;
public interface Criterion {}
package de.scrum_master.app;
public class MyCriterion implements Criterion {
private String criterion;
public MyCriterion(String criterion) {
this.criterion = criterion;
}
@Override
public String toString() {
return "MyCriterion [criterion=" + criterion + "]";
}
}
package de.scrum_master.app;
import java.util.List;
public interface Criteria {
Criteria add(Criterion criterion);
List list();
Criteria setFirstResult(int firstResult);
Criteria setMaxResults(int maxResults);
}
package de.scrum_master.app;
import java.util.LinkedList;
import java.util.List;
public class MyCriteria implements Criteria {
private List<Criterion> criteria = new LinkedList<>();
@Override
public Criteria add(Criterion criterion) {
criteria.add(criterion);
return this;
}
@Override
public List list() {
return new LinkedList();
}
@Override
public Criteria setFirstResult(int firstResult) {
add(new MyCriterion("first result " + firstResult));
return this;
}
@Override
public Criteria setMaxResults(int maxResults) {
add(new MyCriterion("max results " + maxResults));
return this;
}
@Override
public String toString() {
return "MyCriteria [criteria=" + criteria + "]";
}
}
As you can see, methods setFirstResult(..)
and setMaxResults(..)
add special criteria via add(..)
to the internal list. I just did this in order to later illustrate the aspect's effect.
Pagination annotation:
package de.scrum_master.app;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Paginate {}
Driver application:
package de.scrum_master.app;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Application {
@Paginate
public List filter(Set<Criterion> filter, Map<String, String> alias) {
Criteria criteria = new MyCriteria();
for (Criterion criterion : filter)
criteria.add(criterion);
return criteria.list();
}
public static void main(String[] args) {
Set<Criterion> filterCriteria = new HashSet<>();
filterCriteria.add(new MyCriterion("where"));
filterCriteria.add(new MyCriterion("group by"));
new Application().filter(filterCriteria, null);
}
}
The application contains a method annotated by @Paginate
, doing similar things as your sample code. It does not call any pagination methods though. This will be done by the following aspect.
Pagination aspect:
package de.scrum_master.aspect;
import java.util.List;
import de.scrum_master.app.Paginate;
import de.scrum_master.app.Criteria;
public aspect PaginationAspect {
Object around(Criteria criteria) :
call(public List Criteria+.list()) &&
cflow(execution(@Paginate * *(..))) &&
target(criteria)
{
System.out.println(thisJoinPoint);
System.out.println("Original criteria: " + criteria);
criteria.setFirstResult(5);
criteria.setMaxResults(10);
System.out.println("Modified criteria: " + criteria);
return proceed(criteria);
}
}
The pointcut intercepts calls to Criteria+.list()
(the +
includes subclasses), but only when issued in the control flow (cflow()
) of any executing methods annotated by @Paginate
. The call target is bound to a parameter Criteria criteria
so as to use it in the advice method bound to that pointcut. We need a reference to the call target because we want to call the pagination methods upon it. This is what the advice does and it is clearly illustrated by the console output.
Console output:
call(List de.scrum_master.app.Criteria.list())
Original criteria: MyCriteria [criteria=[MyCriterion [criterion=group by], MyCriterion [criterion=where]]]
Modified criteria: MyCriteria [criteria=[MyCriterion [criterion=group by], MyCriterion [criterion=where], MyCriterion [criterion=first result 5], MyCriterion [criterion=max results 10]]]
Enjoy!