How to replace a ConditionalOnMissingBean bean? - java

I use a library which contains
public class MyFilter extends FilterParent { ... }
and
#Configuration
public class AutoConf
{
#Bean
#ConditionalOnMissingBean(FilterParent.class)
public MyFilter myFilter ()
{
return new MyFilter (); // <------- I want to replace this
}
}
which loads correctly. I cannot change this library but I want to load a different filter bean
I tried
#Component
public class MyNewFilter extends MyFilter { ... }
and modified the application class
#SpringBootApplication
public class Application
{
public static void main (String[] args)
{
SpringApplication.run (Application.class, args);
}
#Bean
public CommandLineRunner commandLineRunner (ApplicationContext ctx)
{
return args -> {
// startup code
};
}
#Bean
public MyFilter myFilter ()
{
return new MyNewFilter (); // <----------- THIS IS NEW
}
}
But I still get MyFilter loaded instead of a MyNewFilter.
How can I load a MyNewFilter bean instead of the MyFilter bean loaded by AutoConf, without changing AutoConf?

Just try this.
#SpringBootApplication
public class Application
{
public static void main (String[] args)
{
SpringApplication.run (Application.class, args);
}
#Bean
public CommandLineRunner commandLineRunner (ApplicationContext ctx)
{
return args -> {
// startup code
};
}
#Bean
public MyNewFilter myFilter ()
{
return new MyNewFilter (); // <----------- THIS IS NEW
}
}

#chrylis-cautiouslyoptimistic- was correct, the new component is being loaded. The application is misbehaving for unrelated reasons.
I can't delete the post so I'll accept this in 2 days.

Related

Unable to intercept the advice method in Spring AOP

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

Access command line arguments in springboot in thread

I run my spring boot application with
mvn spring-boot:run -Dspring-boot.run.arguments="test1, test2, test3"
I need this 3 arguments in my runnable. How can i access them in my Compressor runnable class? It prints null.
#Configuration
public class MultiThreadConfig {
#Bean
public TaskExecutor taskExecutor() {
return new SimpleAsyncTaskExecutor(); // Or use another one of your liking
}
#Bean
public CommandLineRunner schedulingRunner(TaskExecutor executor) {
return new CommandLineRunner() {
public void run(String[] args) throws Exception {
executor.execute(new Compressor(args));
}
};
}
}
public class Compressor implements Runnable {
#Autowired
ApplicationArguments appArgs;
String[] args;
// Constructor
public Compressor(String[] args) {
//Initialization of atributes
}
#Override
public void run() {
System.out.println("COMPRESSOR YO");
System.out.println(Arrays.toString(args));
System.out.println(appArgs);
}
}
As Jesper pointed out you are creating the Compressor object by yourself using new keyword so the ApplicationArguments will not be autowired as this instance of Compressor is not managed by Spring. The solution is to create a bean for Compressor object and let it be managed by Spring. For example :
#Configuration
public class MultiThreadConfig {
#Bean
public TaskExecutor taskExecutor() {
return new SimpleAsyncTaskExecutor(); // Or use another one of your liking
}
#Bean
public Compressor compressor(ApplicationArguments applicationArguments) {
return Compressor(applicationArguments);
}
#Bean
public CommandLineRunner schedulingRunner(TaskExecutor executor, Compressor compressor) {
return new CommandLineRunner() {
public void run(String[] args) throws Exception {
executor.execute(compressor);
}
};
}
}
and
public class Compressor implements Runnable {
private final ApplicationArguments appArgs;
private final String[] args;
#Autowired
public Compressor(ApplicationArguments applicationArguments) {
appArgs = applicationArguments;
args = applicationArguments.getSourceArgs();
}
#Override
public void run() {
System.out.println("COMPRESSOR YO");
System.out.println(Arrays.toString(args));
}
}
Note here that I am using constructor injection instead of field injections which is typically better option.
If you do not want to crate a bean in configuration you could also mark Compressor with Spring stereotype :
#Component
public class Compressor implements Runnable {
private final ApplicationArguments appArgs;
private final String[] args;
public Compressor(ApplicationArguments applicationArguments) {
appArgs = applicationArguments;
args = applicationArguments.getSourceArgs();
}
//...
}
and use instance of it in your configuration like shown above :
#Bean
public CommandLineRunner schedulingRunner(TaskExecutor executor, Compressor compressor) {
//...
}

Test Spring/SpringBoot without Application

