Can't get Spring to inject my dependencies - java

I've been trying to get Spring to inject an #Autowired dependency into my application without avail. What am I doing wrong?
I created a bean called TimeService. Its job is to return the current time to anyone that asks.
package com.foxbomb.springtest.domain.time;
import java.util.Date;
import org.springframework.stereotype.Service;
#Service
public class TimeService {
public TimeService() {
super();
System.out.println("Instantiating TimeService...");
}
public Date getTime() {
return new Date();
}
}
Of course, I need to tell Spring about this, so I added the following to web.xml:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Great, and some Spring configuration:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config base-package="com.foxbomb.springtest.domain" />
</beans>
Now all we need is a calling class that would like to use this dependency. Unfortunately, the #Autowired here seems to do nothing:
package com.foxbomb.springtest;
import...
#Configurable
public class TimeManager {
public TimeManager() {
super();
System.out.println("Instantiating TimeManager...");
}
#Autowired
private TimeService timeService;
public Date getTime() {
return timeService.getTime();
}
}
And lastly, a JSP that wants to display the time:
<%#page import="com.foxbomb.springtest.ApplicationContextImpl"%>
<%#page import="com.foxbomb.springtest.TimeManager"%>
<html>
<head>
<title>Spring Test</title>
</head>
<body>
<h1>Autowired Dependencies....</h1>
<p>
Time is now <%=new TimeManager().getTime()%>!
</p>
</body>
</html>
But all I get is:
java.lang.NullPointerException
com.foxbomb.springtest.TimeManager.getTime(TimeManager.java:26)

