Spring annotation details

Posted by anoopd on Tue, 01 Feb 2022 04:27:25 +0100

Annotation configuration has many advantages over XML configuration:

  • It can make full use of java reflection mechanism to obtain class structure information, which can effectively reduce the work of configuration. For example, when configuring ORM mapping with JPA annotation, we don't need to specify the attribute name, type and other information of PO. If the relationship table fields are consistent with the attribute name and type of PO, you don't even need to write task attribute mapping information - because these information can be obtained through Java reflection mechanism.
  • Comments and Java code are located in one file, while XML configuration adopts an independent configuration file. Most configuration information will not be adjusted after program development. If the configuration information and Java code are put together, it will help to enhance the cohesion of the program. With independent XML configuration files, programmers often need to switch between program files and configuration files when writing a function. This kind of incoherence in thinking will reduce the development efficiency.

Therefore, in many cases, annotation configuration is more popular than XML configuration, and annotation configuration has a further trend of popularity. A major enhancement of Spring 2.5 is the introduction of many annotation classes. Now you can use annotation configuration to complete most of the functions of XML configuration. In this article, we will tell you about Bean definition and dependency injection using annotations.

Before using annotation configuration, let's review how beans are traditionally configured and the dependencies between beans are established. The following are three classes: Office, Car and Boss. These three classes need to be configured as beans in the Spring container:

Office has only one property:


                
package com.baobaotao;
public class Office {
    private String officeNo ="001";

    //Omit get/setter

    @Override
    public String toString() {
        return "officeNo:" + officeNo;
    }
}

 

Car has two properties:


                
package com.baobaotao;

public class Car {
    private String brand;
    private double price;

    // Omit get/setter

    @Override
    public String toString() {
        return "brand:" + brand + "," + "price:" + price;
    }
}

 

Boss has two attributes of Office and Car types:


                
package com.baobaotao;

public class Boss {
    private Car car;
    private Office office;

    // Omit get/setter

    @Override
    public String toString() {
        return "car:" + car + "\n" + "office:" + office;
    }
}

 

We declare Office and Car as beans in the Spring container and inject them into the Boss Bean: the following is the configuration file beans that uses traditional XML to do this xml:


                
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    <bean id="boss" class="com.baobaotao.Boss">
        <property name="car" ref="car"/>
        <property name="office" ref="office" />
    </bean>
    <bean id="office" class="com.baobaotao.Office">
        <property name="officeNo" value="002"/>
    </bean>
    <bean id="car" class="com.baobaotao.Car" scope="singleton">
        <property name="brand" value=" red flag CA72"/>
        <property name="price" value="2000"/>
    </bean>
</beans>

 

When we run the following code, the console will correctly type the boss information:


                
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AnnoIoCTest {

    public static void main(String[] args) {
        String[] locations = {"beans.xml"};
        ApplicationContext ctx = 
		    new ClassPathXmlApplicationContext(locations);
        Boss boss = (Boss) ctx.getBean("boss");
        System.out.println(boss);
    }
}

 

This shows that the Spring container has correctly completed the creation and assembly of beans.

Spring 2.5 introduces @ Autowired annotation, which can label class member variables, methods and constructors to complete automatic assembly. Let's take a look at the code for automatic injection of member variables using @ Autowired @:


                
package com.baobaotao;
import org.springframework.beans.factory.annotation.Autowired;

public class Boss {

    @Autowired
    private Car car;

    @Autowired
    private Office office;

    ...
}

 

Spring parses @ Autowired , through a , BeanPostProcessor , so to make @ Autowired , work, you must declare , autowireannotation BeanPostProcessor , Bean in the spring container in advance.


                
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

    <!-- Should BeanPostProcessor Automatically works on dimensions @Autowired of Bean Automatic injection -->
    <bean class="org.springframework.beans.factory.annotation.
        AutowiredAnnotationBeanPostProcessor"/>

    <!-- remove boss Bean Property injection configuration information -->
    <bean id="boss" class="com.baobaotao.Boss"/>
 
    <bean id="office" class="com.baobaotao.Office">
        <property name="officeNo" value="001"/>
    </bean>
    <bean id="car" class="com.baobaotao.Car" scope="singleton">
        <property name="brand" value=" red flag CA72"/>
        <property name="price" value="2000"/>
    </bean>
</beans>

 

