Implement custom annotation in Spring - spring

I want to implement an annotation which registers classes (not instances of classes) with a factory as soon as the application is started. I am using Spring Framework 4.2.7.
Consider a system with a dashboard and multiple widgets. The dashboard has a configuration file which contains a list of widgets to display for the current user. When displayed it reads the configuration and creates the widgets. The widgets will receive additional parameters from the configuration.
Here is a bit of code illustrating this:
public class TestDashboard implements Dashboard {
public void dashboardPreDisplay() {
List<String> widgets = getWidgetList(/* current user in session */);
for (String widgetId : widgets) {
// create instance of DashboardWidget with given ID
DashboardWidget x = widgetFactory.createWidget(widgetId);
}
}
public List<String> getWidgetList(String user) {
// load list of IDs of DashboardWidgets to be displayed for the user
}
#Autowired
private WidgetFactory widgetFactory;
}
#Service
public class WidgetFactory {
public DashboardWidget createWidget(String widgetId) {
// look up Class<> of DashboardWidget with given id in widgetClasses
// construct and initialize DashboardWidget
}
private HashMap<String, Class<?>> widgetClasses;
}
When implementing my widgets I don't want to deal with registering the widget with the factory class. Ideally I would just annotate the widget like that:
#DashboardWidget(id = "uniqueId")
public class DashboardWidgetA implements DashboardWidget {
// ...
}
When the application starts it should scan the classpath for #DashboardWidget annotations and register the classes with the factory, so that the widgets can be constructed by giving the createWidget-method the id of the widget.
At the moment I am a little bit confused. I think Spring has every tool on board to achieve this behavior. But I cannot think of a way how to do it.
Do you have some advice for me?

Nothing prevents you to create your custom annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface DashboardWidget {}
Then you can annotate your Widget's classes and make them spring beans. You have to keep in mind if you want to have them as singletons (scope=singleton) , or separate instances per user (scope=prototype).
You have to implement:
public class WidgetInitializationListener implements ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext context = event.getApplicationContext();
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
String originalClassName = getOriginalClassName(beanDefinitionName, event);
if (originalClassName != null) {
Class<?> clazz = Class.forName(originalClassName);
if (hasWidgetAnnotation(clazz)) {
registerSomewhereYourWidget(context, beanDefinitionName, originalClassName);
}
}
}
}
private String getOriginalClassName(String name, ContextRefreshedEvent event) {
try {
ConfigurableListableBeanFactory factory =
(ConfigurableListableBeanFactory)event.getApplicationContext().getAutowireCapableBeanFactory();
BeanDefinition beanDefinition = factory.getBeanDefinition(name);
return beanDefinition.getBeanClassName();
} catch (NoSuchBeanDefinitionException e) {
LOG.debug("Can't get bean definition for : " + name);
return null;
}
}
So mostly here is nothing to do with spring except you just run through your beans to find annotated ones.

Related

Dependency-inject "dynamically specified" beans based on annotation arguments

