Dynamic Selection of TransactionManager Spring Boot - java

I am converting my existing Spring Application to a Spring Boot Application. In my existing application, we have the need to connect to multiple databases and we had achieved this by having multiple data sources defined and fetching the corresponding bean based on the condition. The transaction manager were also selected using a custom implementation of TransactionInterceptor.
#Override
public TransactionAttributeSource getTransactionAttributeSource() {
final TransactionAttributeSource origTxAttrSource = super.getTransactionAttributeSource();
return new TransactionAttributeSource() {
#Override
public TransactionAttribute getTransactionAttribute(final Method method, final Class<?> targetClass) {
TransactionAttribute txAttr = origTxAttrSource.getTransactionAttribute(method, targetClass);
String database = (String) ThreadContext.get("database");
if (database != null && StringUtils.isNotBlank(database)) {
if (txAttr instanceof DefaultTransactionAttribute) {
((DefaultTransactionAttribute) txAttr).setQualifier("txManager" + database);
}
}
return txAttr;
}
};
}
Through a BeanFactoryPostProcessor we were including this interceptor
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
String[] names = beanFactory.getBeanNamesForType(TransactionInterceptor.class);
for (String name : names) {
BeanDefinition bd = beanFactory.getBeanDefinition(name);
bd.setBeanClassName(MyTransactionInterceptor.class.getName());
}
}
This worked perfectly fine in Spring 4.X.
Now that we are moving towards Spring Boot, I am trying to convert the same approach. I can see that the bean factory is getting called but I don't find calls happening to the Custom Interceptor class. This results in my #Transactional to fail as there are more than one qualifying bean.
Am I missing something with regards to the Spring Boot Configuration?
(This approach of dynamic transaction management was through a reference blog http://blog.tirasa.net/dynamic-springs--at-transactional.html)

The final answer turned out to be setting the factory classes and the factory bean name to null which resulted in the transaction interceptor being invoked. I am yet to figure out how this affects the interceptor call as with the values in these fields (they point to the ProxyTransaction classes as transactionInterceptor bean is created by it).
The final code was of the form -
TransactionInterceptor Class
#Component
public class TransactionInterceptorReplacer implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(final ConfigurableListableBeanFactory factory) throws BeansException {
String[] names = factory.getBeanNamesForType(TransactionInterceptor.class);
for (String name : names) {
BeanDefinition bd = factory.getBeanDefinition(name);
bd.setBeanClassName(MyTransactionInterceptor.class.getName());
bd.setFactoryBeanName(null);
bd.setFactoryMethodName(null);
}
}
}

Related

how to get the right bean by name when have two same name and different class bean