In this way, when the Spring container is started, Autowired annotation beanpostprocessor will scan all beans in the Spring container. When @ Autowired annotation is found in the Bean, it will find the Bean matching it (matching by type by default) and inject it into the corresponding place.

According to the above configuration, Spring will directly use the Java reflection mechanism to automatically inject the two private member variables of "car" and "office" in the Boss. Therefore, after using @ Autowired , for member variables, you can delete their setter methods (setCar() and , setOffice()) from the Boss.

Of course, you can also annotate the method or constructor through @ Autowired , to see the following code:


                
package com.baobaotao;

public class Boss {
    private Car car;
    private Office office;

     @Autowired
    public void setCar(Car car) {
        this.car = car;
    }
 
    @Autowired
    public void setOffice(Office office) {
        this.office = office;
    }
    ...
}

 

At this time, @ Autowired} will find the beans of the input parameter type of the marked method and call the method to inject these beans automatically. The following usage method annotates the constructor:


                
package com.baobaotao;

public class Boss {
    private Car car;
    private Office office;
 
    @Autowired
    public Boss(Car car ,Office office){
        this.car = car;
        this.office = office ;
    }
 
    ...
}

 

Since the 'Boss' constructor has two input parameters, which are' car 'and' office ', @ Autowired will find beans matching their types and create' Boss' beans as input parameters of Boss (car, office).

By default, when using @ Autowired @ annotation for automatic injection, there must be only one matching candidate Bean in the Spring container. When a matching Bean cannot be found, the Spring container will throw a BeanCreationException exception and indicate that it must have at least one matching Bean. We can do an experiment:


                
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd ">
 
    <bean class="org.springframework.beans.factory.annotation.
        AutowiredAnnotationBeanPostProcessor"/> 

    <bean id="boss" class="com.baobaotao.Boss"/>

    <!-- take office Bean Comment out -->
    <!-- <bean id="office" class="com.baobaotao.Office">
    <property name="officeNo" value="001"/>
    </bean>-->

    <bean id="car" class="com.baobaotao.Car" scope="singleton">
        <property name="brand" value=" red flag CA72"/>
        <property name="price" value="2000"/>
    </bean>
</beans>

 

Because the "office Bean" is commented out, there is no Bean of type "office" in the Spring container, and the Boss's office attribute is marked with @ Autowired. When the Spring container is started, an exception is generated.

When it is uncertain that there must be a Bean of a class in the Spring container, @ Autowired(required = false) can be used where the Bean of this class needs to be automatically injected, which is equivalent to telling Spring that there is no error when a matching Bean cannot be found. Let's take a specific example:


                
package com.baobaotao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;

public class Boss {

    private Car car;
    private Office office;

    @Autowired
    public void setCar(Car car) {
        this.car = car;
    }
    @Autowired(required = false)
    public void setOffice(Office office) {
        this.office = office;
    }
    ...
}

 

Of course, in general, where @ Autowired , is used, beans need to be injected. If automatic injection is used and no injection is allowed, it will only be encountered in the development or testing period (for example, in order to quickly start the Spring container, only some module Spring configuration files are introduced), so @ Autowired(required = false) will be rarely used.

The opposite error is that if there are multiple candidate beans in the Spring container, the Spring container will throw a BeanCreationException at startup. Consider the following example:


                
... 
<bean id="office" class="com.baobaotao.Office">
    <property name="officeNo" value="001"/>
</bean>
<bean id="office2" class="com.baobaotao.Office">
    <property name="officeNo" value="001"/>
</bean>
...

 

We have configured two beans of type "office" in the Spring container. When the "office" member variable of the Boss is automatically injected, the Spring container will not be able to determine which Bean to use, so an exception occurs.

Spring allows us to specify the name of the injected Bean through the @ Qualifier} annotation, so the ambiguity is eliminated. The exception can be solved by the following methods:


                
@Autowired
public void setOffice(@Qualifier("office")Office office) {
    this.office = office;
}

 

@'office' in Qualifier("office") is the name of Bean, so when @ Autowired "and @ qualifier are used together, the strategy of automatic injection will change from byType to byName@ Autowired can annotate member variables, methods and constructors, while @ Qualifier's annotation objects are member variables, method arguments and constructor arguments. Because of different annotation objects, Spring does not unify @ Autowired , and @ qualifier , into one annotation class. The following is the code for commenting on member variables and constructor arguments:

Comment on member variables:


                
public class Boss {
    @Autowired
    private Car car;
 
    @Autowired
    @Qualifier("office")
    private Office office;
    ...
}

 

Comment on constructor arguments:


                
public class Boss {
    private Car car;
    private Office office;

    @Autowired
    public Boss(Car car , @Qualifier("office")Office office){
        this.car = car;
        this.office = office ;
	}
}

 

@Qualifier , can only be used in combination with @ Autowired , which is a beneficial supplement to @ Autowired ,. Generally speaking, @ qualifier# comments on the input parameters in the method signature will reduce the readability of the code, while comments on member variables are relatively better.

Spring supports not only the annotation of @ Autowired , defined by itself, but also several annotations defined by JSR-250 specification, namely @ Resource, @ PostConstruct , and @ PreDestroy.

@The function of resource is equivalent to @ Autowired, except that @ Autowired is automatically injected by byType and @ resource is automatically injected by byName by default@ Resource has two important attributes, name and type. Spring resolves the name attribute of @ resource annotation to the name of Bean, while the type attribute resolves to the type of Bean. Therefore, if the name attribute is used, the byName auto injection policy is used, while when the type attribute is used, the byType auto injection policy is used. If neither name nor type attribute is specified, byName automatic injection policy will be used through reflection mechanism.

The Resource annotation class is located in lib / J2EE / common annotations. Com of the Spring distribution package Jar class package, so you must add it to the class library of the project before using it. Let's take an example of using @ Resource:


                
package com.baobaotao;

import javax.annotation.Resource;

public class Boss {
    // Automatically inject beans of type Car
    @Resource
    private Car car;

    // Automatically inject Bean with Bean name of office
    @Resource(name = "office")
    private Office office;
}

 

In general, we don't need to use annotation similar to @ Resource(type=Car.class), because the type information of Bean can be obtained from the code through Java reflection.

To make the annotations of JSR-250 take effect, in addition to marking these annotations in the Bean class, you also need to register a BeanPostProcessor responsible for processing these annotations in the Spring container:

<bean 
  class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/>

 

CommonAnnotationBeanPostProcessor implements the BeanPostProcessor interface, which is responsible for scanning beans annotated with JSR-250 and performing corresponding operations on them.

Beans in the spring container have a life cycle. Spring allows beans to perform specific operations after initialization and before Bean destruction. You can customize the operation methods after initialization / before destruction by implementing the InitializingBean/DisposableBean interface, You can also specify the operation method that is called before initialization / destruction through the init-method/destroy-method property of the <Bean> element. About the life cycle of spring, the author describes it in detail in Chapter 3 of proficient in Spring 2.x - enterprise application development, which can be consulted by interested readers.

JSR-250 defines two annotation classes for specifying methods after initialization / before destruction, namely @ postconstruct and @ predestroy. These two annotations can only be applied to methods. The method of annotating @PostConstruct annotations will be called after class instantiation, and the method of tagging @PreDestroy will be called before class destruction.


                
package com.baobaotao;

import javax.annotation.Resource;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

public class Boss {
    @Resource
    private Car car;

    @Resource(name = "office")
    private Office office;

    @PostConstruct
    public void postConstruct1(){
        System.out.println("postConstruct1");
    }

    @PreDestroy
    public void preDestroy1(){
        System.out.println("preDestroy1"); 
    }
    ...
}

 

You only need to mark @ PostConstruct , or @ PreDestroy in front of the methods, and these methods will be executed by the Spring container after Bean initialization or before Bean destruction.

We know that no matter through the implementation of the InitializingBean/DisposableBean interface or through the init method / destroy method attribute of the < Bean > element, only one initialization / destruction method can be specified for the Bean. However, multiple initialization / destruction methods can be specified by using @ PostConstruct # and @ PreDestroy # annotations, and those annotated with @ PostConstruct # or @ PreDestroy # annotations will be executed during initialization / destruction.

Through the following test code, you can see how the Bean initialization / destruction method is executed:


                
package com.baobaotao;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AnnoIoCTest {

    public static void main(String[] args) {
        String[] locations = {"beans.xml"};
        ClassPathXmlApplicationContext ctx = 
            new ClassPathXmlApplicationContext(locations);
        Boss boss = (Boss) ctx.getBean("boss");
        System.out.println(boss);
        ctx.destroy();// Close the Spring container to trigger the execution of the Bean destruction method
    }
}

 