When you access TimeManager object like this:
Time is now <%=new TimeManager().getTime()%>!
Spring does not know anything about this class. You are basically creating a new object, that's it!
Probably Spring creates an instance of TimeManager and injects dependencies properly (however you should use #Service rather than #Configuration annotation), but you are not using this instance in your JSP. Instead you are creating new, unmanaged instance, that is completely independent and unaware of Spring and dependencies...
I think this will work:
<%= WebApplicationContextUtils.
getWebApplicationContext(application).
getBean(TimeManager.class).
getTime() %>
Ugly? Sure. Because the whole approach of accessing services from JSP (view) is ugly. You should at least have a servlet that accesses Spring beans and puts result in request attributes. These attributes can then be accessed in JSP that you forward to - without ugly scriptlets.
If you really want to do this "the right" way, try Spring MVC or other popular Java web framework.

The #Autowire annotation in TimeManager was not recognized, because you told spring to start searching for annotation configuration information in the wrong package.
TimeManager is in com.foxbomb.springtest.
You have this configuration:
<context:annotation-config base-package="com.foxbomb.springtest.domain"/>
Notice that "com.foxbomb.springtest != com.foxbomb.springtest.domain".
Change your configuration to include this:
<context:annotation-config base-package="com.foxbomb.springtest"/>
Edit: Spring must instantiate TimeManager.
You will need to:
Have TimeManager be a data member of the controller class.
#Autowire TimeManager into the controller class (or get it from spring somehow).
Add the TimeManager to some appropriate scope (maybe session, maybe request, maybe application).
Access this TimeManager on your jsp page. Maybe something like this: It is now ${MyTimeManager.time} balonia watch time.

I tend to agree with #TomaszNurkiewicz; use a framework. However, a framework is way overkill for a lot of applications. If it's overkill for you, this answer details how to do it without all the manual work of getting the bean yourself. IMO doing it manually defeats the purpose.

You should be using #Component (or a child stereotype annotation) rather than #Configuration, and annotating your TimeManager with that. #Configuration is used for Java based container configuration, which is not what you are doing here.

Finally!
I got it working with the code in the above example. The trick was to set up Aspect/J and Weaving. This allows the #Configurable annotated class to automatically realize that its dependent on Spring to inject dependencies.
There wasn't much wrong with the base package :) I accidentally made a xml typo, trying to add the base-package attribute to the context:annotation-config tag. The correct definition was:
<context:annotation-config/>
<context:component-scan base-package="com.foxbomb.springtest"/>
I need actually eventually need to change it to com.foxbomb.springtext to include the class that was trying to access the bean too - as in the end it's also annotated.
Thanks everyone for your help!

Related

Are we not encouraged to write XML configuration using Maven + Spring?

Currently learning to build spring apps. I have been quite sucessful deploying mock applications for now, but one thing has been annoying me, which is not understanding the mechanisms behind the numerous annotations we add to the code. Look, I'm not saying I don't know which purpose they serve, where they act, nor am I questioning their helpfulness.
My point is that I feel that skipping the changes that should be made (or are being made?) in the XML files actually makes me feel that at the end of the day I don't know what I am truly writing. Let me be more specific, so you could answer me with regards to the following example. This is from Spring manual.
Let’s assume we have the following configuration that defines firstMovieCatalog as the primary MovieCatalog
#Configuration
public class MovieConfiguration {
#Bean
#Primary
public MovieCatalog firstMovieCatalog() { ... }
#Bean
public MovieCatalog secondMovieCatalog() { ... }
// ...
}
With such configuration, the following MovieRecommender will be autowired with the
firstMovieCatalog.
public class MovieRecommender {
#Autowired
private MovieCatalog movieCatalog;
// ...
}
The corresponding bean definitions appear as follows.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean class="example.SimpleMovieCatalog" primary="true">
<!-- inject any dependencies required by this bean -->
</bean>
<bean class="example.SimpleMovieCatalog">
<!-- inject any dependencies required by this bean -->
</bean>
<bean id="movieRecommender" class="example.MovieRecommender"/>
</beans>
Okay, so, I think if you can answer me 2 questions regarding this example, It would clear a lot the understanding I am lacking here.
The first thing that is not clear for me: is the annotation process a SEPARATED process from the XML configuration, or is it GENERATING this equivalent XML configuration in some hidden fashion?
Where actually IS this XML configuration file? All spring applications I generated through Initializr just generate the pom.xml file, and it does not include configuration. If I were not using the annotations, would I have to manually write an equivalent configuration in the pom?
is the annotation process a SEPARATED process from the XML configuration, or is it GENERATING this equivalent XML configuration in some hidden fashion?
Spring is not generating any XML or annotation in any case. Spring use XML and annotation processing to get info about which components (classes) are available to use and which beans (instances) to create, inject and use for processing. Then, all these beans could be retrieved by application context (not to confuse with xml of the same name).
Where actually IS this XML configuration file?
Spring first version used XML to configure your app. Later (starting in Spring 3), Spring added annotation support and processing to ease application configuration. Annotations are just another way to configure your components and beans without the hassle of maintaining big XML files (over 1000 lines or even more) or just to avoid dealing with XML at all. Current Spring versions support both configurations, you could also use a mix: using XML and using annotations.
Note that Spring's ApplicationContext has several implementations with different entry points for configuration:
AnnotationConfigApplicationContext accepts a class decorated with #Configuration.
ClassPathXmlApplicationContext accepts the path of a XML file available in application classpath.
If I were not using the annotations, would I have to manually write an equivalent configuration in the pom?
First thing first: POM files are for maven processing, not for Spring. Since you're using Maven, and you want to try using a Spring Boot application without annotations, then you can have this project structure:
- src
- main
- java
/* your Java packages and files */
- com.example
+ App <--- Main entry point
- com.example.service
+ Car <--- Component 1
+ Engine <--- Component 2
- resources
+ my-beans.xml <--- XML configuration. Name can be anything
App class:
package com.example;
import com.example.service.Car;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ApplicationContext;
#Configuration
#ImportResource("classpath:my-beans.xml")
public class App {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(App.class, args);
Car car = ctx.getBean(Car.class);
car.move();
}
}
Car.class:
package com.example.service;
public class Car {
private Engine engine;
public void move() {
engine.start();
}
}
Engine.class:
package com.example.service;
public class Engine {
public void start() {
System.out.println("engine starts");
}
}
my-beans.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean class="com.example.service.Engine" />
<bean class="com.example.service.Car" />
<property name="engine" ref="engine"></property>
</bean>
</beans>
is the annotation process a SEPARATED process from the XML configuration, or is it GENERATING this equivalent XML configuration in some hidden fashion
When you initialize a Spring application, you instantiate an ApplicationContext: it's responsible to load all the context definition (the beans, services...).
ApplicationContext is actually an interface which has several implementations depending on how your context is defined:
ClassPathXmlApplicationContext which reads an XML file
AnnotationConfigApplicationContext for annotations based approach
...
Thus you can see it as a unique process, only the datasource is different: either XML or annotations. But they describe the same thing: a context.