I have a use case where it would be extraordinarily nice to dynamically instantiate beans (using some kind of factory approach) based on annotation-arguments at the injection point. Specifically, I need to be able to specify a type-argument to the bean-creating factory.
A pretty relevant example would be a JSON deserializer that needs the type which it needs to deserialize to.
I envision either:
#Inject
#DeserializeQualifier(Car.class)
private Deserializer<Car> _carDeserializer;
#Inject
#DeserializeQualifier(Bus.class)
private Deserializer<Bus> _busDeserializer;
.. or simply, if it was possible to sniff the type from the generic type argument:
#Inject
private Deserializer<Car> _carDeserializer;
#Inject
private Deserializer<Bus> _busDeserializer;
The big point here is that I would not know beforehand which types was needed in the project, as this would be a generic tool that many projects would include. So you would annotate your #Configuration class with #EnableDeserializer and could then inject any type deserializer (The factory that makes these deserializers can handle any type, but to be able create one, it would need to know the desired type of the deserialized object - plain generics would not cut it, since Java ain't using reified generics).
So, I'd need to be able to inject into the spring context, or using any other Spring magic tricks, some kind of DeserializerFactory that takes the type argument.
Basically, I need to have Spring invoke the following method based based on either, as in the first example, the qualifier argument (or the entire DeserializeQualifier-instance for that matter), or as in the second example, the generic type argument:
DeserializerFactory {
<T> Deserializer<T> createDeserializer(Class<T> type) { ... }
}
You could create a BeanFactoryPostProcessor to set attributes annotated with a custom annotation. I've set up a small Spring Boot project to play around:
// Custom annotation
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
public #interface InjectSomeClassHere {
Class value();
}
// Demo bean
#Component
public class SomeBean {
#InjectSomeClassHere(String.class)
private Class someValue;
public Class getInjectedClass() {
return someValue;
}
}
// The BeanFactoryPostProcessor
#Component
public class SomeBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
Arrays
.stream(beanFactory.getBeanDefinitionNames())
.filter(beanName -> hasAnnotatedField(beanFactory, beanName))
.forEach(beanName -> {
Object bean = beanFactory.getBean(beanName);
Stream.of(bean.getClass().getDeclaredFields()).forEach(field -> setFieldValue(bean, field));
});
}
private boolean hasAnnotatedField(ConfigurableListableBeanFactory beanFactory, String beanName) {
try {
String className = beanFactory.getBeanDefinition(beanName).getBeanClassName();
if (className == null) {
return false;
}
return Arrays.stream(Class.forName(className).getDeclaredFields())
.anyMatch(field -> field.isAnnotationPresent(InjectSomeClassHere.class));
} catch (ClassNotFoundException e) {
// Error handling here
return false;
}
}
private void setFieldValue(Object filteredBean, Field field) {
try {
// Note: field.isAccessible() is deprecated
if (!field.isAccessible()) {
field.setAccessible(true);
}
// Retrieve the value from the annotation and set the field
// In your case, you could call `createDeserializer(fieldValue);` and set the field using the return value.
// Note that you should change the type of `SomeBean#someValue` accordingly.
Class fieldValue = field.getAnnotation(InjectSomeClassHere.class).value();
field.set(filteredBean, fieldValue);
} catch (IllegalAccessException e) {
// Error handling here
e.printStackTrace();
}
}
}
// A small test to verify the outcome of the BeanFactoryPostProcessor
#RunWith(SpringRunner.class)
#SpringBootTest
public class SomeBeanTests {
#Autowired
private SomeBean someBean;
#Test
public void getInjectedClass_shouldHaveStringClassInjected() {
Assert.assertEquals(String.class, someBean.getInjectedClass());
}
}
Please note that this is a very naive implementation and requires further fine tuning. For instance, it scans all attributes in all spring components for the presence of an annotation.
Good luck with your project!

Proxy design pattern with IoC

I am trying to implement proxy design pattern for caching services as below.
public interface IProductService
{
int ProcessOrder(int orderId);
}
public class ProductService : IProductService
{
public int ProcessOrder(int orderId)
{
// implementation
}
}
public class CachedProductService : IProductService
{
private IProductService _realService;
public CachedProductService(IProductService realService)
{
_realService = realService;
}
public int ProcessOrder(int orderId)
{
if (exists-in-cache)
return from cache
else
return _realService.ProcessOrder(orderId);
}
}
How do I to use IoC container (Unity/Autofac) to create real service and cached service objects as I can register IProductService to ProductService or CachedProductService but CachedProductService in turn requires a IProductService object (ProductService) during creation.
I am trying to arrive at something like this:
The application will target IProductService and request IoC container for an instance and depending on the configuration of the application (if cache is enabled/disabled), the application will be provided with ProductService or CachedProductService instance.
Any ideas? Thanks.
Without a container your graph would look like this:
new CachedProductService(
new ProductService());
Here's an example using Simple Injector:
container.Register<IProductService, ProductService>();
// Add caching conditionally based on a config switch
if (ConfigurationManager.AppSettings["usecaching"] == "true")
container.RegisterDecorator<IProductService, CachedProductService>();

MVC: Invoking overloaded constructors conditionally

I have an MVC application where I am implementing CQRS where I have seperated saving data from reading data into seperate interfaces. I am using constructor injection for injecting the concrete instances of these interfaces into the Controller. For constructor injection I am using Unity container. See below example
//The Employee controller
public class EmployeeController : Controller
{
IEmployeeRepository _Writer;
IEmployeeQuery _Reader;
//constructor injection
public EmployeeController(IEmployeeRepository writer, IEmployeeQuery reader)
{
this._Writer = writer;
this._Reader = reader;
}
//To Do: constructor injection for write operations only
public EmployeeController(IEmployeeRepository writer)
{
this._Writer = writer;
}
//To Do: constructor injection for read operations only
public EmployeeController(IEmployeeQuery reader)
{
this._Reader = reader;
}
}
//Registration of the concrete types in the unity container.
public static class Bootstrapper
{
public static void ConfigureUnityContainer()
{
IUnityContainer container = new UnityContainer();
container.RegisterType<IEmployeeRepository, EmployeeRepository>(new HttpContextLifetimeManager<IEmployeeRepository>());
container.RegisterType<IEmployeeQuery, EmployeeQueries>(new HttpContextLifetimeManager<IEmployeeQuery>());
ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory(container));
}
}
//The derived Controller Factory for injection dependencies in the Controller constructor
public class UnityControllerFactory : DefaultControllerFactory
{
IUnityContainer container;
public UnityControllerFactory(IUnityContainer container)
{
this.container = container;
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
try
{
if (controllerType == null)
{
throw new ArgumentNullException("controllerType");
}
if (!typeof(IController).IsAssignableFrom(controllerType))
{
throw new ArgumentException(String.Format("Type requested is not a controller: {0}", controllerType.Name), "controllerType");
}
return container.Resolve(controllerType) as IController;
}
catch (Exception)
{
return null;
}
}
}
I have figured out that for any action I will either be fetching data or writing data but not both. In that case I need to invoke the controller constructors conditionally depending on which of "_Writer" or "_Reader" I need to initialize.
How can this be done ?
Looks like you have one controller where you should use two? If you never need to be able to both read and write I would consider to refactor that component towards single responsibility.
If you don't want to do that I would consider injecting a NullObject instead of not injecting that dependency at all. See this thread.
The TecX project contains an extension that mimics NInject's contextual binding. That would allow you to specify when to inject what dependency. The code can be found inside the TecX.Unity project (folder ContextualBinding). The tests that show how to use it are inside the TecX.Unity.ContextualBinding.Test project).
What about lazy loading components? You resolve both dependencies but only one that is really used is initialized.
Sample here: http://pwlodek.blogspot.com/2010/05/lazy-and-ienumerable-support-comes-to.html

