How to get beans in a hibernate identifier generator? - java

My application use the Spring and hibernate.
Gotta get the MyService bean in the IdentifierGenerator.
Example:
public class MyGenerator implements org.hibernate.id.IdentifierGenerator, org.hibernate.id.Configurable {
#Autowired // doesn't work :(
private MyService myService;
#Override
public void configure(Type type, Properties properties, ServiceRegistry serviceRegistry) throws MappingException {
}
#Override
public Serializable generate(SessionImplementor sessionImplementor, Object obj) {
...

If you really, really have to do it you can define ApplicationContextHolder utility class and use it to store the Spring context:
#Component
public class ApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
synchronized (this) {
if (ApplicationContextHolder.applicationContext == null) {
ApplicationContextHolder.applicationContext = applicationContext;
}
}
}
public static <T> T getBean(Class<T> clazz) {
return applicationContext.getBean(clazz);
}
public static <T> T getBean(String qualifier, Class<T> clazz) {
return applicationContext.getBean(qualifier, clazz);
}
Afterwards use the static method to obtain MyService bean:
public class MyGenerator implements IdentifierGenerator, Configurable {
#Override
public void configure(Type type, Properties properties, ServiceRegistry serviceRegistry) throws MappingException {
MyService service = ApplicationContextHolder.getBean(MyService.class);
}
}
This goes against the principle of dependency injection, but sometimes there is not better way if some objects are not managed by Spring.

Related

Spring parent with 2 child classes use 2 different beans for property

Say I have the following
public abstract class MyClass {
#Resource
protected MyService myService;
doSomething() {
return myService.doSomething();
}
}
public class MyServiceV1 implements MyService {}
public class MyServiceV2 implements MyService {}
public class MyClassV1 extends MyClass {
//WANT TO USE MyServiceV1 implementation
}
public class MyClassV2 extends MyClass {
//WANT TO USE MyServiceV2 implementation
}
I am unable to specify the service implementation that I want to use in each subclass. I have considered using #Qualifier but I would have to re-declare the property in each child class and use it there, and hope that it overrides the parent.
The purpose of these classes is to provide two versions of an API at the same time. So both versions will be active simultaneously.
It does feel partially that this is an anti pattern in terms of how spring is meant to inject beans, so I am open to other approaches.
I think you can try to use Constructor injection to set a particular service in your classes. Smth like this:
public abstract class MyClass {
protected MyService myService;
doSomething() {
return myService.doSomething();
}
}
class MyClassV1 extends MyClass {
MyClassV1(MyService myService) {
this.myService = myService;
}
}
class MyClassV2 extends MyClass {
MyClassV2(MyService myService) {
this.myService = myService;
}
}
#Bean
MyClassV1 myClassV1() {
return new MyClassV1(myServiceV1());
}
#Bean
MyClassV1 myClassV2() {
return new MyClassV2(myServiceV2());
}
#Bean
MyServiceV1 myServiceV1() {
return new MyServiceV1();
}
#Bean
MyServiceV2 myServiceV2() {
return new MyServiceV2();
}
or setter injection:
public abstract class MyClass {
private MyService myService;
public void setMyService(MyService myService) {
this.myService = myService;
}
}
#Component
public class MyClass1 extends MyClass {
#Autowired #Qualifier("myService1")
#Override
public void setMyService(MyService myService) {
super.setMyService(myService);
}
}
#Component
public class MyClass2 extends MyClass {
#Autowired #Qualifier("myService2")
#Override
public void setMyService(MyService myService) {
super.setMyService(myService);
}
}

How to inject bean via generic variable in spring

I have problem with injecting bean with generic types. Look at the example. I will inject to the service a repository which types takes from App class. Now i have exception:
No qualifying bean of type 'asd.IRepository' available: expected single matching bean but found 2: a,b
asd here is package, just for tests.
What can I do in this situation? Is any way to makes it?
public interface IRepository<T, V> {
void print();
}
#Component
public class A implements IRepository<String,String> {
#Override
public void print() {
System.out.println("A");
}
}
#Component
public class B implements IRepository<Double,String> {
#Override
public void print() {
System.out.println("A");
}
}
#Service
public class ServiceABC<V, T> {
#Autowired
private IRepository<V,T> repo;
public void print(){
repo.print();
}
}
#Controller
public class App {
#Autowired
private ServiceABC<String, String> serviceABC;
public static void main(String[] args) {
ApplicationContext ctx =
new AnnotationConfigApplicationContext("asd");
App app = ctx.getBean(App.class);
app.serviceABC.print();
}
It looks like you don't know in advance which implementation of your IRepository interface you will need. And you will know that at runtime. In this case it is a typical case for Factory pattern where you will have a IRepositoryFactory that will have a method thhat retrieves specific implementation by type (for example IRepositoryFactory.getInstance(String type); So in your ServiceABC you may use the IRepository to get specific bean at runtime. So Factory pattern may be an answer to your question. I also wrote an article that deals with this type of problem and proposes the idea of self-populating Factory (using Open source library that provides such utility). Here is the link to the article: Non-intrusive access to "Orphaned" Beans in Spring framework
You have to name your components and autowire by name:
#Component("A")
public class A implements IRepository<String,String> {...}
#Component("B")
public class B implements IRepository<Double,String> {...}
[...]
#Autowired
#Qualifier("B")
private IRepository repo;
Something like that?
#Controller
public class RepositoryFactory {
#Autowired
private IRepository<String, String> a;
#Autowired
private IRepository<Double, String> b;
public IRepository getRepository(String className) {
if(className.equalsIgnoreCase("a")) {
return a;
} else if(className.equalsIgnoreCase("b")) {
return b;
}
return null;
}
}
#Service
public class ServiceABC {
#Autowired
private RepositoryFactory repositoryFactory;
public void print(String className){
repositoryFactory.getRepository(className).print();
}
}
#Controller
public class App {
#Autowired
private ServiceABC serviceABC;
public static void main(String[] args) {
ApplicationContext ctx =
new AnnotationConfigApplicationContext("asd");
App app = ctx.getBean(App.class);
app.serviceABC.print(A.class.getSimpleName());
}
}s

how does Spring decide the order of initializing Bean and plain class?

I have a bean as below,
#Component
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext context = null;
#Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.context = applicationContext;
}
public static <T> T getBean(String beanName) {
return (T) context.getBean(beanName);
}
public static String getActiveProfile() {
return context.getEnvironment().getActiveProfiles()[0];
}
}
it is used by a plain class,
public class ConfigUtil
{
static{
String env = SpringContextUtil.getActiveProfile();
...
}
public static getVal(String key){...}
}
If the Spring Bean SpringContextUtil is initialized after the plain class ConfigUtil, then it is not OK.
So I want to know how Spring decide which one is initilialized earlier? For example, will the invocation of SpringContextUtil.getActiveProfile() trigger the initialization of the Spring bean? or the Spring Bean is only initialized but havn't been injected applicationContext yet, thus leads to a Null Pointer Exeception?
You just need to run your code after spring has been initialized.
If you're using spring-boot, create new bean that implements ApplicationListener<ApplicationReadyEvent> and run your code inside the onApplicationEvent method.
Example
#Component
public class ApplicationListenerBean implements ApplicationListener<ApplicationReadyEvent> {
#Override
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
ConfigUtil.getVal("A");
}
}
However if you want to make ConfigUtil also as a spring bean you need to get rid of static initialization block and use instead #PostConstruct annotation on your init method.
Example
#Component
#DependsOn("springContextUtil")
public class ConfigUtil {
private String env;
#PostConstruct
private void init() {
env = SpringContextUtil.getActiveProfile();
}
public static void getVal(String key) {
System.out.print("Hello");
}
}