How can I create a Spring controller without the use of the annotations?

I am studying for the Spring Core certification and I have some doubts related this question:
What is the #Controller annotation used for? How can you create a
controller without an annotation?
So I know that the #Controller annotation indicates that a particular class serves the role of a controller. The #Controller annotation acts as a stereotype for the annotated class, indicating its role. The dispatcher scans such annotated classes for mapped methods and detects #RequestMapping annotations.
So a controller class is something like this:
#Controller
public class AccountController {
#RequestMapping("/listAccounts")
public String list(Model model) {...}
}
}
Ok, this is pretty clear for me but what exactly means create a controller without an annotation? How can I do it? By XML configuration or how?
Tnx
public class AccountController extends MultiActionController {
public ModelAndView listAccounts() {
// your code
return new ModelAndView("listAccounts.bean", "msg", "As you want")
}
}
web.xml
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.bean</url-pattern>
</servlet-mapping>
dispatcher-servlet.xml
<bean name="/listAccounts.bean" class="p1.AccountController"></bean>
I've come across this: Spring MVC 3.1 without annotations?
It would seem that you can actually create a controller without annotations (I've been working with Spring for little over a year and haven't encountered such a scenario, I don't know why one would want to do this, apart from certification questions of course) and the way to do it is by using a special configuration in the dispatcher-servlet XML file.
Just to comment the reasons why someone would like to configure Spring programatically or throuth XML, it is because it takes some to scan all the files looking for the annotations during runtime, so if we disable the scan and configure it manually the application will be available to service requests much faster and that is very important on high demand sceneraios.

How do I import in a spring xml conditionally [duplicate]

What I would like to achieve is the ability to "dynamically" (i.e. based on a property defined in a configuration file) enable/disable the importing of a child Spring XML context.
I imagine something like:
<import condition="some.property.name" resource="some-context.xml"/>
Where the property is resolved (to a boolean) and when true the context is imported, otherwise it isn't.
Some of my research so far:
Writing a custom NamespaceHandler (and related classes) so I can register my own custom element in my own namespace. For example: <myns:import condition="some.property.name" resource="some-context.xml"/>
The problem with this approach is that I do not want to replicate the entire resource importing logic from Spring and it isn't obvious to me what I need to delegate to to do this.
Overriding DefaultBeanDefinitionDocumentReader to extend the behaviour of the "import" element parsing and interpretation (which happens there in the importBeanDefinitionResource method). However I'm not sure where I can register this extension.
Prior to Spring 4, the closest you can get using standard Spring components is:
<import resource="Whatever-${yyzzy}.xml"/>
where ${xyzzy} interpolates a property from the system properties. (I use a hacky custom version of the context loader class that adds properties from other places to the system properties object before starting the loading process.)
But you can also get away with importing lots of unnecessary stuff ... and use various tricks to only cause the necessary beans to be instantiated. These tricks include:
placeholder and property substitution
selecting different beans using the new Spring expression language,
bean aliases with placeholders in the target name,
lazy bean initialization, and
smart bean factories.
This is now completely possible, using Spring 4.
In your main application content file
<bean class="com.example.MyConditionalConfiguration"/>
And the MyConditionalConfiguration looks like
#Configuration
#Conditional(MyConditionalConfiguration.Condition.class)
#ImportResource("/com/example/context-fragment.xml")
public class MyConditionalConfiguration {
static class Condition implements ConfigurationCondition {
#Override
public ConfigurationPhase getConfigurationPhase() {
return ConfigurationPhase.PARSE_CONFIGURATION;
}
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// only load context-fragment.xml if the system property is defined
return System.getProperty("com.example.context-fragment") != null;
}
}
}
And then finally, you put the bean definitions you want included in the /com/example/context-fragment.xml
See the JavaDoc for #Conditional
As mentioned earlier, this can be easily accomplished with profiles if you're using Spring 3.1+
<!-- default configuration - will be loaded if no profile is specified -->
<!-- This will only work if it's put at the end of the configuration file -->
<!-- so no bean definitions after that -->
<beans profile="default">
<import resource="classpath:default.xml" />
</beans>
<!-- some other profile -->
<beans profile="otherProfile">
<import resource="classpath:other-profile.xml" />
</beans>
otherProfile can be easily activated with e.g.
mvn install -Dspring.profiles.active=otherProfile
if you're using different profiles in tests, just add -DforkMode=never to make sure that the tests will run inside same VM, therefore the param spring.profiles.active wont be lost
With Spring 3.1.x you can use bean profiles to achieve conditional resource import and bean instantiation. This is of course of no help if you are using an earlier version :)
For the record, Robert Maldon explains how to accomplish conditional definition of beans in this post: http://robertmaldon.blogspot.com/2007/04/conditionally-defining-spring-beans.html. It is a bit long to copy it here (besides, I don't think I should copy-paste his article anyway).
The end result with this approach, adapted for your example, is:
<condbean:cond test="${some.property.name}">
<import resource="some-context.xml"/>
</condbean:cond>
It is certainly not so simple as Stephen C's solution, but it is much more poweful.
Another one to consider for Spring 3.0:
<alias name="Whatever" alias=""Whatever-${yyzzy}" />
where ${xyzzy} interpolates a property from the system properties.
Another option is to have your app load a modules-config.xml file that is located in the /conf folder and edit it during the install/config phase to uncomment the modules you want loaded.
This is the solution I'm using with a web application that serves as a container for different integration modules. The web application is distributed with all the different integration modules. A modules-config.xml is placed in tomcat's /conf folder and the conf folder is added to the classpath (via catalina.properties/common.loader property). My web app webapp-config.xml has a <import resource="classpath:/modules-config.xml"/> to get it loaded.
You can override contextInitialized(javax.servlet.ServletContextEvent event) in your own ContextLoaderListener and set required System property before super.contextInitialized(event) called like this
package com.mypackage;
import org.springframework.web.context.ContextLoaderListener;
public class MyContextLoaderListener extends ContextLoaderListener {
public void contextInitialized(javax.servlet.ServletContextEvent event) {
System.setProperty("xyz", "import-file-name.xml");
super.contextInitialized(event);
}
}
And than replace ContextLoaderListener to MyContextLoaderListener in your web.xml
<listener>
<listener-class>com.mypackage.MyContextLoaderListener</listener-class>
</listener>
Now you can use in your spring.xml
<import resource="${xyz}" />
I hope this will help.

