How to get an object with transaction in Spring? - java

I have a Service class like this:
#Service
public class CompanyServiceImpl implements CompanyService {
#Autowired
private CompanyDAO companyDAO;
#Transactional
public void addOrUpdateCompany(Company company) {
companyDAO.addOrUpdateCompany(company);
}
}
Normally, I can have an instance of CompanyService from Spring by:
#Autowired
CompanyService companyService;
But, in some cases, I want to create/get an instalce of CompanyService without #Autowired like this:
CompanyService companyService = XXX.getxxx("CompanyService");
Is there any ways I can do this?

Other way is
#Component
public class ContextHolder implements ApplicationContextAware {
private static ApplicationContext CONTEXT;
public void setApplicationContext(ApplicationContext applicationContext) {
CONTEXT = applicationContext;
}
public static ApplicationContext getContext() {
return CONTEXT;
}
}
And then
CompanyService service = ContextHolder.getContext().getBean(CompanyService.class);

If I understand you correctly, you mean something like - ServiceLocatorFactoryBean
with it you can call somthing like MyService getService(String id)).
Another way, will be to implement some kind of a controller service that will have all other services autowired to it, and will hold a map from their string id to actual instances.
In my opinion the second option is better, since it's more manageable and clear.
Hope that helps you.

You can do it. You need to instancate the application context and then get your been.
Resource res = new FileSystemResource("beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(res);
or
ClassPathResource res = new ClassPathResource("beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(res);
or
ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(
new String[] {"applicationContext.xml", "applicationContext-part2.xml"});
// of course, an ApplicationContext is just a BeanFactory
BeanFactory factory = (BeanFactory) appContext;
and use:
MyObject my = (MyObject)factory.getBean(NAME_OF_YOUR_BEAN);

Related

Creating an object of an #Service bean, is it possible?

i have an restful application with all the #Service,#RestController,#Repository beans. and im autowiring required beans.
Now i want to use the #service class in another class that is not managed by spring, is that possible?
These 2 classes are also in 2 diffrent maven projects if that makes any diffrence
i have tried creating a new object , as expected to no awail.
i have also tried creating diffrent constructors also to no awail.
i have googled for some anwsers but havent found any so now i turn to you experts ;)
The class i want to use!
#Service
public class ProductService {
ProductRepository repository;
#Autowired
public ProductService(ProductRepository repository){
this.repository = repository;
}
}
the Restcontroller
#RestController
#RequestMapping(path = "/product")
public class ProductResource {
#Autowired
ProductService service;
}
Repo
public interface ProductRepository extends JpaRepository<Product,Long> {
}
Here is where i want to create the service.
public static void main(String[] args) {
String chromeDriver = args[0];
String method = args[1];
String domainName = args[2];
ProductService service = new ProductService();
System.setProperty("webdriver.chrome.driver", chromeDriver);
Runner runner = new Runner(method,domainName);
runner.run();
}
As far as I know, you can instantiate a Spring-managed class in a non-Spring-managed class as long as the Spring-managed class (in this case the service) doesn't contain any Spring dependencies in it, as those will remain null (non initialized) because they are never gonna be injected by Spring.
Your service looks good to me because the ProductRepository field is not #autowired, but in your code you're creating the ProductService like this:
ProductService service = new ProductService();
While this parameterless constructor doesn't exist in the ProductService class:
#Service
public class ProductService
{
ProductRepository repository;
#Autowired
public ProductService(ProductRepository repository)
{
this.repository = repository;
}
}
I think this is not possible (I mean, it technically is, but it is not going to work).
To have access to your service class your second application needs to have access to the same Spring Context.
This is because Spring Context in your base application defines necessary dependencies that your second application doesn't have access to. One of the examples for this is ProductRepository repository (which is a way Spring Boot app will talk with the database), have no Spring Context and thus information on DB location and connection URL.
In your case you need to replicate the class and mechanism to connect to DB with DB configuration in your second project.
There's a workaround to "inject" dependencies manually. Create a managed component that stores the reference to the ApplicationContext in a static variable, so that you can access from non-managed classes:
#Component
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public static ApplicationContext applicationContext() {
return applicationContext;
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) {
ApplicationContextProvider.applicationContext = applicationContext;
}
}
public class NoManagedClass {
private final ManagedClass bean;
public NoManagedClass() {
this.bean = ApplicationContextProvider.applicationContext().getBean(ManagedClass.class);
}
}
However, be aware that your non-managed objects should be created after the application context is set. You still have to start your Spring application in the main method so that the beans are initialized.

