I need one Component to be initialized before another. With #DependsOn it would look something like this:
#Component("beana")
public class BeanA{
#PostConstruct
void init(){
// do smth
}
}
#Component("beanb")
#DependsOn("beana")
public class BeanB{
#PostConstruct
void init(){
// do smth
}
}
I now have to tell BeanB that it depends on the initialization of BeanA.
My problem is that I do not want BeanB to know about BeanAs existance (e.g, when BeanB is just publishing Events in an EventBus while initializing and BeanA handles these events). I would like to use an annotation at BeanA stating it should bei initialized before BeanB. So it would something like this:
#Component("beana")
#RequiredBy("beanb")
public class BeanA{
#PostConstruct
void init(){
// do smth
}
}
#Component("beanb")
public class BeanB{
#PostConstruct
void init(){
// do smth
}
}
Is there any Spring annotation or possibility to handle it like this?
I believe there is no out of the box spring annotation for this, but you can easily make your own one.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface RequiredBy {
String[] value();
}
Then it is possible to iterate through all bean definitions and set dependsOn to that required bean.
#Component
public static class RequiredByBeanDefinitionPostProcessor implements BeanDefinitionRegistryPostProcessor {
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
for (String beanName : registry.getBeanDefinitionNames()) {
final BeanDefinition beanDefinition = registry.getBeanDefinition(beanName);
if (beanDefinition.getBeanClassName() == null) {
continue;
}
try {
final Class<?> beanClass = Class.forName(beanDefinition.getBeanClassName());
if (beanClass.isAnnotationPresent(RequiredBy.class)) {
final String[] dependantBeanNames = beanClass.getAnnotation(RequiredBy.class).value();
for (String dependantBeanName : dependantBeanNames) {
BeanDefinition dependantBeanDefinition = registry.getBeanDefinition(dependantBeanName);
dependantBeanDefinition.setDependsOn(beanName);
}
}
}
catch (ClassNotFoundException e) { throw new RuntimeException(e); }
}
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { }
}
And then use it like in you example:
#Component("beanA")
public static class BeanA {
#PostConstruct
private void init() {
System.out.println(this.getClass().getSimpleName());
}
}
#Component("beanB")
#RequiredBy({ "beanC", "beanA" })
public static class BeanB {
#PostConstruct
private void init() {
System.out.println(this.getClass().getSimpleName());
}
}
#Component("beanC")
#RequiredBy("beanA")
public static class BeanC {
#PostConstruct
private void init() {
System.out.println(this.getClass().getSimpleName());
}
}
=>
BeanB
BeanC
BeanA
You can use the #Order annotation as recommended by pvpkiran.
Your code would look something like this:
#Component("beana")
#Order(1)
public class BeanA{
#PostConstruct
void init(){
// do smth
}
}
#Component("beanb")
#Order(2)
public class BeanB{
#PostConstruct
void init(){
// do smth
}
}
Related
this is my code.
class ACondition extends SpringBootConditoin {
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
if (Config.isA()) {
return new ConditionOutcome(true, "ok");
} else {
return new ConditionOutcome(false, "error");
}
}
}
class BCondition extends SpringBootConditoin {
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
if (Config.isA()) {
return new ConditionOutcome(false, "error");
} else {
return new ConditionOutcome(true, "ok");
}
}
}
#Service
#Conditional(ACondition.class)
class APolicy implements Policy {
...
}
#Service
#Conditional(BCondition.class)
class BPolicy implements Policy {
...
}
class PolicyManager {
#Autowired
#Getter
List<Policy> policyList;
...
}
the default value of Config.isA() is true.
I want to make Config.isA() to return false. so I use Mockito.mockstatic.
#Autowired
PolicyManager manager;
#Test
public void get_B_policy() {
try(MockedStatic<Config> mocked = Mockito.mockStatic(Config.class) {
mocked.when(() -> Config.isA()).thenReturn(false);
List<Policy> policyList = manager.getPolicyList();
assertEquals(1, policyList.size()); // this is right
assertTrue(policyList.get(0) instanceof BPolicy); // this is not right
}
}
Why can't mock the online method?
by the way. If I test the BCondition class, the Config.isA() can be mocked. I can enter the branch which I want. It does not work only in conditional annotation.
Spring Context is already loaded by the time it is reaching the Test Case. Hence, Manager already has selected APolicy.
If you could move the static mock config before spring context loads, then it should match your expectations.
mocked.when(() -> Config.isA()).thenReturn(false);
One way of doing it is initialising the static Mock like below -
Junit4
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {PolicyManager.class, APolicy.class, BPolicy.class})
public class ConditionTest
{
#Autowired
PolicyManager manager;
static MockedStatic<Config> mocked = Mockito.mockStatic(Config.class);
#BeforeClass
public static void setup()
{
mocked.when(() -> Config.isA()).thenReturn(false);
}
#AfterClass
public static void clear()
{
mocked.close();
}
#Test
public void get_B_policy() {
List<Policy> policyList = manager.getPolicyList();
assertEquals(1, policyList.size()); // this is right
assertTrue(policyList.get(0) instanceof BPolicy); // should work now
}
}
Junit5
Please use Jupiter's annotations.
#Autowired
PolicyManager manager;
static MockedStatic<Config> mocked = Mockito.mockStatic(Config.class);
#BeforeAll
public static void setup() {
mocked.when(() -> Config.isA()).thenReturn(true);
}
#AfterAll
public static void clear() {
mocked.close();
}
#Test
public void get_B_policy() {
List<Policy> policyList = manager.getPolicyList();
assertEquals(1, policyList.size()); // this is right
assertTrue(policyList.get(0) instanceof BPolicy); // should work now
}
I am implementing custom annotation processing using Spring AOP. I have below code.
public class CacheDemo {
private static ApplicationContext applicationContext;
public static void main(String args[])
{
applicationContext =
new AnnotationConfigApplicationContext(ApplicationConfiguration.class);
}
}
//Application Configuration
#Configuration
#ComponentScan("Demo")
#Component
public class ApplicationConfiguration implements ApplicationContextAware {
#Autowired
TestCacheDemo testCacheDemo;
private static ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
applicationContext = ctx;
}
#Bean
public void testCacheDemoIntialize()
{
testCacheDemo.setApplicationContext(applicationContext);
testCacheDemo.test();
}
}
//CustomAnnotation processor
#Aspect
#Component
public class CustomAnnotationAspect {
#Autowired
private AbstractCacheService cacheService;
#Around("#annotation(Demo.CustomCacheable)")
public Object customCacheable(ProceedingJoinPoint joinPoint) throws Throwable { // This method is not called at all
joinPoint.proceed();
// Some other code to follow
}
// Custom Annotation
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface CustomCacheable {
}
// Annotation user
#Component
public class CacheProvider
{
#Autowired
AbstractCacheService abstractCacheService;
CacheManager<String,String> cacheManager;
#PostConstruct
void init()
{
cacheManager = CreateCache.create(s -> {return s.toUpperCase();});
abstractCacheService.setCacheManager(cacheManager);
}
#CustomCacheable
String getCacheValue(String s)
{
String str=s.toUpperCase();
return str;
}
}
For testing purpose I have created the below bean
#Component
public class TestCacheDemo extends TimerTask
{
private ApplicationContext applicationContext;
private Timer timer;
#Autowired
CacheProvider cacheProvider;
void test()
{
System.out.println("Called test");
for (String beanName : applicationContext.getBeanDefinitionNames()) {
System.out.println(beanName);
}
//CacheProvider cacheProvider = applicationContext.getBean(CacheProvider.class);
//cacheProvider.getCacheValue("Hello");
timer = new Timer();
timer.schedule(this,1000,3000);
}
void setApplicationContext(ApplicationContext ctx)
{
applicationContext=ctx;
}
#Override
public void run() {
cacheProvider.getCacheValue("Hi");
}
}
Whenever the application is started it will call the test method of the TestCacheDemo Class and sets the timer to be fired after 3 secs so that I can call the annotated method getCacheValue from inside the run method of the timer task. But when the annotated method is called the annotation processor is not invoked. Hence I am unable to do annotation processing. Please let me know where is the problem?
To use AspectJ in spring boot you must enable it.
You should add the following annotation to your application main class (CacheDemo) or application configuration class.
#EnableAspectJAutoProxy
when I use a aspect on an annotation, I cannot use AnnotationUtils.getAnnotation,
//here, I cannot not find PulsarListener
PulsarListener annotation = AnnotationUtils.getAnnotation(method, PulsarListener.class);
and when i remove #Aspect ,then it's ok.
#Target({ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface PulsarListener {
String[] topics() default {};
}
#Aspect
#Slf4j
#Component
#Order(0)
public class MDCAspect {
#Around("#annotation(PulsarListener)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
try {
String requestUUID = MDC.get("requestUUID");
if (StringUtils.isEmpty(requestUUID)) {
String uid = ObjectId.get().toHexString();
MDC.put("requestUUID", uid);
}
return joinPoint.proceed();
} finally {
MDC.clear();
}
}
}
#Component
#Slf4j
public class PulsarConsumer {
#PulsarListener(topics = "${topics}")
public void listen(Message<byte[]> receive) {
//doSomething
}
}
public class PulsarPostProcessor implements BeanPostProcessor {
#Value("${pulsar.service.url}")
private String pulsar_service_url;
#Autowired
private ApplicationContext applicationContext;
#Override
public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
Method[] methods = bean.getClass().getDeclaredMethods();
for (Method method : methods) {
//here , i canot not found PulsarListener
//here is the problem
PulsarListener annotation = AnnotationUtils.getAnnotation(method, PulsarListener.class);
if (annotation != null) {
if (log.isDebugEnabled()) {
log.debug("bean :{},method:{}", beanName, method.getName());
}
}
}
}
I have created a bean with method that I want to test. Unfortunately it's a bean with a PostConstruct annotation in it. I don't want to call the PostConstruct method.
How can I do this?
I've tried 2 different ways (as shown in the example below) but none working; init() still gets called.
Can someone please give me a detailed example of how to do this?
DirBean.java
#Singleton
#Startup
public class DirBean implements TimedObject {
#Resource
protected TimerService timer;
#PostConstruct
public void init() {
// some code I don't want to run
}
public void methodIwantToTest() {
// test this code
}
}
MyBeanTest.java
public class MyBeanTest {
#Tested
DirBean tested;
#Before
public void recordExpectationsForPostConstruct() {
new Expectations(tested) {
{
invoke(tested, "init");
}
};
}
#Test
public void testMyDirBeanCall() {
new MockUp<DirBean>() {
#Mock
void init() {
}
};
tested.methodIwantToTest();
}
}
MyBeanTest2.java (WORKS)
public class MyBeanTest2 {
#Tested
DirBean tested;
#Before
public void recordExpectationsForPostConstruct() {
new MockUp<DirBean>() {
#Mock
void init() {}
};
}
#Test
public void testMyDirBeanCall() {
tested.methodIwantToTest();
}
}
MyBeanTest3.java (WORKS)
public class MyBeanTest3 {
DirBean dirBean = null;
#Mock
SubBean1 mockSubBean1;
#Before
public void setupDependenciesManually() {
dirBean = new DirBean();
dirBean.subBean1 = mockSubBean1;
}
#Test
public void testMyDirBeanCall() {
dirBean.methodIwantToTest();
}
}
MyBeanTest4.java (FAILS with NullPointerException on invoke())
public class MyBeanTest4 {
#Tested
DirBean tested;
#Before
public void recordExpectationsForCallsInsideInit() {
new Expectations(tested) {
{
Deencapsulation.invoke(tested, "methodCalledfromInit", anyInt);
}
};
}
#Test
public void testMyDirBeanCall() {
tested.methodIwantToTest();
}
}
Move definition of MockUp type to #Before method:
public class MyBeanTest {
#Tested
DirBean tested;
#Before
public void recordExpectationsForPostConstruct() {
new MockUp<DirBean>() {
#Mock
void init() {
}
};
}
#Test
public void testMyDirBeanCall() {
tested.methodIwantToTest();
}
}
I have this service bean with a sync method calling the internal async method:
#Service
public class MyService {
public worker() {
asyncJob();
}
#Async
void asyncJob() {
...
}
}
The trouble is that the asyncJob is not really called in async way.
I found that this doesn't work because an internal call skips the AOP proxy.
So I try to self-refer the bean:
#Service
public class MyService {
MyService mySelf;
#Autowired
ApplicationContext cnt;
#PostConstruct
public void init() {
mySelf=(MyService)cnt.getBean("myService");
}
public void worker() {
mySelf.asyncJob();
}
#Async
void asyncJob() {
...
}
}
It fails. Again no async call.
So I tried to divide it in two beans:
#Service
public class MyService {
#Autowired
MyAsyncService myAsyncService;
public void worker() {
myAsyncService.asyncJob();
}
}
#Service
public class MyAsyncService {
#Async
void asyncJob() {
...
}
}
Fails again.
The only working way is to call it from a Controller Bean:
#Controller
public class MyController {
#Autowired
MyAsyncService myAsyncService;
#RequestMapping("/test")
public void worker() {
myAsyncService.asyncJob();
}
}
#Service
public class MyAsyncService {
#Async
public void asyncJob() {
...
}
}
But in this case it is a service job. Why I cannot call it from a service?
Found a really nice way to solve this (with java8) in the case where you have a lot of various things you want to both sync and async. Instead of creating a separate XXXAsync service for each 'synchronous' service, create a generic async service wrapper:
#Service
public class AsyncService {
#Async
public void run(final Runnable runnable) {
runnable.run();
}
}
and then use it as such:
#Service
public class MyService {
#Autowired
private AsyncService asyncService;
public void refreshAsync() {
asyncService.run(this::refresh);
}
public void refresh() {
// my business logic
}
public void refreshWithParamsAsync(String param1, Integer param2) {
asyncService.run(() -> this.refreshWithParams(param1, param2));
}
public void refreshWithParams(String param1, Integer param2) {
// my business logic with parameters
}
}
I solved the third method (divide it in two beans) changing the async method's access modifier to public:
#Service
public class MyService {
#Autowired
MyAsyncService myAsyncService;
public void worker() {
myAsyncService.asyncJob();
}
}
#Service
public class MyAsyncService {
#Async
public void asyncJob() { // switched to public
...
}
}
In my case, it was easier to remove the #Async annotation and use the taskExecutor directly to submit my task:
Before
#Async("taskExecutor")
private Future<U> executerEnAsync(
final T pInput) {
final U resultat = this.appelerBS(pInput);
return new AsyncResult<U>(resultat);
}
After
#Autowired
private AsyncTaskExecutor taskExecutor;
private Future<U> executerEnAsync(
final T pInput) {
final Future<U> future = taskExecutor.submit(new Callable<U>() {
#Override
public U call() {
final U resultat = appelerBS(pInput);
return resultat;
}
});
return future;
}