Добавить поле в прокси-класс, созданный с помощью Javassist
Я создаю класс Proxy, используя Javassist ProxyFactory со следующим кодом:
ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(entity.getClass());
factory.setInterfaces(new Class[] { MyCustomInterface.class });
.....
Class clazz = factory.createClass();
Object result = clazz.newInstance();
Проблема в том, что мне также нужно добавить поле в класс. Но если я сделаю CtClass proxy = ClassPool.getDefault().get(clazz.getName());
это дает NotFoundException
Как я могу добавить поле класса, созданного с помощью createClass? Есть ли лучший способ сделать то, что я пытаюсь сделать?
1 ответ
Это основано на вашем ответе на мой комментарий.
Вы действительно можете использовать MyCustomInterface
и ваш proxyClass для создания своего рода миксина в Java. Но вам все равно придется привести из класса прокси к MyCustomInterface
чтобы иметь возможность вызывать методы.
Давайте начнем.
Создание вашего прокси
Сначала вы создаете свой прокси, который вы уже делали:
// this is the code you've already posted
ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(entity.getClass());
factory.setInterfaces(new Class[] { MyCustomInterface.class });
Обработчик метода: магия
Прокси Javassist позволяют вам добавить MethodHandler. Он в основном действует как InvocationHandler в обычном Java Proxy, что означает, что он работает как метод-перехватчик.
Обработчик метода будет вашим миксином! Сначала вы создаете новый MethodHandler с настраиваемым полем, которое вы действительно хотите добавить в класс, вместе с объектом сущности, который вы начали проксировать:
public class CustomMethodHandler implements MethodHandler {
private MyEntity objectBeingProxied;
private MyFieldType myCustomField;
public CustomMethodHandler(MyEntity entity) {
this.objectBeingProxied = entity;
}
// code here with the implementation of MyCustomInterface
// handling the entity and your customField
public Object invoke(Object self, Method method, Method proceed, Object[] args) throws Throwable {
String methodName = method.getName();
if(methodNameFromMyCustomInterface(methodName)) {
// handle methodCall internally:
// you can either do it by reflection
// or if needed if/then/else to dispatch
// to the correct method (*)
}else {
// it's just a method from entity let them
// go. Notice we're using proceed not method!
proceed.invoke(objectBeingProxied,args);
}
}
}
(*) Обратите внимание, что даже если я скажу в комментарии для внутренней обработки вызова, вы можете получить реализацию интерфейса в другом месте, которое не является вашим обработчиком метода, и просто вызвать его отсюда.
Собираем все вместе
ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(entity.getClass());
factory.setInterfaces(new Class[] { MyCustomInterface.class });
Class cls = factory.createClass();
// bind your newly methodHandler to your proxy
((javassist.util.proxy.Proxy) cls).setHandler(new CustomMethodHandler(entity));
EntityClass proxyEntity = cls.newInstance();
Теперь вы должны быть в состоянии сделать ((MyCustomInterface)proxyEntity).someMethodFromTheInterface()
и пусть это будет обработано вашим CustomMethodHandler
Подводя итоги
- Вы создаете прокси с помощью Proxy Factory из javassist
- Вы создаете свой собственный класс MethodHandler, который может получать вашу прокси-сущность и поле, с которым вы хотите работать
- Вы связываете methodHandler с вашим прокси, чтобы вы могли делегировать реализацию интерфейса
Имейте в виду, что эти подходы не идеальны, так как код в классе Entity не может ссылаться на интерфейс, пока вы сначала не создадите прокси.
Если что-то не очень понятно для вас, просто прокомментируйте, и я сделаю все возможное, чтобы разъяснить вам.