Java configuration in Spring framework - java

I have classes A, B and their implementation AImpl, BImpl.
interface A {
}
interface B {
}
interface C {
}
class AImpl{
#Inject
AImpl(B b){}
}
class BImpl{
#Inject
BImpl(String foo, C c){}
}
class CImpl{
}
To configure dependencies in Guice I would write smt like
bind(A.class).to(AImpl);
bind(C.class).to(CImpl);
#Provides B provideB(C c){
return new BImpl("foo", c)
}
In spring I can do smt like
#Bean public A a() {
return new AImpl(b())
}
#Bean public B b() {
return new BImpl("foo", c());
}
#Bean public C c() {
return new CImpl();
}
There are several downsides
I should write that AImpl needs B in 2 places (constructor and config).
I should write more code (CImp and AImpl requires method creation instead of one expression)
Are there any way to impruve my spring configuration without doing xml?
upd
I don't want to polute my classes with spring related annotations like #Component either. And I would prefer constructor injection to any kind of other injections. Scanning is not preferable solution too.
So, can I do Spring in Guice way?
upd2
So I want archive
Autowiring
Constructor-injection
Without
XML
PathScan

Instead of creating Bean using java code you can use Autowiring. You can define ComponentScan in your java configuration. You do not need to use any XML file.

This creates beans OK without xml and polluting beans with Spring annotation:
interface A {
}
interface B {
}
interface C {
}
class AImpl implements A {
AImpl(B b) {
}
}
class BImpl implements B {
BImpl(String foo, C c) {
}
}
class CImpl implements C {
}
#Configuration
class Config {
#Bean public A a() {
return new AImpl(b());
}
#Bean public B b() {
return new BImpl("foo", c());
}
#Bean public C c() {
return new CImpl();
}
}
public class T1 {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
}
}

Related

Spring create generic service multiple times using generic in constructor

I have a service that uses some object as a generic
#Component
#RequiredArgsConstructor
public class SomeGenericService<T extends Base> {
private final T base;
public void someWork(String info) {
base.someAction(info);
}
}
I also have 3 Base implementations marked with #Component(Base1, Base2, Base3)
I want spring itself to create a service with the generic it needs, for the following example
#Component
#RequiredArgsConstructor
public class Runner implements CommandLineRunner {
private final SomeGenericService<Base1> s1;
private final SomeGenericService<Base2> s2;
private final SomeGenericService<Base3> s3;
#Override
public void run(String... args) throws Exception {
String someString = "text";
s1.someWork(someString);
s2.someWork(someString);
s3.someWork(someString);
}
}
But after the launch, the spring does not understand what I want from it.
Parameter 0 of constructor in SomeGenericService required a single bean, but 3 were found:
- base1: defined in file [Base1.class]
- base2: defined in file [Base2.class]
- base3: defined in file [Base3.class]
Is it possible to set this to automatic, without manually configuring it via the #Bean annotation for each service?
You need to define how those beans should be injected. It's a good practice to have some #Configurations for this purpose. Something like:
#Configuration
#Import({
Base1.class,
Base2.class,
Base3.class
})
public class SomeConfig {
#Bean
SomeGenericService<Base1> someGenericService1() {
return new SomeGenericService(new Base1());
}
#Bean
SomeGenericService<Base2> someGenericService2() {
return new SomeGenericService(new Base2());
}
#Bean
SomeGenericService<Base3> someGenericService3() {
return new SomeGenericService(new Base3());
}
}

Alternative to #Qualifier in spring boot

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

Spring autowiring with parameter

I am trying to do autowiring using dynamic parameter, I know we can declare a class with #component and make the class available for autowiring, but what if I have a class with parametrized construtor. I can we use autowiring and intialise the object with paramter?
Please see the below snippet.
#Component
public class A{
public A(Object B){
// do something
}
}
public class C{
#Autowire
private A a;
public foo(){
B b = getBfromSomewhere();
// create object of A using parameter B
// like a = new A(b);
}
}
I got the answer after going through the configuration annotation. One should first must tell spring, how it should create the bean for example.
#Configuration
public class Appconfig{
#Bean(name="a")
public A getA(B b){
return A(b);
}
}
Later when you are using it.
public class C{
#Autowire
private BeanFactory factory;
public foo(){
B b = getBfromSomewhere();
A a = factory.getBean(A.class,b)
}
}

Spring Null Pointer Exception in #Autowired Object

Hello I am kind of new in Spring in Dependency Injection.
I have made few config files which has beans inside it and I am injecting those beans using #Autowired annotation.
Configs:
#Configuration
#Component
public class FirstConfig {
#Bean
A getA() {
return new A(secondConfig.getB());
}
#Autowired
SecondConfig secondConfig;
}
SecondConfig
#Configuration
public class SecondConfig {
#Bean
B getB() {
return new B();
}
}
And Last Config
#Configuration
public class ThirdConfig {
#Bean
D getD() {
return new D();
}
}
Here is the service using A()
#Component
public class XYZService
{
private C c;
#Autowired
private A a;
public XYZService()
{
this.c = a.doSomething("Hello world");
}
}
Also, if this helps,
#Component
public class B implements someInteface
{
#Autowired
private D d;
}
I am getting NPE on this line: this.c = a.doSomething("Hello world");
Any idea what's wrong?
You can't use autowired properties in the class consturctor since Spring just inject the #Autowired properties after the creation of that class. However you can use the autowired properties in the method with annotation #PostConstruct which will run right after the constructor run.
#Component
public class XYZService
{
private C c;
#Autowired
private A a;
public XYZService()
{
// Move the initialization to #PostConstruct
}
#PostConstruct
private void init() {
this.c = a.doSomething("Hello world");
}
}
To use one configuration into another you can Import the configuration using #Import(ConfigurationClass.class) annotation. In your case -
#Configuration
#Component
#Import(SecondConfig.class)
public class FirstConfig {
#Bean
A getA() {
return new A(secondConfig.getB());
}
#Autowired
SecondConfig secondConfig;
}
You could also use #ComponentScan annotation to let your configuration automatically detect components from your Configuration file like shown below. This is particularly useful when you want to use a class as a bean
#Configuration
#Component
#ComponentScan(basepackages = "com.yourBasePackage")
public class FirstConfig {
#Bean
A getA() {
return new A(secondConfig.getB());
}
#Autowired
SecondConfig secondConfig;
}

modify spring bean programatically in runtime using ApplicationContext

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.

Categories

Resources