I'm leaning spring boot 2.0, I use #Value annotation as same as early release, but it's not work in #Configration annotation.
application.yml
test:
a: test
TestApplication.java
#SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(TestApplication.class, args);
System.out.println(context.getEnvironment()
.getProperty("test.a"));//got test
}
}
TestConfigration.java
#Configuration
public class TestConfigration{
#Value("${test.a}")
String a;
#Bean
public Bean getBean(){
System.out.println(a);//there!!!the a is NULL!!! WHY?
return new Bean();
}
}
TestController.java
#RestController
#RequestMapping("/")
public class TestController{
#Value("${test.a}")
String a;
#RequestMapping("/")
public String test(){
return a;//got test
}
}
why?
Emmmm ... there is no problem in the questions's code , it works.
I ignored a warn log when spring boot start:
2018-03-26-21:21:27 WARN [org.springframework.context.annotation.ConfigurationClassPostProcessor] - Cannot enhance #Configuration bean definition 'TestConfigration' since its singleton instance has been created too early. The typical cause is a non-static #Bean method with a BeanDefinitionRegistryPostProcessor return type: Consider declaring such methods as 'static'.
it's because I register a org.mybatis.spring.mapper.MapperScannerConfigurer with #Bean but miss static keywords, it's necessary if a implementation of BeanFactoryPostProcessor register and #Value is used in same configuration class.
I don't know it and I used to be wrong for too long,I didn't find it out because the db configuration and mybatis configuration is not in same class in my past project.
thanks to #ailav and #Impulse The Fox ,and forgive my weird English:)
Related
In Spring Framework is it possible to eliminate the entire Spring.xml and use a configuration class with #Configuration and #Bean annotation for creating bean, and for all other purpose use a spring.xml?
Yes, you can have pure java configuration in Spring. You have to create a class and annotate it with #Configuration. We annotate methods with #Bean and instantiate the Spring bean and return it from that method.
#Configuration
public class SomeClass {
#Bean
public SomeBean someBean() {
return new SomeBean();
}
}
If you want to enable component scanning, then you can give #ComponentScan(basePackages="specify_your_package") under the #Configuration. Also the method name as someBean serves as bean id. Also if you have to inject a dependency, you can use constructor injection and do as following:
#Configuration
public class SomeClass {
#Bean
public SomeDependency someDependency() {
return new SomeDependency();
}
#Bean
public SomeBean someBean() {
return new SomeBean(someDependency());
}
}
Yes,most of (maybe all of)official guides uses absolutely no xml configuration file,just annotations.
I'm trying to create a demo AOP application but it just does not work right.
I read through all tutorials and got it working with #RestController but as I tried it with a plain java spring driven application I just can't get it to work. Please review my files and tell me where my mistake lies in.
Application Class
#SpringBootApplication
#ComponentScan("com.xetra.experimental")
#EnableAspectJAutoProxy
public class AoptryoutnowebApplication {
public static void main(String[] args) {
SpringApplication.run(AoptryoutnowebApplication.class, args);
DefaultClassToAspectImpl defaultClassToAspectImpl = new DefaultClassToAspectImpl();
defaultClassToAspectImpl.doStuff();
}
}
ClassToAspect Interface
public interface ClassToAspect {
void doStuff();
}
ClassToAspect Implementation
#Component
public class DefaultClassToAspectImpl implements ClassToAspect {
#FooAnnotation
public void doStuff(){
System.out.println("DoStuff!");
}
}
Annotation for Pointcut
public #interface FooAnnotation {
}
Aspect Class
#Aspect
public class FooAspect {
#Pointcut("#annotation(FooAnnotation)")
public void methods(){
}
#Before("methods()")
public void doAspect(){
System.out.println("FooAspect before");
}
}
Try this:
replace #EnableAspectJAutoProxy with #EnableAspectJAutoProxy(proxyTargetClass = false)
change pointcut to
#Pointcut("execution (* your.package..*.*(..)) && #annotation(fooAnnotation))")
The problem is you are using a non Spring managed instance by doing new DefaultClassToAspectImpl(). Spring AOP only works for Spring managed beans, because by default Spring AOP is proxy based.
So instead of doing new DefaultClassToAspectImpl() you should be obtaining the instance from the ApplicationContext. When using Spring Boot the SpringApplication.run call returns an ApplicationContext. Here you can use one of the getBean methods to obtain the instance you want.
ApplicationContext ctx = SpringApplication.run(AoptryoutnowebApplication.class, args);
ClassToAspect bean = getBean(ClassToAspect.class);
bean.doStuff();
This way you get the Spring managed
I'm a bit new to Spring Boot. I have an Application.java class where I have some code:
#SpringBootApplication(exclude = JpaRepositoriesAutoConfiguration.class)
public class Application {
private static final Logger log = LoggerFactory.getLogger(Application.class);
...
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
#Bean
public CommandLineRunner App(DBReportRepository dbReportRepository,
ClientRepository clientRepository, FileParser fileParser,
MessagingService messagingService, ClientReportFactoryImpl clientReportFactory) ...
I was wondering is it a good practice to pass so many parameters (which are #Services annotated classes) to CommandLineRunner.
Or am I making Spring Boot do too much and there is another way to make Spring Boot be aware of those #Services classes.
Turned out my spring boot configuration was missing an AppConfig class. Without it I could only use #Autowired with #Component classes or #Service classes which were passed as arguments to CommandLineRunner. I created the AppConfig.java looking like this:
#Configuration
#ComponentScan(basePackages = "your.main.package")
public class AppConfig {
}
And after that I could use the #Autowired everywhere possible.
I wonder why the field injection works in the #SpringBootApplication class and the constructor injection does not.
My ApplicationTypeBean is working as expected but when I want to have a constructor injection of CustomTypeService I receive this exception:
Failed to instantiate [at.eurotours.ThirdPartyGlobalAndCustomTypesApplication$$EnhancerBySpringCGLIB$$2a56ce70]: No default constructor found; nested exception is java.lang.NoSuchMethodException: at.eurotours.ThirdPartyGlobalAndCustomTypesApplication$$EnhancerBySpringCGLIB$$2a56ce70.<init>()
Is there any reason why it does not work at #SpringBootApplication class?
My SpringBootApplication class:
#SpringBootApplication
public class ThirdPartyGlobalAndCustomTypesApplication implements CommandLineRunner{
#Autowired
ApplicationTypeBean applicationTypeBean;
private final CustomTypeService customTypeService;
#Autowired
public ThirdPartyGlobalAndCustomTypesApplication(CustomTypeService customTypeService) {
this.customTypeService = customTypeService;
}
#Override
public void run(String... args) throws Exception {
System.out.println(applicationTypeBean.getType());
customTypeService.process();
}
public static void main(String[] args) {
SpringApplication.run(ThirdPartyGlobalAndCustomTypesApplication.class, args);
}
public CustomTypeService getCustomTypeService() {
return customTypeService;
}
My #Service class:
#Service
public class CustomTypeService {
public void process(){
System.out.println("CustomType");
}
}
My #Component class:
#Component
#ConfigurationProperties("application.type")
public class ApplicationTypeBean {
private String type;
SpringBootApplication is a meta annotation that:
// Other annotations
#Configuration
#EnableAutoConfiguration
#ComponentScan
public #interface SpringBootApplication { ... }
So baiscally, your ThirdPartyGlobalAndCustomTypesApplication is also a Spring Configuration class. As Configuration's javadoc states:
#Configuration is meta-annotated with #Component, therefore
#Configuration classes are candidates for component scanning
(typically using Spring XML's element) and
therefore may also take advantage of #Autowired/#Inject at the field
and method level (but not at the constructor level).
So you can't use constructor injection for Configuration classes. Apparently it's going to be fixed in 4.3 release, based on this answer and this jira ticket.
I have the following configuration bean for a non web app
#Configuration
public class MyBeans {
#Bean
#Scope(value="prototype")
MyObject myObject() {
return new MyObjectImpl();
}
}
On the other side I have my class
public class MyCommand implements Command {
#Autowired
private MyObject myObject;
[...]
}
How can I make myCommand be autowired with the configuration in MyBeans without using XML so I can inject mocks in my other test classes?
Thanks a lot in advance.
With XML-based configuration you'd use the ContextConfiguration annotation. However, the ContextConfiguration annotation doesn't appear to work with Java Config. That means that you have to fall back on configuring your application context in the test initialization.
Assuming JUnit4:
#RunWith(SpringJUnit4ClassRunner.class)
public class MyTest{
private ApplicationContext applicationContext;
#Before
public void init(){
this.applicationContext =
new AnnotationConfigApplicationContext(MyBeans.class);
//not necessary if MyBeans defines a bean for MyCommand
//necessary if you need MyCommand - must be annotated #Component
this.applicationContext.scan("package.where.mycommand.is.located");
this.applicationContext.refresh();
//get any beans you need for your tests here
//and set them to private fields
}
#Test
public void fooTest(){
assertTrue(true);
}
}