How to use Entity Framework context with dependency injection?

I'm trying to setup a base Repository class that can use the Entity Framework edmx model context. The problem I'm having is that I need to find an interface that the EF EDMX object context implements so I can pass to the constructor via dependency injections. I've got around this before by using a DataFactory that creates it and stores it in the HttpContext but that kills the ability to unit test. Any help would be appreciated. Thanks!
public abstract class BaseRepository<T> where T : EntityObject
{
private MyDataModelContext _dataContext;
private ObjectSet<T> dbset;
protected BaseRepository(IObjectContext dataContext)
{
_dataContext = dataContext;
dbset = _dataContext.CreateObjectSet<T>();
}
.....
I've always created a DataContextFactory that passes my own interface to the Context, and passed that to my repositories like so:
The context interface:
public IMyDataContext {
// One per table in the database
IDbSet<Class1> Class1s { get;set; }
// etc
// The standard methods from EF you'll use
void Add( object Entity );
void Attach( object Entity );
void Delete( object Entity );
void SaveChanges();
}
The context factory:
public class MyDataContextFactory : IMyDataContextFactory {
public IMyDataContext GetContext() {
// TODO: Use the service locator pattern to avoid the direct instanciation
return new MyDataContext();
}
}
The context factory interface:
public interface IMyDataContextFactory {
IMyDataContext GetContext();
}
The repository:
public class MyClass1Repository {
private readonly IMyDataContextFactory factory;
public MyClass1Repository( IMyDataContextFactory Factory ) {
// TODO: check for null
this.factory = Factory;
}
public List<MyClass1> GetAll() {
using ( IMyDataContext db = this.factory.GetContext() ) {
return db.Class1s.ToList();
}
}
// TODO: Other methods that get stuff
}
Then when I want to test the repository, I pass in a fake IMyDataContextFactory that returns a fake IMyDataContext from GetContext().
In time I notice duplication in repositories, and can push certain methods into the base repository: GetAll(), Save(), GetById() sometimes if I have consistent primary keys, etc.

Spring - Qualify injection candidates by designated environment

Edit:
Perhaps a more concise way to ask this question is: Does Spring provide a way for me to resolve ambiguous candidates at injection time by providing my own listener/factory/decision logic?
In fact, arguably the #Environmental qualifier on the member field below is unnecessary: if an #Inject-ion is ambiguous... let me help? In fact, #ResolveWith(EnvironmentalResolver.class) would be alright too..
When Spring attempts to inject a dependency (using annotations) I understand that I need to #Qualifier an #Inject point if I am to have multiple components that implement that interface.
What I'd like to do is something like this:
class MyFoo implements Foo {
#Inject
#Environmental
private Bar bar;
}
#Environmental(Environment.Production)
class ProductionBar implements Bar {
}
#Environmental({Environment.Dev, Environment.Test})
class DevAndTestBar implements Bar {
}
I would expect that I need to create some kind of ambiguity resolver which would look something (vaguely) like this:
class EnvironmentalBeanAmbiguityResolver {
// set from configuration, read as a system environment variable, etc.
private Environment currentEnvironment;
public boolean canResolve(Object beanDefinition) {
// true if definition has the #Environmental annotation on it
}
public Object resolve(Collection<Object> beans) {
for (Object bean : beans) {
// return bean if bean #Environmental.values[] contains currentEnvironment
}
throw new RuntimeException(...);
}
}
One example of where this would be useful is we have a service that contacts end-users. Right now I just have a hacked together AOP aspect that before the method call to the "MailSender', checks for a "Production" environment flag and if it is not set, it sends the email to us instead of the users email. I'd like to instead of wrapping this in an AOP aspect specific to mail sending, instead be able to differentiate services based on the current environment. Sometime's it is just a matter of "production" or "not production" as I've demonstrated above, but a per-environment definition works too.
I think this can be reused for region too... e.g. #Regional and #Regional(Region.UnitedStates) and so on and so forth.
I'd imagine #Environmental would actually be a #Qualifier that way if you wanted to depend directly on something environmental you could (an #Environmental(Production) bean would likely depend directly on an #Environmental(Production) collaborator - so no ambiguity for lower level items --- same a #Regional(US) item would depend on other #Regional(US) items expiclitly and would bypass my yet-to-be-understood BeanAmbiguityResolver)
Thanks.
I think I solved this!
Consider the following:
public interface Ambiguity {
public boolean isSatisfiedBy(BeanDefinitionHolder holder);
}
#Target({ METHOD, CONSTRUCTOR, FIELD })
#Retention(RUNTIME)
public #interface Ambiguous {
Class<? extends Ambiguity> value();
}
#Target(TYPE)
#Retention(RUNTIME)
public #interface Environmental {
public static enum Environment {
Development, Testing, Production
};
Environment[] value() default {};
}
#Named
public class EnvironmentalAmbiguity implements Ambiguity {
/* This can be set via a property in applicationContext.xml, which Spring
can use place holder, environment variable, etc. */
Environment env = Environment.Development;
#Override
public boolean isSatisfiedBy(BeanDefinitionHolder holder) {
BeanDefinition bd = holder.getBeanDefinition();
RootBeanDefinition rbd = (RootBeanDefinition) bd;
Class<?> bc = rbd.getBeanClass();
Environmental env = bc.getAnnotation(Environmental.class);
return (env == null) ? false : hasCorrectValue(env);
}
private boolean hasCorrectValue(Environmental e) {
for (Environment env : e.value()) {
if (env.equals(this.env)) {
return true;
}
}
return false;
}
}
#Named
public class MySuperDuperBeanFactoryPostProcessor implements
BeanFactoryPostProcessor, AutowireCandidateResolver {
private DefaultListableBeanFactory beanFactory;
private AutowireCandidateResolver defaultResolver;
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory arg)
throws BeansException {
if (arg instanceof DefaultListableBeanFactory) {
beanFactory = (DefaultListableBeanFactory) arg;
defaultResolver = beanFactory.getAutowireCandidateResolver();
beanFactory.setAutowireCandidateResolver(this);
return;
}
throw new FatalBeanException(
"BeanFactory was not a DefaultListableBeanFactory");
}
#Override
public Object getSuggestedValue(DependencyDescriptor descriptor) {
return defaultResolver.getSuggestedValue(descriptor);
}
#Override
public boolean isAutowireCandidate(BeanDefinitionHolder holder,
DependencyDescriptor descriptor) {
Ambiguity ambiguity = getAmbiguity(descriptor);
if (ambiguity == null) {
return defaultResolver.isAutowireCandidate(holder, descriptor);
}
return ambiguity.isSatisfiedBy(holder);
}
private Ambiguity getAmbiguity(DependencyDescriptor descriptor) {
Ambiguous ambiguous = getAmbiguousAnnotation(descriptor);
if (ambiguous == null) {
return null;
}
Class<? extends Ambiguity> ambiguityClass = ambiguous.value();
return beanFactory.getBean(ambiguityClass);
}
private Ambiguous getAmbiguousAnnotation(DependencyDescriptor descriptor) {
Field field = descriptor.getField();
if (field == null) {
MethodParameter methodParameter = descriptor.getMethodParameter();
if (methodParameter == null) {
return null;
}
return methodParameter.getParameterAnnotation(Ambiguous.class);
}
return field.getAnnotation(Ambiguous.class);
}
}
Now if I have an interface MyInterface and two classes that implement it MyFooInterface and MyBarInterface like this:
public interface MyInterface {
public String getMessage();
}
#Named
#Environmental({ Environment.Testing, Environment.Production })
public class MyTestProdInterface implements MyInterface {
#Override
public String getMessage() {
return "I don't always test my code, but when I do, I do it in production!";
}
}
#Named
#Environmental(Environment.Development)
public class DevelopmentMyInterface implements MyInterface {
#Override
public String getMessage() {
return "Developers, developers, developers, developers!";
}
}
If I want to #Inject MyInterface I would get the same multiple bean definition error that one would expect. But I can add #Ambiguous(EnvironmentalAmbiguity.class) and then the EnvironmentalAmbiguity will tell which bean definition it is satisfied by.
Another approach would have been to use a List and go through them all seeing if they are satisfied by a given bean definition, this would mean that the dependnecy wouldn't need the #Ambiguous annotation. That might be more "IoC-ish" but I also thought it might perform poorly. I have not tested that.

Resources