calling a method from a different class in springBoot

i want to call a method which is containing applicationContext which works fine when run independently but when i try calling it from a different class appContext.getBean(DataSource.class) returns null.
public class Action {
private static Logger log = LoggerFactory.getLogger(Action.class);
#Autowired
MessageConfigProperties messageProperties;
#Autowired
AutomatorApp automatorApp;
#Autowired
Apps gapps;
#Autowired
Deploy d;
#Autowired
Deploy depstatusChk;
#Autowired
private ApplicationContext appContext;
#Autowired
CreateSaltFileService createSaltFile;
#Autowired
DeployFactory depFactory;
#Autowired
IMoveAppsService moveAppsService;
#Autowired
IUserEnvironmentService userEnvService;
#Autowired
IEnvironmentService envService;
#Autowired
Session session;
private Logger logger = LoggerFactory.getLogger(Action.class);
private ServletContext context;
#Context
public void setServletContext(ServletContext context) {
System.out.println("servlet context set here");
this.context = context;
}
#POST
#Path("/register/")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#RequiresRoles("admin")
#RequiresPermissions("automator:register")
public Session register(Session credentials, #BeanParam Session springContext) throws AppException {
System.out.println("into action class");
System.out.println("-->>>" +appContext.getBean(DataSource.class));
appContext.getBean(DataSource.class);
logger.info(messageProperties.getGreetings());
// logger.trace("Inside Session");
System.out.println("Inside Session");
credentials.setDatasource(springContext.getDatasource());
when this Action method is called from
this method agentGroup in different class
#POST
#Produces(MediaType.APPLICATION_JSON)
#ApiOperation(value = "Get List of agent group", response =
AgentGroup.class, responseContainer = "List")
public ArrayList<AgentGroup> agentGroup(Session credentials, #BeanParam Session springContext) throws AppException, ConfigException, InterruptedException {
Session r=objA.register(credentials, springContext);
int sessionId=r.getSessionId();
}
You #Autowire ApplicationContext appContext just to get datasource (appContext.getBean(DataSource.class)).
Why do not #Autowire DataSource datasource directly?
It will make make business logic of your Action class independend from Spring.
If you really want to access ApplicationContext you may try another approach
make Action implements ApplicationContextAware
private ApplicationContext ctx;
#Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
ctx = applicationContext;
}
See Circular dependency in Spring for details.
edit:
your Action class has no annotation. no org.springframework.web.bind.annotation. RestController, no #Component or #Service. are you sure it is managed by Spring? if it is just created using new then Autowire will not work and fields will be null.

How to get bean using application context in spring boot