I have two spring bean. They are name is same but the class is defferent.
Here is the bean definitions.
This one is the first.
#Bean(name = "approve_sign_up_project_request|Task_1tm7e53")
public StudentTaskToResponseDataConverter perfectUserProfileVO() {
return studentTaskVO -> {
ResponseVo vo = toResponseVO(studentTaskVO);
vo.setData(new PerfectUserProfileVO());
return vo;
};
}
This one is the second
#Bean(name = "approve_sign_up_project_request|Task_1tm7e53")
public UserTaskCompleter perfectUserProfile() {
return new UserTaskCompleter() {
#Override
public void validate(Task task, CompleteUserTaskDTO dto) throws RuntimeException {
Long studentId = getStudentId(task);
UserProfileIntegrityValidatedResultDTO results = userService.
validateTheIntegrityOfUserProfile(studentId);
if (results.getComplete()) {
//nothing to do for now
}else {
LOGGER.error("Validated failed cause the student -- {} not yet perfected the profile",
studentId);
throw new UserProfileImperfectException("Missing fields are " + results.getMissingFields());
}
}
#Override
public void executeBusinessLogic(Task task, CompleteUserTaskDTO dto) {
}
#Override
public Map<String, Object> getTheVariablesForCompleterUserTask(Task task, CompleteUserTaskDTO dto) {
return null;
}
};
}
And When I use below code to get the bean the spring will throw an excetpion. But I did not understand the reason.
I think the spring will give me the right bean when I use the bean name and bean class to get it. But actully I am wrong the spring did not give it.
Here is the code of to get bean
private UserTaskCompleter getBean(CompleteUserTaskDTO dto) {
String beanName = dto.getProcessDefinitionKey() + "|" + dto.getActivityId();
return applicationContext.getBean(beanName, UserTaskCompleter.class);
}
Here is the exception
org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'approve_sign_up_project_request|Task_1tm7e53' is expected to be of type 'com.hikedu.backend.camunda.beanconfig.taskcompleter.UserTaskCompleter' but was actually of type 'com.hikedu.backend.camunda.beanconfig.tasktoresponsedatecoverter.signupprojectprocess.SignUpProjectProcessTaskConverterConfig$$Lambda$625/484805627'
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:384)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:205)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1091)
at com.hikedu.backend.camunda.beanconfig.taskcompleter.BaseUserTaskCompleter.getBean(BaseUserTaskCompleter.java:45)
at com.hikedu.backend.camunda.beanconfig.taskcompleter.BaseUserTaskCompleter.validate(BaseUserTaskCompleter.java:29)
at com.hikedu.backend.service.impl.camunda.signupprojectprocess.BaseSignUpProjectProcessServiceImpl.completeUserTask(BaseSignUpProjectProcessServiceImpl.java:165)
at com.hikedu.backend.controller.SignUpProjectProcessUserTaskController.completerStudentTask(SignUpProjectProcessUserTaskController.java:93)
at com.hikedu.backend.controller.SignUpProjectProcessUserTaskController$$FastClassBySpringCGLIB$$a695dddd.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
Can someone tell me how to get the right bean by name when have two same name and different calss bean.
The answer that Himly quotes, does not let Spring create beans with the same name.
It actually prevents it from starting up, since building the application will fail.
If multiple beans are defined with the same name, then the one defined later will override the one defined earlier. As a result, in your case only one bean named approve_sign_up_project_request|Task_1tm7e53 will exist, unless you disable the bean definition overriding.
I already understaned the reason.
When definitioned two same name and different type bean. The spring will choose the last one to overriding others.
In my case there are just one bean named "approve_sign_up_project_request|Task_1tm7e53" and the type is StudentTaskToResponseDataConverter.
When I use the name and the UserTaskCompleter type to get the bean form beanFactory
the spring cannot find it and then the spring will throw the excetpion.
How to allow the spring create same name bean ?
I find the answer from here
Here is the import part of the answer
You may use an initializer when building your Spring Boot app:
#SpringBootApplication
public class SpringBootApp {
public static void main(String... args) {
new SpringApplicationBuilder(SpringBootApp.class)
.initializers(new ApplicationContextInitializer<GenericApplicationContext>() {
#Override
public void initialize(GenericApplicationContext applicationContext) {
applicationContext.setAllowBeanDefinitionOverriding(false);
}
})
.run(args);
}
}
Or with java 8:
new SpringApplicationBuilder(SpringBootApp.class)
.initializers((GenericApplicationContext c) -> c.setAllowBeanDefinitionOverriding(false) )
.run(args);

Best way for imlement Strategy design pattern in Spring

