I'm new to Spring so I'm just trying to understand how it works. I've developed a simple servletprojectusing spring to manage hibernate framework.
I have a service
#Service("service")
#Transactional
public class CdServiceImpl {
#Autowired
private HibernateUtility hibernateutility;
public int saveCd(CD cd) {
return hibernateutility.saveCd(cd);
}
public List getCd(String searchedCd) {
return hibernateutility.getCd(searchedCd);
}
public List getAllCd() {
return hibernateutility.getAllCd();
}
public void deleteCd(int id) {
hibernateutility.deleteCd(id);
}
public User getUser(String username, String password) {
return hibernateutility.getUser(username, password);
}
}
And then I use it in the servlet
context.scan("it.project");
context.refresh();
CdServiceImpl service = (CdServiceImpl) context.getBean("service");
context.register(ApplicationContextConfig.class);
context.refresh();
1) It works but I have two question. It is the right way to work?
2) I've tried to set a field in the servlet like:
#Autowired
private CdServiceImpl service
and then I remove the context.scan ecc part and it gave me nullpointerexception. Why?
Doing that I also defined a new bean
#Bean
public CdServiceImpl getCdServiceImpl() {
return new CdServiceImpl();
}
Why it doesn't work? I know that maybe this is a noob question but I'm tryingto figure out how spring works
Basically as soon as you start doing things like new **ApplicationContext you need to scratch your head, take 2 steps away from the keyboard and think if you really want to do this. In 99% of the cases this isn't what you want or at least should do.
Instead have the ContextLoaderListener load your configuration. Assuming you don't have a web.xml use the the AbstractContextLoaderInitializer base class.
public ApplicationInitializer extends AbstractContextLoaderInitializer {
protected WebApplicationContext createApplicationContext() {
return new AnnotationConfigWebApplicationContext(ApplicationContextConfig.class);
}
}
Note: As this is for bootstrapping your application you need to create the the context. You could also use AbstractAnnotationConfigDispatcherServletInitializer which eliminates this need, but also creates a DispatcherServlet which you don't need.
Now that your configuration is automatically loaded you can use the WebApplicationContextUtils to get the context to do the lookup. Do this in the init method of your servlet.
public class YourServlet extends GenericServlet {
private CdServiceImpl service;
public void init() throws ServletException {
ApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
service = ctx.getBean(CdServiceImpl.class);
}
}
Now you initialize the bean once and don't need to handle anything anymore. You could also use #Autowired and manuallly have it injected.
public class YourServlet extends GenericServlet {
#Autowired
private CdServiceImpl service;
public void init() throws ServletException {
ApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
ctx.getAutowireCapableBeanFactory().autowireBean(this);
}
}
Related
I am new at spring MVC framework and i am currently working in a web application that uses a session scoped bean to control some data flow.
I can access these beans in my application context using #Autowired annotation without any problem in the controllers. The problem comes when I use a class in service layer that does not have any request mapping (#RequestMapping, #GetMapping nor #PostMapping) annotation.
When I try to access the application context directly or using #Autowired or even the #Resource annotation the bean has a null value.
I have a configuration class as follow:
#Configuration
#EnableAspectJAutoProxy
#EnableJpaRepositories(repositoryFactoryBeanClass = EnversRevisionRepositoryFactoryBean.class, basePackages = "com.quantumx.nitididea.NITIDideaweb.repository")
public class AppConfig implements WebMvcConfigurer {
#Bean (name = "lastTemplate")
#SessionScope
public LastTemplate getlastTemplate() {
return new LastTemplate();
}
//Some extra code
}
The POJO class is defined as :
public class LastTemplate {
private Integer lastId;
public LastTemplate(){
}
public Integer getLastId() {
return lastId;
}
public void setLastId(Integer lastId) {
this.lastId = lastId;
}
}
The I have a Test class that is annotated as service and does not have any request mapping annotated method:
//#Controller
#Service
public class Test {
// #Autowired
// private ApplicationContext context;
// #Autowired
#Resource(name = "lastTemplate")
public LastTemplate lastTemplate;
// #Autowired
// public void setLastTemplate(LastTemplate lastTemplate) {
// this.lastTemplate = lastTemplate;
// }
public Test() {
}
// #RequestMapping("/test")
public String testing() {
// TemplateForma last = (TemplateForma) context.getBean("lastInsertedTemplate");
// System.out.println(last);
System.out.println(lastTemplate);
// System.out.println(context.containsBean("lastTemplate"));
// System.out.println(context.getBean("lastTemplate"));
System.out.println("Testing complete");
return "Exit from testing method";
// return "/Messages/Success";
}
}
As you can see, there is a lot of commented code to show all the ways i have been trying to access my application context, using an Application context dependency, autowiring, declaring a resource and trying with a request mapping. The bean is null if no controller annotation and request mapping method is used and throws a java null pointer exception when I use the context getBean() methods.
Finally I just test my class in a controller that i have in my app:
#RequestMapping("/all")
public String showAll(Model model) {
Test test = new Test();
test.testing();
return "/Administrator/test";
}
Worth to mention that I also tried to change the scope of the bean to a Application scope and singleton, but it not worked. How can access my application context in a service class without mapping a request via controller?
Worth to mention that I also tried to change the scope of the bean to a Application scope and singleton, but it not worked
It should have worked in this case.
How can access my application context in a service class without mapping a request via controller?
Try one of these :-
#Autowired private ApplicationContext appContext;
OR
Implement ApplicationContextAware interface in the class where you want to access it.
Edit:
If you still want to access ApplicationContext from non spring managed class. Here is the link to article which shows how it can be achieved.
This page gives an example to get spring application context object with in non spring managed classes as well
What worked for me is that session scoped bean had to be removed in the application configuration declaration and moved to the POJO definition as follows:
#Component
#SessionScope
public class LastTemplate {
private Integer lastId;
public LastTemplate(){
}
public Integer getLastId() {
return lastId;
}
public void setLastId(Integer lastId) {
this.lastId = lastId;
}
}
The I just call the bean using #Autowired annotation.
I know there are lot of questions on this topic. I have read the spring boot doc and all of the solutions here. According spring boot doc, #ServerEndpoint is a Javax annotation and #Autowired components are spring-boot managed. These two cannot be used together. The solution to this would be to add SpringConfigurator as configurator of the ServerEndpoint. When I tried this I do get the following error:
Failed to find the root WebApplicationContext. Was ContextLoaderListener not used?
There is no example in the spring-boot websocket page to use ContextLoaderListener. How can use ContextLoaderListener so that components can be injected into #ServerEndpoint annotated controllers?
The following is my code.
Websocket controller
#ServerEndpoint(value = "/call-stream", configurator = SpringConfigurator.class)
public class CallStreamWebSocketController
{
#Autowired
private IntelligentResponseService responseServiceFacade;
// Other methods
}
Websocket configurations
#Configuration
public class WebSocketConfiguration
{
#Bean
public CallStreamWebSocketController callStreamWebSocketController()
{
return new CallStreamWebSocketController();
}
#Bean
public ServerEndpointExporter serverEndpointExporter()
{
return new ServerEndpointExporter();
}
}
Edit:
This has been tagged as a duplicate of this question. I have tried the solution specified in the answers. The solution is to add SpringConfigurator as configurator of the #ServerEndpoint. After adding this I still do get the error mentioned in the details.
After some research I found a way to force spring-boot to inject a component into an externally managed/instantiated class.
1) Add a generic method to your class extending ApplicationContextAware to return a bean.
#Component
public class SpringContext implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
SpringContext.context = context;
}
public ApplicationContext getApplicationContext() {
return context;
}
// Generic method to return a beanClass
public static <T> T getBean(Class<T> beanClass)
{
return context.getBean(beanClass);
}
}
2) Use this method to initialize the class object you want to be injected
private IntelligentResponseService responseServiceFacade = SpringContext.getBean(IntelligentResponseService.class);
So after the above changes my websocket controller would look like this
#ServerEndpoint(value = "/call-stream", configurator = SpringConfigurator.class)
public class CallStreamWebSocketController
{
private IntelligentResponseService responseServiceFacade = SpringContext.getBean(IntelligentResponseService.class);
// Other methods
}
I am working on a Spring Boot application wherein I am using that application to expose a SOAP webservice. I am using Apache CFX framework for SOAP impl in Spring boot app. I am using Annotation based approach.
I am facing issue in setting the Application Context from the Spring Boot Configuration file in one of the Beans. Below is my code.
#SpringBootApplication
#ComponentScan("com.test")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
The configuration file is as below.
#Configuration
public class WebServiceConfiguration {
//All individual bean definitions should go here
#Autowired
ApplicationContext appContext;
#Bean
public ServletRegistrationBean cxfServlet() {
return new ServletRegistrationBean(new CXFServlet(), "/soap-api/*");
}
#Bean(name = Bus.DEFAULT_BUS_ID)
public SpringBus springBus() {
return new SpringBus();
}
#Bean(name="IValidator")
public IValidator getValidator(){
return new Validator();
}
#Bean(name="SOAPprocessImpl")
public IPSoap getService() {
return new SOAPprocessImpl();
}
#Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(springBus(), getService());
endpoint.publish("/WS_1.0");
endpoint.setWsdlLocation("process.wsdl");
return endpoint;
}
Now I have the bean SOAPprocessImpl implementation in which I need to get the Application Context so that I can get handle to the Validator bean. I have declared SOAPprocessImpl as a bean in the configuraton file. The code is as below
#javax.jws.WebService (endpointInterface="com.test.IPSoap")
public class SOAPprocessImpl implements IPSoap, ApplicationContextAware {
private static ApplicationContext context;
public static ApplicationContext getApplicationContext() {
return context;
}
#Override
public void setApplicationContext(ApplicationContext ac)
throws BeansException {
context = ac;
}
private static final Logger logger = Logger.getLogger(SOAPprocessImpl.class.getName());
private IValidator validator = (IValidator) context.getBean("IValidator"); // context is NULL here
public IRResponse GetBalance(TSSearchParams SearchParams) {
// Some processing logic
}
}
So the issue is that when I run the boot application by deploying to the embedded Tomcat then the Application Context is not getting set in the SOAPprocessImpl class even after implementing the ApplicationContextAware. I also tried Autowiring but that also is not working.
Strangely I tried to see if I can get the ApplicationContext in the Configuration file where all the bean are defined. Here it is getting setting properly.
Can anyone help me how to solve this issue. I am new to Spring Boot and may have missed some configutaion. Thanks in advance.
Option(1): To fix the issue, you need to use #Configuration to register your SOAPprocessImpl bean to the Spring container as shown below so that ApplicationContext object can be injected :
#Configuration
#javax.jws.WebService (endpointInterface="com.test.IPSoap")
public class SOAPprocessImpl implements IPSoap, ApplicationContextAware {
private static ApplicationContext context;
private IValidator validator;
public static ApplicationContext getApplicationContext() {
return context;
}
#Override
public void setApplicationContext(ApplicationContext ac)
throws BeansException {
SOAPprocessImpl.context = ac;
}
#PostConstruct//use PostConstruct
public void init() {
validator = (IValidator) context.getBean("IValidator");
}
//add your current code
}
The important point is that you can't use the context object until the bean is prepared by the container, so you need to use #PostConstruct method as shown above to initialise your variables.
Option2 (recommended):
The best approach is that you can use #Autowired to inject IValidator object into SOAPprocessImpl as shown below so that you don't need your SOAPprocessImpl bean to be aware of ApplicationContextAware. Spring container will inject the instance for the implementation provided for the IValidator class (provided it is under the packages of #Componentscan).
#Component
#javax.jws.WebService (endpointInterface="com.test.IPSoap")
public class SOAPprocessImpl implements IPSoap {
private static final Logger logger = Logger.getLogger(SOAPprocessImpl.class.getName());
#Autowired //spring directly injects this object
private IValidator validator;
public IRResponse GetBalance(TSSearchParams SearchParams) {
// Some processing logic
}
}
I have a Controller class which is invoked first in my application. There I was planning to retrieve a value from a Map from a Service class.
Here's the controller:
#Controller
public class AppController {
public Service doSomethingWithTheMap(String key) {
return ServiceImpl.getMapValueFor(key).exec();
}
}
I get issues because during the initialization, well during the put of values to the Service's Map to be more precise, I require the BeanFactory because the values in the Map are Service implementations.
Doing it in a static block will cause the BeanFactory to be null because it is not injected yet I would guess.
So ending up with this initMap() call makes me feel a bit like ... there should be a better solution.
Any hints somebody?
I have to admit that I am new to Spring and maybe I mess things up here. FYI the Map came into my mind after having endless if else checks deciding which Service to call based on a String input. Therefore I replaced it with the Map and a simple one liner in the Controller.
ServiceImpl.getMapValueFor(key).exec();
Here' the Service class:
#Service
public class ServiceImpl {
private static Map<String, Service> map;
private static ApplicationContext context;
#Autowired
public void setApplicationContext(ApplicationContext factory) {
this.context = factory;
}
public static Service getMapValueFor(String key) {
if (map == null) {
initMap();
}
return map.get(key);
}
private static void initMap() {
/*
* FIXME: We can not init the map in a static block or directly
* initialize it since the factory is not injected until execution of a
* static block and will be null.
*/
BeanFactory factory = context;
map = new HashMap<String, Service>();
map.put("key", factory.getBean(SomeService.class));
}
}
The first thing I want to say is that you have a bug, because you are using a HashMap with no synchronization! - Don't be alarmed many (if not most) java developers would make the same mistake.
Unless you have oversimplified the code, your service looks more like a command than a service; A service is a singleton. It is not impossible for services to have methods without arguments, but I would say it is uncommon. Are you sure you should not be using prototype beans instead of singletons ?
Typically the number of services are finite, and if you have multiple services of the same type you would use the #Qualifier when autowiring them. In any case this code looks dodgy to me, so perhaps you should try to explain the problem at a higher level, because there may be a better way than you current code-path.
Your service class ServiceImpl must implement the interface org.springframework.context.ApplicationContextAware to get the instance of Spring's application context.
Here is a very basic solution, it uses the fact that the name of a #Bean is the name of the method which creates it, you will probably need a better strategy. The idea is to hide getBean inside a Provider class which can then be Autowired (and tested)
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
public class Main {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
CallableProvider provider = ctx.getBean(CallableProvider.class);
System.out.println(provider.getCommand("aCommand").call());
System.out.println(provider.getCommand("bCommand").call());
}
public static class Config {
#Bean
public ACommand aCommand() {
return new ACommand();
}
#Bean
public BCommand bCommand() {
return new BCommand();
}
#Bean
public CallableProvider callableProvider() {
return new CallableProvider();
}
}
public static class CallableProvider implements ApplicationContextAware {
private ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
public Command getCommand(String name) {
return context.getBean(name, Command.class);
}
}
public static class ACommand implements Command {
// autowire stuff
#Override
public String call() {
return "A";
}
}
public static class BCommand implements Command {
// autowire stuff
#Override
public String call() {
return "B";
}
}
public interface Command {
String call();
}
}
Is there any way to inject dependencies into manually created objects?
public class MyCommand {
#Inject Repository repository;
}
public Repository {
#Inject EntityManager em;
}
MyCommand command = new MyCommand();
Repository is properly registered the jersey ResourceConfig and can be injected in objects that are created through the CDI container for example a resource class.
But since I create the Command myself the #Inject annotation gets ignored.
Is there a way to get a registered class beside #Inject and #Context?
Something like Application.get(Repository.class)
public class MyCommand {
Repository repository;
public MyCommand() {
repository = Application.get(Repository.class);
}
}
----- EDIT -----
Thanks to your help and some rethinking I found a solution for my problem.
The first thing is that it's possible to inject the ServiceLocator without any preperation into you objects.
The second thing is that I moved from normal commands with a execute method to a a command bus system.
The reason for that is I have no controle over the creation of commands so there clean way to get dependencies injected.
The new approach looks like this:
class CommandBus {
private final ServiceLocator serviceLocator;
#Inject
public CommandBus(ServiceLocator serviceLocator) {
this.serviceLocator = serviceLocator;
}
public void dispatch(Command command) {
Class handlerClass = findHandlerClassForCommand(command);
CommandHandler handler = (CommandHandler) serviceLocator.getService(handlerClass);
handler.handle(command);
}
}
interface CommandHandler {
void handle(Command command);
}
interface Command {
}
class ConcreteCommand implements Command {
// I'm just a dto with getters and setters
}
class ConcreteHandler implements CommandHandler {
private final SomeDependency dependency;
#Inject
public ConcreteHandler(SomeDependency dependency) {
this.dependency = dependency;
}
#Override
public void handle(ConcreteCommand command) {
// do some things
}
}
And in my resources I have something like this:
#Path("/some-resource")
class Resource {
#Context
private CommandBus bus;
#POST
#Consumes(MediaType.APPLICATION_JSON)
public void runCommand(ConcreteCommand command) {
bus.dispatch(command);
}
}
As pointed out by jwells - HK2 is an injection framework :)
I spent some time looking into it - I have to say, I find it much more complicated than say guice or spring. Maybe this is due to the fact that I use Dropwizard and it makes it not as easy to access the Service locators.
However, here is how you can do that.
First, you will have to get a reference to your ServiceLocator. It must be the same ServiceLocator that jersey is using as well. You can access it for example like:
How to get HK2 ServiceLocator in Jersey 2.12?
In my example code I will use an event listener, which is due to my Dropwizard Setup.
You now have 2 choices: Register your command with your Service Locator and have the injection framework handle creation, or pass the ServiceLocator to your command in order to use it.
I wrote up a quick example using Dropwizard and jersey:
public class ViewApplication extends io.dropwizard.Application<Configuration> {
#Override
public void run(Configuration configuration, Environment environment) throws Exception {
environment.jersey().register(new ApplicationEventListener() {
#Override
public void onEvent(ApplicationEvent event) {
if (event.getType() == ApplicationEvent.Type.INITIALIZATION_FINISHED) {
ServiceLocator serviceLocator = ((ServletContainer) environment.getJerseyServletContainer())
.getApplicationHandler().getServiceLocator();
ServiceLocatorUtilities.bind(serviceLocator, new AbstractBinder() {
#Override
protected void configure() {
bind(new Repository("test")).to(Repository.class);
bind(MyCommandInjected.class).to(MyCommandInjected.class);
}
});
MyCommandInjected service = serviceLocator.getService(MyCommandInjected.class);
MyCommandManual tmp = new MyCommandManual(serviceLocator);
}
}
#Override
public RequestEventListener onRequest(RequestEvent requestEvent) {
return null;
}
});
}
#Override
public void initialize(Bootstrap<Configuration> bootstrap) {
super.initialize(bootstrap);
}
public static void main(String[] args) throws Exception {
new ViewApplication().run("server", "/home/artur/dev/repo/sandbox/src/main/resources/config/test.yaml");
}
#Path("test")
#Produces(MediaType.APPLICATION_JSON)
public static class HelloResource {
#GET
#Path("asd")
public String test(String x) {
return "Hello";
}
}
public static class Repository {
#Inject
public Repository(String something) {
}
}
public static class MyCommandInjected {
#Inject
public MyCommandInjected(final Repository repo) {
System.out.println("Repo injected " + repo);
}
}
public static class MyCommandManual {
public MyCommandManual(final ServiceLocator sl) {
Repository service = sl.getService(Repository.class);
System.out.println("Repo found: " + service);
}
}
}
In the Run method, i get access to my ServiceLocator. I bind my classes in there (so there is an example of how to do that). You can alternatively also register Binders with jersey directly - they will use the correct ServiceLocator.
The 2 classes MyCommandInjected and MyCommandManual are examples of how you can create this command.
The relevant line for you is probably:
Repository service = sl.getService(Repository.class);
This asks the service locator for a new instance of the Repository.
Now, this is just a quick example. I am much more fond of the guice bridge than using HK2 directly :) I find it much easier to use and much clearer. Using the guice-jersey-bridge you can do everything through guice and it will automatically do the right thing.
Hope that brings some inside,
Artur
You can use the inject method of ServiceLocator in order to inject already created objects. ServiceLocator is the basic registry of HK2 and should be available in your resource.