I want to write integration tests that will have to use Spring Framework and a custom JPA provider. Straightforward answer as I thought would be to create a test class and annotate it as follows:
#RunWith(SpringRunner.class)
#SpringBootTest
public class Test { ...
Hoping for the all of the default auto-configuration required to happen on its own. But it doesn't the error is:
java.lang.IllegalStateException: Unable to find a #SpringBootConfiguration, you need to use #ContextConfiguration or #SpringBootTest(classes=...) with your test
Can I avoid creating the:
#SpringBootApplication
public class TestApp { ...
and only use src/test/java folder and provide configuration with "#SpringBootTest(classes=...)"? What kind of configuration class do I need then?
I just got the problem let me go back to begining;
First add a configuration class ApplicationContainer
public final class ApplicationContainer {
private static volatile ApplicationContainer singleton;
private ApplicationContext context;
private ApplicationContainer()
{
}
public static ApplicationContainer getInstance()
{
if(ApplicationContainer.singleton == null)
{
synchronized(ApplicationContainer.class)
{
if(ApplicationContainer.singleton == null)
{
ApplicationContainer.singleton = new ApplicationContainer();
}
}
}
return ApplicationContainer.singleton;
}
public void setApplicationContext(ApplicationContext context)
{
this.context = context;
}
public <T> T getBean(Class<T> requiredType)
{
if(this.context == null)
{
throw new IllegalStateException("ApplicationContainer is not started");
}
return this.context.getBean(requiredType);
}
public void start()
{
if(this.context == null)
{
this.context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
}
}
public void stop()
{
if(this.context == null)
{
return;
}
if(this.context instanceof AnnotationConfigApplicationContext)
{
((AnnotationConfigApplicationContext)this.context).close();
}
this.context = null;
}
#Configuration
#ComponentScan("com.domain.package")
public static class ApplicationConfig
{
}}
And change your test class's like that:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {ApplicationContainer.ApplicationConfig.class})
public class YourTestIT {
#Autowired
private ApplicationContext applicationContext;
#Before
public void up() {
ApplicationContainer.getInstance().setApplicationContext(this.applicationContext);
}
#After
public void down() {
ApplicationContainer.getInstance().stop();
}
//test cases
}
then you can #Autowired your repository classes and use directly also you should add IT extension your test class name.

How use #Autowired with Annotation config?

i create simple spring project and i need to use annotation #Autowired but when i run project, i get exception NullPointerException.
This is my classes:
Main.java
public class Main {
#Autowired
private static InjectClass injectClass;
public static void setInjectClass(InjectClass injectClass) {
Main.injectClass = injectClass;
}
public static void main(String[] args) {
injectClass.hello(); //NullPointerException
}
}
ConfigurationBean
#Configuration
public class ConfigurationBean {
#Bean
public InjectClass injectClass(){
return new InjectClass();
}
}
InjectClass
public class InjectClass {
public void hello(){
System.out.println("Autowired success!");
}
}
You need to initiate application contex before using any bean.
You can do it by writing following code in starting of your main method.
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(
ConfigurationBean.class);

How to use #EnableTransactionManagement in combination with a StaticMethodMatcherPointcutAdvisor

Given the following service:
public interface MyService {
void method();
}
And it's implementation:
#Service
public class MyServiceImpl implements MyService {
#Transactional
#CustomAnnotation
#Override
public void method() {
...
}
}
I would like to use a StaticMethodMatcherPointcutAdvisor in the following manner:
public class MyPointcutAdvisor extends StaticMethodMatcherPointcutAdvisor {
...
#Override
public boolean matches(Method method, Class targetClass) {
Method m = method;
if(annotationPresent(method)) {
return true;
}
Class<?> userClass = ClassUtils.getUserClass(targetClass);
Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);
specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
if(annotationPresent(specificMethod )) {
return true;
}
return false;
}
...
}
The problem is that Spring uses an InfrastructureAdvisorAutoProxyCreator to create a Proxy of that class, whereas the DefaultAdvisorAutoProxyCreator would create the proxy for the MyPointcutAdvisor, but the MyPointcutAdvisor is only given the proxy as targetClass parameter. Thus, the PointcutAdvisor cannot find the annotation and therefore does not match.
For completion this is my Configuration-class:
#Configuration
#EnableTransactionManagement
public class MyConfiguration {
#Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
return new DefaultAdvisorAutoProxyCreator();
}
#Bean
public MyPointcutAdvisor myPointcutAdvisor() {
return new MyPointcutAdvisor();
}
...
}
My question is: Is there a way to use #EnableTransactionManagement in combination with a StaticMethodMatcherPointcutAdvisor ?
Workarounds:
Put #CustomAnnotation into the service interface: I want to have clean interfaces.
Add #Role(BeanDefinition.ROLE_INFRASTRUCTURE) to MyPointCutAdvisor bean configuration, thus, the InfrastructureAdvisorAutoProxyCreator will create the proxy. This seems like the wrong way, since this bean is not infrastructure
Copy the beans from ProxyTransactionManagementConfiguration, remove #EnableTransactionManagement and remove #Role(BeanDefinition.ROLE_INFRASTRUCTURE), thus the DefaultAdvisorAutoProxyCreator will create the proxy, which is my current workaround and results in the following configuration:
#Configuration
public class MyWorkaroundConfiguration {
#Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
return new DefaultAdvisorAutoProxyCreator();
}
#Bean
public MyPointcutAdvisor myPointcutAdvisor() {
return new MyPointcutAdvisor();
}
#Bean
public TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource();
}
#Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
TransactionInterceptor transactionInterceptor) {
BeanFactoryTransactionAttributeSourceAdvisor advisor =
new BeanFactoryTransactionAttributeSourceAdvisor();
advisor.setTransactionAttributeSource(transactionAttributeSource());
advisor.setAdvice(transactionInterceptor);
return advisor;
}
#Bean
public TransactionInterceptor transactionInterceptor(
PlatformTransactionManager transactionManager) {
TransactionInterceptor interceptor = new TransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource());
interceptor.setTransactionManager(transactionManager);
return interceptor;
}
...
}
Using #EnableAspectJAutoProxy instead of the DefaultAutoProxyCreator works for me.
#Configuration
#EnableAspectJAutoProxy
#EnableTransactionManagement
public class MyConfiguration {
}
This also allows using #Aspect like M. Deinum suggested.

Categories