Initializing Spring in not-web annotation-configured application - spring

My java app is using Spring stereotype annotations (#Controller, #Component) and autowire annotations to manage dependency injections.
It is not web application, just plain jar. Also it's pure-annotation based code, i.e. no xml at all.
What is right way to initialize Spring annotation based application context and default configuration just from the main method?

Use #Configuration to name a AppConfig which is equivalent to applicationContext.xml.
#Configuration
public class AppConfig {
#Bean(initMethod = "init")
public Foo foo() {
return new Foo();
}
#Bean(destroyMethod = "cleanup")
public Bar bar() {
return new Bar();
}
}
And in main method to new a AnnotationConfigApplicationContext.
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
Foo foo = ctx.getBean(Foo.class);
//etc
}

Related

Can #Configuration work without #componentScan (Spring JavaConfig #annotaion)

--Appconfig.java
#Configuration
public class AppConfig {
#Bean(name="helloBean")
public HelloWorld helloWorld() {
return new HelloWorldImpl();
}
}
--interface.java
public interface HelloWorld {
void printHelloWorld(String msg);
}
--ipml.java
public class HelloWorldImpl implements HelloWorld {
public void printHelloWorld(String msg) {
System.out.println("Hello! : " + msg);
--
}
--App.java
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new
new AnnotationConfigApplicationContext(AppConfig.class);
HelloWorld obj = (HelloWorld) context.getBean(HelloWorldImpl.class);
obj.printHelloWorld("Spring3 Java Config");
}
}
My program can works, but my question is why I don't need to add #componentScan in Appconfig.java .
It seems to #Configuration and #Bean can be found by Spring whithout using #componentScan.
I thought if you want to use #annotation ,you must use #componentScan or
context:component-scan(xml),
am I right?
#ComponentScan allows spring to auto scan all your components with #Component annotated. Spring uses the base-package attribute, which indicates where to find components.
#Configuration is meta annotated with #Component, which marks it eligible for classpath scanning.
#Configuration (AppConfig class) is registered when you use
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
#Bean doesn't need #ComponentScan as all these beans are created explicitly when spring encounters this annotation.

Manually loading application context to write getBean() in spring boot application