At this time, you will see that the @ postConstruct1() method marked with @ PostConstruct , will be triggered when the Spring container is started and the Boss , Bean is created, while the @ preDestroy1() method marked with @ PreDestroy , will be triggered when the Boss , Bean is destroyed before the Spring container is closed.

Spring 2.1 adds a new context Schema namespace, which provides convenient configuration for annotation driven, property file introduction, load time weaving and other functions. We know that the annotation itself will not do anything. It only provides metadata information. For metadata information to really work, the processor responsible for processing these metadata must work.

The "autowirediannotationbeanpostprocessor" and "CommonAnnotationBeanPostProcessor" described earlier are the processors that process these annotation metadata. However, it is unwieldy to define these beans directly in the spring configuration file. Spring provides us with a convenient way to register these beanpostprocessors, which is < context: annotation config / >. Please see the following configuration:


                
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:context="http://www.springframework.org/schema/context"
     xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
 http://www.springframework.org/schema/context 
 http://www.springframework.org/schema/context/spring-context-2.5.xsd">
 
    <context:annotation-config/> 

    <bean id="boss" class="com.baobaotao.Boss"/>
    <bean id="office" class="com.baobaotao.Office">
        <property name="officeNo" value="001"/>
    </bean>
    <bean id="car" class="com.baobaotao.Car" scope="singleton">
        <property name="brand" value=" red flag CA72"/>
        <property name="price" value="2000"/>
    </bean>
</beans>

 

< context: annotationconfig / > four beanpostprocessors, AutowiredAnnotationBeanPostProcessor, CommonAnnotationBeanPostProcessor, PersistenceAnnotationBeanPostProcessor and requiredannotationbeanpostprocessor, will be implicitly registered with the Spring container.

Before using the context namespace in the configuration file, the context namespace must be declared in the < beans > element.

Although we can use the automatic injection function in the Bean class through @ Autowired , or @ Resource , beans are defined in the XML file through < Bean > -- that is, beans are defined in the XML configuration file, and the function of automatic injection is provided for Bean member variables, method parameters or constructor parameters through @ Autowired , or @ Resource. Can you also define beans through annotations and completely remove the configuration defined by beans from the XML configuration file? The answer is yes. We can achieve this goal through the @ Component annotation provided by Spring 2.5.

Why can @ Repository only be marked on DAO classes? This is because the annotation not only identifies the class as a Bean, but also encapsulates the data access exception thrown in the marked class as the data access exception type of spring. Spring itself provides a rich data access exception structure independent of specific data access technology, which is used to encapsulate exceptions thrown by different persistence layer frameworks, making exceptions independent of the underlying framework.

Spring 2.5 adds three additional annotations with similar functions on the basis of @ Repository: @ Component, @ Service and @ constreller, which are used at different levels of the software system:

  • @Component is a generalized concept, which only represents a component (Bean) and can act at any level.
  • @Service usually works in the business layer, but at present, this function is the same as @ Component.
  • @Constreller usually acts on the control layer, but at present, this function is the same as @ Component.

By using @ Repository, @ Component, @ Service and @ constructor annotations on the class, Spring will automatically create the corresponding BeanDefinition object and register it in the ApplicationContext. These classes become Spring managed components. These three annotations are used in the same way as @ Repository except for classes at different software levels.

 

 

Next, we completely use annotations to define beans and complete the assembly between beans:


                
package com.baobaotao;

import org.springframework.stereotype.Component;

@Component
public class Car {
    ...
}

 

Just use the @ Component annotation at the class definition to define a class as a Bean in the Spring container. The following code defines Office} as a Bean:


                
package com.baobaotao;

import org.springframework.stereotype.Component;

@Component
public class Office {
    private String officeNo = "001";
    ...
}

 

In this way, we can inject @ Car # and @ Office Bean # defined above into the Boss class through @ Autowired #.


                
package com.baobaotao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component("boss")
public class Boss {
    @Autowired
    private Car car;

    @Autowired
    private Office office;
    ...
}

 

@Component # has an optional input parameter to specify the name of the Bean. In boss, we define the Bean name as "boss". In general, beans are singleton. Where beans need to be injected, they can be automatically injected only through the byType policy, so it is unnecessary to specify the name of the Bean.