Override default-lazy-init=true for Spring bean definitions

I am maintaining a large Java EE system. Most of the business logic is converted from EJB:s into POJO:s configured in several spring context configuration files. EJB:s are mostly used as Facades, that looks up the business logic spring beans from a context composed of all spring context configuration files mentioned earlier. For this we use the AbstractStatelessSessionBean provided with the spring framework.
All these configuration files have the default-lazy-init=true directive, which means that the business logic beans are not created until they are actually used by the system. This is preferable most of the time since republishing in developer mode becomes faster.
But when large merges are made, we are having problems finding all the configuration errors, such as missing dependencies.
My idea is to write some form of integration test, with the purpose of finding those errors. This means, i think, that I need to find a way to override all default-lazy-init=true declarations when creating the application context.
Is there any way of doing this programmatically, or perhaps with some test-only context file that includes all the actual context files?
Let's say currently you have a single applicationContext.xml file containing all bean definitions:
<beans default-lazy-init="true">
<!-- all your beans -->
</beans>
Rename it to applicationContext-main.xml or something and remove default-lazy-init="true" attribute. Now create two applicationContext.xml files:
<beans default-lazy-init="true">
<import resource="applicationContext-core.xml"/>
</beans>
and:
<beans default-lazy-init="false">
<import resource="applicationContext-core.xml"/>
</beans>
As you can see the only difference is the value of default-lazy-init. During development your team can use the former version of applicationContext.xml that includes all the beans with lazy-init. On staging and testing environments switch it to the latter so that all beans included in applicationContext-core.xml are created eagerly.
I believe that the best way is to control lazy init of beans is to leave the default-lazy-init out of all config files except the topmost as Tomasz Nurkiewicz suggests. I did however in this case need a quick and dirty fix to verify all bean definitions. (It is a bit of a process to change the lazy init policy.)
I came up with a simple BeanFactoryPostProcessor which seems to do the job:
public class NonLazyInitBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
for (String beanName : beanFactory.getBeanDefinitionNames()) {
beanFactory.getBeanDefinition(beanName).setLazyInit(false);
}
}
}
If included in a context file, it will override the lazy init flag set by any included context files.
<beans default-lazy-init="false">
<bean class="example.NonLazyInitBeanFactoryPostProcessor" />
<import resource="applicationContext-core.xml"/>
</beans>
If I try to create a context from the above xml, configuration errors previously hidden by lazy initialization will show up immediately as exceptions.
There is one 'but' in this PostProcessor
for (String beanName : beanFactory.getBeanDefinitionNames()) {
beanFactory.getBeanDefinition(beanName).setLazyInit(false);
}
This for loop will iterate only over top most beans not including e.g internal(local) bean defintions ...
You can't access the scanner from the context - it's completely private, but since you can step into the source code, you can see what's required to set up your own. I used Spring's own ReflectionTestUtils to set my own configured scanner in the context:
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
BeanDefinitionDefaults defaults = new BeanDefinitionDefaults();
defaults.setLazyInit(true);
scanner.setBeanDefinitionDefaults(defaults);
ReflectionTestUtils.setField(context, "scanner", scanner);
context.scan("com.some.path");
You can do this anywhere you have access to the Application Context before the component scan takes place.

