Spring Dersleri Sıfırdan Orta Seviyeye kadar temel kavramların detaylı anlatımı
1 - Overview of Spring Framework and First Lesson : Inversion Of Control (IOC) and Dependency Injection
Spring is a lightweight framework. It can be thought of as a framework of frameworks because it provides support to various frameworks such as Struts, Hibernate, Tapestry, EJB, JSF etc. The framework, in broader sense, can be defined as a structure where we find solution of the various technical problems.
The Spring framework comprises several modules such as IOC, AOP, DAO, Context, ORM, WEB MVC etc.
1-1 IoC is a generic term meaning rather than having the application call the methods in a framework, the framework calls implementations provided by the application.
DI is a form of IoC, where implementations are passed into an object through constructors/setters/service look-ups, which the object will 'depend' on in order to behave correctly.
Bu case'de, Employee ve Address class'ları arasında bir dependency yani tight coupling vardır. Employee class'ı Address class'ına dependent'dır, bağımlıdır. Inversion of control senaryosunda yandaki kod'u şu şekilde yazarız.
|
2-2 These are the design patterns that are used to remove dependency from the programming code. They make the code easier to test and maintain( Bunlar koddan dependency'yi kaldırmak için kullanılan design pattern'lardır. Başka hiçbir class'a bağımlı olmayan bir class'ı sürdürmek ve test etmek çok daha kolaydır.) Let's understand this with the following code:
class Employee
{
Address address;
Employee()
{
address=new Address();
}
}
Daha önce yaratılmış olan bir Address object vardır. Employee class'ının constructor'ı parametre olarak bu object'e reference alır. Artık Employee class'ı Address class'ına bağımlı değildir. Bu iki class arasında loose coupling bir ilişki vardır. Dependency object'i yani Address object'i Employee class'ına inject etmek, Spring Framework'deki IOC container'ın görevidir. IOC container'a XML ile veya annotation ile metadata sağlarız.
|
class Employee{
Address address;
Employee(Address address)
{
this.address=address;
}
}
Thus, IOC makes the code loosely coupled. In such case, there is no need to modify the code if our logic is moved to new environment.
In Spring framework, IOC container is responsible to inject the dependency. We provide metadata to the IOC container either by XML file or annotation.
2-3 Advantage of Dependency Injection
- makes the code loosely coupled so easy to maintain (Employee class'ına dependent object'i yani Address class'ını inject ederek iki class'ı loosely coupled yapar. Bu kodu sürdürmek ve test etmek artık çok kolaydır.)
- makes the code easy to test
"Any nontrivial application is made up of two or more classes that collaborate with each other to perform some business logic. Traditionally, each object is responsible for obtaining its own references to the objects it collaborates with (its dependencies). When applying DI, the objects are given their dependencies at creation time by some external entity that coordinates each object in the system. In other words, dependencies are injected into objects." ( Herhangi bir basit Java uygulaması 2 veya daha fazla class'dan oluşur. Bu class'lar birbirlerine bağımlıdır. Class'lar bağımlı oldukları class'ları elde etmekle yükümlüdür. Dependency injection sayesinde, bir object yaratılırken, bu object'in bağımlı olduğu tüm class'ların object'leri bu object'e inject edilir. Yani örneğin, Employee object'i düşünürsek, bu class'a bağımlı olan tüm class'ların object'leri Employee objet'e inject edilir. Dolayısıyla Employee object artık hiçbir class'a bağımlı değildir.)
Dependency Injection was originally called Inversion of Control (IoC) because the normal control sequence would be the object finds the objects it depends on by itself and then calls them. Here, this is reversed: The dependencies are handed to the object when it's created. This also illustrates the Hollywood Principle at work: Don't call around for your dependencies, we'll give them to you when we need you. ( Dependency injection'a önceden Inversion of control denilirdi. Bu isim de şu mantıkla bulunmuştu. Bir object, örneğin Employee object, bağımlı olduğu object'leri yani Address object'i kendisi bulur ve çağırırdı. Inversion of Control ile ise, bu senaryo tam tersine çevrilmiştir. Object yani Employee object yaratılırken, bu object'in bağımlı olduğu tüm object'ler yani Address object Employee object'e inject edilir. Bu, Hollywood Principle'a benzer; sen bağımlı olduğun class'ları arama biz sana onları verelim. Sen yaratılırken ihtiyacın olan tüm object'leri biz sana verdik. Artık sen kimseye(hiçbir class'a) bağımlı değilsin. Bağımsızsın. Kolay sürdürülebilir ve kolay test edilebilirsin. )
If you don't use DI, you're probably wondering why it's a big deal. It delivers a key advantage: loose coupling. Objects can be added and tested independently of other objects, because they don't depend on anything other than what you pass them. When using traditional dependencies, to test an object you have to create an environment where all of its dependencies exist and are reachable before you can test it. With DI, it's possible to test the object in isolation passing it mock objects for the ones you don't want or need to create. Likewise, adding a class to a project is facilitated because the class is self-contained, so this avoids the "big hairball" that large projects often evolve into.
The challenge of DI is writing an entire application using it. A few classes are no big deal, but a whole app is much more difficult. For entire applications, you frequently want a framework to manage the dependencies and the interactions between objects. DI frameworks are often driven by XML files that help specify what to pass to whom and when. Spring is a full-service Java DI framework;
2-4 The Really Short Version
Dependency injection means giving an object its instance variables. Really. That's it.
Here, we have a reference variable... uh, dependency... named "myDatabase." We initialize it in the constructor. Bu case bir tight coupling örneğidir. Example object, DatabaseThingie object'e bağımlıdır çünkü Example constructor çağırıldığında DatabaseThingie class'ından bir object yaratılır. Example class'ından bir object yaratılırken myDatabase isimli object de yaratılır. DoStuff() method'unu çağırdığımızda myDatabase object'i üzerinde işlem yapılır.
|
The Slightly Longer Version, Part I: Dependency Non-Injection
public class Example {
private DatabaseThingie myDatabase;
public Example() {
myDatabase = new DatabaseThingie();
}
public void DoStuff() {
...
myDatabase.GetData();
...
}
}
The Slightly Longer Version, Part II: Dependency Injection
If we wanted to, we could pass the variable into the constructor. That would "inject" the "dependency" into the class. Now when we use the variable (dependency), we use the object that we were given rather than the one we created. ( Bu örnekte, Example class'ının constructor'ı parametre olarak zaten yaratılmış olan bir object'e reference alır. Dolayısıyla class içinde bir object yaratmaktansa, myDatabase isimli reference variable'ın zaten yaratılmış bir object'e refer etmesini sağlıyoruz. Bu da bize loose coupling sağlıyor. Example class'ından bir object yaratılırken bu class'ın bağımlı olduğu tüm object'ler bu class'a inject ediliyor, dolayısıyla artık bu class tüm class'lardan bağımsız oluyor. )
public class Example {
private DatabaseThingie myDatabase;
public Example() {
myDatabase = new DatabaseThingie();
}
public Example(DatabaseThingie useThisDatabaseInstead) {
myDatabase = useThisDatabaseInstead;
}
public void DoStuff() {
...
myDatabase.GetData();
...
}
}
That's really all we should know. The rest is just variations on the theme. You could set the dependency (<cough> reference variable) in a setter method. You could set the dependency by calling a setter method that's defined in a special interface. You can have the dependency be an interface and then polymorphically pass in some polyjuice. Whatever.
The Slightly Longer Version, Part III: Why Do We Do This?
MockDatabase object'i Example object'e enjekte ettik.
MockDatabase, DatabaseThingie'nin subclass'ıdır.
Dolayısıyla, parameter olarak DatabaseThingie object alabilen Example constructor, DatabaseThingie object de alabilir.
|
it's handy for isolating classes during testing.
public class ExampleTest {
TestDoStuff() {
MockDatabase mockDatabase = new MockDatabase();
// MockDatabase is a subclass of DatabaseThingie,
// so we can "inject" it here:
Example example = new Example(mockDatabase);
example.DoStuff();
mockDatabase.AssertGetDataWasCalled();
}
}
public class Example {
private DatabaseThingie myDatabase;
public Example() {
myDatabase = new DatabaseThingie();
}
public Example(DatabaseThingie useThisDatabaseInstead) {
myDatabase = useThisDatabaseInstead;
}
public void DoStuff() {
...
myDatabase.GetData();
...
}
}
That's it. Dependency injection is really just passing in an instance variable.
( Yukarıda Example class'ını tanımladık. ExampleTest class'ında Example class'ından object yaratırız. DatabaseThingie veya bunun subclass'larından bir object yaratıp bu object'i Example class'ının constructor'una vererek bir Example object yarattık. Böylece Example object'e MockDatabase object'i inject etmiş olduk.
2-5 Every java based application has a few objects that work together to present what the end-user sees as a working application. When writing a complex Java application, application classes should be as independent as possible of other Java classes to increase the possibility to reuse these classes and to test them independently of other classes while doing unit testing. Dependency Injection (or sometime called wiring) helps in gluing these classes together and same time keeping them independent.
Consider you have an application which has a text editor component and you want to provide spell checking. Your standard code would look something like this:
public class TextEditor {
private SpellChecker spellChecker;
public TextEditor() {
spellChecker = new SpellChecker();
}
}
What we've done here is create a dependency between the TextEditor and the SpellChecker. In an inversion of control scenario we would instead do something like this:
public class TextEditor {
private SpellChecker spellChecker;
public TextEditor(SpellChecker spellChecker) {
this.spellChecker = spellChecker;
}
}
Here TextEditor should not worry about SpellChecker implementation. The SpellChecker will be implemented independently and will be provided to TextEditor at the time of TextEditor instantiation and this entire procedure is controlled by the Spring Framework.
Here, we have removed the total control from TextEditor and kept it somewhere else (ie. XML configuration file) and the dependency ( ie. class SpellChecker) is being injected into the class TextEditor through a Class Constructor. Thus flow of control has been "inverted" by Dependency Injection (DI) because you have effectively delegated dependances to some external system.
Second method of injecting dependency is through Setter Methods of TextEditor class where we will create SpellChecker instance and this instance will be used to call setter methods to initialize TextEditor's properties.
Thus, DI exists in two major variants and following two sub-chapters will cover both of them with examples:
You can mix both, Constructor-based and Setter-based DI but it is a good rule of thumb to use constructor arguments for mandatory dependencies and setters for optional dependencies.
Code is cleaner with the DI principle and decoupling is more effective when objects are provided with their dependencies. The object does not look up its dependencies, and does not know the location or class of the dependencies rather everything is taken care by the Spring Framework.
Spring dersinin tamamını burada yayınlamak zor olacağı için dersin tamamını bir pdf içerisine yazdım. PDF'e link'den erişebilirsiniz : https://drive.google.com/file/d/1sFIjIjjraoGAmO3C7qL660FImVLEGQ2h/view?usp=sharing
Hiç yorum yok:
Yorum Gönder