In a spring application, we write like this to get a bean through manually loading spring application context.
ApplicationContext context = new ClassPathXmlApplicationContext("path/to/applicationContext.xml");
JobLauncher launcher=(JobLauncher)context.getBean("launcher");
How to do the similar thing in spring boot ?
Being a newbie...need help
#SpringBootApplication
public class Application {
public static void main(String[] args) throws Exception {
ApplicationContext app = SpringApplication.run(Application .class, args);//init the context
SomeClass myBean = app.getBean(SomeClass.class);//get the bean by type
}
#Bean // this method is the equivalent of the <bean/> tag in xml
public SomeClass getBean(){
return new SomeClass();
}
#Bean
public MyUtilClass myUtil(SomeClass sc){
MyUtilClass uc = new MyUtilClass();
uc.setSomeClassProp(sc);
return uc;
}
}
You can also your xml file to declare the beans instead of the java config, just use #ImportResource({"classpath*:applicationContext.xml"})
Edit: To answer the comment: Make the util class a spring bean(using #Component annotation and component scan or the same as SomeClass shown above) and then you can #Autowire the bean you like. Then when you want to use the Util class just get it from the context.

Testing Mock Bean in Spring with Spock

I'm being hit with the issue that spock doesn't allow Mocks to be created outside of the specification - How to create Spock mocks outside of a specification class?
This seems to be still outstanding so am asking is that giving that i've got a complex and nested DI graph what is the most efficient way to 'inject' a mock representation deep in the graph?
Ideally, I have one bean definition set for normal deployment and another when running unit tests and it is this definition set being the applicable Mocks
e.g.
#Configuration
#Profile("deployment")
public class MyBeansForDeployment {
#Bean
public MyInterface myBean() {
return new MyConcreateImplmentation();
}
}
&&
#Configuration
#Profile("test")
public class MyBeansForUnitTests {
#Bean
public MyInterface myBean() {
return new MyMockImplementation();
}
}
Since Spock 1.1, you can create mocks outside of a specification class (detached mocks). One of the options is DetachedMockFactory. Take a look at the documentation or my answer to the question you linked.
You could try to implement a BeanPostProcessor that will replace the beans that you want with test doubles, such as shown below:
public class TestDoubleInjector implements BeanPostProcessor {
...
private static Map<String, Object[]> testDoubleBeanReplacements = new HashMap<>();
public void replaceBeanWithTestDouble(String beanName, Object testDouble, Class testDoubleType) {
testDoubleBeanReplacements.put(beanName, new Object[]{testDouble, testDoubleType});
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (testDoubleBeanReplacements.containsKey(beanName)) {
return testDoubleBeanReplacements.get(beanName)[TEST_DOUBLE_OBJ];
}
return bean;
}
In your test, setup your mocks like shown below before initializing the application context. Make sure to include the TestDoubleInjector as a bean in your test context.
TestDoubleInjector testDoubleInjector = new TestDoubleInjector()
testDoubleInjector.replaceBeanWithTestDouble('beanToReplace', mock(MyBean.class), MyBean.class)
It could be done using HotSwappableTargetSource
#WebAppConfiguration
#SpringApplicationConfiguration(TestApp)
#IntegrationTest('server.port:0')
class HelloSpec extends Specification {
#Autowired
#Qualifier('swappableHelloService')
HotSwappableTargetSource swappableHelloService
def "test mocked"() {
given: 'hello service is mocked'
def mockedHelloService = Mock(HelloService)
and:
swappableHelloService.swap(mockedHelloService)
when:
//hit endpoint
then:
//asserts
and: 'check interactions'
interaction {
1 * mockedHelloService.hello(postfix) >> { ""Mocked, $postfix"" as String }
}
where:
postfix | _
randomAlphabetic(10) | _
}
}
And this is TestApp (override the bean you want to mock with proxy)
class TestApp extends App {
//override hello service bean
#Bean(name = HelloService.HELLO_SERVICE_BEAN_NAME)
public ProxyFactoryBean helloService(#Qualifier("swappableHelloService") HotSwappableTargetSource targetSource) {
def proxyFactoryBean = new ProxyFactoryBean()
proxyFactoryBean.setTargetSource(targetSource)
proxyFactoryBean
}
#Bean
public HotSwappableTargetSource swappableHelloService() {
new HotSwappableTargetSource(new HelloService());
}
}
Have a look at this example https://github.com/sf-git/spock-spring

Inject test beans into main method

I'm using JavaConfig to manage and wire Spring beans into my Java app. The Java application is a main method - and basically runs as a batch job, invoked via a bash file. Is there a way that I can use a different (test) config in my main method?
public static void main(String[] args) {
final ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
// do Stuff
}
I have used the following annotations successfully before in my test classes:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { TestConfig.class })
, but this does not work for "main" applications. Short of passing in the Spring context to use as an argument, not sure what I can do here. Thanks
You should be able to use profiles in your actual config class to do what you want as well.
By setting the desired Profile you can "inject" the different beans you want.
Your ApplicationConfig might look like:
#Configuration
#Import({
JndiDataConfig.class,
TestDataConfig.class,
)
public class ApplicationConfig {
...
where TestDataConfig looks (in part) like:
#Configuration
#Profile("test")
public class TestDataConfig {
#Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}
}
and where JndiDataConfig looks like:
#Configuration
#Profile("production")
public class JndiDataConfig {
#Bean
public DataSource dataSource() throws Exception {
Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}
}

Custom spring property source does not resolve placeholders in #Value

I'm trying to build a Spring 3.1 PropertySource which reads its values from Zookeeper nodes. For connecting to Zookeeper I am using Curator from Netflix.
For that I've built a custom property source which reads the value of a property from Zookeeper and returns it. This works fine when I am resolving the property like this
ZookeeperPropertySource zkPropertySource = new ZookeeperPropertySource(zkClient);
ctx.getEnvironment().getPropertySources().addLast(zkPropertySource);
ctx.getEnvironment().getProperty("foo"); // returns 'from zookeeper'
However, when I try to instantiate a bean which has a field with an #Value annotation then this fails:
#Component
public class MyBean {
#Value("${foo}") public String foo;
}
MyBean b = ctx.getBean(MyBean.class); // fails with BeanCreationException
This problem has most likely nothing to do with Zookeeper but with the way I'm registering the property sources and creating the beans.
Any insight is highly appreciated.
Update 1:
I'm creating the app context from an XML file like this:
public class Main {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
ctx.registerShutdownHook();
}
}
The class which connects to Zookeeper is a #Component.
#Component
public class Server {
CuratorFramework zkClient;
public void connectToZookeeper() {
zkClient = ... (curator magic) ...
}
public void registerPropertySource() {
ZookeeperPropertySource zkPropertySource = new ZookeeperPropertySource(zkClient);
ctx.getEnvironment().getPropertySources().addLast(zkPropertySource);
ctx.getEnvironment().getProperty("foo"); // returns 'from zookeeper'
}
#PostConstruct
public void start() {
connectToZookeeper();
registerPropertySource();
MyBean b = ctx.getBean(MyBean.class);
}
}
Update 2
This seems to work when I'm using XML-less configuration, i.e. #Configuration, #ComponentScan and #PropertySource in combination with an AnnotationConfigApplicationContext. Why doesn't it work with a ClassPathXmlApplicationContext?
#Configuration
#ComponentScan("com.goleft")
#PropertySource({"classpath:config.properties","classpath:version.properties"})
public class AppConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
Answering to your Update 2: This does not work with your original configuration(registering a PropertySource using #PostConstruct) because the PropertySource is being registered very late, by this time your target bean has already been constructed and initialized.
Typically the injection of the placeholders happens via a BeanFactoryPostProcessor which is very early in the Spring lifecycle(beans have not been created at this stage) and if a PropertySource is registered at that stage, then placeholders should be resolved.
The best approach though is to use a ApplicationContextInitializer, get a handle on the applicationContext and to register the propertySource there:
public class CustomInitializer implements ApplicationContextInitializer<ConfigurableWebApplicationContext> {
public void initialize(ConfigurableWebApplicationContext ctx) {
ZookeeperPropertySource zkPropertySource = new ZookeeperPropertySource(zkClient);
ctx.getEnvironment().getPropertySources().addFirst(zkPropertySource);
}
}

Resources