I want implement strategy design pattern in spring boot application. I create BeanPostProcessor for construct strategy resolver:
#Component
public class HandlerInAnnotationBeanPostProcessor implements BeanPostProcessor {
private final UnpHandlersResolver unpHandlersResolver;
public HandlerInAnnotationBeanPostProcessor(UnpHandlersResolver unpHandlersResolver) {
this.unpHandlersResolver = unpHandlersResolver;
}
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Annotation[] annotations = bean.getClass().getAnnotations();
for (Annotation annotation : annotations) {
if (annotation instanceof HandlerIn) {
if (bean.getClass() != UnpHandler.class)
throw new RuntimeException("Not UnpHandler bean annotated by HandlerIn");
SmevMessageType[] type = ((HandlerIn) annotation).type();
for (SmevMessageType smevMessageType : type) {
unpHandlersResolver.setHandler(smevMessageType, (UnpHandler) bean);
}
}
}
return bean;
}
}
And I create resolver:
#Slf4j
#Component
public class UnpHandlersResolverImpl implements UnpHandlersResolver {
private Map<SmevMessageType, UnpHandler> map = new HashMap<>();
#Override
public void setHandler(SmevMessageType messageType, UnpHandler unpHandler) {
map.put(messageType, unpHandler);
}
#Override
public UnpHandler getUnpHandler(SmevMessageType type) {
UnpHandler sendRequestHandler = map.get(type);
if (sendRequestHandler == null)
throw new IllegalArgumentException("Invalid SendRequestHandler type: " + type);
return sendRequestHandler;
}
}
My BeanPostProcessor scan all beans with annotation HandlerIn and add to resolver's mup. I think it's wrong to do that:
unpHandlersResolver.setHandler(smevMessageType, (UnpHandler) bean);
But I not understand how can I add find beans to resolver. Before this implementation I faind beans in #Postconstruct method of resolver like:
context.getBeansWithAnnotation(HandlerIn.class);
But in this solution I have context in resolver and I think is bad.
Tell me how to properly implement what I want? In short, I want to have a set of classes that implement different behaviors. And the class that controls them. Give the class a parameter so that he chooses the right strategy and gives it to me. Like this:
Handler handler = handlersResolver.getHandler(messageType);
Result result = handler.somthing(param);
I'm going to try to make a simple example.
Interface Greeting {
void sayHello();
String getSupportedLanguage();
}
Then you have X number of implementations and you can loop through them in your "resolver"'s constructor to build the map. (I've seen this called a Proxy or a Decorator in code though, i.e. GreetingProxy or GreetingDecorator)
#Service
public GreetingResolver {
private Map<String, Greeting> languageToGreetingMap = new HashMap<>();
#Autowired
public GreetingResolver(List<Greeting> greetings) {
for (Greeting greeting : greetings) {
languageToGreetingMap.put(greeting.getSupportedLanguage(), greeting);
}
}
public void sayGreetingForLanguage(String language) {
languageToGreetingMap.get(language).sayHello();
}
}
This is a basic example of how one can do the strategy pattern in Spring. Every interface implementation of "Greeting" only knows about itself and what it can support. We then autowire all implementations in a list and loop through to create the map once and then during runtime only the relevant entry from the map in retrieved and used.
Note: this was typed "free hand" directly in the web page so please forgive any typos in the code.

BeanPostProcessor analog in servlet 3.0

I have a little experience with java Servlets and JSP, but i worked with Spring. In Spring we have interface named BeanPostProcessor. I used this interface implementation to create custom annotations. Code example
public class InjectRandomIntAnnotationBeanPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object bean, String string) throws BeansException {
Field[] fields = bean.getClass().getDeclaredFields();
for (Field field : fields) {
InjectRandomInt annotation = field.getAnnotation(InjectRandomInt.class);
if (annotation != null) {
int min = annotation.min();
int max = annotation.max();
Random r = new Random();
int i = min + r.nextInt(max - min);
field.setAccessible(true);
ReflectionUtils.setField(field, bean, i);
}
}
return bean;
}
#Override
public Object postProcessAfterInitialization(Object o, String string) throws BeansException {
return o;
}
}
When i begin working with servlets i mentioned this annotation #WebServlet(urlPatterns = "/user/login")
The question is: is it any functional in servlets that is similar with BeanPostProcessor in Spring and where is this annotation #WebServlet injected?
Example:
I mean annotations in Spring are injected with BeanPostProcessor, for examle annotation #AutoWired is declared by AutoWiredAnnotationBeanPostProcessor class, but where the annotation #WebServlet is injected(or declared)
Servlets are not managed by Spring Container. So their annotations are processed by the servlet api implementation they run on, i.e. Tomcat.
Depending on what you want to achieve, you can simply extends HttpServlet and override the init method with your logic.
If you need to do some wiring you can also take advantage of SpringBeanAutowiringSupport. See also
Spring service not injected in web servlet
I want to inject an object in servlet using Spring

With Spring, how do I get a list of classes having a particular instance?

