How to get the Spring Data JPA Repository Factory? - java

Since I got no answer to my previous question I tried to tweak the example given in the Spring documentation for customizing repositories. There ist a Method getRepository(Class repositoryInterface) which looks like It ist the right place to map my repository Overrides:
public class MyRepositoryFactoryBean<R extends JpaRepository<T, I>, T, I extends Serializable>
extends JpaRepositoryFactoryBean<R, T, I> {
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
return new MyRepositoryFactory<>(entityManager);
}
private static class MyRepositoryFactory<T, I extends Serializable> extends JpaRepositoryFactory {
private EntityManager entityManager;
#Resource
private Map<Class<?>, Class<?>> overrideRepositories;
public MyRepositoryFactory(EntityManager entityManager) {
super(entityManager);
this.entityManager = entityManager;
//Test
overrideRepositories = new HashMap<>();
overrideRepositories.put(CustomerRepository.class, Customer2Repository.class);
}
protected Object getTargetRepository(RepositoryMetadata metadata) {
return super.getTargetRepository(metadata);
// return new MyRepositoryImpl<T, I>((Class<T>)
// metadata.getDomainClass(), entityManager);
}
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
// The RepositoryMetadata can be safely ignored, it is used by the
// JpaRepositoryFactory
// to check for QueryDslJpaRepository's which is out of scope.
return JpaRepository.class;
}
#SuppressWarnings("unchecked")
#Override
public <E> E getRepository(Class<E> repositoryInterface, Object customImplementation) {
if (overrideRepositories != null) {
Class<?> override = overrideRepositories.get(repositoryInterface);
if (override != null) {
repositoryInterface = (Class<E>) override;
}
}
return super.getRepository(repositoryInterface, customImplementation);
}
}
}
I configured it like this: #EnableJpaRepositories(repositoryFactoryBeanClass=MyRepositoryFactoryBean.class)
Normally you would autowire the repositories themselves which doesn't work because there are two Interfaces with the same Type and I don't know how to tell Spring which one to use.
If I autowire the factory instead, I can call getRepository each time I need a specific one. But how do I get this factory? Does Spring Data JPA somehow expose this as a bean? I can't find anything on google concerning this. Or is this approach entirely wrong?

You can use the ApplicationContext instance to get your MyRepositoryFactoryBean bean class. All you have to do is implement the ApplicationContextAware interface in order to get access to the ApplicationContext instance.
public class myClass implements ApplicationContextAware{
private static ApplicationContext ac;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ac = applicationContext;
}
}
Now you can use ac.getBean("MyRepositoryFactoryBean") to get the factory directly from the ApplicationContext. Once you have that bean you can call getRepository on it.

Related

Autowire Java Bean created with NewInstance from an external library

