public class ChangeLoggers { customer, @Named("CUSTOMER_NAME") String customerName); public static T create(Class clazz, final CurrentUser currentUser, final ChangeLogBase base) { return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new InvocationHandler() { public Object invoke(Object o, Method method, Object[] objects) throws Throwable { Annotation[][] annotations = method.getParameterAnnotations(); ... ChangeLog changeLog = ChangeLog.aNewChangeLog().type(type).createdFrom(currentUser.email()).createdOn(currentUser.time()).build(); Object parent = null; int index = 0; for (Annotation[] annotation : annotations) { if (annotation.length > 0 && annotation[0] instanceof Named) { Named named = (Named) annotation[0]; //check the value of the tag if (objects[index] != null && !Strings.empty(objects[index].toString())) { changeLog.tag(named.value(), objects[index].toString()); } } else if (annotation.length > 0 && annotation[0] instanceof LogParent) { parent = objects[index]; }else if (annotation.length > 0 && annotation[0] instanceof ChangeLogModule.ChangeLogAction){ ChangeLogModule.ChangeAction actualAction = (ChangeLogModule.ChangeAction) objects[index]; changeLog.setMessageKey(actualAction.getMessageKey()); } index++; } if (parent != null) { base.save(changeLog, parent); } return changeLog; } }); } } //the change log action interface that will represent the actions that are log public interface ChangeAction { String getMessageKey(); } @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.PARAMETER}) @BindingAnnotation //annotation that indicate the public @interface ChangeLogAction { } injector = Guice.createInjector(new AbstractModule() { ... @Provides public MyChangeLogger getMyChangeLogger(CurrentUser currentUser, ChangeLogBase base){ return ChangeLoggers.create(MyChangeLogger.class,currentUser,base); } ... }); public interface MyChangeLogger extends ChangeLogger { ChangeLog log(@ChangeLogAction ChangeLogModule.ChangeAction action, @Named("DEVICE") Long id, @Named("DEVICE_IDENTIFICATION") String identificationNumber, @Named("CONTRACT") String lastReferentContractCode, @LogParent Device device); } //implementation that shows the concrete action public class DeviceInstallationAction implements ChangeLogModule.ChangeAction { public String getMessageKey() { return "installDevice"; } } @Test public void testName() throws Exception { MyChangeLogger logger = injector.getInstance(MyChangeLogger.class); ChangeLog changeLog = logger.log(new DeviceInstallationAction(), 1l, "serial number", "1234567", device); assertEquals("installDevice", changeLog.getMessageKey()); }