Autowired annotation doesn't work inside custom dozer converter

I am using next dozer custom converter
public class MyCustomDozerConverter extends DozerConverter<MyObject, String> {
#Autowired
private AppConfig appConfig;
public MyCustomDozerConverter() {
super(MyObject.class, String.class);
}
#Override
public String convertTo(MyObject source, String destination) {
String myProperty = appConfig.getWhatever();
// business logic
return destination;
}
#Override
public MyObject convertFrom(String source, MyObject destination) {
// business logic
return null;
}
}
My problem is when it goes through convertTo method inside the converter, I always got appConfig instance with null value which of course cause a null pointer exception
Note: my spring boot class have these annotations above:
#SpringBootApplication
#EnableAutoConfiguration
#ComponentScan({"com.xxx"})
#EntityScan("com.xxx")
#EnableJpaRepositories("com.xxx")
I solved this by next trick:
1- Using static with appConfig property.
2- instantiate it by spring so when dozer use default empty constructor it will find appConfig have
a value already (which assigned before to it by spring)
And here are the code i used for this:
#Component //import org.springframework.stereotype.Component;
public class MyCustomDozerConverter extends DozerConverter<MyObject, String> {
private static AppConfig appConfig;
// dozer needs this constructor to create an instance of converter (so it's a mandatory constructor)
public MyCustomDozerConverter() {
super(MyObject.class, String.class);
}
#Autowired // Spring will pass appConfig to constructor
public MyCustomDozerConverter(AppConfig appConfig) {
this();
this.appConfig = appConfig;
}
#Override
public String convertTo(MyObject source, String destination) {
String myProperty = appConfig.getWhatever();
// business logic
return destination;
}
#Override
public MyObject convertFrom(String source, MyObject destination) {
// business logic
return null;
}
}
UPDATE: Another solution
Another trick is using Spring ApplicationContextAware to get a singleton object from getBean method:
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
#Component
public class ApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static ApplicationContext getContext() {
return context;
}
}
Then create a static method inside AppConfig class and return an instance of the single bean matching the required type:
import org.springframework.context.annotation.Configuration;
import com.tripbru.ms.experiences.ApplicationContextHolder;
#Configuration
public class AppConfig {
// Static method used to return an instatnce
public static AppConfig getInstance() {
return ApplicationContextHolder.getContext().getBean(AppConfig.class);
}
// Properties
}
Then calling it direct inside the dozer converter by AppConfig.getInstance();
public class MyCustomDozerConverter extends DozerConverter<MyObject, String> {
private static AppConfig appConfig;
public MyCustomDozerConverter() {
super(MyObject.class, String.class);
appConfig = AppConfig.getInstance(); // Here are we intializing it by calling the static method we created.
}
#Override
public String convertTo(MyObject source, String destination) {
String myProperty = appConfig.getWhatever();
// business logic
return destination;
}
#Override
public MyObject convertFrom(String source, MyObject destination) {
// business logic
return null;
}
}
Try constructor dependency injection
private AppConfig appConfig;
#Autowired
MyCustomerDozerConverter(AppConfig appConfig)
{
this.appConfig = appConfig;
}
You can put following line in the CustomConverter so that Spring will autowire it.
public class MyCustomDozerConverter extends DozerConverter<MyObject, String> {
#Autowired
private AppConfig appConfig;
public MyCustomDozerConverter() {
super(MyObject.class, String.class);
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
...
}

How to inject dependency into JerseyTest?

I want to inject MyService directly into my JerseyTest using CDI. Is it possible? MyService is succcefull injected into MyResource but I get NullPointerException when I try to access it from MyJerseyTest.
public class MyResourceTest extends JerseyTest {
#Inject
MyService myService;
private Weld weld;
#Override
protected Application configure() {
Properties props = System.getProperties();
props.setProperty("org.jboss.weld.se.archive.isolation", "false");
weld = new Weld();
weld.initialize();
return new ResourceConfig(MyResource.class);
}
#Override
public void tearDown() throws Exception {
weld.shutdown();
super.tearDown();
}
#Test
public void testGetPersonsCount() {
myService.doSomething(); // NullPointerException here
// ...
}
}
I think you need to provide an instance of org.junit.runner.Runner where you will do the weld initialization. This runner should also be responsible for providing an instance of your Test class with necessary dependencies injected. An example is shown below
public class WeldJUnit4Runner extends BlockJUnit4ClassRunner {
private final Class<?> clazz;
private final Weld weld;
private final WeldContainer container;
public WeldJUnit4Runner(final Class<Object> clazz) throws InitializationError {
super(clazz);
this.clazz = clazz;
// Do weld initialization here. You should remove your weld initialization code from your Test class.
this.weld = new Weld();
this.container = weld.initialize();
}
#Override
protected Object createTest() throws Exception {
return container.instance().select(clazz).get();
}
}
And your Test class should be annotated with #RunWith(WeldJUnit4Runner.class) as shown below.
#RunWith(WeldJUnit4Runner.class)
public class MyResourceTest extends JerseyTest {
#Inject
MyService myService;
// Test Methods follow
}

Categories