After using @ Component annotation, the spring container must enable class scanning mechanism to enable annotation driven Bean definition and annotation driven Bean automatic injection strategy. Spring 2.5 extends the context namespace to provide this function. Please see the following configuration:


                
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
 http://www.springframework.org/schema/context 
 http://www.springframework.org/schema/context/spring-context-2.5.xsd">
    <context:component-scan base-package="com.baobaotao"/>
</beans>

 

Here, all the configuration contents of beans defined by < Bean > elements have been removed. Just add a line of < context: component scan / > configuration to solve all the problems - the Spring XML configuration file has been extremely simplified (of course, the configuration metadata is still needed, but exists in the form of comments)< Context: the base package attribute of component scan / > specifies the class package to be scanned. All classes in the class package and its recursive sub package will be processed.

< context: component scan / > it is also allowed to define filters to include or exclude some classes under the base package. Spring supports the following four types of filtering methods, which are described in the following table:


Filter typeexplain
notesIf com baobaotao. Someannotation is an annotation class. We can filter out the classes that use this annotation.
Class name assignmentFilter through the fully qualified class name. For example, you can specify that com baobaotao. Boss included in the scan, while com baobaotao. Car is excluded.
regular expression Define the filtered class through regular expression, as follows: com \ baobaotao\. Default.*
AspectJ expressionDefine the filtered class through AspectJ expression, as follows: com baobaotao..* Service+

Here is a simple example:

<context:component-scan base-package="com.baobaotao">
    <context:include-filter type="regex" 
        expression="com\.baobaotao\.service\..*"/>
    <context:exclude-filter type="aspectj" 
        expression="com.baobaotao.util..*"/>
</context:component-scan>

 

It is worth noting that the < context: component scan / > configuration item not only enables the function of scanning class packages to implement annotation driven Bean definitions, but also enables the function of annotation driven automatic injection (that is, autowirediannotationbeanpostprocessor and CommonAnnotationBeanPostProcessor are also implicitly registered internally). Therefore, when < context: component scan / > is used, You can remove < context: annotation config / >.

By default, beans defined through @ Component , are singleton. If you need to use beans with other Scope, you can achieve the goal through @ Scope , annotation, as shown in the following code:

@scopee


                
package com.baobaotao;
import org.springframework.context.annotation.Scope;
...
@Scope("prototype")
@Component("boss")
public class Boss {
    ...
}

 

In this way, when you get the # boss # Bean from the Spring container, every time you return a new instance.

In addition to providing @ Component annotation, Spring 2.5 also defines several annotations with special semantics, namely: @ Repository, @ Service, @ Controller. In the current Spring version, these three annotations are equivalent to @ component , but from the naming of annotation classes, it is easy to see that these three annotations correspond to persistence layer, business layer and control layer (Web layer) respectively. Although these three comments are nothing new compared with @ component , at present, Spring will add special functions to them in future versions. Therefore, if the Web application adopts the classic three-tier hierarchical structure, it is best to annotate the classes in the hierarchy with @ Repository, @ Service , and @ Controller , respectively in the persistence layer, business layer and control layer, and annotate the relatively neutral classes with @ component , respectively.

With these IOC annotations, can we completely get rid of the original XML configuration? The answer is No. There are several reasons:

  • Annotation configuration is not necessarily superior to XML configuration. If the dependency of beans is fixed (such as which DAO classes are used by the Service), the configuration information will not be adjusted during deployment, so the annotation configuration is better than the XML configuration; On the contrary, if this dependency will be adjusted during deployment, XML configuration is obviously better than annotation configuration, because annotation is an adjustment to Java source code. You need to rewrite the source code and recompile to implement the adjustment.
  • If the Bean is not a class written by itself (such as JdbcTemplate, SessionFactoryBean, etc.), annotation configuration cannot be implemented. At this time, XML configuration is the only available way.
  • Annotation configuration is often class level, while XML configuration can be more flexible. For example, compared with @ Transaction transaction annotation, Transaction configuration using aop/tx namespace is more flexible and simple.

Therefore, in the implementation of applications, we often need to use annotation configuration and XML configuration at the same time. For the configuration of class level and will not change, annotation configuration can be given priority; For those third-party classes and configurations that are prone to adjustment, XML configuration should be given priority. Spring will fuse the meta information of the two configuration methods before implementing Bean creation and Bean injection.