Reprint--On SPRING4 generic dependency Injection

Source: Internet
Author: User

Reproduced from a Sdn-4o4notfound

Many new features have been updated in Spring 4.0, and one of the more important is the support for dependency injection of beans with generics. This change in Spring4 allows the code to use generics for further refinement optimizations.

1. Benefits of generic Dependency Injection

Generic dependency injection allows us to use spring for dependency injection while simplifying the code with the benefits of generics, putting reusable code all into one class for later maintenance and modification. It also increases the reusability of the code without adding code. Let's use an example to briefly explain the benefits of generics:

Let's say we've defined two entity classes Student and faculty:

public class Student {      private int id;      private String name;      private double grade;            public Student (int ID, String name, double grade) {          this.id = ID;          this.name = name;          This.grade = grade;      }        @Override public      String toString () {          return "Student [id=" + ID + ", name=" + name + ", grade=" + Grade + "]"; 
   }  
}
public class Faculty {      private int id;      private String name;      Private String evaluation;            public Faculty (int ID, string name, string evaluation) {          this.id = ID;          this.name = name;          This.evaluation = evaluation;      }        @Override public      String toString () {          return "Faculty [id=" + ID + ", name=" + name + ", evaluation=" + evaluatio n + "]";      }  
}

Then we need the persistence layer bean to invoke the methods inside the student and faculty. Before spring supports generic dependency injection, we need to define a persistence layer bean for two entity classes, and then fetch our entity class from the database and call the methods in the entity class. The code for using this primitive method is as follows:

public class Studentrepository {public        Student Getbean (String beanname) {          //get corresponding Student      }            Public void Printstring (Student s) {          System.out.println (s);      }  
}
public class Facultyrepository {public        Faculty Getbean (String beanname) {          //get corresponding Faculty      }            Public void Printstring (Faculty f) {          System.out.println (f);      }  }

As you can see, each entity class needs to write a new persistence layer bean, and the entity class type in each persistence layer bean is write-dead and the reusability is poor. More importantly, because the entity classes contained in each persistence layer bean are different, the repeated methods in the persistence layer bean (printstring in the example above) need to be implemented once in each persistence layer bean, which greatly increases the maintenance cost of the code.

Of course, there are some ways to solve this problem partially. For example, we can define a persistence layer Bean's parent class baserepository, and then write a common pirntstring method in it:

public class Baserepository {public      void printstring (Object o) {          System.out.println (o);      }  }

Next, we can invoke the Baserepository method in each persistence layer bean to implement printstring:

public class Studentrepository extends baserepository{public          Student Getbean (String beanname) {          // Gets the corresponding Student      } public            void printstring (Student s) {          super.printstring (s);      }  }

  

In this case, the printstring implementation was actually written only once, so we improved the reusability of the code. At the same time, when the Printstring method is not simple to print to the console, but with complex code and logic, we can put all the code in the Baserepository, convenient for later modification and maintenance. However, this approach still requires each persistence layer bean to write a printsring method to invoke the parent class method, although this method has only a simple line, and when the similar method comes up, the number of code is considerable.

In addition to joining the parent class, there are other ways to reduce the amount of code and improve the reusability of the code. For example, we can add setter methods to the parent class so that the business layer can manually inject classes of entity classes (such as Student.class) for the persistence layer, but there is no very good solution.

But when we use generics, these problems are solved. We just need to define a persistence layer bean,baserepository, which is the parent class in the example above, without requiring any subclasses:

public class Baserepository<t> {public      T Getbean (String beanname) {          //get the corresponding T      } public            void Printstring (T t) {          System.out.println (t);      }  }

  

This persistence layer bean can contain all of the methods that we want to reuse in the persistence layer. With generics, our persistence layer code can be used on all entity classes, and we can also add methods specific to some entity classes by inheriting them conveniently. We didn't add extra code, but increased the code reuse, and we put all the reusable code together to facilitate later maintenance and modification.

The above-mentioned content is the advantage of the generics themselves, and the Spring 4.0 generic dependency injection is not directly linked. However, the generic dependency injection that spring 4.0 supports is very important for us to use generics: Before Spring 4.0, spring's dependency injection function does not automatically recognize the generic class in the example above and differentiate it from the different persistence layer beans. So before Spring 4.0,,baserepository<student> and baserepository<faculty> would be considered the same type, which would normally need to be distinguished by name and other means. But now, spring will correctly recognize the declared generic category and classify the persistence layer bean according to generics. So student and faculty's persistent layer beans can be correctly differentiated and injected into the previous layer. This provides a great convenience for us to use generics in our code.

2. Implementation of generic Dependency injection

Let's look at how the dependency injection of the bean using generics should be implemented:

Dependency injection using generic beans is essentially the same as the dependency injection of ordinary beans, and can be injected in XML configuration files and annotations in two ways. However, due to the presence of the angle brackets ("<>") in the generics, the compilation error occurs during the XML configuration file Dependency injection process. Some compilers will still get an error if they escape the angle brackets. Therefore, in order to avoid unnecessary hassles, we recommend that you use annotations to make dependency injection with generic beans.

There are several ways to use annotations for dependency injection:

2.1 Using annotation @configuration for dependency injection

Like regular beans, we can use annotation @configuration to declare the beans we need to use generics and to make dependency injections.

First we need to create a new class as our configuration class:

@Configuration public  class Myconfiguration {    }

The role of annotation @configuration is to tell Spring that the class is a configuration class so that spring automatically scans all the beans declared in the class and joins them in the spring container. But before we do, we need to add Component-scan to the Spring configuration file:

<context:component-scan base-package= "com.somepackage.*</context:component-scan>

Note @configuration will then be scanned and the bean declared inside will be added into the spring container.

After that, we can use annotations to inject a dependency on a bean with generics.

First, we need to declare two persistent layer beans that need to be used, one is student's persistence layer bean and the other is faculty. Only after the two beans have been declared and added to the spring container will spring be able to inject us with dependency.

The methods for declaring these two beans in a configuration class are as follows:

@Configuration public  class Myconfiguration {      @Bean public      baserepository<student> Studentrepository () {          return new baserepository<student> () {};      }            @Bean public      baserepository<faculty> facultyrepository () {          return new baserepository<faculty> () {};      }  }

The annotation @bean is the same as the normal use of spring, which is to declare a new bean. When spring scans the configuration class, it adds the bean declared here to the spring container for later use. The name of each bean here is the method name, such as Studentrepository, and the bean type is the type of the object returned (not the return type of the method, the return type of the method can be a type that cannot be instantiated, such as interface).

If you also need to declare other beans, such as you do not need to get data from the database, you can also add them to this configuration class.

Then we can define our business layer bean and manipulate the data using the method of the business layer bean calling the persistence layer. Here we use the Printstring method as an example:

> @Service public  class Exampleservice {      @Autowired private baserepository<student> Studentrepo; Automatic injection of baserepository<student> () {}      @Autowired private baserepository<faculty> Facultyrepo; Automatic injection of baserepository<faculty> () {} public            void Test () {          Student s = Studentrepo.getbean ("Studentbean");          Studentrepo.printstring (s);                    Faculty f = Facultyrepo.getbean ("Facultybean");          Facultyrepo.printstring (f);      }  }

In the business layer, we can use annotation @autowired for dependency injection. @Autowired By default depends on the class of the field, and Spring4 's new feature is to put the specific type of generics (as in the business layer above baserepository<student> Student) is also a classification method (Qualifier) of the class. This way our Studentrepo and Facultyrepo are the same class baserepository, but because the specific types of generics are different, they are also separated from each other.

Here I first created two entity class instances and added them to the configuration class just mentioned. Then these two beans will be added to the spring container, and we can get them in the Getbean method. The names of the two beans are studentbean and facultybean, consistent with the names that are filled in in the business layer bean:

@Bean public  Student Studentbean () {      return new Student (1, "Anna", 3.9);    @Bean public  Faculty Facultybean () {      return new Faculty (2, "Bob", "A");  }

  

Of course, if you have other ways to acquire entity classes, such as your project integrating Hibernate ORM or other tools to connect to the database, you do not need to add the corresponding bean to the spring container, and the implementation of the Getbean method can be changed accordingly. I'm just using these two entity-class beans as an example.

Then when we invoke the test method of the business layer, the result of the console printing is:

Student [Id=1, Name=anna, grade=3.9]faculty [id=2, Name=bob, Evaluation=a]

And when we try to invoke the wrong method in the business layer:

Facultyrepo.printstring (s);

, a compilation error occurs.

Read here, there may be some people have found that this example has two doubts. First, this example does not prove that we successfully implemented dependency injection at runtime because we passed the Printstring method to the student and faculty instances at run time. Second, when we declare the bean, the declared class is not baserepository, but an anonymous subclass of Baserepository.

To answer these two questions, I have defined a new approach in Baserepository:

public void Printtype () {      Type genericsuperclass = This.getclass (). Getgenericsuperclass ();      Class<t> Entityclass = (class<t>) ((Parameterizedtype) genericsuperclass). Getactualtypearguments () [0];      System.out.println (Entityclass);  }

  

The first two lines of this code are the classes that determine the generic t at run time in a class with generics. If you need to use the specific type of T at run time in baserepository, you should use this method to get it. Here is a more detailed explanation:

Because of the Java erase mechanism, generic t is only valid at compile time and will be erased at runtime, so we cannot get the class of T directly at runtime, and some methods that are valid for general classes, such as T.class and T.getclass (), are illegal for generics. So we need to get the class of T through the reflection mechanism.

The first line of this code is to get the parent class of the current class, and then in the second row, the class of the current class generic is obtained by looking at the class parameters of the parent class. That is, this is a way to see the specific type of a generic in a subclass through the parameters of the parent class. Therefore, this method has some usage requirements: First, this method must be used by subclasses of the generic class, and the generic class itself cannot use this method. In addition, this subclass must inherit a specific class at the location of the generic, rather than the generic T. For example, when a class inherits basebaen<a>, this method can be used, but it cannot be used when inheriting basebean<t>, because a is a specific class, and T is generic, and even if we take t from the parent class at run time, because there is a erase mechanism, We still don't know what class T is.

It is worth mentioning that Spring4 also adds support for generic dependency injection in the same way. So if we want to use Spring4 's new functionality, we have to define the bean as a subclass of the generic class, as in the example above, new Basebean<a> () {}. This bean is an anonymous subclass of Basebean, inherited by Basebean<a>. In this way, spring can obtain the class (A) of the generic t correctly, and use this as a basis to help us implement dependency injection.

Spring's documentation and source code have instructions for annotation dependency injection, which you can look for if you are interested.

At the same time, we will find that the Printtype method above does not receive any instances, so this method can help us to determine whether the generic dependency injection is successful. To test, I made the following modifications to the business layer's test method:

public void Test () {      Student s = Studentrepo.getbean ("Studentbean");      Studentrepo.printstring (s);      Studentrepo.printtype ();                Faculty f = Facultyrepo.getbean ("Facultybean");      Facultyrepo.printstring (f);      Facultyrepo.printtype ();  }

Then, when we call the test method for testing, the console prints the following information:

Class Com.somepackage.Studentclass Com.somepackage.Faculty

This information shows that we have correctly acquired the generic T class without passing in the instance, and the generic dependency injection succeeded.

Note:

1. The @bean annotations mentioned earlier in this article can also be used in classes that are annotated with @component annotations, but spring suggests that this practice is only used in factory classes and is not recommended for large-scale use. In addition, spring has some limitations on the association of Beans declared in a configuration class that is not in the @component annotation callout, as detailed in the spring documentation. For the correct use of annotation @component, see the next section.

2. In addition to using @autowired annotations for dependency injection, we can also use @resource annotations for dependency injection. Because @resource is a priority for dependency injection by name, we'd better make the field name the same as the bean name.

2.2 Using annotation @component and so on for dependency injection

In the previous section we describe the implementation and some principles of generic dependency injection using the configuration class of @configuration annotation annotations. We have mentioned that if we want the generic dependency injection of spring to succeed, we must define the bean as a subclass of the class that uses generics. The most common way to define a subclass is to define a new class and then inherit.

Therefore, we can declare a new bean using the @component annotation and its sub-annotations (such as @controller, @Service, and @repository). As mentioned in the previous section, this subclass must inherit a specific type at the location of the generic and cannot inherit the generic T, otherwise spring's automatic dependency injection will not succeed.

In addition, these annotations are used in the same way as no generics, so let's look at the specific code:

I have written two subclasses of Studentrepository and Facultyrepository for the previous two types of baserepository:

@Repository public  class Studentrepository extends baserepository<student> {    }
@Repository public  class Facultyrepository extends baserepository<faculty> {    }

and annotated with the annotation @repository. In this case, spring will scan to these two classes and create two corresponding beans to be added to the spring container. Of course, if you want to use spring's automatic scanning function correctly, you need to include the Component-scan in the spring configuration file, please refer to the previous section for detailed practice.

It is important to note that a bean has been added to the spring container using the @repository annotation. Therefore, if you have written the @configuration configuration class in the previous section, be sure to comment out the @configuration annotation so that spring does not scan the configuration class or comment out the @bean annotations of the two persistence layer beans in the configuration class. Let spring no longer scan these two persistent layers. Otherwise, if spring uses @autowired annotations for dependency injection, there are two errors for the same type of bean.

Here is our business layer code:

public class Exampleservice {      @Autowired private baserepository<student> Studentrepo;//Automatic injection Baserepository <Student> () {}      @Autowired private baserepository<faculty> Facultyrepo;//auto-inject baserepository< Faculty> () {} public            void Test () {          Student s = Studentrepo.getbean ("Studentbean");          Studentrepo.printstring (s);          Studentrepo.printtype ();                    Faculty f = Facultyrepo.getbean ("Facultybean");          Facultyrepo.printstring (f);          Facultyrepo.printtype ();      }  }

The code for the business layer is the same as the previous section without any modifications. Note that in the two fields that require dependency injection, the type we declare is still the generic class baserepository, not the subclass Studentrepository and facultyrepository we just defined. In fact, we do not need to know the types of these subclasses, we can call the methods of the subclass, which is the strong point of spring dependency injection.

When we call the test method, the console prints the following information:

Student [Id=1, Name=anna, Grade=3.9]class com.somepackage.StudentFaculty [id=2, Name=bob, Evaluation=a]class Com.somepackage.Faculty

From this information we can see that spring can also be injected successfully when declaring the bean's type to be different from the dependency injection target type. This is because SPRING4 begins to classify the specific type of generics as a method of classifying beans (Qualifier), so spring can successfully differentiate between baserepository<student> and baserepository< Faculty>, and their subclasses.

However, there is also a problem with this example: because the place where the dependency injection is declared is the parent class baserepository, how do we decide that spring is injecting us with subclasses studentrepository and Facultyrepository, Or the baserepository of the parent class? In fact, we don't have to worry about this at all, because we don't declare any beans of the parent class baserepository type, only the bean of the subclass type is declared. So if spring dependency injection succeeds, it must inject the bean of the subclass type. But here we also validate this conjecture with our code.

For validation, I covered the printstring method of the parent class baserepository in Studentrepository and facultyrepository:

@Repository
public class Studentrepository extends baserepository<student> { @Override public void Printstring ( Student s) { System.out.println ("I am Studentrepo-" + s.tostring ()); } }

@Repository public  class Facultyrepository extends baserepository<faculty> {      @Override public      Void Printstring (Faculty f) {          System.out.println ("I am Facultyrepo-" + f.tostring ());      }  }

  

Then when we call the business layer's test method for testing, the console makes the following information:

I am Studentrepo-student [id=1, Name=anna, Grade=3.9]class Com.hpe.bboss.autotest.dao.StudentI am facultyrepo-faculty [Id=2, Name=bob, Evaluation=a]class com.hpe.bboss.autotest.dao.Faculty

This shows that spring is injecting us with the subclass Studentrepository and facultyrepository We want, not the parent class baserepository.

Note:

1. When multiple subclasses of the @component annotation callout inherit a parent class at the same time, and the specific type of the generic is also the same, the dependency injection in the above method throws an exception. This is because the @autowired annotation defaults to only one bean with the same type as the specified field, and throws an exception when multiple beans satisfy the condition. The solution to this problem is using @primary annotations, using @qualifier annotations and its sub-annotations, using bean name injection, and so on. Since this issue is a spring dependency injection, rather than a generic dependency injection, it is not an option, so please consult the spring documentation and other information for a specific solution.

2. Generic Lai injection is not limited to the use of persistent layers. We can also use generic dependency injection in other places, such as the business layer, based on the persistence layer using generic dependency injection. The relevant examples are well found on the Internet, I will not copy and paste, if interested, please check it yourself.

2.3 Comparison of two kinds of dependency injection methods

The two types of dependency injection described earlier are essentially two different ways of declaring beans. As mentioned earlier, spring has good support for both of these claims, but the two methods themselves have a larger difference. In the first way, we declare by @configuration annotation callout configuration class. In the second way, we use annotations to declare directly in the subclass. Let's take a brief look at the pros and cons of both approaches.

In the first declaration, all beans are declared in the configuration class. Therefore, in subsequent maintenance, we do not need to look at the source code of each class to make some changes to the state of the bean. In addition, this means that we do not need to create a subclass for each bean, which makes the management of the directory simple. But using this method means that every time we declare a new bean we need to add a method and at least one annotation to the configuration class, and sometimes we need to add some methods to the anonymous subclass, and the length of the configuration class becomes very long when the number of beans is large, which is not easy to understand and manage.

In the second way, we do not need to maintain the configuration file, all the work required to declare a bean, such as name, class, join the spring container, etc., is done by one annotation. At the same time, due to the existence of subclasses, we can easily add methods and fields, covering methods and so on. But using this approach also means that when we need to modify the state of the bean, we have to find the appropriate class to operate on. And a lot of subclasses will make our directory more complicated, especially the empty class, itself does not have much meaning, but make the management of the directory becomes very troublesome.

In summary, the two approaches each have their own advantages and disadvantages, and which method to use should be based on the specific circumstances of the project. In General, when the sub-class is more hollow, it may be appropriate to use the first method, whereas the second method is more appropriate. In some difficult-to-determine situations, the use of both methods is sometimes a choice to consider. However, the use of both methods increases the difficulty of maintenance and is recommended for careful use.

3. Generic Dependency Injection Summary and Outlook

The newly added generic dependency injection feature in Spring 4.0 is a useful feature. It helps us to reduce maintenance costs by simplifying the code with great generics. According to my study and usage, spring's overall quality of support for generic dependency injection is very good. The implementation of generic dependency injection is not very different from ordinary dependency injection, so it is easy to learn and simple to use. People who want to see this article also try to use the generic dependency injection when using spring in the future.

However, there are some improvements to Spring4 's generic dependency injection. My intention in this study of spring generics injection was to find a simple injection method that would allow me to reduce the number of declared classes as much as possible while using spring dependency injection. But after studying for a while, I found that spring must now declare a new class for each bean, whether it be an anonymous subclass or an empty one, or spring will not be able to do the dependency injection correctly. But when we don't need to add any functionality to the subclass, there are too many anonymous subclasses or empty subclasses, and this configuration class becomes inefficient, both in terms of declarative and maintenance management. I hope that later spring updates will automatically create these anonymous subclasses for us, or, in some other way, that we can successfully declare some of the beans that use generics without having to configure the class and not need subclasses, and rely on the generic type for dependency injection. For example, I would like to declare beans

@Repository public  class Baserepository<t> {public        void printstring (T t) {          System.out.println (t);      } public            void Printtype () {          Type genericsuperclass = This.getclass (). Getgenericsuperclass ();          Class<t> Entityclass = (class<t>) ((Parameterizedtype) genericsuperclass). Getactualtypearguments () [0];          System.out.println (Entityclass);      }  }

Then, when a dependency injection is made, spring can automatically generate an anonymous subclass from the type of the field and inject it:

@Autowired private baserepository<student> Studentrepo; Automatic injection of baserepository<student> () {}  @Autowired private baserepository<faculty> Facultyrepo; Auto-inject baserepository<faculty> () {}

If spring can do this, I believe our development will become more efficient.

    

Reprint--On SPRING4 generic dependency injection

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.