I am doing a Spring Boot Project and using the OpenCSV library to parse some csvs into POJOs to be persisted to db.
OpenCSV uses the annotation #CsvCustomBindByName to map a CSV field to a Java object.
The converter = DepartmentConverter.class is a custom converter that is instantiated with:
Class<? extends AbstractBeanField<T,K>>.newInstance()
by the library, at runtime.
The problem is that because the custom field converter is instantiated reflectively by the OpenCSV library, it cant autowire beans because it is not registered in the Spring Context.
How can i make that dynamically instantiated converter be aware of the Spring context or the other way around. Some kind of interceptor? Thanks!
//Spring Managed class
public class Specialization {
#CsvCustomBindByName(required = true, converter = DepartmentConverter.class)
private Department department;
....
}
In my DepartmentConverter i need to use a Spring JPARepository to retrieve some data. DepartmentRepository can not be autowired.
#Component
public class DepartmentConverter extends AbstractBeanField<Department, String> {
#Autowired
private DepartmentRepository departmentRepository;
public DepartmentConverter() {
}
#Override protected Object convert(String val) throws CsvConstraintViolationException, ResourceNotFoundException {
//use departmentRepository
...
}
}
The newInstance() call you're referring to is in the HeaderColumnNameMappingStrategy class, which calls the instantiateCustomConverter() method to do the newInstance() call.
Create a subclass and override the method:
#Override
protected BeanField<T, K> instantiateCustomConverter(Class<? extends AbstractBeanField<T, K>> converter) throws CsvBadConverterException {
BeanField<T, K> c = super.instantiateCustomConverter(converter);
// TODO autowire here
return c;
}
As can be seen in this answer to Spring #Autowired on a class new instance, you can do the autowiring as follows:
autowireCapableBeanFactory.autowireBean(c);
So the subclass would be something like:
public class AutowiredConverterMappingStrategy extends HeaderColumnNameMappingStrategy {
private final AutowireCapableBeanFactory beanFactory;
public AutowiredConverterMappingStrategy(AutowireCapableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
#Override
protected BeanField<T, K> instantiateCustomConverter(Class<? extends AbstractBeanField<T, K>> converter) throws CsvBadConverterException {
BeanField<T, K> c = super.instantiateCustomConverter(converter);
this.beanFactory.autowireBean(c);
return c;
}
}
To use it, you'd need something like this:
#Component
class MyComponent {
#Autowired
private AutowireCapableBeanFactory beanFactory;
public <T> List<T> parseCsvToBean(Reader reader, Class<? extends T> type) {
return new CsvToBeanBuilder(reader)
.withType(type)
.withMappingStrategy(new AutowiredConverterMappingStrategy(this.beanFactory))
.build()
.parse();
}
}
That is of course just an example. Your CsvToBean setup may be more complex, but the key part is the withMappingStrategy() call, and that the code is itself in a Spring Bean, so it has access to the bean factory.

Inject spring beans into a non-managed class

I have this non-managed class that I want to inject spring beans (that I don't known a-priory what they are). How can I do that?
For example, let's say I have the following class:
public class NonManagedClass extends APIClass {
#Resource
private Service1 service;
#Resource
private Service2 service2;
// here i can declare many different dependencies
#Resource
private ServiceN serviceN;
#Override
public void executeBusinessStuffs() {
// business logics
}
}
I need in someway to let spring inject these dependencies in my class. I have access to these objects after created, so it's easy to me call any method that can accomplish this functionality. For example:
#Service
public void SomeAPIService {
#Resource
private BeanInjector beanInjector; // I'm looking for some funcionality of spring like this
public void someProcessingFunction(Class<? extends APIClass> clazz) throws Exception {
APIClass instance = clazz.getConstructor().newInstance();
beanInjector.injectBeans(instance);
instance.executeBusinessStuffs();
}
}
Does Spring have such functionality to inject beans based on fields annotation for a non-managed class?
Replace BeanInjector with ApplicationContext and you are almost there. From there you can get the AutowireCapableBeanFactory which provides some handy methods like createBean and autowireBean.
#Service
public void SomeAPIService {
#Resource
private ApplicationContext ctx;
public void someProcessingFunction(Class<? extends APIClass> clazz) throws Exception {
APIClass instance = ctx.createBean(clazz);
instance.executeBusinessStuffs();
}
}
or if you really like to construct stuff yourself instead of using the container:
#Service
public void SomeAPIService {
#Resource
private ApplicationContext ctx;
public void someProcessingFunction(Class<? extends APIClass> clazz) throws Exception {
APIClass instance = clazz.getConstructor().newInstance();
ctx.getAutowireCapableBeanFactory().autowireBean(instance);
instance.executeBusinessStuffs();
}
}

Using #Autowired to set Class in Generics

I'm trying to setup a generic DAO and service for a project i'm working at, it all seems to be working except I have to manually set the class<T> clazz everywhere I instantiate a repository, which is ok for the ones I have an entire class for (such as the clientRepository one below).
I tried to do the #Autowired setClazz(Class<T> clazz) in the AbstractRepository to solve it but it just doesn't work, when I debug findById(Long id), the clazz is null.
Is there any way to do this or do I really have to call the setClazz everywhere?
AbstractRepository
public abstract class AbstractRepository<T extends IGenericEntity> {
#PersistenceUnit(unitName = "clima_PU")
private EntityManagerFactory entityManagerFactory;
private Session session;
private Class<T> clazz;
#PersistenceContext
protected EntityManager entityManager;
#Autowired
public void setClazz(Class<T> clazz) {
this.clazz = clazz;
}
public T findByID(Long id) {
return (T) entityManager.find(clazz, id);
}
}
GenericRepository
#Repository
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class GenericRepository<T extends AbstractEntity> extends AbstractRepository<T> implements IGenericRepository<T> {
}
ClientRepository
public class ClientRepository extends GenericRepository<ClientEntity> {
/* Custom Queries for Clients */
}

Java Dropwizard initialize ConstraintValidator

Let's say I have a custom ConstraintValidator:
public class FooValidator implements ConstraintValidator<ValidFoo, String> {
#Override
public void initialize(final ValidFoo foo) {
// No-op
}
#Override
public boolean isValid(final String foo, final ConstraintValidatorContext context) {
}
}
I'd like to be able to initialize this class by passing some configuration from the ServiceConfiguration in Dropwizard run or initialize.
Is this possible?
First, it's worth noting that the upcoming Dropwizard 2.0.0 release has built in support for this
For now, the process is a bit involved. You basically want to re-bootstrap the Hibernate validation but with a custom constraint validator factory that would support injection.
It's gonna involve about 4 custom classes, so bear with me. Here goes:
First, we start by registering a custom feature to wrap this functionality, into our Application class:
public void run(MainConfiguration config, Environment environment) throws Exception {
// ...
environment.jersey().register(InjectingValidationFeature.class);
}
Now we define the feature: InjectingValidationFeature - it basically registers our custom implementations within the service container:
public class InjectingValidationFeature implements Feature {
#Override
public boolean configure(FeatureContext context) {
context.register(new AbstractBinder() {
#Override
protected void configure() {
bindFactory(ValidatorFactory.class).to(Validator.class).in(Singleton.class);
bind(InjectingConfiguredValidator.class).to(ConfiguredValidator.class).in(Singleton.class);
bind(InjectingConstraintValidatorFactory.class).to(ConstraintValidatorFactory.class).in(Singleton.class);
}
});
return true;
}
}
Now we define those classes that we are registering above. Let's start with the core piece, the InjectingConstraintValidatorFactory which is what Hibernate Validator will actually use to create the constraint validators. Note that because we are registering them in the container, we can actually start injecting stuff already, here is our custom ConstraintValidatorFactory making use of the service locator to make dependency injection possible:
public class InjectingConstraintValidatorFactory implements ConstraintValidatorFactory {
private final ServiceLocator serviceLocator;
#Inject
public InjectingConstraintValidatorFactory(ServiceLocator serviceLocator) {
this.serviceLocator = serviceLocator;
}
#Override
public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) {
return this.serviceLocator.createAndInitialize(key);
}
#Override
public void releaseInstance(ConstraintValidator<?, ?> instance) {
this.serviceLocator.preDestroy(instance);
}
}
Now our factory for the central javax.validation.Validator interface:
public class ValidatorFactory implements Factory<Validator> {
private final ConstraintValidatorFactory constraintValidatorFactory;
#Inject
public ValidatorFactory(ConstraintValidatorFactory constraintValidatorFactory) {
this.constraintValidatorFactory = constraintValidatorFactory;
}
#Override
public Validator provide() {
return Validation.byDefaultProvider().configure().constraintValidatorFactory(
this.constraintValidatorFactory).buildValidatorFactory()
.getValidator();
}
#Override
public void dispose(Validator instance) {
// Nothing
}
}
And finally, our InjectingConfiguredValidator, notice how it's just using DropwizardConfiguredValidator but with an #Inject which would allow us to receive the validator from our ValidatorFactory above:
public class InjectingConfiguredValidator extends DropwizardConfiguredValidator {
#Inject
public InjectingConfiguredValidator(Validator validator) {
super(validator);
}
}
That's it. With the above, we managed to both register an injection-aware Validator with Jersey and also into our service container so you can also #Inject Validator anywhere and use it however you like.

Spring proxy to choose implementation based on annotation and runtime value

I would like to inject a proxy implementation of an interface to a component and then let spring choose the right implementation based on a runtime property (and the value of an annotation at the implementation class). So my component does not have to care about choosing the right one.
It is kind of like a scope. But i think scopes are only for handling different instances of the same implementation class. Am i wrong with this?
I would like this to run for arbitrary interfaces without creating a service locator or some other construct for every new service.
Here is an example.
Suppose I have an interface defining a service
package test;
public interface IService {
void doSomething();
}
and two implementations:
package test;
import javax.inject.Named;
#Named
#MyAnnotation("service1")
public class Service1 implements IService {
#Override
public void doSomething() {
System.out.println("this");
}
}
...
package test;
import javax.inject.Named;
#Named
#MyAnnotation("service2")
public class Service2 implements IService {
#Override
public void doSomething() {
System.out.println("that");
}
}
Now I would like to inject an IService to another component and let spring choose the correct implementation based on some queryable run time property and the value of MyAnnotation.
Is there a way to do this in a general way in spring?
EDIT:
I have a Context that holds some value. It is a thread local in this case.
package test;
public class MyValueHolder {
private static final ThreadLocal<String> value = new ThreadLocal<>();
public static void set(String newValue) {
value.set(newValue);
}
public static String get() {
return value.get();
}
public static void reset() {
value.remove();
}
}
And I have an component which uses IService
package test;
import javax.inject.Inject;
import javax.inject.Named;
#Named
public class MyComponent {
#Inject
private IService service;
public void myImportantWorkflow(){
MyValueHolder.set("service1");
service.doSomething();
MyValueHolder.set("service2");
service.doSomething();
}
}
The injected service should only be a proxy. Depending on the value set in MyValueHolder the call to doSomething should delegate to service1 or service2. So in this example it should delegate to doSomething on service1 in the first call and to service2 in the second call.
I could write such a delegator implementing the IService interface and use it for this one service. But then i have to repeat this for every other service . I hoped spring could do something like this with proxies almost by itself. Of course i have to provide some method to look beans up based on the value hold in the thread local and register it to spring. But i have no idea if that is even possible without modifying the spring framework. And if it is possible how to accomplish this.
You could use a ProxyFactoryBean to create the proxies and a TargetSource to do the lookup.
For example (not tested)
public class AnnotatedBeanTargetSource implements TargetSource, BeanFactoryAware {
private ConfigurableListableBeanFactory beanFactory;
private Class<? extends Annotation> annotationType;
private Class<?> implementedIterface;
private Map<String, Object> beans;
#Override
public Class<?> getTargetClass() {
return this.implementedIterface;
}
#Override
public boolean isStatic() {
return false;
}
#Override
public Object getTarget() throws Exception {
if (this.beans == null) {
this.beans = lookupTargets();
}
return this.beans.get(MyValueHolder.get());
}
protected Map<String, Object> lookupTargets() {
Map<String, Object> resolvedBeans = new HashMap<String, Object>();
String[] candidates = beanFactory.getBeanNamesForAnnotation(annotationType);
for (String beanName : candidates) {
Class<?> type = beanFactory.getType(beanName);
if (this.implementedIterface.isAssignableFrom(type)) {
Annotation ann = AnnotationUtils.getAnnotation(type, annotationType);
resolvedBeans.put((String) AnnotationUtils.getValue(ann), beanFactory.getBean(beanName));
}
}
return resolvedBeans;
}
#Override
public void releaseTarget(Object target) throws Exception {
// nothing to do
}
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
public Class<? extends Annotation> getAnnotationType() {
return annotationType;
}
public void setAnnotationType(Class<? extends Annotation> annotationType) {
this.annotationType = annotationType;
}
public Class<?> getImplementedIterface() {
return implementedIterface;
}
public void setImplementedIterface(Class<?> implementedIterface) {
this.implementedIterface = implementedIterface;
}
}
This is what I would do:
#Named
public class MyComponent {
// introduce a marker interface for Injecting proxies
#InjectDynamic
IService service
...
public void useIService() {
service.doSomething();
...
service.doSomethingElse();
...
service.doFinally();
}
}
Define a BeanPostProcessor that scans for bean with fields annotated with #InjectDynamic, then creates and inject a Proxy implementing the type required by the field.
The Proxy implementation will look in the applicationContext for beans implementing Supplier<T> (Java 8 or guava versions) where <T> is the type of the field annotated with #InjectDynamic.
Then you can define
#Name
public IServiceSupplier implements Supplier<IService> {
#Override
public IService get() {
// here you implement the look-up logic for IService
}
}
In this way the look-up of active the current implementation is decoupled from the Proxy and can be change by target type.

Categories

Resources