I have tried using BeanPostProcessor
#Override
public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException
{
log.info("postProcessBeforeInitialization bean : " + beanName);
if (bean instanceof TestAware)
{
testaware.add(((TestAware) bean).getClass());
log.info("Added testAware bean : " + beanName);
}
return bean;
}
But the problem, there are some classes which does not have bean definition.
Is there any alternative or improved way to get .
Thanks in advance.
If you want to get all instances of a subtype in the application context then it is simple enough to look through them:
#Component
public class GetSubtypesOfClass implements ApplicationContextAware {
#Override
public void setApplicationContext(ApplicationContext ac) throws BeansException {
List<Subtype> matches = new ArrayList<>();
for (String s : ac.getBeanDefinitionNames()) {
Object bean = ac.getBean(s);
if (Subtype.class.isAssignableFrom(bean.getClass())) {
matches.add(bean);
}
}
}
}
If however you are wondering why instances of your subtype aren't getting made available in the application context then you will need to load them explicitly in a context xml, or implicitly by classpath scanning.
Assuming you are asking how to find subtypes of TestAware
As suggested by it's name, BeanPostProcessor only "processes" spring managed beans, from the doc:
Factory hook that allows for custom modification of new bean instances
So you are using the wrong tool here. You probably should use some reflection to do what you want, for example, using Reflections:
// adapted from the home page sample
Reflections reflections = new Reflections("your.package");
Set<Class<? extends TestAware>> testAwares = reflections.getSubTypesOf(TestAware.class);
NB: the above sample code will give you subtypes, not instances of subtypes.

How can I select Spring bean instance at runtime

Based on parameters passed to a method, I need to select from one of many Spring beans that are implementations of the same class, but configured with different parameters.
E.g. if user A invokes the method, I need to call dooFoo() on bean A, but if it's user B then I need to call the very same method, only on bean B.
Is there a 'Springier' way of doing this other than sticking all the beans in a map, and deriving a key from the parameters passed to my method?
We face that issue in our project, and we solve it through a Factory-Like class. The client class -the one that needed the bean at runtime- had an instance of the factory, that was injected through Spring:
#Component
public class ImTheClient{
#Autowired
private ImTheFactory factory;
public void doSomething(
Parameters parameters) throws Exception{
IWantThis theInstance = factory.getInstance(parameters);
}
}
So, the IWantThis instance depends on the runtime value of the parameters parameter. The Factory implementation goes like this:
#Component
public class ImTheFactoryImpl implements
ImTheFactory {
#Autowired
private IWantThisBadly anInstance;
#Autowired
private IAlsoWantThis anotherInstance;
#Override
public IWantThis getInstance(Parameters parameters) {
if (parameters.equals(Parameters.THIS)) {
return anInstance;
}
if (parameters.equals(Parameters.THAT)) {
return anotherInstance;
}
return null;
}
}
So, the factory instance holds reference to both of the posible values of the IWantThis class, being IWantThisBadly and IAlsoWantThis both implementations of IWantThis.
Seems like do you want a ServiceLocator using the application context as registry.
See ServiceLocatorFactoryBean support class for creating ServiceLocators mapping keys to bean names without coupling client code to Spring.
Other option is to use a naming convention or annotation based configuration.
for example, assuming that you annotate Services with #ExampleAnnotation("someId"), you can use something like the following Service Locator to retrieve them.
public class AnnotationServiceLocator implements ServiceLocator {
#Autowired
private ApplicationContext context;
private Map<String, Service> services;
public Service getService(String id) {
checkServices();
return services.get(id);
}
private void checkServices() {
if (services == null) {
services = new HashMap<String, Service>();
Map<String, Object> beans = context.getBeansWithAnnotation(ExampleAnnotation.class);
for (Object bean : beans.values()) {
ExampleAnnotation ann = bean.getClass().getAnnotation(ExampleAnnotation.class);
services.put(ann.value(), (Service) bean);
}
}
}
}
Sticking them in a map sounds fine. If it's a Spring-managed map (using util:map, or in Java config), that's better than creating it somewhere else, because then Spring owns all the object references and can manage their lifecycle properly.
If the beans (A, B) you are talking about are SessionScope its no problem at all, they will be selected correctly.
public class BusinessLogic {
private BaseClassOfBeanAandB bean;
public void methodCalledByUserAorB() {
bean.doFoo();
}
}

Categories

Resources