Why Is Spring's Applicationcontext.Getbean Considered Bad

Why is Spring's ApplicationContext.getBean considered bad?

I mentioned this in a comment on the other question, but the whole idea of Inversion of Control is to have none of your classes know or care how they get the objects they depend on. This makes it easy to change what type of implementation of a given dependency you use at any time. It also makes the classes easy to test, as you can provide mock implementations of dependencies. Finally, it makes the classes simpler and more focused on their core responsibility.

Calling ApplicationContext.getBean() is not Inversion of Control! While it's still easy to change what implemenation is configured for the given bean name, the class now relies directly on Spring to provide that dependency and can't get it any other way. You can't just make your own mock implementation in a test class and pass that to it yourself. This basically defeats Spring's purpose as a dependency injection container.

Everywhere you want to say:

MyClass myClass = applicationContext.getBean("myClass");

you should instead, for example, declare a method:

public void setMyClass(MyClass myClass) {
this.myClass = myClass;
}

And then in your configuration:

<bean id="myClass" class="MyClass">...</bean>

<bean id="myOtherClass" class="MyOtherClass">
<property name="myClass" ref="myClass"/>
</bean>

Spring will then automatically inject myClass into myOtherClass.

Declare everything in this way, and at the root of it all have something like:

<bean id="myApplication" class="MyApplication">
<property name="myCentralClass" ref="myCentralClass"/>
<property name="myOtherCentralClass" ref="myOtherCentralClass"/>
</bean>

MyApplication is the most central class, and depends at least indirectly on every other service in your program. When bootstrapping, in your main method, you can call applicationContext.getBean("myApplication") but you should not need to call getBean() anywhere else!

Spring ApplicationContext.getBean returns wrong class

I was eventually able to get things to work using the applicationContext().getBean("beanName", CertificateProductApplicationService.class). The problem was deeper in my code in the CertificateProductApplicationServiceCabImpl class which used and returned the wrong datasource.

ApplicationContext.getBean vs new keyword

If your ChartOfAccount entity is on the Application Context and has injected dependencies, then an instance created with new will not get any of those injected dependencies.

The getBean() technique will get you what you want, but it is considered a bad practice in that you are hard-coding the dependency on ChartOfAccount in your code. Though with your approach and that tight loop in your code, you really don't have a choice.

If ChartOfAccount is an entity that gets persisted, it seems strange to me that you would put an instance of it on the Application Context (even with prototypical creation). The more common pattern here would be to use something like the Spring support for data access objects. Here are the docs for Hibernate:

http://docs.spring.io/spring/docs/2.0.8/reference/orm.html

Five years ago that's what you would have done. However, You might want to consider using JPA and Spring data for this: http://projects.spring.io/spring-data-jpa/. It auto-generates CRUD repositories with a lot of nice features out of the box: query generation, pagination, etc.

Alternative to ApplicationContext.getBean() in Spring

Since Spring 4.3 you can autowire all implementations into a Map consisting of pairs beanName <=> beanInstance:

public class APIHandler {

@Autowired
private Map<String, SomeInterface> impls;

public ResponseEntity post(@RequestBody HandlingClass requestBody) {
String beanName = "..."; // resolve from your requestBody
SomeInterface someInterface = impls.get(beanName);
someInterface.doSomething();
}
}

assuming you have two implementations like following

// qualifier can be omitted, then it will be "UseClass1" by default
@Service("beanName1")
public class UseClass1 implements SomeInterface { }

// qualifier can be omitted, then it will be "UseClass2" by default
@Service("beanName2")
public class UseClass2 implements SomeInterface { }

Why is applicationContext.getBean(beanName) returning null, when it clearly exists?

Bean Naming

@Component is a class level annotation. During the component scan, Spring Framework automatically detects classes annotated with @Component.

@Component
class CarUtility {
// ...
}

By default, the bean instances of this class have the same name as the class name with a lowercase initial. On top of that, we can specify a different name using the optional value argument of this annotation.

Annotation based Configuration

For stereotype annotation based bean, if the name is not explicitly specified with the value field of stereotype annotations, then the name is again generated by AnnotationBeanNameGenerator which is an implementation of the BeanNameGenerator strategy interface here

If the annotation's value doesn't indicate a bean name, an appropriate name will be built based on the short name of the class (with the first letter lower-cased). For example:

com.xyz.FooServiceImpl -> fooServiceImpl

With component scanning in the classpath, Spring generates bean names for unnamed components, following the rules described earlier: essentially, taking the simple class name and turning its initial character to lower-case. However, in the (unusual) special case when there is more than one character and both the first and second characters are upper case, the original casing gets preserved. These are the same rules as defined by doc java.beans.Introspector.decapitalize (which Spring uses here).



Related Topics



Leave a reply



Submit