I am developing a SpringBoot project and I want to get the bean by its name using applicationContext. I have tried many solution from web but could not succeed. My Requirement is that I have a controller
ControllerA
and inside the controller I have a method getBean(String className). I want to get instance of registered bean. I have hibernate entities and I want to get an instance of the bean by passing the name of class only in getBean method.
Please help if someone know the solution.
You can Autowire the ApplicationContext, either as a field
#Autowired
private ApplicationContext context;
or a method
#Autowired
public void context(ApplicationContext context) { this.context = context; }
Finally use
context.getBean(SomeClass.class)
You can use ApplicationContextAware.
ApplicationContextAware:
Interface to be implemented by any object that wishes to be notified
of the ApplicationContext that it runs in. Implementing this interface
makes sense for example when an object requires access to a set of
collaborating beans.
There are a few methods for obtaining a reference to the application context. You can implement ApplicationContextAware as in the following example:
package hello;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
#Component
public class ApplicationContextProvider implements ApplicationContextAware {
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public ApplicationContext getContext() {
return applicationContext;
}
}
Update:
When Spring instantiates beans, it looks for ApplicationContextAware implementations, If they are found, the setApplicationContext() methods will be invoked.
In this way, Spring is setting current applicationcontext.
Code snippet from Spring's source code:
private void invokeAwareInterfaces(Object bean) {
.....
.....
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware)bean).setApplicationContext(this.applicationContext);
}
}
Once you get the reference to Application context, you get fetch the bean whichever you want by using getBean().
actually you want to get the object from the Spring engine, where the engine already maintaining the object of your required class at that starting of the spring application(Initialization of the Spring engine).Now the thing is you just have to get that object to a reference.
in a service class
#Autowired
private ApplicationContext context;
SomeClass sc = (SomeClass)context.getBean(SomeClass.class);
now in the reference of the sc you are having the object.
Hope explained well. If any doubt please let me know.
Even after adding #Autowire if your class is not a RestController or Configuration Class, the applicationContext object was coming as null. Tried Creating new class with below and it is working fine:
#Component
public class SpringContext implements ApplicationContextAware{
private static ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws
BeansException {
this.applicationContext=applicationContext;
}
}
you can then implement a getter method in the same class as per your need to get the bean. Like:
applicationContext.getBean(String serviceName,Interface.Class)
Using SpringApplication.run(Class<?> primarySource, String... arg) worked for me. E.g.:
#SpringBootApplication
public class YourApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(YourApplication.class, args);
}
}
As an alternative approach you can use ConfigurableApplicationContext to get bean of any class which is annotated with #Component, #Repository or #Service.
Let's say you want to get a bean of the class BaseComponent :
#Service
public class BaseComponent {
public String getMessage() {
return "hello world";
}
}
Now you can use ConfigurableApplicationContext to get the bean:
#Component
public class DemoComponent {
#Autowired
ConfigurableApplicationContext applicationContext;
public BaseComponent getBeanOfBaseComponent() {
return applicationContext.getBean(BaseComponent.class);
}
}
You can use the ApplicationContextAware class that can provide the application context.
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext ctx = null;
public static ApplicationContext getApplicationContext() {
return ctx;
}
#Override
public void setApplicationContext(final ApplicationContext ctx) throws BeansException {
ApplicationContextProvider.ctx = ctx;
}
/**
* Tries to autowire the specified instance of the class if one of the specified
* beans which need to be autowired are null.
*
* #param classToAutowire the instance of the class which holds #Autowire
* annotations
* #param beansToAutowireInClass the beans which have the #Autowire annotation
* in the specified {#classToAutowire}
*/
public static void autowire(Object classToAutowire, Object... beansToAutowireInClass) {
for (Object bean : beansToAutowireInClass) {
if (bean == null) {
ctx.getAutowireCapableBeanFactory().autowireBean(classToAutowire);
}
}
}
}
If you are inside of Spring bean (in this case #Controller bean) you shouldn't use Spring context instance at all. Just autowire className bean directly.
BTW, avoid using field injection as it's considered as bad practice.
One API method I use when I'm not sure what the bean name is org.springframework.beans.factory.ListableBeanFactory#getBeanNamesForType(java.lang.Class<?>). I simple pass it the class type and it retrieves a list of beans for me. You can be as specific or general as you'd like to retrieve all the beans associated with that type and its subtypes, example
#Autowired
ApplicationContext ctx
...
SomeController controller = ctx.getBeanNamesForType(SomeController)
Easy way in configration class call the BEAN annoted method . Yes u heard it right---- :P calling SpringBoot #Bean annoted method return the same bean from config .I was trying to call a logout in #predestroy method in config class from a bean and direcltly called the method to get the same bean .
P.S. : I added debug in the #bean annotated method but it didn't entered the method even when i called it.Sure to blame -----> Spring Magic <----
You can use ServiceLocatorFactoryBean. First you need to create an interface for your class
public interface YourClassFactory {
YourClass getClassByName(String name);
}
Then you have to create a config file for ServiceLocatorBean
#Configuration
#Component
public class ServiceLocatorFactoryBeanConfig {
#Bean
public ServiceLocatorFactoryBean serviceLocatorBean(){
ServiceLocatorFactoryBean bean = new ServiceLocatorFactoryBean();
bean.setServiceLocatorInterface(YourClassFactory.class);
return bean;
}
}
Now you can find your class by name like that
#Autowired
private YourClassfactory factory;
YourClass getYourClass(String name){
return factory.getClassByName(name);
}
Just use:
org.springframework.beans.factory.BeanFactory#getBean(java.lang.Class)
Example:
#Component
public class Example {
#Autowired
private ApplicationContext context;
public MyService getMyServiceBean() {
return context.getBean(MyService.class);
}
// your code uses getMyServiceBean()
}

Better Design to access XmlBeanFactory with Spring

I am trying to find a better what to do this. In Spring a lot of my classes need to load beans (objects) from XmlBeanFactory. So I put the following line into most of my classes
private static XmlBeanFactory beanFactory = new XmlBeanFactory(
new ClassPathResource("config.xml"));
Does anyone know of a better what for me to do this so I don't have to have this in most of my classes?
You can make your class implement BeanFactoryAware that will give you instance of the bean factory, so you could call one of BeanFactory.getBean(..) methods directly.
public class MyFactoryBean implements BeanFactoryAware {
private BeanFactory beanFactory;
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public void someMethod() {
MyBean myBean = beanFactory.getBean("myBean", MyBean.class);
...
}
}

How to inject ApplicationContext itself

I want to inject an ApplicationContext itself to a bean.
Something like
public void setApplicationContext(ApplicationContect context) {
this.context = context;
}
Is that possible in spring?
Previous comments are ok, but I usually prefer:
#Autowired private ApplicationContext applicationContext;
Easy, using the ApplicationContextAware interface.
public class A implements ApplicationContextAware {
private ApplicationContext context;
public void setApplicationContext(ApplicationContext context) {
this.context = context;
}
}
Then in your actual applicationContext you only need to reference your bean.
<bean id="a" class="com.company.A" />
Yes, just implement the ApplicationContextAware -interface.
I saw some comments above about #Autowired not working still. The following may help.
This will not work:
#Route(value = "content", layout = MainView.class)
public class MyLayout extends VerticalLayout implements RouterLayout {
#Autowired private ApplicationContext context;
public MyLayout() {
comps.add(context.getBean(MyComponentA.class)); // context always null :(
}
You must do this:
#Autowired
public MyLayout(ApplicationContext context) {
comps.add(context.getBean(MyComponentA.class)); //context is set :)
}
or this:
#PostConstruct
private void init() {
comps.add(context.getBean(MyComponentA.class)); // context is set :)
}
Also note that Upload is another component that must be set within the scope of #PostConstruct. This was a nightmare for me to figure out. Hope this helps!
I almost forgot to mention that the #Scope annotation may be necessary for your Bean, as seen below. This was the case when using Upload within a Bean because the UI is not instantiate/attached prior to the Bean being created and will cause a Null Reference Exception to be thrown. It won't do so when using #Route, but will when using #Component - so the latter is not an option and if #Route is not viable, then I would recommend using #Configuration class to create the bean with the prototype scope.
#Configuration
public class MyConfig {
#Bean
#Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
public MyComponentA getMyBean() {
return new MyComponentA();
}
}
Special solution: get Spring beans from any (non Spring) classes
#Component
public class SpringContext {
private static ApplicationContext applicationContext;
#Autowired
private void setApplicationContext(ApplicationContext ctx) {
applicationContext = ctx;
}
public static <T> T getBean(Class<T> componentClass) {
return applicationContext.getBean(componentClass);
}
}

Categories