Aspects are not executed

I'm trying to test a simple Aspect.
The app compiles and runs fine, BUT I do not get the Aspect executed. Or at least, I do not get the output the aspect should produce.
(my aim is to write an exception logger for any ex that occures in the app. but first this test aspect should run...)
Maybe someone who has more experience in aspects see's what I'm doing wrong?
package business;
public interface Customer {
void addCustomer();
}
import org.springframework.stereotype.Component;
#Component
public class CustomerImpl implements Customer {
public void addCustomer() {
System.out.println("addCustomer() is running ");
}
}
#RequestScoped #Named
//this is backing bean for jsf page
public class Service {
#Inject
Customer cust;
add() {
System.out.println("Service is running ");
cust.addCustomer();
}
}
#Aspect
public class AspectComp {
#Before("within(business..*)")
public void out() {
System.out.println("system out works!!");
}
}
Spring:
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
">
<context:annotation-config />
<context:component-scan base-package="business" />
<aop:aspectj-autoproxy />
</beans>
Output:
Service is running
addCustomer() is running
The Aspect statement is missing.
You are creating your Component with its constructor, and not getting it from Spring container! That's the problem, or you must use AspectJ's load-time weaver.
Just inject your component (CustomerImpl) in your service and then use the injected instance.
I remember having a similar problem once; Spring wasn't actually loading the proxy as it did not recognize the #Aspect annotation as being an annotation-scanable bean. I added the #Component annotation to the #Aspect notation and Spring started scanning it.
I never looked into the reasons why this happened, and why I needed to do that, so I cannot confirm that is the "proper" way of doing things. My gut would tell me that I had something missing in my config file; I can't imagine why Spring would not scan for #Aspect beans.
The other thing you can do, is to explicitly declare your Aspect bean in the XML config file as well to see if this the same type of problem you're having.
You can also enable debug logging in the Spring framework and see if your bean is being loaded by Spring. If not, then it gives you an idea where to start looking.

Categories

Resources