In my Spring application, I have components that use Spring's caching mechanism. Each #Cacheable annotation specifies the cache that is to be used. I'd like to autodiscover all the caches that are needed at startup so that they can be automatically configured.
The simplest approach seemed to create a marker interface (ex: CacheUser) to be used by each caching component:
#Component
public class ComponentA implements CacheUser {
#Cacheable("dictionaryCache")
public String getDefinition(String word) {
...
}
}
I would then have Spring autodiscover all the implementations of this interface and autowire them to a configuration list that can be used when configuring the cache manager(s). This works.
#Autowired
private Optional<List<CacheUser>> cacheUsers;
My plan was to take each discovered class and find all methods annotated with #Cacheable. From there I would access the annotation's properties and obtain the cache name. I'm using AnnotationUtils.findAnnotation() to get the annotation declaration.
That's where the plan falls apart. Spring actually wires proxies instead of the raw component, and the annotations aren't copied over to the proxies' methods. The only workaround I've found exploits the fact that the proxy implements Advised which provides access to the proxied class:
((Advised)proxy).getTargetSource().getTargetClass().getMethods()
From there I can get the original annotations, but this approach is clearly brittle.
So two questions, really:
Is there a better way to get to the annotations defined by the proxied class?
Can you suggest any other way to discover all uses of #Cacheable in my project? I'd love to do without a marker interface.
Thanks!
Spring has a lot of infrastructure interfaces which can help you tap into the lifecycle of the container and/or beans. For your purpose you want to use a BeanPostProcessor and the SmartInitializingSingleton.
The BeanPostProcessor will get a callback for all the beans constructed, you will only need to implement the the postProcessAfterInitialization method. You can in that method detect the annotations and fill a list of caches.
Then in the SmartInitializingSingletons afterSingletonsInstantiated method you use this list to bootstrap/init your caches.
Something like the following (it is untested but should give you an idea).
public class CacheInitialingProcessor implements BeanPostProcessor, SmartInitializingSingleton {
private final Set<String> caches = new HashSet<String>();
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
Class<?> targetClass = AopUtils.getTargetClass(bean);
ReflectionUtils.doWithMethods(targetClass, new ReflectionUtils.MethodCallback() {
#Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
Cacheable cacheable = AnnotationUtils.getAnnotation(method, Cacheable.class);
if (cacheable != null) {
caches.addAll(Arrays.asList(cacheable.cacheNames()));
}
}
});
return bean;
}
#Override
public void afterSingletonsInstantiated() {
for (String cache : caches) {
// inti caches.
}
}
}
Related
My current situation:
I want to inject the following class into my application:
public interface IConfigAccessor<T extends IConfig> {
...
}
ConfigAccessors are a proxy-objects, created dynamically at runtime. The creation of these object works as follows:
public class ConfigFactory implements IConfigFactory {
private final IConfigUpdater updater;
#Inject
public ConfigFactory(IConfigUpdater updater) {
this.updater = updater;
}
#Override
public <T extends IConfig> IConfigAccessor<T> register(final String configKey, final Class<T> configClass) {
ConfigCache<T> configCache = new ConfigCache<>(new SomeOtherThings(), configKey, configClass);
updater.register(configCache);
return new ConfigAccessor<>(configCache, configKey, configClass);
}
}
As you can see, to create these objects, I need to inject the ConfigUpdater and other depdencies. This means, that guice needs to be fully configured already.
To get the instance out of Guice, I use the following code:
IConfigFactory configClient = injector.getInstance(IConfigFactory.class);
IConfigAccessor<ConcreteConfig> accessor = configClient.register("key", ConcreteConfig.class)
How I want to inject them via Guice:
Currently, I can get the requried objects, but I have to manually pass them around in my application.
Instead, what I want to have is the following:
public class SomeClass {
#Inject
public SomeClass(#Config(configKey="key") IConfigAccessor<ConcreteConfig> accessor) {
// hurray!
}
}
What's the correct approach/technology to get this working?
After a lot of research, I'm feeling a bit lost on how to approach this topic. There are a lot of different things Guice offers, including simple Providers, custom Listeners which scan classes and identify custom annotations, FactoryModuleBuilders and more.
My problem is quite specific, and I'm not sure which of these things to use and how to get it working. I'm not even sure if this is even possible with Guice?
Edit: What I have so far
I have the following annotation which I want to use inside constructor paramters:
#Target({ ElementType.FIELD, ElementType.PARAMETER })
#Retention(RetentionPolicy.RUNTIME)
public #interface InjectConfig {
String configKey();
}
Inside the module, I can bind a provider to IConfigAccessor (with the above annotation) as such:
bind(IConfigAccessor.class).annotatedWith(InjectConfig.class)
.toProvider(new ConfigProvider<>());
However, there are two problems whith this:
The provider cannot provide IConfigAccessor. To create such an instance, the provider would need an IConfigUpdater, but since I use 'new' for the provider, I can't inject it.
Inside the provider, there is no way to find out about the configKey used in the Annotation.
Second approach:
Let's assume that I already know all configurations and configKeys I want to inject during startup. In this case, I could loop over all possible configKeys and have the following binding:
String configKey = "some key";
final Class<? extends IConfig> configClass =...;
bind(IConfigAccessor.class).annotatedWith(Names.named(configKey))
.toProvider(new ConfigProvider<>(configKey, configClass));
However, problem (1) still resides: The provider cannot get an IConfigUpdater instance.
The main problem here is that you cannot use the value of the annotation in the injection. There is another question which covers this part:
Guice inject based on annotation value
Instead of binding a provider instance, you should bind the provider class, and get the class by injecting a typeliteral.
That way, your config factory can look like that:
public class ConfigFactory<T extends IConfig> implements IConfigFactory {
#Inject private final IConfigUpdater updater;
#Inject private TypeLiteral<T> type;
#Override
public IConfigAccessor<T> register(final String configKey) {
Class<T> configClass = (Class<T>)type.getRawType();
ConfigCache<T> configCache = new ConfigCache<>(new SomeOtherThings(), configKey, configClass);
updater.register(configCache);
return new ConfigAccessor<>(configCache, configKey, configClass);
}
}
And then SomeClass:
public class SomeClass {
#Inject
public SomeClass(ConfigFactory<ConcreteConfig> accessor) {
ConcreteConfig config = accessor.register("key");
}
}
Since SomeClass needs to know "key" anyway, this is not too much a change information-wise. The downside is that the SomeClass API now gets a factory instead of the concrete config.
[EDIT]
And here is someone who actually did inject annotated values using custom injection.
I have a bean declared with annotation #Bean
#Bean
public Set<DefaultMessageListenerContainer> beans() {
Set<DefaultMessageListenerContainer> containerSet = new HashSet<DefaultMessageListenerContainer>();
return containerSet;
}
I have some operations to be performed when I am destroying the bean. How can I achieve that?
I know I can use #predestroy annotation on a method in a class annotated with #Component but not sure how can I do that when declared #Bean annotation.
EDIT :
#Bean(destroyMethod="stopContainers")
public Set<DefaultMessageListenerContainer> containers() {
Set<DefaultMessageListenerContainer> containerSet = new HashSet<DefaultMessageListenerContainer>();
return containerSet;
}
public void stopContainers(){
Set<DefaultMessageListenerContainer> containerSet = containers();
......
}
}
But I am getting an error , Couldn't find a destroy method named 'stopContainers' on bean with name 'containers'
How to fix this?
Expanded from other comment - here's an example to wrap:
#Bean(destroyMethod="stopContainers")
public StoppableSetWrapper<DefaultMessageListenerContainer> containers() {
StoppableSetWrapper<DefaultMessageListenerContainer> wrapper = new StoppableSetWrapper<>();
return wrapper;
}
public class StoppableSetWrapper<T> {
private final Set<T> containers = new HashSet<T>();
public boolean add(T container) {
return containers.add(container);
}
// other Set related methods as needed...
public void stopContainers() {
// clean up...
}
}
The code which uses the injected/autowired bean will need to be updated since the bean type has changed.
Generally you can specify destroyMethod parameter for the #Bean annotation. And define the particular implementation for this method in your bean class.
As you're using Set you have no chance to add destroyMethod into the Set.class. So you have to wrap it (as Andrew proposed).
Actually, I don't like this kind of approach at all. It seems more preferable not to use Set of beans and find another workaround (by destroying them one by one). In my opinion, you can implement a separate manager class performing operations on your containers.
I have a custom annotation #UniqueModel, which is validated by a ConstraintValidator:
#Component
public class UniquePlaceValidator implements ConstraintValidator<UniqueModel, Model> {
#Autowired
private ModelRepository repository;
public UniqueModelValidator() {
}
public void initialize(UniqueModel constraint) {
}
#Override
public boolean isValid(Model model, ConstraintValidatorContext context) {
if (repository == null)
return true;
Model dbModel = repository.findByNameAndMail(model.getName(), model.getMail());
return dbModel == null;
}
The problem is, that I need to do the validation before the safe()-method of the repository is called, otherwise the field injection won't work.
I therefor created a delegate-method with a #Valid-annotation, in order to force the unique-validation before:
Model save(#Valid Model model {
return repository.save(model);
}
Unfortunately this doesn't work, it seems like the #Valid-annotation is ignored by Spring.
How can I assure the correct timing of validation?
Depending on your Bean validation configuration you may need to annotate your repository bean with #ValidateOnExecution.
But I'm not sure if Spring does support this annoation (see SPR-10641) hence I'm using Spring's own #Validated annotation in my repository and service interfaces and method level validation works fine!
See also this question and have a look into MethodValidationPostProcessor which clearly states "Target classes with such annotated methods need to be annotated with Spring's #Validated annotation at the type level". So it seems to be pretty clear that you have to use #Validated instead of #ValidateOnExecution until SPR-10641 is fixed.
How can you perform a check at startup-time on all the usages of an annotation?
For instance, I have this aspect, that is applied to the methods annotated with #Protect, that applies some security policy. Across the system, we have methods annotated with #Protect("valid-operation-1"), #Protect("valid-operation-2") or #Protect("INVALID-operation"). As soon as the application starts up, I'd like to check the arguments provided for all these annotations in order to detect such misconfigurations.
In particular, I'll check that we have a bean defined in the Spring application context whose ID matches the argument of the annotation. That means, to protect the method void drive(), I'll annotate with #Protect("drive"), and expect a bean protect_drive to be present in the application context.
You can easily just wait until the method is invoked, then the advice is called, and you check the argument. Then you'll see that INVALID-operation is wrongly defined. But this is too late.
Is it possible to have this checked for all annotated methods when the application starts?
If the Classes you want to check are Spring Beans, then you can use a BeanPostProcessor.
public class OnlyAScratchForAnPostProcessor {
#Inject
private ApplicationContext context;
#Override
public Object postProcessAfterInitialization(final Object bean,
final String beanName) throws BeansException {
ReflectionUtils.doWithMethods(bean.getClass(), new MethodCallback() {
#Override
public void doWith(Method method) throws IllegalArgumentException,
IllegalAccessException {
String expecedNameFromAnnotation = scanAnnotation(method);
if(expecedNameFromAnnotation != null) {
if(context.beanByName(expecedNameFromAnnotation) != null) {
throw new RuntimeException("illegal configuration");
}
}
}
String scanAnnotation(Method method){...}
}, ReflectionUtils.USER_DECLARED_METHODS);
}
I want to reinject singleton-scoped dependencies into prototype Spring beans, after they have been deserialized.
Say I've got a Process bean, which depends on a Repository bean. The Repository bean is a scoped as a singleton, but the Process bean is prototype-scoped. Periodically I serialize the Process, and then later deserialize it.
class Process {
private Repository repository;
// getters, setters, etc.
}
I don't want to serialize and deserialize the Repository. Nor do I want to put "transient" on the member variable that holds a reference to it in Process, nor a reference to some kind of proxy, or anything other than a plain old member variable declared as a Repository.
What I think I want is for the Process to have its dependency filled with a serializable proxy that points (with a transient reference) to the Repository, and, upon deserialization, can find the Repository again. How could I customize Spring to do that?
I figure I could use a proxy to hold the dependency references, much like . I wish I could use that exact technique. But the proxy I've seen Spring generate isn't serializable, and the docs say that if I use it with a singleton bean, I'll get an exception.
I could use a custom scope, perhaps, on the singleton beans, that would always supply a proxy when asked for a custom-scoped bean. Is that a good idea? Other ideas?
I used this instead, without any proxy:
public class Process implements HttpSessionActivationListener {
...
#Override
public void sessionDidActivate(HttpSessionEvent e) {
ServletContext sc = e.getSession().getServletContext();
WebApplicationContext newContext = WebApplicationContextUtils
.getRequiredWebApplicationContext(sc);
newContext.getAutowireCapableBeanFactory().configureBean(this, beanName);
}
}
The example is for a web environment when the application server serializes the session, but it should work for any ApplicationContext.
Spring provides a solution for this problem.
Take a look at the spring documentation http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-atconfigurable.
7.8.1 Using AspectJ to dependency inject domain objects with Spring
...
The support is intended to be used for objects created outside
of the control of any container. Domain objects often fall into
this category because they are often created programmatically
using the new operator, or by an ORM tool as a result of a database query.
The trick is to use load time weaving. Just start the jvm with -javaagent:path/to/org.springframework.instrument-{version}.jar. This agent will recognize every object that is instantiated and if it is annotated with #Configurable it will configure (inject #Autowired or #Resource dependencies) that object.
Just change the Process class to
#Configurable
class Process {
#Autowired
private transient Repository repository;
// getters, setters, etc.
}
Whenever you create a new instance
Process process = new Process();
spring will automatically inject the dependencies.
This also works if the Process object is deserialized.
How about added using aspects to add an injection step when you deserialize the object?
You would need AspectJ or similar for this. It would work very similarly to the #Configurable function in Spring.
e.g. add some advice around the a "private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException" method
This article may also help: http://java.sun.com/developer/technicalArticles/Programming/serialization/
I think the idea of serializing a bean and then forcing a reinjection of dependencies is not the best architecture.
How about having some sort of ProcessWrapper bean instead which could be a singleton. It would be injected with the Repository and either manages the deserialization of the Process or has a setter for it. When a new Process is set in the wrapper, it would call setRepository() on the Process. The beans that use the Process could either be set with the new one by the wrapper or call the ProcessWrapper which would delegate to the Process.
class ProcessWrapper {
private Repository repository;
private Process process;
// getters, setters, etc.
public void do() {
process.do();
}
public void setProcess(Process process) {
this.process = process;
this.process.setRepository(repository);
}
}
Answering my own question: how I've solved the problem so far is to create a base class which serializes and deserializes using a cheap little proxy. The proxy contains only the name of the bean.
You'll note that it uses a global to access the Spring context; a more elegant solution might store the context in a thread-local variable, something like that.
public abstract class CheaplySerializableBase
implements Serializable, BeanNameAware {
private String name;
private static class SerializationProxy implements Serializable {
private final String name;
public SerializationProxy(CheaplySerializableBase target) {
this.name = target.name;
}
Object readResolve() throws ObjectStreamException {
return ContextLoader.globalEvilSpringContext.getBean(name);
}
}
#Override
public void setBeanName(String name) {
this.name = name;
}
protected Object writeReplace() throws ObjectStreamException {
if (name != null) {
return new SerializationProxy(this);
}
return this;
}
}
The resulting serialized object is 150 bytes or so (if I remember correctly).
The method applicationContext.getAutowireCapableBeanFactory().autowireBean(detachedBean); can be used to reconfigure a Spring-managed bean that was serialized and then de-serialized (whose #Autowired fields become null). See example below. The serialization details are omitted for simplicity.
public class DefaultFooService implements FooService {
#Autowired
private ApplicationContext ctx;
#Override
public SerializableBean bar() {
SerializableBean detachedBean = performAction();
ctx.getAutowireCapableBeanFactory().autowireBean(detachedBean);
return detachedBean;
}
private SerializableBean performAction() {
SerializableBean outcome = ... // Obtains a deserialized instance, whose #Autowired fields are detached.
return outcome;
}
}
public class SerializableBean {
#Autowired
private transient BarService barService;
private int value;
public void doSomething() {
barService.doBar(value);
}
}