I need to modify some spring bean in runtime by webservice. Im using ApplicationContext .
ConfigurableApplicationContext configContext = (ConfigurableApplicationContext)applicationContext;
ConfigurableListableBeanFactory registery = configContext.getBeanFactory();
registery.registerSingleton("XXX", new MyNewBeanDefintion());
in my #Configuration class there is
#Bean
public ParentClass campaignSelection(){
if(type.equals("X")) {
return new X();
}
else if(type.equals("Y")){
return new Y();
}
return null;
}
with simply
public interface ParentClass {
public Item selectOneItem();
}
public class X implements ParentClass {
#Override
public Item selectOneItem() {
// return item
}
}
public class Y implements ParentClass {
#Override
public Item selectOneItem() {
// return item
}
}
and i need i need the bean to switch between X , Y in runtime
To replace injected instance of campaignSelection bean you can use marker interface, e.g.
public interface CampaignChangeAware {
void onCampaignChange(ParentClass newCampaign);
}
Make other classes that have to be updated implement this interface. Then you will be able to update beans using code
Map<String, CampaignChangeAware> beansToUpdate = context.getBeansOfType(CampaignChangeAware.class);
for (CampaignChangeAware bean : beansToUpdate.values()) {
bean.onCampaignChange(newCampaign);
}
But it doesn't affect already instantiated beans with scope other that singleton as spring doesn't manage such beans.
Related
I have this scenario
team A is implementing an interface Vehicle as ClassAVehicle
team B is implementing a dashboard service in which it uses vehicle implementation
Now team A have new implementation of Vehicle as ClassBVehicle. And team B wants to use it. One way I know is that use of #Qualifier annotation. But for this I require to change team B's code.
So do I have tight coupling here? Can I have some XML based configuration so that team B's code resolves new ClassBVehicle instance automatically?
interface Vehicle{
int getNoTyre();
}
class ClassAVehicle{
int getNoTyre(){
return 1;
}
}
class ClassBVehicle{
int getNoTyre(){
return 2;
}
}
class Dashboard{
// Here everything is fine until classBVehicle is not there
// Now I want to use new classBVehicle.
// One way I see is that using #Qualifier but will it not be tight coupling?
#Autowired
Vehicle oldAInstance;
}
If you use xml to define bean, your way is good to decouple. Another way is that you can use ApplicationContext to get bean dynamically in annotation program. There are two way to getBean with beanName or beanClass. The below is sample:
#Service
public class BService {
private Vehicle vo;
#Autowired
ApplicationContext context;
public void getVehicle(String beanName){
this.vo = (Vehicle) context.getBean(beanName);
}
public void getVehicle(Class beanClz){
this.vo = (Vehicle) context.getBean(beanClz);
}
public void print(){
System.out.println("---class is "+vo.getClass());
}
}
public interface Vehicle {
}
#Component
public class OneVehicle implements Vehicle{
}
#Component
public class TwoVehicle implements Vehicle{
}
#SpringBootApplication
public class SpringDependenciesExampleApplication implements ApplicationRunner {
#Autowired
BService bService;
public static void main(String[] args) {
SpringApplication.run(SpringDependenciesExampleApplication.class, args);
}
#Override
public void run(ApplicationArguments applicationArguments) throws Exception {
bService.getVehicle("oneVehicle");
bService.print();
}
}
// output is ---class is class OneVehicle
I have two classes from custom library, that i can't change. Bass class have only constructor with custom param, that's not a bean. I want to pass param via child constructor, but i have no idea how to do that, so please help)
I tried this, but doesn't work. Idea underline param in Child constructor.
#Bean
public ChildClass childClass() {
return new ChildClass(new CustomParam(5));
}
Base class- can't use #Component, that class from library
public abstract class BaseClass {
private CustomParam customParam;
protected BaseClass(CustomParam customParam) {
this.customParam = customParam;
}
public Integer getCustomParam() {
return customParam.getParamValue();
}
}
Child class. My own extension
#Component
public class ChildClass extends BaseClass {
//idea underline customParam "could not autowire"
public ChildClass(CustomParam customParam) {
super(customParam);
}
}
Param class- can't use #Component, that class from library
public class CustomParam {
private Integer paramValue;
public CustomParam(Integer paramValue) {
this.paramValue = paramValue;
}
public Integer getParamValue() {
return paramValue;
}
public void setParamValue(Integer paramValue) {
this.paramValue = paramValue;
}
}
CustomParam need not to be annotated with #Component annotation, still you can declare it as bean using#Bean annotation
Config class
#Bean
public ChildClass childClass() {
return new ChildClass(customParam());
}
#Bean
public CustomParam customParam() {
return new CustomParam(5);
}
This should work. If you instantiate your bean like this you don't need the #Component annotation on your ChildClass. Make sure your bean definition is in a configurationclass (#Configuration) and your config is part of the component scan.
#Configuration
public class Config {
#Bean
public BaseClass childClass() {
return new ChildClass(new CustomParam(5));
}
}
i have a problem with Singleton with the Dagger2 library for Android.
My problem is im using the #Singleton but getting two different objects =[
i Have 2 Components and 2 Modules:
DispatcherComponent which includes the DispatcherModule class that provides a Dispatcher.
the Dispatcher needs instance UserStore which is provided by StoreModule.
#Singleton
#Component(modules={AppModule.class, StoreModule.class, DispatcherModule.class})
public interface DispatcherComponent {
void inject(SomeClass someClass);
}
and the DispatcherModule.class is as follows
#Module
public class DispatcherModule {
#Provides
#Singleton
public Dispatcher provideDispatcher(UserStore store) {
Log.d("DEBUG", "userStore : " + store.toString());
return new Dispatcher(store);
}
and now the StoreComponent.class
#Singleton
#Component(modules={AppModule.class, StoreModule.class})
public interface StoreComponent {
void inject(SomeOtherClass otherClass);
}
and StoreModule.class
#Module
public class StoreModule {
#Provides
#Singleton
public UserStore provideUserStore() {
return new UserStore();
}
now when im trying to inject UserStore im getting two different objects =/
public class SomeOtherClass extends Acitivity {
#Inject UserStore mStore;
public void onCreate(Bundle savedInstance) {
StoreComponent comp = ((MyApp) getApplication).getStoreComponent();
comp.inject(this);
Log.d("DEBUG", "userStore2 :" + mStore.toString());
}
}
public class SomeClass {
#Inject Dispatcher mDispatcher;
public SomeClass (Application application) {
((MyApp) application).getDispatcherComponent().inject(this);
}
and last, this is how i create the components:
public class MyApp extends Application {
public void onCreate() {
StoreModule store = new StoreModule();
StoreComponent storeComponent = DaggerStoreComponent.builder().appModule(new AppModule(this)).storeModule(storeModule).build();
DispatcherComponent disComp = DaggerDispatcherComponent.builder().appModule(new AppModule(this)).storeModule(storeModule).dispatcherModule(new DispatcherModule()).build();
}
now, when im running the Application, i get 2 different objects ! can someone help me ? how should i fix it? i dont want to have a god component..
THanks!
Note that #Singleton dost not make the object to be singleton actually, instead, it just use the DoubleCheck class to cache which is hold by the component impl generated by dagger. Check DaggerDispatcherComponent for more detail.
For this case, you can change StoreModule like below:
#Module
public class StoreModule {
private UserStore mStore;
#Provides
#Singleton
public UserStore provideUserStore() {
return getStore();
}
private UserStore getStore() {
//lazy initialized and make sure thread safe
if (null == mStore) {
#synchronized(StoreModule.class) {
if (null == mStore) {
mStore = new UserStore();
}
}
}
return mStore;
}
}
I have one interface and two implementations for this interface
Interface definition:
public interface DoSomething {}
Two implementations:
public ImplementationOne implements DoSomething{}
public ImplementationTwo implements DoSomething{}
Then inside another class, I want to get a different implementaion (either ImplementationOne or ImplementationTwo) based on the condition, how can I do that using Spring?
Something like..
Public ServiceManager {
Private DoSomething doSomething = null;
Public void do() {
If (condition1) {
doSomething = new ImplementationOne();
} else {
doSomething = new ImplementationTwo();
}
}
}
You should definitely auto wire ApplicationContext type using #Autowire annotation. Then if you did it like this:
#Autowire
ApplicationContext context
Then you should get your desired bean like this:
context.getBean(yourDesiredType.class)
Like that you can get any bean you want to be placed under any matching type according to your example.
Another option to consider is have a configuration bean - for example -
#Configuration
public class EntityRepositoryConfiguration {
private Map<Entity, EntityRepository> entityEntityRepositoryMap = new HashMap<>();
protected EntityRepositoryConfiguration() {
entityEntityRepositoryMap.put(Entity.Book, new BookRepository());
}
#Bean
public EntityRepository getByEntityType(Entity entity) {
return entityEntityRepositoryMap.get(entity);
}
}
And then inject the configuration bean to your other beans and use the getEntityType method (for example) to get beans injected.
#Bean
public TimedRepository timedRepository(RealRepository repo) {
return new TimedRepository(repo, timer); // Adds some metrics
}
#Bean
public RealRepository realRepository(DataSource ds) {
return new RealRepository(ds); // The real jdbc implementation
}
In the old XML days I would configure the real repository as an anonymous inner bean. Is it possible to do something similar with the new Java configuration approach? Instantiating the real repository inside the timedRepository factory method is not an option because I want Spring to pick up on annotations on RealRepository.
The motivation is to avoid any other beans to get hold of the real repository implementation. I should also mention that both beans implement a Repository interface that'll be used by any beans depending on the repository (they should not have to know about TimedRepository or RealRepository.
I dont think theres an equivalent to inner or local beans when using java based configuration. I'd probably try to create the RealRepository in the TimedRepositories bean method as well by asking for all dependencies in the method signature. But if you really need to have spring to take care of the RealRepository dependencies you need to use the bean factory.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader=AnnotationConfigContextLoader.class)
public class ConfigTest {
#Autowired TimedRepository timedRepo;
#Test
public void testRepository() {
Assert.assertNotNull(timedRepo);
}
#Configuration
static class TimedRepositoryConfiguration {
#Autowired
private AutowireCapableBeanFactory beanFactory;
#Bean
public TimedRepository timedRepository() {
RealRepository realRepository = (RealRepository) beanFactory.createBean(RealRepository.class, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, true);
return new TimedRepository(realRepository);
}
public RealRepository realRepository() {
return new RealRepository();
}
}
static class RealRepository {
}
static class TimedRepository {
private RealRepository realRepo;
public TimedRepository(RealRepository r) {
this.realRepo = r;
}
}
}
You can just instantiate the beans manually:
public class BeanThatDependsOnRealRepository() {
private final Repository repository;
#Inject
public BeanThatDependsOnRealRepository(DataSource dataSource) {
this.repository = new RealRepository(dataSource);
}
}
This is essentially what an anonymous inner bean does in XML. You've just explicitly constructed it and obtained its dependencies from Spring in the constructor of the enclosing class.
Late answer, but this is possible in Spring Core 4+ (and possibly Spring Core 3) with some trickery.
While standard Spring semantics do not support inner bean creation using JavaConfig, the internal functionality around inner beans can be taken advantage of to produce the same results.
Inner beans are produced during property value resolution by the BeanDefinitionValueResolver (see BeanDefinitionValueResolver#resolveValueIfNecessary). The concept of "inner beans" within Spring is primarily enclosed within this value resolver (which is the only producer of inner beans) and within bean factories under the term "contained beans" (from parent class DefaultSingletonBeanRegistry).
We can trick Spring into producing additional inner beans by defining a property as a BeanDefinition, according to the resolution strategy presented in BeanDefinitionValueResolver:
#Configuration
public class MyConfiguration {
private static Logger logger = LoggerFactory.getLogger(MyConfiguration.class);
private RealRepository realRepository;
private Timer timer;
public MyConfiguration(#SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") RealRepository realRepository, Timer timer) {
this.realRepository = realRepository;
this.timer = timer;
logger.info("Constructed MyConfiguration {}", this);
}
#Bean
public TimedRepository timedRepository() {
TimedRepository timedRepository = new TimedRepository(this.realRepository, this.timer);
logger.info("Created timed repo: {}", timedRepository);
return timedRepository;
}
public RealRepository realRepository(DataSource dataSource) {
RealRepository realRepository = new RealRepository(dataSource);
logger.info("Created real repo: {}", realRepository);
return realRepository;
}
#Override
public String toString() {
return "MyConfiguration{" +
"realRepository=" + realRepository +
", timer=" + timer +
'}';
}
}
#Component
public class InnerBeanInjectionBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor, PriorityOrdered {
#Override
public int getOrder() {
// Preempt execution of org.springframework.context.annotation.ConfigurationClassPostProcessor
return 0;
}
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
String[] beanDefinitionNameList = ((ConfigurableListableBeanFactory) registry).getBeanNamesForType(MyConfiguration.class, true, false);
assert beanDefinitionNameList.length == 1;
BeanDefinition configurationBeanDefinition = registry.getBeanDefinition(beanDefinitionNameList[0]);
BeanDefinition realRepositoryBeanDefinition = BeanDefinitionBuilder.genericBeanDefinition(MyConfiguration.class)
.setScope(BeanDefinition.SCOPE_SINGLETON)
.setFactoryMethod("realRepository")
.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR)
.getBeanDefinition();
configurationBeanDefinition.getConstructorArgumentValues()
.addGenericArgumentValue(realRepositoryBeanDefinition);
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Do nothing
}
}
The obvious issue with this solution is that it requires manual processing through a BeanDefinitionRegistryPostProcessor, which is a lot of work for a small gain here. What I would suggest instead is the following:
Create a custom annotation (e.g., #InnerBean)
Attach this annotation to methods in #Configuration classes and candidate component classes where desired
Adapt the BeanDefinitionRegistryPostProcessor to scan classes for #InnerBean-annotated static methods (component-classes should be scanned in #postProcessBeanFactory and configuration classes in #postProcessBeanDefinitionRegistry)
Attach the bean definition to the containing bean definition's autowired constructor fields (or setter fields if that is your convention)
The following is an example:
#Target(ElementType.METHOD)
public #interface InnerBean {
}
#Configuration
public class MyConfiguration {
private static Logger logger = LoggerFactory.getLogger(MyConfiguration.class);
private RealRepository realRepository;
private Timer timer;
public MyConfiguration(#SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") RealRepository realRepository, Timer timer) {
this.realRepository = realRepository;
this.timer = timer;
logger.info("Constructed MyConfiguration {}", this);
}
#Bean
public TimedRepository timedRepository() {
TimedRepository timedRepository = new TimedRepository(this.realRepository, this.timer);
logger.info("Created timed repo: {}", timedRepository);
return timedRepository;
}
#InnerBean
public static RealRepository realRepository(DataSource dataSource) {
RealRepository realRepository = new RealRepository(dataSource);
logger.info("Created real repo: {}", realRepository);
return realRepository;
}
#Override
public String toString() {
return "MyConfiguration{" +
"realRepository=" + realRepository +
", timer=" + timer +
'}';
}
}
#Component
public class InnerBeanInjectionBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor, PriorityOrdered {
private static Logger logger = LoggerFactory.getLogger(InnerBeanInjectionBeanFactoryPostProcessor.class);
private Set<BeanDefinition> processedBeanDefinitionSet = new HashSet<>();
#Override
public int getOrder() {
// Preempt execution of org.springframework.context.annotation.ConfigurationClassPostProcessor
return 0;
}
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
ConfigurableListableBeanFactory beanFactory = (ConfigurableListableBeanFactory) registry;
String[] configBeanDefinitionNames = beanFactory.getBeanNamesForAnnotation(Configuration.class);
Arrays.stream(configBeanDefinitionNames)
.map(beanFactory::getBeanDefinition)
.filter(this::isCandidateBean)
.peek(this.processedBeanDefinitionSet::add)
.forEach(this::autowireInnerBeans);
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
Arrays.stream(beanFactory.getBeanDefinitionNames())
.map(beanFactory::getBeanDefinition)
.filter(this::isCandidateBean)
.filter(beanDefinition -> !this.processedBeanDefinitionSet.contains(beanDefinition))
.forEach(this::autowireInnerBeans);
}
private boolean isCandidateBean(BeanDefinition beanDefinition) {
return beanDefinition.getBeanClassName() != null && beanDefinition.getBeanClassName().startsWith("com.example.demo.");
}
private void autowireInnerBeans(BeanDefinition beanDefinition) {
// Get #InnerBean methods
assert beanDefinition instanceof AnnotatedBeanDefinition;
AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDefinition;
Set<MethodMetadata> innerBeanMethods = annotatedBeanDefinition.getMetadata().getAnnotatedMethods(InnerBean.class.getName());
// Attach inner beans as constructor parameters
for (MethodMetadata method : innerBeanMethods) {
String innerBeanName = method.getMethodName();
if (!method.isStatic()) {
logger.error("#InnerBean definition [{}] is non-static. Inner beans must be defined using static factory methods.", innerBeanName);
continue;
}
BeanDefinition innerBeanDefinition = BeanDefinitionBuilder.genericBeanDefinition(beanDefinition.getBeanClassName())
.setScope(BeanDefinition.SCOPE_SINGLETON)
.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR)
.setFactoryMethod(innerBeanName)
.getBeanDefinition();
beanDefinition.getConstructorArgumentValues()
.addGenericArgumentValue(new ConstructorArgumentValues.ValueHolder(innerBeanDefinition, method.getReturnTypeName(), method.getMethodName()));
}
}
}
There will be a few benefits and caveats of doing this. One large benefit is that the bean lifecycle will be managed by the Spring IoC container, meaning that lifecycle callbacks (such as #PostConstruct and #PreDestroy) will be called. The bean can be automatically managed according to the lifecycle of the parent. Caveats include that the beans cannot be injected as factory-method parameters (although with a bit of work you might able to fix this) and that AOP proxying will not be applied to these methods within #Configuration classes (i.e., realRepository() should never be called as it will not reference the singleton inner bean -- instead, the instance field should always be referenced). Further proxying (similar to ConfigurationClassEnhancer.BeanMethodInterceptor) would need to be added in order to apply this.