I found a strange thing while learning Spring tech.
I inject a java.lang.String type bean into a bean property which type is java.io.File, but the program still runs normally.
I want to know
What happened internally?
Is it a valid usage or a trick?
Here is the spring configuration file stringtofile.xml.
<?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:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"
default-lazy-init="true">
<bean id="file_str"
class="java.lang.String"
c:_="C:\tmp\test.hi"/>
<bean id="file"
class="stringtofile.FileWrapper"
p:file-ref="file_str"/>
</beans>
Here is my test classes.
package stringtofile;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.File;
public class FileWrapper {
File file;
public File getFile() {
return file;
}
public FileWrapper setFile(File file) {
this.file = file;
return this;
}
public static void main(String[] args) {
ApplicationContext ctx =
new ClassPathXmlApplicationContext("stringtofile.xml");
FileWrapper fileWrapper =
(FileWrapper) ctx.getBean("file");
System.out.println(fileWrapper.getFile());
}
}
It is done by the PropertyEditors in your case the FileEditor
Check the documentation here for more details: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html
Related
I am trying to use the properties file and inject it to my fields of a class, but I am getting null .
I have created a properties file called fortune.properties:
fortune1=Have a good day
fortune2=Have a bad day
fortune3=Have a medioccure day
Then I am loading it in the applicationContext.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">
<!-- Enable Component Scanning -->
<context:component-scan base-package="com.luv2code.springdemo"></context:component-scan>
<context:property-placeholder location="classpath:fortune.properties" />
</beans>
In the RandomFortuneClass, I am using value injection using #Value() annotation:
package com.luv2code.springdemo;
import java.util.Random;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
#Component()
public class RandomFortuneService implements FortuneService {
#Value("${fortune1}")
private String fortune1;
#Value("${fortune2}")
private String fortune2;
#Value("${fortune3}")
private String fortune3;
String[] fortunes = new String[] {fortune1, fortune2, fortune3};
private Random random = new Random();
#Override
public String getFortune() {
int index = random.nextInt(fortunes.length);
return fortunes[index];
}
}
Then I am declaring fortuneService in SwimCoach class using constructor injection:
package com.luv2code.springdemo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
#Component()
public class SwimCoach implements Coach {
private FortuneService fortuneService;
#Autowired
SwimCoach(#Qualifier("randomFortuneService") FortuneService fs) {
fortuneService = fs;
}
#Override
public String getDailyWorkout() {
System.out.println();
return "Swim 5m everyday";
}
#Override
public String getDailyFortune() {
return fortuneService.getFortune();
}
}
Now when I try to call the method getDailyFortune in the AnnotationDemoApp class, I always get null as output.
package com.luv2code.springdemo;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AnnotationDemoApp {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Coach theCoach = context.getBean("swimCoach", Coach.class);
System.out.println(theCoach.getDailyFortune());
context.close();
}
}
What is the error here?
Kindly comment if more information is needed.
Here is my file hierarchy:
PS: I am following the spring & hibernate course for beginners by Chad Darby.
You simply need to move fortune.properties into /src/main/resources, but let me suggest a cleaner way...
First, drop the xml configuration, it's outdated by several years and everything you can do there can be done via java annotations.
Second, add your property files to src/main/resources.
Third, tell Spring where to resolve your properties from, you can do this by adding #PropertySources({#PropertySource("fortune.properties")}) to a configuration class like this (also note that I've added the #ComponentScan and #Configuration annotations here):
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.PropertySource;
#Configuration
#ComponentScan
#PropertySource("fortune.properties")
public class Config {
}
Fourth, in your main method, use the AnnotationConfigApplicationContext as opposed to the ClassPathXmlApplicationContext. I think you'll find it's much cleaner.
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
Coach theCoach = context.getBean("swimCoach", Coach.class);
System.out.println(theCoach.getDailyFortune());
}
As a HelloWorld example, in my spring Beans.xml file, I have:
<?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">
<bean id = "helloWorld" class = "com.tutorialspoint.HelloWorld">
<property name = "message" value = "Hello World!"/>
</bean>
</beans>
The main file:
package com.tutorialspoint;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
obj.getMessage();
}
}
The class file:
package com.tutorialspoint;
public class HelloWorld {
private String message;
public void setMessage(String message){
this.message = message;
}
public void getMessage(){
System.out.println("Your Message : " + message);
}
}
The error seems like this:
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'helloWorld' defined in class path resource [Beans.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.tutorialspoint.HelloWorld]: Is the constructor accessible?; nested exception is java.lang.IllegalAccessException: class org.springframework.beans.BeanUtils (in module spring.beans) cannot access class com.tutorialspoint.HelloWorld (in module HelloSpring) because module HelloSpring does not export com.tutorialspoint to module spring.beans
at spring.beans#5.2.5.RELEASE/org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1320)
at spring.beans#5.2.5.RELEASE/org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1214)
at spring.beans#5.2.5.RELEASE/org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557)
at spring.beans#5.2.5.RELEASE/org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
at spring.beans#5.2.5.RELEASE/org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
at spring.beans#5.2.5.RELEASE/org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at spring.beans#5.2.5.RELEASE/org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
at spring.beans#5.2.5.RELEASE/org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at spring.beans#5.2.5.RELEASE/org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:882)
at spring.context#5.2.5.RELEASE/org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878)
at spring.context#5.2.5.RELEASE/org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
at spring.context#5.2.5.RELEASE/org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:144)
at spring.context#5.2.5.RELEASE/org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:85)
at HelloSpring/com.tutorialspoint.MainApp.main(MainApp.java:8)
Other folks on this site got the same problem, and I've looked at it. However, I've tried all the answers provided but none of them solved my issue. I've used several versions of the path inside and there's no empty line above the first line of my Beans.xml. What's the problem here?
Trying to autowire Spring bean with property, but still getting NPE. Snippets:
INFO: Loading XML bean definitions from class path resource [autoWireByName.xml]
Exception in thread "main" today do push-ups for 30 mins
java.lang.NullPointerException
at com.springAutoWireByName.KabadiCoach.getFortune(KabadiCoach.java:23)
at com.springAutoWireByName.AutoWireByName.main(AutoWireByName.java:13)
KabadiCoach.java
package com.springAutoWireByName;
public class KabadiCoach {
private SadFortune sadFortune;
/*public KabadiCoach(){
System.out.println("inside default Constructor");
}*/
public String getDailyWorkout()
{
return "today do push-ups for 30 mins";
}
public void setSadFortune(SadFortune fortune) {
sadFortune = fortune;
}
public String getFortune() {
return sadFortune.getSadFortune();
}
}
SadFortune.java
package com.springAutoWireByName;
public class SadFortune {
public String getSadFortune()
{
System.out.println();
return "your day wont be good enough Sorry!!!";
}
}
<?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 definitions here -->
<bean name="Fortune" class="com.springAutoWireByName.SadFortune">
</bean>
<bean id="myCoach" class="com.springAutoWireByName.KabadiCoach" autowire="byName" />
<!-- this is just a prototype to define actual just make use of this file-->
</beans>
main
package com.springAutoWireByName;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AutoWireByName {
public static void main(String[] args) {
ClassPathXmlApplicationContext context =new ClassPathXmlApplicationContext("autoWireByName.xml");
KabadiCoach co = context.getBean("myCoach",KabadiCoach.class);
System.out.println(co.getDailyWorkout());
System.out.println(co.getFortune());
}
}
after running the above code I am getting the error message as listed and when I change the method to static
public static String getSadFortune()
{
System.out.println();
return "your day wont be good enough Sorry!!!";
}
In class 2 I got the desired output. Why?
Basically the SadFortune member is still null, which is why it works if the getSadFortune() method will be made static.
Until now you only tell Spring to instantiate a Fortune bean and a KabadiCoach, but you have to tell Spring that some members in the KabadiCoach need to be autowired too.
Try this in your Spring configuration file:
<bean id="fortune" class="com.springAutoWireByName.SadFortune"></bean>
<bean id="myCoach" class="com.springAutoWireByName.KabadiCoach" autowire="byName">
<property name="fortune" ref="fortune" />
</bean>
EDIT: Sorry, overread the autowire="byName" attribute. In this case you probably just have write the name in lower case.
<bean id="fortune" class="com.springAutoWireByName.SadFortune"/>
<bean id="myCoach" class="com.springAutoWireByName.KabadiCoach" autowire="byName"/>
I'm trying to weave in code before the call of start();
This is the TestClass I want to advice:
package com.test;
public class TestClass {
public static void main(String[] args) {
new TestClass().start();
}
private void start() {
System.out.println("Test started");
}
}
This is the aspect including the advice:
package com.test;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class LogAspect {
#Before("call(void com.test.TestClass.start())")
public void logBefore(JoinPoint joinPoint) {
System.out.println("logBefore() is running");
}
}
This is my spring.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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 ">
<aop:aspectj-autoproxy/>
<context:annotation-config/>
<context:component-scan base-package="com.test"/>
I have also tried to name the beans explicitly in the XML like this:
<aop:aspectj-autoproxy/>
<bean id="test" class="com.test.TestClass" />
<bean id="aspect" class="com.test.LogAspect" />
This is my project setup (I'm using the Spring Tool Suite version 3.1.0):
The result is that TestClass.start is called just fine, but the advice is not applied.
What do I have to change so the advice is applied?
Thanks.
Edit: I finally got it to work:
After editing my code according to your suggestions I had a look at this tutorial. This led me to let my TestClass implement an interface. And that fixed the problem.
Here is my final setup:
The TestClass including its interface:
package com.test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
interface TestClass {
void start();
}
public class TestClassImpl implements TestClass {
public static void main(String[] args) {
ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(
"Spring-Config.xml");
TestClass t1 = (TestClass) appContext.getBean("myTest");
t1.start();
}
#Override
public void start() {
System.out.println("test");
}
}
The aspect:
package com.test;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
#Aspect
public class LogAspect {
#Before("execution(void com.test.TestClass.start(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("logging before "
+ joinPoint.getSignature().getName());
}
}
And the Spring-Config.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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 ">
<aop:aspectj-autoproxy/>
<bean id="myTest" class="com.test.TestClassImpl" />
<bean id="logAspect" class="com.test.LogAspect" />
</beans>
Thanks for your help.
A call AspectJ pointcut applys when the method is called from another class. As you're calling this directly yourself you should use an execution pointcut, so change:
#Before("call(void com.test.TestClass.start())")
to
#Before("execution(void com.test.TestClass.start())")
Also let the Spring create the bean instead of creating one directly yourself.
Aside: Note, you can keep your Spring XML files separate from your source files in src/resources and they will be picked up by ClassPathXmlApplicationContext.
I want to trigger a method when a particular method from another class is invoked that is why I thought of using #Pointcut.
The code below is almost identical to the that I am coding and I don't what else I have to add.
public class OrgManagerImpl implements OrgManager {
public IOrg getOrg(String orgShortName) {
}
}
and this is the class that will should be triggered:
#Aspect
public class OrgManagerSynchronizer {
#Pointcut("execution(* com.alvin.OrgManager.getOrg(..))")
public void classMethods() {}
#Before("classMethods()")
public void synchronize(JoinPoint jp) {
//code should be executed. but does not execute.
}
}
and in my .xml this was specified:
aop:aspectj-autoproxy
What more should I add? What to do next?
Check below things.
1) Check if OrgManagerImpl is either defind as bean in context xml or it is marked as #Component & in context xml you have or that classes's package.
2) If above thing is correct then try changing pointcut as below
#Pointcut("execution(* get*(..))")
This pointcut intercepts all get methods. See if with this point cut your synchronize method is working or not. If it works then at least your spring configurations are fine. you just need to refine pointcut expression. But if this also doesn't work then there is something wrong with your spring aop configuration itself, so we can concentrate on those.
Also if this doesn't work then try to give some more info like you context xml, bean java classes etc.
Two things you need to check.
aop:aspectj-autoproxy is enabled in configuration
The Point Cut / Aspect / Target are spring bean
The below is my xml config example.
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:aspectj-autoproxy/>
<context:component-scan base-package="com.techoffice"/>
<bean class="com.techoffice.example.ExampleAspect"/>
</beans>
ExampleAspect.java
package com.techoffice.example;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
#Aspect
public class ExampleAspect {
#Pointcut("execution (* com.techoffice..*(..))")
public void anyRun() {}
#Before(value="anyRun()")
public void beforeAnyRun(JoinPoint jointPoint){
System.out.println("Before " + jointPoint.getClass().getName() + "." + jointPoint.getSignature().getName());
}
#After(value="anyRun()")
public void afterAnyRun(JoinPoint jointPoint){
System.out.println("After " + jointPoint.getClass().getName() + "." + jointPoint.getSignature().getName());
}
}
HelloWorldExample
package com.techoffice.example;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;
#Component
public class HelloWorldExample {
public void run(){
System.out.println("run");
}
public static void main(String[] args){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
HelloWorldExample helloWorldExample = context.getBean(HelloWorldExample.class);
helloWorldExample.run();
}
}