Spring Boot: Is it safe to pass many Instances for CommandLineRunner - java

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.

Related

spring boot 2.0 #Value does not work in #Configration

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:)

Spring AOP adviced methods do not work. My configuration seems ok

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

Spring boot: inject mock into Runner class

I have a spring boot application and some other components which application should interact with. However, in my unit testing I am using just application functionality and I would like to mock outer API calls. I am stuck as I can't find the way to mock case like this:
My start class with main method:
#ComponentScan("com.sample.application")
#SpringBootApplication
public class MyApp implements CommandLineRunner {
#Autowired
private OuterAPI outerAPI;
public static void main(String[] args) {
SpringApplication.run(AdRedirectorMain.class, args);
}
#Override
public void run(String... args) throws Exception {
outerAPI.createInstances();
}
...
}
And here is my test class example:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = MyApp.class)
public class MyAppTest {
// any tests
}
I am working with Spring Boot, JUnit, Mockito.
So, I am facing the problem - how could I avoid this method call createInstances() with Mockito, via reflection or in any other way.
Have a look at Mocking and spying beans in the Spring Boot documentation.
You can use #MockBean in your test class to replace an autowired bean with a Mockito mock instance.
You can use #MockBean http://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/mock/mockito/MockBean.html
or you can define an interface that you OuterAPI implements then for your test you provide a dummy implementation that makes a dummy call instead of actual call to the outerAPI.createInstances();
Another option that you have is to have configuration class like this:
#Configuration
#Profile(value = {"yourtest-profile"})
public class TestConfiguration{
#Primary
#Bean
public OuterAPI outerAPI() {
return Mockito.mock(OuterAPI.class);
}
}
and put it under scr/test/java

Spring MVC unit test. Spring Boot Application eads wrong configuration

I have some problem with configuration my unit test. I have mock-test class configuration:
#TestConfiguration
public class TestContext {
#Bean
// #Qualifier("userTestServiceImplWithMongoDB")
public UserService userService() {
return Mockito.mock(UserService.class, "userServiceImplWithMongoDB");
}
#Bean
// #Qualifier("taskTestServiceImplWithMongoDB")
public TaskService taskService() {
return Mockito.mock(TaskService.class, "taskServiceImplWithMongoDB");
}
}
This file is in src/test/java directory. I start my application with class:
#SpringBootApplication
#Configuration
#EnableMongoRepositories
#ComponentScan
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
protected final SpringApplicationBuilder configure(final SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}
My problem is that application starts with wrong configuration - this one from my test config class.
I use MongoDB repositories and I have service layer using thats repositories.
I followed this instructions: Some tutorial.
I am new to Spring, do you know what I can do to fox it?
Have a nice day :)
The tutorial you are following is not for spring, not spring-boot. It is still a good tutorial to use MockMvc, however it does not cover how to load the context through spring-boot.
Check this to setup a unit test with Spring-Boot: http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
public class YourTest {
...
}

XML-less configuration for spring

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);
}
}

Categories

Resources