I am creating a Proxy class using Javassist ProxyFactory with the following code:
ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(entity.getClass());
factory.setInterfaces(new Class[] { MyCustomInterface.class });
.....
Class clazz = factory.createClass();
Object result = clazz.newInstance();
The problem is that I also need to add a field to the class. But if I do CtClass proxy = ClassPool.getDefault().get(clazz.getName()); it gaves a NotFoundException
How can I add a field the class created with createClass? Is there a better way to do what I am trying to do?
This is based in your reply to my comment.
You can indeed use MyCustomInterface and your proxyClass to create sort of a mixin in Java. But you'll still have to cast from proxy class to MyCustomInterface to be able to call the methods.
Let's get started.
Creating your proxy
First you create your proxy has you already were doing:
// this is the code you've already posted
ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(entity.getClass());
factory.setInterfaces(new Class[] { MyCustomInterface.class });
Method handler: Doing the magic
Javassist proxies allow you to add a MethodHandler. It basically acts has a an InvocationHandler would in a regular Java Proxy, that means it works as a method interceptor.
The method handler will be your mixin! First you create a new MethodHandler with the custom field you actually want to add to the class, along with the entity object you've started proxying:
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);
}
}
}
(*) Notice that even if I say in the comment to handle the call internally you can have the interface implementation in another place that isn't your method handler and just call it from here.
Getting everything together
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();
You should now be able to do ((MyCustomInterface)proxyEntity).someMethodFromTheInterface() and let it be handled by your CustomMethodHandler
Summing up
You create a proxy using Proxy Factory from javassist
You create your own MethodHandler class that can receive your proxied entity and the field you want to operate
You bind the methodHandler to your proxy so you can delegate interface implementation
Keep in mind that these approach isn't perfect, being one of the short comings the code in Entity class cannot refer to the interface unless you first create the proxy.
If anything wasn't very clear for you, just comment and I'll do my best to clarify you.
Related
Objective: (as the question title says): to efficiently create proxy (domain) objects using CGLib or any other reflection library (e.g. ByetBuddy?)
Having our domain class (please note Lombok annotations):
#Getter
#Setter
#RequiredArgsConstructor
public class DomainFoo {
#NonNull
private final Integer id;
// some other fields, final or otherwise!
public void doSomething() {
// do something here!
}
}
I'm trying to create a proxy object of type DomainFoo which responds only to getId method with the given ID (see below), otherwise (calling any other method) it throws an UnsupportedOperationException.
I managed to do this using GCLib (Spring version if it matters):
public static final ObjenesisStd OBJENESIS = new ObjenesisStd();
public static Factory newFoo(Integer id) {
val enhancer = new Enhancer();
enhancer.setSuperclass(domainClass);
enhancer.setCallbackType(MethodInterceptor.class);
val proxyClass = enhancer.createClass();
// Since there's no default constructor in domains:
val instantiator = OBJENESIS.getInstantiatorOf(proxyClass);
val proxyInstance = instantiator.newInstance();
val factory = (Factory) proxyInstance;
factory.setCallbacks(new Callback[]{new DomainProxyInterceptor(id)});
return factory; // see below for why it's called factory!
}
where DomainProxyInterceptor is:
#RequiredArgsConstructor
private class DomainProxyInterceptor implements MethodInterceptor {
#NonNull
private final Integer id;
#Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if (method.getDeclaringClass() != Object.class && method.getName().equals("getId")) {
return id;
} else {
throw new NotSupportedException(method.getName());
}
}
}
Everything works just fine. Now to make it efficient, I'm trying to cache factory and actually use it a factory:
// newFooFactory is the above method rename!
private static final Factor FOO_FACTORY = newFooFactory(0);
public static DomainFoo newFoo(Integer id) {
val callbacks = new Callback[]{new DomainProxyInterceptor(id)};
return (DomainFoo) FOO_FACTORY.newInstance(callbacks);
}
But calling the above method throws a NoSuchMethodError:
Exception in thread "main" java.lang.NoSuchMethodError: com/example/demo/DomainFoo$$EnhancerByCGLIB$$8423c3c4.<init>()V (loaded from file:/Users/rad/works/demo/target/classes/ by jdk.internal.loader.ClassLoaders$AppClassLoader#85a856f6) called from class com.example.demo.DomainFoo$$EnhancerByCGLIB$$8423c3c4 (loaded from file:/Users/rad/works/demo/target/classes/ by jdk.internal.loader.ClassLoaders$AppClassLoader#85a856f6).
at com.example.demo.DomainFoo$$EnhancerByCGLIB$$8423c3c4.newInstance(<generated>)
at com.example.demo.DomainFactory.proxyFoo(DomainFactory.java:28)
at com.example.demo.DemoApplication.main(DemoApplication.java:6)
Which presumably is because Factory.newInstance tries to use the normal constructor but it's not there (because we created the object using Objenesis?).
I also tried ReflectionFactory to create the factory instance, but it has the same issue.
Questions (they're kinda related):
Do I need to worry about efficiency at all? (Those objects are normal domains, so there's usually too many of them.)
Is there a better way to do the above? In terms of efficiency or otherwise? Specifically what's the right way to instantiate using GCLib when there's no default constructor?
Is there a way to fix the above issue? Resolving NoSuchMethodError failure?
Any other library or solution I could try?
Thanks.
Cglib and ByteBuddy are 2 good libraries to create a proxy. Objenesis will do something more on top. It will prevent calling a constructor of the proxied class. You can look at org.easymock.internal.ClassProxyFactory or use it to create such a proxy. It is quite similar to your code.
I am just learning byte buddy, and want to create the simplest getter invoker, but, unfortunately could find how to do it. So, the problem:
I have a lot classes, that I scan and if I find in a class a field that is annotated with FooAnnotation annotation I must be able to get the value of this field using this class instance getter method.
This is my code:
interface GetterInvoker<T, S> {
//this method must `return instance.getterMethod()`;
S getValueFrom(T instance);
}
abstract class AsbtractGetterInvoker implements GetterInvoker {
//...some logic
}
Main code
//input
Class<?> scannedClass = ...
Field annotatedField = ....
Method getterMethod = ....
//generating class
Class<?> tempClass = new ByteBuddy()
.subclass(AsbtractGetterInvoker.class, ConstructorStrategy.Default.IMITATE_SUPER_CLASS)
.method(named("getValueFrom").and(takesArguments(1)))
.intercept(???????)
.make()
.load(getClass().getClassLoader())
.getLoaded();
Could anyone say how to do it?
You'd create such an implementation using MethodCall which allows you to do exactly this.
I'm implementing a factory a class in charge of managing tokens across an application. I'll explain the problem I'm facing after this simplified example:
Suppose we have our factory class:
TokenManagerFactory.java:
public class TokenManagerFactory {
public static TokenManager create(String tokenType)
{
if ("JWT".equals(tokenType))
return new JwtTokenManagerImpl();
return null;
}
}
Then our abstract interface:
public abstract interface TokenManager {
public String generateToken();
public boolean verifyToken();
}
And finally the implementation JwtTokenManagerImpl:
public class JwtTokenManagerImpl implements TokenManager {
//..Implementation of methods defined in interface (generateToken() and
// verifyToken())
public String aMethodNotDefinedInInterface() {
return "A very cool String";
}
}
Now in our main we want to create an instance of JwtTokenManager:
main {
TokenManager tm = TokenManagerFactory.create("JWT");
tm.aMethodNotDefinedInInterface(); // <-- Compilation error.
}
The method aMethodNotDefinedInInterface() is undefined for the type
TokenManager
How do I adjust this design pattern so this error does not occur? Downcasting for when doing such calls seems like a harsh solution, is there a higher level adjustment I could make to accommodate this scenario?
Thanks.
I marked the solution I ended up using.
How do I adjust this design pattern so this error does not occur?
You have to make a choice : either working with a base common type to manipulate all subclasses in an uniform way from the API of the base common type or casting it to a specific type to be able to invoke specific method of a subclass.
Some ideas to solve your issue :
Add the method in the interface.
If the method is expected for some implementations but not for all you could add it in the interface with a default implementation (that throws UnsupportedOperationException for example). You could override it in the subclass that wants to support it.
It will work but will also do your code more brittle as the exception would be throw only at runtime.
Provide an additional factory method that returns the concrete subclass in its declaration.
Or as alternative enrich the actual method to return a generic type inferred by the target type specified in the return of the invoker. It is not type safe but it spares an explicit cast.
It would give something like :
#SuppressWarnings("unchecked")
public static <T extends TokenManager> T create(String tokenType) {
if ("JWT".equals(tokenType)) {
return (T) new JwtTokenManagerImpl();
}
return null;
}
That you invoke :
JwtTokenManagerImpl token = create("JWT");
Use the decorator pattern to enrich the behavior of some objects if it matches to your need. You should rely on a common method in TokenManager that the decorator will enrich.
You could so write something like :
TokenManager tm = new TokenFooDecorator(TokenManagerFactory.create("JWT"));
You could change TokenManagerFactory to accept an interface instead?
public interface JwtTokenManager extends TokenManager {
String aMethodNotDefinedInInterface();
}
public class TokenManagerFactory {
public static <T extends TokenManager> T create(Class<T> managerInterface) {
if (managerInterface == JwtTokenManager.class) {
return (T) new JwtTokenManagerImpl();
}
return null;
}
}
Then where you use the factory can be something like:
public static void main(String[] args) {
JwtTokenManager tm = TokenManagerFactory.create(JwtTokenManager.class);
tm.aMethodNotDefinedInInterface();
}
If aMethodNotDefinedInInterface() is required for all Token managers, then it should be added to the interface.
Otherwise, this suggests that you need a different flow for each Token Manager, in which case you might want to use the Bridge design pattern.
In this case, the Implementor hierarchy will be the token managers, and the Abstraction hierarchy will consist of the different flows implementations.
Then you can match the flow you want with the token implementation you want.
You'll still need to add the method to the interface, and either:
Add an empty implementation where its not relevant.
Throw UnsupportedOperationException exception, indicating that the flow/token manager combination is illegal.
I am going to talk about what is wrong with your design first:
Here is your code:
TokenManager tm = TokenManagerFactory.create("JWT");
tm.aMethodNotDefinedInInterface(); // <-- Compilation error.
The question is, why does the create method take a token when you only have one possible value for that token? You might as well always return the concrete type.
You are going to tell me that you might want to do this:
String token = getTokenFromSomewhere(); // it may or may not be JWT
TokenManager tm = TokenManagerFactory.create(token);
tm.aMethodNotDefinedInInterface(); // <-- Compilation error.
In that case, you don't know what type of TokenManager you have returned, so you can't call a method on it unless it is in the interface.
You might now say that you would want to do either of the two cases - with a known token or an unknown token. In this case, the known token case is a bit of a mis-use of the factory method since its design is to return any type of token. You are using the factory in two different ways. You could therefore either create a specific factory method for the particular token, or just use a cast.
A method Not Defined In the Interface, cannot be invoked using the Interface type. So the reference variable tm of Interface type TokenManager has to be cast to one of the sub type, before invoking the specific method(that is not in the interface, but in the specific class).
if (tm instanceof JwtTokenManagerImpl ) {
JwtTokenManagerImpl jwtTm = (JwtTokenManagerImpl) tm;
jwtTm.aMethodNotDefinedInInterface();
}
I have an interface class reference and an object I created using a dynamic proxy that implements the interface above.
final Class<?> interfaceType;
final Object interfaceImplementation = Proxy.newProxyInstance(...)`
I want to make the proxy instance available in the Guice application context by binding the interface to the proxy object. I tried but failed to quickly achieve this with Guice.
Is it possible to do something like:
bind(interfaceType).toInstance(interfaceImplementation);
so that I can inject the interface into other classes?
The problem is that the return type of Proxy.newProxyInstance() is just Object, but bind(...).toInstance(...) wants to ensure that the type is the same as the ? from Class<?> interfaceType. At some point, you're going to need to do an unchecked cast to get everything working. Here's how I'd write it:
private <T> T newProxyInstance(ClassLoader loader, InvocationHandler handler, Class<T> primaryInterface, Class<?>... extraInterfaces) {
Class<?>[] allInterfaces = Lists.asList(primaryInterface, extraInterfaces)
.stream()
.toArray(Class<?>[]::new);
#SuppressWarning("unchecked")
T proxy = (T) Proxy.newProxyInstance(loader, allInterfaces, handler);
return proxy;
}
then I think
bind(interfaceType)
.toInstance(newProxyInstance(loader, handler, interfaceType[, ...]));
would work.
I'm trying to port some C# code to Java and I'm having trouble with a proxy implementation.
I have this class:
public class Comic
{
public int ComicID;
private List<Volume> _volumes;
public List<Volume> GetVolumes()
{
return _volumes;
}
public void SetVolumes(List<Volume> volumes)
{
_volumes = volumes;
}
}
And I'm trying to add an interceptor to a specific Method call for this entity, but I also need access to it's fields since I'll be using them.
After looking around for how to implement proxies in Java, I got this:
public void Load(Class<?> type)
{
// - type is a Comic.class
ClassLoader appLoader = this.getClass().getClassLoader();
MyProxyHandler proxyHandler = new MyProxyHandler();
Object proxy = Proxy.newProxyInstance(appLoader, new Class[] { ComicInterface.class }, proxyHandler);
}
The problem is, the type is dynamic so I don't know which will be, and I don't want the code to have a requirement of having interfaces of everything, so I looked up on how to build a dynamic interface:
public class InterfaceLoader extends ClassLoader
{
public InterfaceLoader(ClassLoader loader)
{
super(loader);
}
public Class<?> GetInterface(Class<?> type) throws Exception
{
String interfaceName = type.getName() + "$Proxy";
Class<?> interfaceType = null;
interfaceType = findLoadedClass(interfaceName);
if (interfaceType != null) { return interfaceType; }
// - According to the link
byte[] classData = new InterfaceBuilder().BuildInterface(interfaceName, type.getDeclaredMethods());
return defineClass(interfaceName, classBytes, 0, classBytes.length);
}
}
And then I'd use it with the Proxy:
public void Load(Class<?> type)
{
// - type is a Comic.class
ClassLoader appLoader = this.getClass().getClassLoader();
InterfaceLoader loader = new InterfaceLoader(appLoader);
Class<?> dynamicInterface = loader.GetInterface(type);
// - dynamicInterface on debug is "console.Comic$Proxy", seems fine
MyProxyHandler proxyHandler = new MyProxyHandler();
Object proxy = Proxy.newProxyInstance(appLoader, new Class[] { dynamicInterface }, proxyHandler);
}
The exception I get is
java.lang.IllegalArgumentException: interface console.Comic$Proxy is not visible from class loader
I've looked up the exception but only found two solutions, which is to make sure the name doesn't conflict with any type (and I'm sure it doesn't) and to use this.getClass().getClassLoader() instead of type.getClass().getClassLoader() (same error).
What am I doing wrong?
And another issue is, how can I get the "original" object so I can get/set values from fields like ComicID? Using the above proxy method I can intercept methods fine, but I lose access to it's fields.
I've read about it here to get the InvocationHandler, but I don't know how to get the object from the handler and I couldn't find an example with it.
There is only one classLoader that can see your dynamically generated interface, and that is the InterfaceLoader loader. You can probably make the error go away by passing that to newProxyInstance.
But, I'm pretty sure this won't do you any good. None of the java code you are writing is loaded by that class loader, so you won't be able to call through that interface except by reflection.
If you want to use a proxy, then you'll have to make Comic implement an interface, and then use that interface everywhere. In that case, the answer to your next question is that you would pass an instance of the original object to your proxy handler constructor.
If you really want to do this dynamic interception thing, you probably want to look at using ASM (http://asm.ow2.org/) or Javaassist to build a dynamic subclass of Comic that overrides the method you want to intercept, instead of using a proxy.