Spring /#Beans annotation - spring

I am reading spring doc. and I am wondering what difference is between these two examples. Do I need to create Bar #bean ?
public class AppConfig {
#Bean
public Foo foo() {
return new Foo(bar());
}
#Bean
public Bar bar() {
return new Bar();
}
}
VS
public class AppConfig {
#Bean
public Foo foo() {
return new Foo(bar());
}
public Bar bar() {
return new Bar();
}
}

In the first one, Bar is a Spring bean, whereas in the second one, it's not.
So, if Bar must be autowired with other Spring beans, or if it has Spring annotations (like Transactional, etc.), the second example won't work as expected: the Bar instance is a simple POJO, that Spring is not aware of.

Related

Spring Retry: How to make all methods of a #Bean retryable?

I would like to create a #Bean of a third party service like Keycloak (or any other) which may or may not be reachable at any given time. This object should retry all methods of the resulting Keycloak bean.
I have tried the following:
#Configuration
#EnableRetry
class KeycloakBeanProvider() {
#Bean
#Retryable
fun keycloak(oauth2ClientRegistration: ClientRegistration): Keycloak {
return KeycloakBuilder.builder()
.serverUrl(serverUrl)
.realm(oauth2ClientRegistration.clientName)
.grantType(OAuth2Constants.CLIENT_CREDENTIALS)
.clientId(oauth2ClientRegistration.clientId)
.clientSecret(oauth2ClientRegistration.clientSecret)
.build()
}
}
But this way only the bean creation will be retried, not actual method calls on the bean. I know #Retryable can be used on class level but I don't own the Keycloak class so I can't add it there.
How can I make the methods of the resulting Keycloak bean retryable?
You have to annotate the Keycloak with #Retryable.
#SpringBootApplication
#EnableRetry
public class So70593939Application {
public static void main(String[] args) {
SpringApplication.run(So70593939Application.class, args);
}
#Bean
ApplicationRunner runner(Foo foo) {
return args -> {
foo.foo("called foo");
foo.bar("called bar");
};
}
}
#Component
#Retryable
class Foo {
public void foo(String in) {
System.out.println("foo");
throw new RuntimeException("test");
}
public void bar(String in) {
System.out.println("bar");
throw new RuntimeException("test");
}
#Recover
public void recover(Exception ex, String in) {
System.out.println(ex.getMessage() + ":" + in);
}
}
foo
foo
foo
test:called foo
bar
bar
bar
test:called bar
If you can't annotate the class (e.g. because it's from another project), you need to use a RetryTemplate to call its methods instead of using annotation-based retry.
You can manually instrument your class. Check documentation.
#Bean
public Keycloak myService() {
ProxyFactory factory = new ProxyFactory(RepeatOperations.class.getClassLoader());
factory.setInterfaces(Keycloak.class);
factory.setTarget(createKeycloak());
Keycloak keycloak = (Keycloak) factory.getProxy();
JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
pointcut.setPatterns(".*.*");
RetryOperationsInterceptor interceptor = new RetryOperationsInterceptor();
((Advised) keycloak).addAdvisor(new DefaultPointcutAdvisor(pointcut, interceptor));
return keycloak;
}

Spring #Bean(name ="name") vs #Bean #Qualifier("name")

Is there any differences between the following 2 bean declaration?
#Bean(name = "bean1")
public A getA() {
return new A();
}
#Bean
#Qualifier("bean1")
public A getA() {
return new A();
}
Both can be autowired using #Qualifier
#Autowire
public void test(#Qualifier("bean1") A a) {
...
}
With value() you don't have to specify attribute name, like #Qualifier("bean1"). Attribute name() reference the same value as value() because of custom annotation #AliasFor(..) from Spring, therefore they are just different names with the same behavior.
You can use
#Autowire
public void test(A bean1) {
...
}
if you use
#Bean(name = "bean1")
not with
#Bean
#Qualifier("bean1")
The first part is fundamentally the same, the second part is what you basically need when two or more beans of same type exist. The first part is just the preference one might have.
You can have multiple beans with same Qualifier name but bean name in spring application context needs to be unique
#Bean
#Qualifier("qualifier1")
Foo foo()
{
return new Foo();
}
#Bean
#Qualifier("qualifier1")
Bar bar()
{
return new Bar();
}
The above code is acceptable. but in the case of bean, it is not.

Spring Configuration - Inject Mock Beans

I am using Spring, Junit and Mockito. I need to override beans defined in the main spring configuration using another mockito test configuration (injecting mock beans only as needed). Nested beans have been #Autowired in the application.
Update:
Based on alfcope's answer below, it is important to add the name attribute so that spring can allow the primary bean (mock) to override the original one. Otherwise you get this exception:
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
The info message in the spring log shows:
Skipping bean definition for [BeanMethod:name=bar,declaringClass=test.package.MockitoTestConfiguration]: a definition for bean 'bar' already exists. This top-level bean definition is considered as an override.
Example:
I have a simplified example below that works. Here, Bar is the nested inside Foo, and I need to mock Bar for testing:
#Component
public class Foo
{
#Autowired
private Bar bar;
public String getResponseFromBar(String request)
{
String response = bar.someMethod(String request);
//do something else with this reponse
return response;
}
}
#Component
public class Bar {
public String someMethod(String request) {
String response = null;
//do something
return response;
}
}
Now for testing, let's say I want to inject a mockbar instead of the real bar. How can I achieve this in my test class below?
#Profile("test")
#Configuration
public class MockitoTestConfiguration {
//adding the name attribute is important.
#Bean(name="mockBar")
#Primary
public Bar bar() {
logger.debug("injecting mock bar");
return Mockito.mock(Bar.class);
}
}
Actual test case:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath*:test-context.xml")
public class FooTest {
#Autowired
Foo foo;
#Autowired
Bar mockBar; //need this to set up the mock response in the test case.
#Test
public void testAMethodInFoo_WithBarInjectedByMockito() {
//set up the mockBar response
Mockito.when(mockBar.someMethod("1")).thenReturn("1-response");
String response = foo.getResponseFromBar();
assertEquals("1-response", response);
}
}
Based on the ConfigurationClassBeanDefinitionReader code I guess you are using xml configuration to define your main bean. If so, just add a name when creating your mockito bean.
#Bean(name="mockbar")
#Primary
public Bar bar() {
logger.debug("injecting mock bar");
return Mockito.mock(Bar.class);
}
This way Spring will allow you to have both beans, and as you are using #Primary it will be the one used by your tests.
Spring overriding primary bean with non-primary bean
ConfigurationClassBeanDefinitionReader Code
Alternatively, if you use Mockito, you can do this and completely do away with the extra MockitoTestConfiguration class and named primary mock beans in the test profile. Just simply do this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath*:test-context.xml")
public class FooTest {
#Autowired
#InjectMocks
Foo foo;
#Mock
Bar mockBar; //need this to set up the mock response in the test case.
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#Test
public void testAMethodInFoo_WithBarInjectedByMockito() {
//set up the mockBar response
Mockito.when(mockBar.someMethod("1")).thenReturn("1-response");
String response = foo.getResponseFromBar();
assertEquals("1-response", response);
}
}

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

Initializing Spring in not-web annotation-configured application

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
}

Resources