Spring learning notes (basic usage)

preface

Spring is a lightweight open source framework in the field of Java EE programming. It was first proposed and subsequently created by Rod Johnson in 2002. It is an application-based framework for solving the complexity of enterprise programming development and realizing agile development. The composition of the framework is shown in the figure below. The core modules include control inversion container, aspect oriented programming, Web development support, etc.

Spring has the following advantages over traditional EJB s.

  • IOC support: provide IOC container, which can control the dependencies between objects by Spring to avoid excessive program coupling caused by hard coding.
  • AOP support: using aspect oriented programming can easily solve some difficult problems of traditional OOP.
  • Transaction support: use declarative transaction to flexibly manage transactions and improve development efficiency and quality.
  • Embrace open source: it provides the integration and encapsulation of various excellent frameworks and third-party libraries to reduce the difficulty of use.
  • Lightweight and efficient: Spring's jar package is very small, takes up less resources and runs efficiently.

Chapter I Spring IOC

Section I coupling in program

  1. Coupling in programs

Coupling in programs refers to the interdependence between codes. For example, in the following code, the business layer needs to call the persistence layer to save. At this time, the business layer has a direct dependence on the persistence layer. If the persistence layer implementation class needs to be replaced, the business layer code needs to be modified, which does not comply with the opening and closing principle.

public class AccountServiceImpl implements AccountService {
    @Override
    public void saveAccount() {
        // Here, the business layer directly relies on the implementation classes of the persistence layer
        AccountDao accountDao = new AccountDaoImpl(); 
        accountDao.saveAccount();
    }
}
  1. Use factory mode to solve coupling

Firstly, the creation of persistence layer objects is handed over to a factory class, and the dependency will be changed from service layer - > persistence layer to service layer - > factory class. Then create the objects we need through reflection in the factory class, and place the full class name in the external configuration file to solve the direct dependencies in the code.

// 1. Define a general factory, read the full class name from the configuration file, and create objects through reflection
public class BeanFactory{
    private static Properties env = new Properties();
    
    // Load the class name information in the configuration file: key = accountDao value = org.example.dao.impl.AccountDaoImpl
    static{
        try {
            InputStream inputStream = BeanFactory.class.getResourceAsStream("/beans.properties");
            env.load(inputStream);
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    // Constructing objects from a generic factory
    public static Object getBean(String key){
         Object ret = null;
         try {
             Class clazz = Class.forName(env.getProperty(key));
             ret = clazz.newInstance();
         } catch (Exception e) {
            e.printStackTrace();
         }
         return ret;
     }
}

// 2. The service layer obtains the required dependencies from the factory through the key
public class AccountServiceImpl implements AccountService {
    @Override
    public void saveAccount() {
        AccountDao accountDao = BeanFactory.getBean("accountDao");
        accountDao.saveAccount();
    }
}

Now, when dynamically replacing the persistence layer implementation class, you only need to modify the external configuration file. The above solution idea of transferring the business layer's dependence on the persistence layer to the factory is called inversion of control.

Control inversion is a concept and an idea, which refers to handing over the call right of objects directly controlled by program code to the container, and realizing the assembly and management of objects through the container. Control inversion is the transfer of object control right, which is reversed from the program code itself to the external container, through which object creation, attribute assignment and dependency management are realized.

  1. Using Spring to resolve program coupling

Coupling in programs is a common problem. We don't need to write the above similar code repeatedly for every application. The industry has provided a general and recognized excellent solution Spring. Let's see how to use Spring to solve the coupling problem in programs.

  • Import Spring dependencies

    <?xml version="1.0" encoding="UTF-8"?>


    4.0.0

      <groupId>org.example</groupId>
      <artifactId>Spring-demo01</artifactId>
      <version>1.0-SNAPSHOT</version>
    
      <dependencies>
          
          <!-- introduce spring-context Dependency, while introducing spring-core,spring-beans,spring-aop,spring-expression-->
          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-context</artifactId>
              <version>5.2.9.RELEASE</version>
          </dependency>
         
      </dependencies>
    

Note: generally, we will choose the newer Spring 5 version, which requires your JDK version to be 1.8 or above and Tomcat version to be 8.0 or above.

  • Configure IOC container

A configuration file is required to configure the mapping relationship between key and full class name. This file name is generally application.xml and can be placed in any class path.

<?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.xsd">

    <!-- to configure accountDao -->
    <bean id="accountDao" class="org.example.dao.impl.AccountDaoImpl"/>
    
</beans>
  • Using IOC containers

In the business code, the required dependencies are obtained through the container, rather than directly creating dependent objects.

public class AccountServiceImpl implements AccountService {
    @Override
    public void saveAccount() {
        // Obtain the accountDao implementation class through the IOC container of Spring
        ApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml");
        AccountDao accountDao = (AccountDao)ctx.getBean("accountDao");
        
        accountDao.saveAccount();
    }
}

Section II IOC container

  1. ApplicationContext interface

The ApplicationContext interface inherits from BeanFactory and represents the IOC container of Spring. The container reads the instructions in the configuration metadata to instantiate, configure and assemble objects. In order to adapt to different scenarios, Spring provides multiple implementation classes. The common ones are as follows:

Implementation class description
ClassPathXmlApplicationContext loads the configuration file from the classpath, which is generally used by independent applications
FileSystemXmlApplicationContext loads the configuration file from the disk location. It is limited by the operating system and is not commonly used
AnnotationConfigApplicationContext loads configuration files from Java code, which is generally applicable to the annotation development mode based on Java configuration
XmlWebApplicationContext is applicable to the container implementation class of web applications. It can be used only when web related dependencies are introduced

The difference between ApplicationContext and BeanFactory

BeanFactory provides the most basic container functions, and ApplicationContext is a complete superset of BeanFactory, adding more enterprise specific functions.

  • Aspect oriented programming (AOP)
  • Web application (WebApplicationContext)
  • Internationalization (MessageSource)
  • Event publication

In particular, BeanFactory is always lazy to initialize beans, while ApplicationContext initializes all singleton beans when the container is started by default.

  1. Container creation

The creation of a container requires configuration metadata, which can be in the form of XML, annotations or Java code.

  • XML configuration mode

    //Load the metadata configured in services.xml and daos.xml to create a Spring IOC container
    ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

  • Annotation configuration mode

    //Loading annotations for spring configuration configuration creates a container
    ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);

The code of spring Configuration is as follows. It is generally a Configuration class (@ Configuration annotated class).

// Spring configuration class
//    1. @ComponentScan: scan the spring components in the com.itheima.Spring package
//    2. @Import: import configuration information from other classes
@Configuration
@ComponentScan(basePackages = "com.itheima.Spring")
@Import({ JdbcConfig.class })
public class SpringConfiguration {}

Of course, you can also use ordinary Component classes (@ Component annotated classes) or classes with JSR-330 metadata annotations.

  • Programming configuration mode

    public static void main(String[] args) {
    //Define a container (parameterless)
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();

      // Register configuration class
      ctx.register(AppConfig.class, OtherConfig.class);
      ctx.register(AdditionalConfig.class);
      
      // Scan configuration classes and refresh containers
      ctx.scan("com.acme");
      ctx.refresh();
      
      // Use container
      MyService myService = ctx.getBean(MyService.class);
      myService.show();
    

    }

  1. Closure of containers

In the Web environment, Spring's Web-based container implementation already has appropriate code, which can normally close the Spring IoC container when the relevant Web application is closed. In a non Web application environment, please register a close hook with the JVM so that Spring can call the destroy method set on your singleton bean to release all resources.

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public final class Boot {

    public static void main(final String[] args) throws Exception {
        // Create container
        ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");

        // Register close hook
        ctx.registerShutdownHook();	
    }
}

In what scenario is the JVM's close hook effective?

A: normal exit, System.exit(), Ctrl+C interrupt, OutofMemory downtime, Kill pid kill process (except kill -9), system shutdown, etc.

  1. Use of containers

After the container is created, we can retrieve the bean instance by using the method T getBean(String name, Class requiredType).

// Retrieving Spring instances by key and type
PetStoreService service = context.getBean("petStore", PetStoreService.class);

In addition, the ApplicationContext interface has several other ways to retrieve bean s, but ideally, your application code should never use them.

// After the object is obtained through the key, the forced type conversion is performed
Person person = (Person)ctx.getBean("person");

// Get objects through type matching (Note: only one Bean can be of Person type at this time)
Person person = ctx.getBean(Person.class);

In addition to the method of obtaining the object, the container also provides some auxiliary methods to obtain the relevant information of the container.

// Get the total number of beans
int count = ctx.getBeanDefinitionCount();

// Get the IDs of all beans
String[] beanDefinitionNames = ctx.getBeanDefinitionNames();

// Get the IDs of all beans of a certain type
String[] beanNamesForType = ctx.getBeanNamesForType(UserDao.class);

// Judge whether there is a bean with the specified id (the alias specified by name cannot be judged)
boolean isExist = ctx.containsBeanDefinition("userDao")
 
// Judge whether there is a bean with the specified id (you can judge the alias specified by name)
boolean isExist = ctx.containsBean("userDao")
  1. Container extension
  • BeanPostProcessor

The BeanPostProcessor interface can implant some custom logic before and after the initialization life cycle of a Bean to perform secondary processing on the created Bean.

// 1. Create a BeanPostProcessor
public class MyBeanPostProcessor implements BeanPostProcessor {
    
	/**
    * It is executed after the Bean completes instantiation and injection and before initialization
    *
    * @bean 		Bean object after injection has been completed
    * @beanName 	bean id attribute of
    * @return 		Modified Bean object
    */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    /**
    * Execute after the Bean completes initialization
    *
    * @bean 		Initialized Bean object completed
    * @beanName 	bean id attribute of
    * @return 		Modified Bean object
    */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Bean '" + beanName + "' created : " + bean.toString());
        return categroy;
    }
}

<!-- 2. Will customize BeanPostProcessor Register to Spring container-->
<bean id="myBeanPostProcessor" class="xxx.MyBeanPostProcessor"/>
  • BeanFactoryPostProcessor

The beanfactoryprocessor interface is used to read configuration metadata and modify it before instantiation of the Bean. Spring defines some built-in Bean factory post processors, such as PropertyPlaceholderConfigurer, which are used to parse placeholders in configuration files and perform string replacement operations.

<!-- Register a built-in BeanFactoryPostProcessor: PropertyPlaceholderConfigurer 
	1. locations: Specify an external property profile, typically properties format
	2. properties: Specify some attribute values in the form of key value pairs
	3. systemPropertiesMode: Check System attribute
		* never					Never find System attribute
		* fallback				If from properties-ref and location If the required attribute value is not found in, go to System Attribute lookup
		* override/evironment	Always find System Property and override values configured in other ways
-->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
	<property name="locations" value="classpath:com/something/strategy.properties"></property>
	<property name="properties" value="custom.strategy.class=com.something.DefaultStrategy"></property>
</bean>

<!-- Use a placeholder to get the property value of the configuration, which will be displayed in the Bean Replace before instantiation-->
<bean id="serviceStrategy" class="${custom.strategy.class}"/>

In addition, the Bean factory post processor related to properties also has PropertyOverrideConfigurer, which is used to override the configured properties.

<!-- Register a built-in BeanFactoryPostProcessor: PropertyOverrideConfigurer 
	location: Specify an external configuration file to store the attributes and values to be overwritten to beanName.property=value And supports composite properties
--> 
<context:property-override location="classpath:override.properties"/>

An example of an override.properties configuration file is shown below.

# The value of the driverClassName property of the overriding dataSource is com.mysql.jdbc.Driver
dataSource.driverClassName=com.mysql.jdbc.Driver
# Override tom's fred.bob.sammy attribute value of 123
tom.fred.bob.sammy=123
  • Some processing details of Ban(Factory)PostProcessor
    1. Bean(Factory)PostProcessor is container wide and is effective for all beans in the container.
    2. Bean(Factory)PostProcessor will be instantiated as soon as possible. Even if you configure lazy loading property for them, the container will ignore it.
    3. You have defined multiple bean (factory) postprocessors. You can control their execution order by implementing the Ordered interface and using the order attribute.
    4. If you use @ Bean to create Bean(Factory)PostProcessor, you must ensure that the return type is the implementation class of the interface, otherwise the container cannot detect correctly.
    5. Some infrastructure classes in AOP are implemented based on BeanPostProcessor, so the implementation class of this interface and the Bean that depends on it are not suitable for automatic proxy.
    6. Beanfactoryprocessor cannot modify the Bean definition that implements the beanfactoryprocessor interface.
    7. Although bean can be instantiated early through BeanFactory.getBean(), this is not recommended because it can bypass bean post-processing.
  1. Multi file configuration
  • Import external profile

You can also write all metadata in the same configuration file, or import metadata configuration in other files through tags.

<beans>
    <!-- The import root label is beans External profile for-->
    <import resource="services.xml"/>
    <import resource="resources/messageSource.xml"/>
    <import resource="/resources/themeSource.xml"/>
    
    <!-- use * As wildcard (Note: it must be ensured that the parent profile name cannot be satisfied * The format that can be matched, otherwise circular recursive inclusion will occur)-->
    <import resource="dao/spring-*-dao.xml"/>


    <bean id="bean1" class="..."/>
    <bean id="bean2" class="..."/>
</beans>
  • Import external properties file

Some contents that need to be modified frequently, such as Jdbc connection information, can be placed separately in a small configuration file for easy maintenance.

<!--How to import an external attribute file: use PropertyPlaceholderConfigurer -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="db-connection" value="db.properties"></property>
</bean>

<!-- Method 2 of introducing external property file: To introduce context Namespace-->
<!-- <context:property-placeholder location="classpath:db.properties"/>-->

<!--Configure connection pool-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${db.driverclass}"></property>
    <property name="url" value="${db.url}"></property>
    <property name="username" value="${db.username}"></property>
    <property name="password" value="${db.password}"></property>
</bean>

An example of the db.properties file is as follows:

db.driverclass=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/userDb?characterEncoding=utf-8
db.username=root
db.password=root

Section 3 Bean instantiation

  1. Create by construction method

The top-level tag of the Spring configuration file is beans, and multiple tags can be configured. A simple bean configuration example is as follows:

<bean id="accountService" class="org.example.service.impl.AccountServiceImpl"/>

Where the id attribute is the unique global id, which is used to get the object from the container. The class attribute is the corresponding fully qualified class name. By default, the parameterless constructor in the reflection call class is used to create the object. If the parameterless constructor is missing, it cannot be created.

  1. Create through FactoryBean

With the increasing complexity of the business, the creation of some complex objects cannot be directly realized through the new keyword, such as Connect, SqlSessionFactory, etc. Therefore, Spring provides a way to implement the FactoryBean interface to complete the configuration of complex objects. First, implement the FactoryBean interface, as shown in the following example.

Then reference the FactoryBean implementation class in the configuration file to create the required objects. The configuration is as follows:

<bean id="conn" class="com.baizhiedu.factorybean.ConnectionFactoryBean"/>

It is worth noting that this is very similar to the configuration of simple objects, but in fact, Spring will detect the class configured by the class attribute. If it is the implementation class of the FactoryBean interface, the complex objects created by this class (such as Connection) are obtained through the id value.

Tip: if you want to obtain an object of FactoryBean type, you can obtain it through ctx.getbean ("& conn"), and the obtained object is the ConnectionFactoryBean object.

  1. Create through normal factory classes

Although the above method solves the problem of complex object creation, the factory class used must implement the factrybean interface. In order to integrate some legacy systems, Spring can also use ordinary factory classes to create the required objects.

  • The factory method is static

This method is also used to specify the factory class in the class attribute, but the static method of creating the object needs to be additionally specified through the factory method attribute. An example is as follows:

public class StaticFactory {
  	// Create static methods for objects
  	public static IAccountService createAccountService() {
    	return new AccountServiceImpl();
  	}
}

<bean id="accountService" class="com.itheima.factory.StaticFactory"   
      factory-method="createAccountService"></bean>  
  • The factory method is non static

If the factory method is a non static method, leave the class attribute blank and use the factory bean attribute to reference the factory class object. An example is as follows:

public class InstanceFactory {
    // Non static methods for creating objects
    public IAccountService createAccountService(){  
        return new AccountServiceImpl();  
    }  
}  

<!-- Create factory class object -->
<bean id="instancFactory" class="com.itheima.factory.InstanceFactory"></bean>  

<!-- Create business object
        * factory-bean attribute:Used to specify the factory class bean of id
        * factory-method attribute:Specifies the method for creating objects in the instance factory
-->
 <bean id="accountService"   
       factory-bean="instancFactory"   
       factory-method="createAccountService"></bean> 

Section 4 Bean properties

  1. Bean life cycle

Life cycle refers to the whole process of bean from creation to destruction. Spring provides some mechanisms to allow us to intervene in the management of bean life cycle.

  • @PostConstruct /@PreDestroy annotation

@PostConstruct and @ PreDestroy are the life cycle annotations of JSR-250, which are implemented by Spring in the commonannotation beanpostprocessor. Methods annotated by @ PostConstruct will be called during Bean initialization, and methods annotated by @ PreDestroy will be called during Bean destruction.

public class CachingMovieLister {

    @PostConstruct
    public void populateMovieCache() {
        // populates the movie cache upon initialization...
    }

    @PreDestroy
    public void clearMovieCache() {
        // clears the movie cache upon destruction...
    }
}
  • InitializingBean/DisposableBean interface

If the bean implements the InitializingBean interface, the container will call its afterPropertiesSet() method when creating the bean. If the DisposableBean interface is implemented, the container will call its destroy() method when destroying the bean.

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>

public class AnotherExampleBean implements InitializingBean, DisposableBean {

    public void afterPropertiesSet() {
        // do some initialization work
    }

    public void destroy() {
        // do some destruction work (like releasing pooled connections)
    }
}
  • Init method / destroy method attribute

For XML based configuration metadata, you can use the init method / destroy method attribute to specify the name of the parameterless and return value free method as the initialization method and destruction method.

<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init" destroy-method="cleanup"/>


public class ExampleBean {

    public void init() {
        // do some initialization work
    }

    public void cleanup() {
        // do some destruction work (like releasing pooled connections)
    }
}

To simplify configuration, you can use the tag's default init method / default destroy method attribute to specify the default initialization and destruction methods for beans in all child tags. However, it must be noted that the Spring container guarantees to call the configured initialization callback immediately after providing all dependencies for the bean. Therefore, the initialization callback is called in the original bean reference, which means that the AOP interceptor has not been applied to the bean.

Tip: when configuring metadata with Java code, use the initMethod and destroyMethod properties of @ Bean to specify the initialization and destruction methods.

  • Execution sequence of different life cycle mechanisms

If multiple lifecycle mechanisms are configured for a bean, the method will execute only once when the method names are the same. If the method names are inconsistent, the lifecycle method will be called according to the principle that annotation configuration takes precedence, interface configuration takes precedence, and XML configuration is executed last.

1)use @PostConstruct Annotation method

2)InitializingBean Callback interface defined afterPropertiesSet()method

3)Custom configured init() method

4)use @PreDestroy Annotation method

5)DisposableBean Callback interface defined destroy()method

6)Custom configured destroy() method
  • Start and stop event callbacks

The start and stop event callbacks of beans can be defined by implementing the Lifecycle interface. When the container itself receives start and stop signal s (for example, for the stop / restart scenario at runtime), it will cascade these calls to all Lifecycle implementations defined in this context.

// Bean that implements the Lifecycle interface
@Component
public class MyLifeCycleBean1 implements Lifecycle {
	private boolean status = true;

    // When the container start s, execute this method if isRunning==false
	@Override
	public void start() {
		System.out.println("start");
	}
    
    // Determine whether the Bean is running
	@Override
	public boolean isRunning() {
		System.out.println("isRunning");
		status = !status;
		return status;
	}
    
    // When the container stop s, if isRunning==true, execute this method
	@Override
	public void stop() {
		System.out.println("stop");
	}
}

public class SpringTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MyLifeCycleBean1.class);
        ctx.registerShutdownHook();
        ctx.start();
        //ctx.stop(); //  After registering the close hook, this method is called when the container is closed
    }
}
/*
isRunning
start
isRunning
stop
 */

If you want to control the start and stop life cycles more finely, you can implement the SmartLifecycle interface. If the isAutoStartup method returns true, the container can be automatically started after creation; Use the getPhase() method to return the appropriate phase value to control the start and stop sequence. The greater the value, the later the execution and the earlier the destruction. The default value is integer.max_ Value (if the LifeCycle interface is implemented, it is 0 by default). Please refer to the official Spring documentation for detailed usage.

  1. Scope of Bean

The scope of a Bean determines the scope of the Bean and whether to create a new instance when obtaining an object. It is configured through the scope attribute.

  • singleton

The scope of the default configuration is singleton, which means that the scope of a single instance is created only once. Generally, it is created when the container is started and destroyed when the container is destroyed.

<bean id="accountDao" class="org.example.dao.impl.AccountDaoImpl" scope="singleton"/>
  • prototype

Prototype represents the prototype scope (multiple instances). Each time an object is obtained from the container, a new instance will always be created and returned.

<bean id="accountDao" class="org.example.dao.impl.AccountDaoImpl" scope="prototype"/>

Different from other scopes, Spring does not manage the full life cycle of the prototype Bean and does not call the configured life cycle destruction callback.

Note: some details about injecting prototype beans into singleton beans

  • When relying on the prototype Bean in a singleton Bean, because the singleton Bean will only be initialized by the IoC container once and its dependency will only be processed once, the dependent prototype Bean will also become an "implicit" Singleton.

  • There are two ways to solve this problem. One is to rely on lookup every time when using the prototype Bean, so that the IoC container will recreate the prototype Bean every time;

  • Another way is to use @ Lookup annotation. This is an official solution. It should be noted that the methods using @ Lookup annotation must be declared as abstract methods.

  • Scope for web

In Web applications, some special bean scopes are provided, such as request, session, application and websocket, which are consistent with the scopes of request, session, ServletContext and websocket in HTTP. They are not explained in detail here. For details, please refer to the official Spring documentation.

  • Custom scope

The Bean Scope mechanism is extensible, and you can define your own Scope or even redefine some built-in scopes (except singleton and prototype). To integrate custom scopes into the Spring container, you need to implement the Scope interface. The Scope interface has four methods to get objects from the Scope, delete objects from the Scope, and then destroy them.

// Returns an object from the base range. If the object cannot be found, a new instance is created to return
Object get(String name, ObjectFactory objectFactory)

// Delete the object from the underlying scope and return the deleted value. If the object is not found, return null
Object remove(String name)
    
// Registers the callback that the scope should perform when destroying the scope or the specified object in the scope
void registerDestructionCallback(String name, Runnable destructionCallback)
    
// Gets the session identifier of the underlying scope. The identifier of each scope is different. For a session scoped implementation, this identifier can be a session identifier.
String getConversationId()

After writing a custom Scope implementation, you need to make the Spring container aware of your new Scope. Register a new Scope in the Spring container using the following method.

Scope threadScope = new SimpleThreadScope();
beanFactory.registerScope("thread", threadScope);

In addition to registering Scope using the program, you can also register Scope declaratively by using the CustomScopeConfigurer class, as shown in the following example:

......

    <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
        <property name="scopes">
            <map>
                <entry key="thread">
                    <bean class="org.springframework.context.support.SimpleThreadScope"/>
                </entry>
            </map>
        </property>
    </bean>

    <bean id="thing2" class="x.y.Thing2" scope="thread"/>

......

Starting with Spring 3.0, thread scopes are available, but are not registered by default. For more information, see the documentation for SimpleThreadScope.

  1. Bean aware interface

Aware interface (Aware) means that beans declare to the container that they need some infrastructure dependency. These interface callbacks are filled after the regular bean attribute, but are called before initializing callbacks (such as InitializingBean, afterPropertiesSet or custom init-method).

  • ApplicationContextAware

When ApplicationContext creates an object instance that implements the ApplicationContextAware interface, the instance will get a reference to the ApplicationContext.

// ApplicationContextAware interface definition
public interface ApplicationContextAware {
    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
  • BeanNameAware

When ApplicationContext creates a class that implements the BeanNameAware interface, the class will get a reference to the name defined in its associated object definition.

// BeanNameAware interface definition
public interface BeanNameAware {
    void setBeanName(String name) throws BeansException;
}
  • Other sensing interfaces

    Name Injected Dependency
    Event publisher of ApplicationEventPublisherAware ApplicationContext
    BeanFactoryAware BeanFactory
    BeanClassLoaderAware class loader (used to load Bean classes)
    LoadTimeWeaverAware Weaver (used to process class definitions at load time)
    MessageSourceAware configuration policy for parsing messages (supports parameterization and internationalization)
    NotificationPublisherAware Spring JMX notifies Publishers
    ResourceLoaderAware resource loader (for low-level resource access)
    ServletConfigAware the ServletConfig that the current container is running (valid only in Web environments)
    ServletContextAware the ServletContext that the current container is running (valid only in the Web environment)

Note: using these interfaces binds your code to the Spring API and does not follow the "inversion of control" style. Therefore, we recommend using them for infrastructure beans that need to access containers programmatically.

  1. Automatic assembly of Bean

The Spring container can automatically assemble the relationship between collaborative beans, that is, after creating objects, it can automatically find the required dependencies from the container and inject them. This can greatly reduce the need to specify attributes or constructor parameters, and when a dependency is added in the class, the dependency can be met automatically without modifying the configuration.

There are four modes of automatic assembly, which are specified by the autowire attribute of the bean tag.

Mode Explanation
No (default) does not perform automatic assembly, and only the ref attribute defines the dependencies between beans, which can provide better control and clarity.
byName is automatically assembled according to the attribute name, and Spring finds the matching dependent Bean in the container through the attribute name for injection.
byType is automatically assembled according to the attribute type. If a unique matching dependent Bean is found, it will be injected. However, if more than one is found, a fatal exception is thrown.
Similar to byType, constructor is suitable for automatic assembly of constructor parameters. The difference is that if no matching dependent Bean is found, a fatal exception will also be thrown.

<!-- byName Automatic injection -->
<bean id="myStudent" class="com.bjpowernode.ba04.Student" autowire="byName">
    <property name="name" value="Li Si"/>
    <property name="age" value="22" />
    <!--Assignment of reference type-->
    <!--<property name="school" ref="mySchool" />-->
</bean>

<!-- byType Automatic injection -->
<bean id="myStudent" class="com.bjpowernode.ba05.Student" autowire="byType">
    <property name="name" value="Zhang San"/>
    <property name="age" value="26" />
    <!--Assignment of reference type-->
    <!--<property name="school" ref="mySchool" />-->
</bean>

If multiple beans are matched using ByType auto assembly, you can set the autowire candidate property of some beans to false and remove them from the candidate list. Alternatively, you can set the primary attribute of a Bean to true as the primary candidate. The above two configurations are only valid for automatic assembly by type (byType/Constructor), and automatic assembly by name (byName) is not affected.

  1. Bean definition inheritance

In Bean configuration, you can specify another Bean as the parent Bean of the Bean through the parent attribute, so as to inherit some reusable configuration data, overwrite some values or add other values as needed. This is an embodiment of a template method mode.

<!-- ParentBean Definition of. General will ParentBean Mark as abstract,Use as pure template Bean Use, and can be omitted class attribute -->
<bean id="inheritedTestBean" abstract="true" >
    <property name="name" value="parent"/>
    <property name="age" value="1"/>
</bean>

<!-- ChildBean Definition of-->
<bean id="inheritsWithDifferentClass" class="org.springframework.beans.DerivedTestBean" init-method="initialize"
      parent="inheritedTestBean"> 
    <property name="name" value="override"/>
    <!-- Will from ParentBean inherit age Property injection -->
</bean>

ChildBean can inherit the scope, life cycle, attribute injection and other information of the Bean from ParentBean, but some information such as dependencies and automatic assembly mode are always obtained from subclasses, which requires us to pay more attention in the development process.

be careful:

  1. Spring inheritance is object level inheritance, and subclasses inherit the attribute values of parent objects. Therefore, in spring, different classes can inherit from each other.

  2. Java is class level inheritance, which inherits the class structure information of the parent class. If you use java code to configure metadata, you can directly use the inheritance mechanism of Java to reuse the configuration information of metadata.

  3. Bean other properties

  • Alias (name/alias)

In addition to the unique id attribute, a Bean can also define several aliases to be compatible with different business systems. It is usually implemented by using the name attribute or label.

<!-- to configure service,And specify aliases separated by commas, semicolons, or spaces-->
<bean id="accountService" name="accountService2,accountService3" class="org.example.service.impl.AccountServiceImpl"/>

<!--use alias Label definition alias-->
<alias name="accountService2" alias="accountServiceA"/>
  • Initialization order (depends on)

The dependencies on attribute can specify the initialization time dependency or the corresponding destruction time dependency when the Bean is a singleton.

<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />

<!--
	depends-on: In initialization beanOne Force initialization before manager and accountDao,Destroy first when destroying manager and accountDao,Re destruction beanOne. 
-->
<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
    <property name="manager" ref="manager" />
</bean>
  • Lazy init

The singleton Bean is instantiated by default when the container is created. If you want the Bean to be re created when it is used, you can specify the lazy init attribute to be true.

<!-- to configure ExpensiveToCreateBean Delayed loading -->
<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>

If you need to configure multiple beans, you can also select the default lazy init attribute of the beans tag to set overridable default values for all beans inside it.

<beans default-lazy-init="true">
    <!-- no beans will be pre-instantiated... -->
</beans>

Tip: if you use annotation configuration, you can use @ Lazy annotation to realize Lazy loading, and the annotation can be placed on the injection point marked @ Autowired or @ Inject. In this case, it leads to the injection of an inert parsing agent.

Section 5 dependency injection configuration

Dependency injection is an implementation of the idea of control inversion. It means that if another object needs to be called for assistance during program operation, it is not necessary to create the callee in the code, but depends on the external container, which is created and passed to the program.

  1. Constructor Inject

Constructor based injection is accomplished by calling the parameterized constructor by the container, and each parameter represents a dependency.

<!--Construct injection, using name attribute
	1. name: Specifies the constructor parameter name.(In progress set Composite attribute names can be used during injection, such as student.birthday.year)
	2. value: Used to inject basic attribute values and String
	3. ref: Used to inject reference types
	4. Set, null, inline Bean The injection of shall be done using sub tags
-->
<bean id="myStudent" class="com.bjpowernode.ba03.Student">
    <constructor-arg name="myage" value="22" />
    <constructor-arg name="myname" value="Li Si"/>
    <constructor-arg name="mySchool" ref="mySchool"/>
</bean>

In rare cases where the constructor argument name cannot be used (usually, if the bytecode is compiled without debugging information), the constructor can be matched through the parameter index, and the index attribute can be omitted if it can be inferred.

<!--Construct injection, using index,The position of the parameter. The position of the construction method parameter from left to right is 0,1,2-->
<bean id="myStudent2" class="com.bjpowernode.ba03.Student">
    <constructor-arg index="1" value="28"/>
    <constructor-arg index="0" value="Zhang San"/>
    <constructor-arg index="2" ref="mySchool" />
</bean>

<!--Construction injection, omitted index attribute-->
<bean id="myStudent3" class="com.bjpowernode.ba03.Student">
    <constructor-arg  value="Zhang Feng"/>
    <constructor-arg  value="28"/>
    <constructor-arg  ref="mySchool" />
</bean>

You can also use a more concise syntax for constructor injection configuration, but it is best to use an IDE that supports automatic attribute completion when creating bean definitions, otherwise typos are found at run time rather than design time.

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:c="http://www.springframework.org/schema/c"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="thingOne" class="x.y.ThingTwo"/>
    <bean id="thingTwo" class="x.y.ThingThree"/>

    <!-- use c Namespaces simplify how construct injection is written 
  			1. Basic type usage c:Attribute name="Attribute value"
			2. Reference type usage c:Attribute name_ref="Attribute value"
	-->
    <bean id="thingOne" class="x.y.ThingOne" 
          c:thingTwo-ref="thingTwo" 
          c:thingThree-ref="thingThree" 
          c:email="[emailprotected]"/>
</beans>

Tip: when using a factory to create a Bean object, you can inject the parameters of the factory method through the constructor Arg tag in the same way as the constructor injection.

  1. Set method injection

The injection based on the Set method is to call the object's set method for attribute injection after the object is created.

<bean id="now" class="java.util.Date"></bean> 

<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
    <property name="name" value="test"></property>
    <property name="age" value="21"></property>
    <property name="birthday" ref="now"></property>
</bean>

Similarly, you can use the p namespace to simplify writing.

<beans xmlns="http://www.Springframework.org/schema/beans"
    xmlns:p="http://www.Springframework.org/schema/p"
    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.xsd">

    <bean id="now" class="java.util.Date"></bean> 
    
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl4" 
          p:name="test" 
          p:age="21" 
          p:birthday-ref="now"/>
</beans> 
  1. Injection collection properties

As the name suggests, it is to inject attributes into the collection members in the class. It is also injected by the set method, except that the data types of variables are collections.

<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
    <!-- Inject data into the array -->
    <property name="myStrs">
        <set>
            <value>AAA</value>
            <value>BBB</value>
            <value>CCC</value>
        </set>
    </property>

    <!-- injection list Aggregate data -->
    <property name="myList">
        <array>
            <value>AAA</value>
            <value>BBB</value>
            <value>CCC</value>
        </array>
    </property>

    <!-- injection set Aggregate data -->
    <property name="mySet">
        <list>
            <value>AAA</value>
            <value>BBB</value>
            <value>CCC</value>
        </list>
    </property>

    <!-- injection Map data -->
    <property name="myMap">
        <props>
            <prop key="testA">aaa</prop>
            <prop key="testB">bbb</prop>
        </props>
    </property>

    <!-- injection properties data -->
    <property name="myProps">
        <map>
            <entry key="testA" value="aaa"></entry>
            <entry key="testB">
                <value>bbb</value>
            </entry>
        </map>
    </property>
</bean> 

Tip: there are two kinds of set data: single column set (array,list,set) and double column set (map,entry,props,prop). The injection methods of the same type of set are compatible.

If the Bean has an inheritance relationship, you can merge the collection of parent classes through the merge attribute.

<!-- Define a ParentBean,And give adminEmails Set for injection -->
<beans>
    <bean id="parent" abstract="true" class="example.ComplexObject">
        <property name="adminEmails">
            <props>
                <prop key="administrator">[emailprotected]</prop>
                <prop key="support">[emailprotected]</prop>
            </props>
        </property>
    </bean>

<!-- Define a ChildBean,Also give adminEmails Set for injection 
	1. merge: Inherit the properties in the parent class collection and override them selectively
-->
    <bean id="child" parent="parent">
        <property name="adminEmails">
            <props merge="true">
                <prop key="sales">[emailprotected]</prop>
                <prop key="support">[emailprotected]</prop>
            </props>
        </property>
    </bean>
<beans>
  1. Injection prototype Bean

If a Bean is singleton and depends on a prototype Bean, the prototype Bean cannot be injected directly in the above way. A new instance must be created every time it is used.

  • Implementation aware interface

Obtain the reference of the container by implementing the aware interface ApplicationContextAware or beanfactory aware, and then call the getBean method to create the prototype Bean.

public class SingleBeanDemo implements BeanFactoryAware {
    private BeanFactory beanFactory;

    // Implement the abstract method of aware interface, call back when creating Bean and inject the reference of container
    @Override
	public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }
    
    // Get prototype Bean instance from container
	protected PrototypeBeanDemo getPrototypeBeanDemo(){
        this.beanFactory.getBean("prototypeBeanDemo", PrototypeBeanDemo.class)();
    }
    
    // Using prototype beans in business methods
    public void bizMethod() {
        PrototypeBeanDemo prototypeBeanDemo = getPrototypeBeanDemo();
    }
}
  • Use find method

The above method relies on the Spring framework, and the program code actively obtains the object, which does not comply with the principle of inversion of control. The following will use the XML configuration method to complete the above code by Spring.

// SingleBeanDemo is created as an abstract class, and getPrototypeBeanDemo() method is set as an abstract method to be implemented by Spring for us
public abstract class SingleBeanDemo {
    protected abstract PrototypeBeanDemo getPrototypeBeanDemo();
    
    public void bizMethod() {
        PrototypeBeanDemo prototypeBeanDemo = getPrototypeBeanDemo();
    }
}

<!-- PrototypeBeanDemo definition-->
<bean id="prototypeBeanDemo" class="fiona.apple.PrototypeBeanDemo" scope="prototype">
</bean>

<!-- SingleBeanDemo definition
	lookup-method: Specify the lookup method and the prototype for the lookup Bean
-->
<bean id="singleBeanDemo" class="fiona.apple.SingleBeanDemo">
    <lookup-method name="getPrototypeBeanDemo" bean="prototypeBeanDemo"/>
</bean>

It can also be configured by annotation, and the value attribute of @ Lookup can be resolved according to the return type declared by the Lookup method.

@Component
public abstract class SingleBeanDemo {
    @Lookup /*("prototypeBeanDemo")*/
    protected abstract PrototypeBeanDemo getPrototypeBeanDemo();

    public void bizMethod() {
        PrototypeBeanDemo prototypeBeanDemo = getPrototypeBeanDemo();
    }
}

be careful:

  1. The signature of the lookup method must be in the form of < public|protected > [Abstract] themethodname (no arguments).
  2. The search method is allowed to be non abstract, and Spring will overwrite the original method.
  3. Since Spring implements this method through CGLib, neither this class nor lookup method can be modified by final.
  4. The lookup method is not applicable to the factory method and the @ Bean method in the configuration class. Therefore, in this case, the instance is not created by Spring and cannot be dynamically proxied.
  • Inject ObjectFactory

    //First inject the ObjectFactory of the singleton, and then obtain the instance of the prototype Bean through the getObject method
    @Component
    public class SingleBeanDemo {
    @Autowired
    ObjectFactory prototypeBeanDemoFactory;

      public void bizMethod() {
          PrototypeBeanDemo prototypeBeanDemo = (PrototypeBeanDemo)factory.getObject();
      }
    

    }

  1. Circular dependency problem

During dependency injection, if A depends on B, B depends on C, and C depends on A, there may be A circular dependency problem and throw A beancurrentyincreationexception. The following will use cases to analyze three different injection scenarios.

// There are three objects that need to be created, and they depend on each other (A depends on B, B depends on C, and C depends on A).
public class StudentA { private StudentB studentB; public void setStudentB(StudentB studentB) { this.studentB = studentB; } public StudentA() { } public StudentA(StudentB studentB) { this.studentB = studentB; } } 
public class StudentB { private StudentC studentC ; public void setStudentC(StudentC studentC) { this.studentC = studentC; } public StudentB() { } public StudentB(StudentC studentC) { this.studentC = studentC; } } 
public class StudentC { private StudentA studentA; public void setStudentA(StudentA studentA) { this.studentA = studentA; } public StudentC() { } public StudentC(StudentA studentA) { this.studentA = studentA; } } 
  • Injection through constructor

First, the dependencies described above are described through constructor injection. When creating the container, the expected beancurrentyincreationexception exception is thrown.

<bean id="a" class="cn.mayday.springrecycledp.demo1.StudentA"> 
    <constructor-arg index="0" ref="b"></constructor-arg> 
</bean> 

<bean id="b" class="cn.mayday.springrecycledp.demo1.StudentB"> 
    <constructor-arg index="0" ref="c"></constructor-arg> 
</bean> 

<bean id="c" class="cn.mayday.springrecycledp.demo1.StudentC"> 
    <constructor-arg index="0" ref="a"></constructor-arg> 
</bean> 

The Spring container will place each Bean identifier being created in a "currently created Bean pool", and the Bean identifier will always be maintained in this pool during the creation process. Therefore, if you find yourself in the "currently created Bean pool" during Bean creation, you will throw a beancurrentyincreationexception, indicating circular dependency; The created Bean will be cleared from the "currently created Bean pool".

According to the above source code implementation analysis: the Spring container first creates A single instance StudentA, which depends on StudentB, and then puts A in the "currently created Bean pool". At this time, StudentB is created, StudentB depends on StudentC, and then puts B in the "currently created Bean pool". At this time, StudentC is created, and StudentC depends on StudentA. However, StudentA is already in the pool, so an error will be reported, Because the beans in the pool are uninitialized, they will rely on errors (the initialized beans will be removed from the pool).

  • Injection through Set method (single case range)

Let's take another look at using the Set method injection and setting the Bean range to the singleton range. It is found that no exception is thrown when creating the container.

<bean id="a" class="cn.mayday.springrecycledp.demo1.StudentA" scope="singleton"> 
    <property name="studentB" ref="b"></property> 
</bean> 

<bean id="b" class="cn.mayday.springrecycledp.demo1.StudentB" scope="singleton"> 
    <property name="studentC" ref="c"></property> 
</bean> 

<bean id="c" class="cn.mayday.springrecycledp.demo1.StudentC" scope="singleton"> 
    <property name="studentA" ref="a"></property> 
</bean> 

Why not throw exceptions using the Set method? The key is that spring instantiates the Bean object before setting the object properties. Spring instantiates the Bean object with the constructor first, and then stores it in a Map. When StudentA, StudentB and StudentC are instantiated, then it sets the properties of the object. At this time, StudentA depends on StudentB, and will go to the Map to take out the single instance StudentB object in it, and so on. There will be no looping problem.

  • Injection via Set method (prototype scope)

If the scope of a Bean is a prototype, a beancurrentyincreationexception exception will be thrown even if Set injection is used.

<bean id="a" class="cn.mayday.springrecycledp.demo1.StudentA" scope="prototype"> 
    <property name="studentB" ref="b"></property> 
</bean> 

<bean id="b" class="cn.mayday.springrecycledp.demo1.StudentB" scope="prototype"> 
    <property name="studentC" ref="c"></property> 
</bean> 

<bean id="c" class="cn.mayday.springrecycledp.demo1.StudentC" scope="prototype"> 
    <property name="studentA" ref="a"></property> 
</bean> 

Because the "prototype" scope beans, the Spring container does not perform level 3 caching, so it is impossible to expose a Bean under creation in advance.

  1. Static variable injection

When we encapsulate tool classes, most of them provide static methods, and static methods can only access static variables. At this time, we need to inject the dependencies we need into static variables.

  • Injection through Set method

    @Component
    public class RedisLockUtil {
    private static RedisTemplate<Object, Object> redisTemplate;

      @Autowired
      public void setRedisTemplate(RedisTemplate<Object, Object> redisTemplate) {
          RedisLockUtil.redisTemplate = redisTemplate;
      }
    

    }

  • Use @ PostConstruct annotation

    @Component
    public class RedisLockUtil {
    private static RedisTemplate<Object, Object> redisTemplate;

      @Autowired
      private RedisTemplate<Object,Object> redisTemplate_copy;
    
      @PostConstruct
      public void init(){
      	RedisLockUtil.redisTemplate=redisTemplate_copy;
      }
    

    }

  1. Common classes use beans

If a class is an ordinary Java and is not managed by the Spring container, how to use the Bean instance created by the Spring container?

  • SpringUtils tool class

Define a tool class, implement the container aware interface, and provide the function of obtaining beans to the outside through static methods.

@Component
public class SpringUtils implements ApplicationContextAware {
    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringUtils.applicationContext = applicationContext;
    }

    public static <T> T getBean(Class<T> requiredType) {
        return applicationContext.getBean(requiredType);
    }

    public static <T> T getBean(String beanName) {
        return (T) applicationContext.getBean(beanName);
    }

    public static <T> T getBean(Class<T> requiredType, Object... args) {
        return applicationContext.getBean(requiredType, args);
    }

    public static <T> T getBean(String beanName, Object... args) {
        return (T) applicationContext.getBean(beanName, args);
    }

    public static <T> T getBean(String beanName, Class<T> requiredType) {
        return (T) applicationContext.getBean(beanName, requiredType);
    }

    public int getBeanDefinitionCount(){
        return applicationContext.getBeanDefinitionCount();
    }

    public String[] getBeanDefinitionNames(){
        return applicationContext.getBeanDefinitionNames();
    }
}
  • Static Bean

Modify the code of the Bean and set the this pointer of the Bean to a static variable when initializing the callback.

@Component
public class FundDispatchLogUtils {
    // Define a static variable to hold the this pointer
    private static FundDispatchLogUtils instance;

    // Save this in a static variable
    @PostConstruct
    public void init() {
        instance = this;
    }

    // Provides a static method to get this
    public static FundDispatchLogUtils getInstance() {
        return instance;
    }
}
  1. Custom type processor

Spring converts the string type data in the configuration file into the type data corresponding to the member variables in the object through the type converter, and then completes the injection. When spring does not provide a specific type converter, the programmer needs to define the type converter himself.

  • Implement the Converter interface

    /**

    • Customize the type converter to complete the conversion of string - > date.
      */
      public class MyDateConverter implements Converter<String, Date> {
      private String pattern;

      public void setPattern(String pattern) {
      this.pattern = pattern;
      }

      @Override
      public Date convert(String source) {
      Date date = null;
      try {
      SimpleDateFormat sdf = new SimpleDateFormat(pattern);
      date = sdf.parse(source);
      } catch (ParseException e) {
      e.printStackTrace();
      }
      return date;
      }
      }

  • Register custom type converter

Section 6 annotation based IOC configuration

Starting from version 2.x, Spring provides annotation to configure IOC containers and register beans, making full use of the context provided in the program to make the configuration shorter and cleaner.

When using ClassPathXmlApplicationContext based on XML configuration to build IOC containers, you need to turn on the annotation configuration switch in 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"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- Turn on the annotation configuration switch -->
    <context:annotation-config/>

</beans>

Turning on the annotation configuration switch will automatically register the following postprocessors and scan the annotations on the Bean in the context of the application that defines it.

  1. AutowiredAnnotationBeanPostProcessor
  2. CommonAnnotationBeanPostProcessor
  3. PersistenceAnnotationBeanPostProcessor
  4. RequiredAnnotationBeanPostProcessor

You can also use the AnnotationConfigApplicationContext to build a container directly based on the annotation configuration, and then use the @ importquote annotation to import the XML configuration.

@Configuration
@ImportResource("classpath:beans.xml")
public class AppConfig {

}

be careful

  1. The annotation configuration invades the source code, and the configuration is scattered, which is not conducive to maintenance. It should be selected in combination with the actual situation.

  2. Spring gives priority to injecting the attributes of annotation configuration. If the same attributes are configured in XML, the previous configuration will be overwritten.

  3. Mark components with @ Component

@The Component annotation acts on classes, interfaces, enumerations or other annotations, marks the element as a Spring component, and the unique value attribute is used to specify the name of the component.

// Define a Bean whose id is the lowercase initial of the class name (userServiceImpl)
@Component("userServiceImpl")
public class UserServiceImpl implements UserService {
}

In order to distinguish whether the component belongs to the persistence layer, Service layer or control layer, three semantic annotations are defined: @ Repository, @ Service and @ Controller. There may be some special semantics in the future, such as @ Repository being supported as a marker for automatic exception conversion in the persistence layer.

  1. Scan components using @ Componnet

@ComponentScan is generally used on the @ Configuration class to automatically detect the marked components and register the stereotype class with the ApplicationContext with the corresponding BeanDefinition instance.

@Configuration
@ComponentScan(basePackages = "org.example") // Base packages: scanned package names separated by semicolons, commas, or spaces.
// @ComponentScan("org.example")
public class AppConfig  {
    ...
}

The equivalent XML configuration is as follows.

<!-- Configure component scanning and implicitly enable annotation configuration(<context:annotation-config>) -->
<context:component-scan base-package="org.example"/>
  • Configure scan filter

By default, all components marked by @ Component and its derived annotations will be scanned. You can modify and extend this behavior by setting filters through the includeFilters and excludeFilters properties. The types and descriptions of filters are shown in the following list.

Filter Type Example Expression Description
annotation (default) org.example.SomeAnnotation is an annotation that exists at the type level of the target component.
assignable org.example.SomeClass the class (or interface) to which the target component can be assigned (extended or implemented).
aspectj org.example... The AspectJ type expression to be matched by the Service + target component.
regex org.example.Default. The regular expression to match the target component class name.
Custom org.example.mytypefilter is a custom implementation of the org.springframework.core.type. Typefilter interface.

/**
	@Configuration: Declare this class as a configuration class
	@ComponentScan: Component scan
		basePackages: Package name scanned
		includeFilters: Contains filters that scan only components that meet the criteria
		excludeFilters: Exclude the filter and do not scan the components that meet the conditions
*/
@Configuration
@ComponentScan(basePackages = "org.example",
        includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
        excludeFilters = @Filter(Repository.class))
public class AppConfig {
    ...
}

The equivalent XML configuration is as follows.

<beans>
    <context:component-scan base-package="org.example">
        <context:include-filter type="regex" expression=".*Stub.*Repository"/>
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>
</beans>

You can also refer to the following. The following is a case used in an online project.

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
@RestController
@EnableTransactionManagement
@ComponentScan(basePackages = { "com.szkingdom.kfms.**", "com.szkingdom.fs.**", "com.szkingdom.koca.**" })
@MapperScan(basePackages = { "com.szkingdom.**.dao.**" })
public class KfmsBootApplication {
    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(KfmsBootApplication.class);
        springApplication.run(args);
    }
}

Tips:
You can set the useDefaultFilters (or use default filters) property to false to disable the removal of default filters, which will disable automatic detection of classes with @ Component, @ Repository, @ Service, @ Controller or @ Configuration annotations.

  • Custom name generation policy

When a component is automatically detected during scanning, its bean name is generated by the BeanNameGenerator policy known to the scanner. The configured value attribute value is used by default. If it is not configured, the lowercase initial of the class name is used as the bean name.

// Use the value attribute value myMovieLister as the bean name
@Service("myMovieLister")
public class SimpleMovieLister {
    // ...
}

// Use movieFinderImpl as the bean name
@Repository
public class MovieFinderImpl implements MovieFinder {
    // ...
}

If you want to modify the name generation policy, you can implement the BeanNameGenerator interface, provide a parameterless constructor, and then configure it in the nameGenerator property of the @ ComponentScan annotation.

// nameGenerator: Specifies the custom name generation policy
@Configuration
@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class)
public class AppConfig {
    ...
}

The equivalent XML configuration is as follows:

<beans>
    <context:component-scan base-package="org.example"
        name-generator="org.example.MyNameGenerator" />
</beans>
  • Generate component index

When starting in a large application, component scanning may take a long time. You can improve the startup performance of a large application by creating a candidate static list (index) at compile time.

<!-- If you add this dependency to each module containing components, the index will be automatically generated at compile time META-INF/spring.components In the file -->
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-indexer</artifactId>
        <version>5.1.3.RELEASE</version>
        <optional>true</optional>
    </dependency>
</dependencies>

be careful

  1. If the ApplicationContext detects a component index at startup, the index will be used directly instead of component scanning. This means that you must add dependencies to all modules that have components.

  2. You can use the global attribute spring.index.ignore=true to ignore the component index and go back to component scanning.

  3. Configure the properties of the Bean

  • Use @ Scope to configure the Scope of the component

The component is registered as a singleton Scope by default. If you modify it, you can use the @ Scope annotation.

@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
    // ...
}

be careful:

Unlike the Scope attribute in XML, the @ Scope attribute is only valid on specific Bean classes (for annotated components) or factory methods (for @ Bean methods) and cannot be inherited.

Instead of relying on annotation based methods, you can implement the ScopeMetadataResolver interface to provide custom policies for scope resolution.

@Configuration
@ComponentScan(basePackages = "org.example", scopeResolver = MyScopeResolver.class)
public class AppConfig {
    ...
}

Equivalent XML configuration

<beans>
    <context:component-scan base-package="org.example" scope-resolver="org.example.MyScopeResolver"/>
</beans>

When using some non single scopes, it may be necessary to generate proxies for scope objects. You can use the scoped proxy attribute configuration. The following configuration generates a standard JDK dynamic proxy.

// scopedProxy: select a dynamic proxy. Options include no, interfaces, and targetClass
@Configuration
@ComponentScan(basePackages = "org.example", scopedProxy = ScopedProxyMode.INTERFACES)
public class AppConfig {
    ...
}

The equivalent XML configuration is as follows:

<beans>
    <context:component-scan base-package="org.example" scoped-proxy="interfaces"/>
</beans>
  1. Use @ Autowired for automatic injection

@Autowired annotation is generally used for automatic injection of reference type attributes, and can act on constructors, methods, parameters, member variables and annotations.

  • Apply to a single variable

If a single variable is annotated, uniqueness matching is performed, that is, matching by type is preferred. If there are multiple matching types, the variable name is used for matching.

public class MovieRecommender {
    @Autowired
    private CustomerPreferenceDao customerPreferenceDao;
}
  • Apply to arrays or single column collections

If the annotated is an array or a single column collection, all beans of this type are injected into it in order of definition.

public class MovieRecommender {
    @Autowired
    private MovieCatalog[] movieCatalogs;
    
    @Autowired
    private Set<MovieCatalog> movieCatalogs;
}

If you want to precisely control their injection order, you can implement the Ordered interface or use the @ Order/@Priority annotation.

  • Apply to a two column set

If a double column collection is annotated (the key is of String type), all beans of this type can also be injected, and the id attribute of the Bean is used as the key value of the key value pair.

public class MovieRecommender {
    @Autowired
    private Map<String, MovieCatalog> movieCatalogs;
}
  • Apply to constructor

If the constructor is annotated, it means that the function is used to instantiate the Bean object, and attribute injection is carried out by constructing parameters.

public class MovieRecommender {
    private final CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }
}

You can annotate multiple constructors as "candidates". Spring will select the one with the most parameters for instantiation, but you must ensure that their required attribute is false.

public class MovieRecommender {
    private final CustomerPreferenceDao customerPreferenceDao;
    private final MovieFinder movieFinder;

    // The required property must be modified to false
    @Autowired(required = false)
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }
    
    // The required property must be modified to false
    @Autowired(required = false)
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao, MovieFinder movieFinder) {
        this.customerPreferenceDao = customerPreferenceDao;
        this.movieFinder = movieFinder;
    }
}

If the constructor is not annotated with @ Autowired, it is instantiated with a parameterless construct by default. If there is no parameterless construct, then there must be a unique parameterless construct.

  • Applied to other methods

@Autowired can also support other types of methods in annotated beans, but in general, traditional setter methods are used more.

public class SimpleMovieLister {
    private MovieFinder movieFinder;

    // Annotate the setter method: first, injecting the movieFinder parameter, then calling the setMovieFinder method.
    @Autowired
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}

public class MovieRecommender {
    private MovieCatalog movieCatalog;
    private CustomerPreferenceDao customerPreferenceDao;

    // Annotation arbitrary method
    @Autowired
    public void prepare(MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }
}
  • Dependency necessity

During automatic injection, if a suitable Bean cannot be found, a NoSuchBeanDefinitionException exception will be thrown, which can be modified through the required attribute.

public class SimpleMovieLister {
    private MovieFinder movieFinder;

    @Autowired(required = false)
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}
  • Inject built-in Bean

The @ Autowired annotation can directly inject common solvable dependencies without special settings, such as BeanFactory, ApplicationContext, Environment, ResourceLoader, ApplicationEventPublisher and MessageSource.

public class MovieRecommender {
    @Autowired
    private ApplicationContext context;
}
  • Use generics as auto assembly qualifiers

In addition to the @ Qualifier annotation, you can also use Java generic types as matching criteria for automatic assembly.

@Configuration
public class MyConfiguration {
	// Instantiate a StringStore object, which implements store < string >
    @Bean
    public StringStore stringStore() {
        return new StringStore();
    }

    // Instantiate an IntegerStore object, which implements store < integer >
    @Bean
    public IntegerStore integerStore() {
        return new IntegerStore();
    }
}

@Autowired
private Store<String> s1; // Inject StringStore
@Autowired
private Store<Integer> s2; // Inject IntegerStore
  1. Use @ Resource for automatic injection

@Resource is an annotation defined by JSR-250. Both @ Autowired and @ Autowired are used for attribute injection and can act on classes, member variables and methods. Common attributes are name and type.

  • name and type are not specified

  • Specify both name and type

  • Specify name only

  • Specify type only

    public class SimpleMovieLister {
    private MovieFinder movieFinder;

      // Match only by the name of myMovieFinder. Throw an exception if it fails
      @Resource(name="myMovieFinder")
      public void setMovieFinder(MovieFinder movieFinder) {
          this.movieFinder = movieFinder;
      }
      
      // Match by movieFinder name first, or by type if it fails
      @Resource
      public void setMovieFinder(MovieFinder movieFinder) {
          this.movieFinder = movieFinder;
      }
    

    }

matters needing attention

Data injection annotations @ Autowired, @ Inject, @ Resource and @ Value are processed by BeanPostProcessor. This means that you cannot apply these annotations in your own BeanPostProcessor or beanfactoryprocessor type, if any. These types must be explicitly "connected" using XML or Spring @Bean methods.

  1. Additional annotations for injecting data
  • Inject literals with @ Value

@The Value annotation is generally used for literal injection, and will parse ${} to take values from the environment for replacement.

@Configuration
@ImportResource("classpath:beans.xml")
public class AppConfig {

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dataSource() {
        return new DriverManagerDataSource(url, username, password);
    }
}

be careful
@The Value annotation will give priority to finding beans with the same name in the container for injection. Literal injection will be carried out only when no suitable Bean is found.

  • Necessity of using @ Required tag attribute

@Required usually acts on the setter method, marking that a property must be injected when instantiating a Bean, otherwise a BeanInitializationException will be thrown. For details, please refer to the implementation of RequiredAnnotationBeanPostProcessor.

public class Student {
    private String name;

    // The tag name attribute must be injected (Note: even if NULL is injected, it is injected, @ Required does not do null pointer detection)
    @Required
    public void setName(String name) {
        this.name = name;
    }
}

The equivalent XML configuration is as follows.

<!-- student bean The definition of must be name Property -->
<bean id = "student" class="com.how2java.w3cschool.required.Student">
    <property name = "name" value="Patrick Star"/>
</bean>
  • Use @ Primary to adjust auto injection priority

Using automatic assembly, there may be multiple candidate objects suitable for injection. The @ Primary annotation can specify a Bean as the Primary Bean. If there is a unique Primary Bean in the candidate object list, this value is used for injection.

@Configuration
public class MovieConfiguration {
    @Bean
    @Primary // Specify that the Bean is the primary Bean and inject first
    public MovieCatalog firstMovieCatalog() { ... }

    @Bean
    public MovieCatalog secondMovieCatalog() { ... }
}

The equivalent XML configuration is as follows.

<bean id="firstMovieCatalog"  class="example.SimpleMovieCatalog" primary="true"/>
<bean id="secondMovieCatalog" class="example.SimpleMovieCatalog"/>
  • Match qualification using @ Qualifier

@Qualifier is used to specify a qualifier to narrow the matching range of automatic assembly. It can act on member variables, constructor parameters or other method parameters.

public class MovieRecommender {
    @Autowired
    @Qualifier("main") // Use @ qualifier on the member variable (specify the matching qualifier as main)
    private MovieCatalog movieCatalog;
    
    private CustomerPreferenceDao customerPreferenceDao;
    
    // Use @ Qualifier on method parameters
    @Autowired
    public void prepare(@Qualifier("main") CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }
}

Assuming that the Bean of MovieCatalog type is configured as follows, it will connect with simplemoiecatalog1.

@Component
@Qualifier("main")
public class MovieCatalog extends SimpleMovieCatalog1{
}

@Component
@Qualifier("action")
public class MovieCatalog extends SimpleMovieCatalog2{
}

The XML configuration with the same effect is as follows.

<bean id="simpleMovieCatalog1" class="example.SimpleMovieCatalog1">
    <qualifier value="main"/>
</bean>

<bean id="simpleMovieCatalog2" class="example.SimpleMovieCatalog2">
    <qualifier value="action"/> 
</bean>

Tips:

  1. Each Bean will use the id attribute as the default qualifier value. As in the above example, you can also use @ Qualifier("simpleMovieCatalog2") to connect another instance.
  2. @Qualifier is also valid for collection type variables. It will limit the matching range before injection (Note: the qualifier value of Bean is not unique).
  • Custom qualifier

You can customize some combination annotations based on @ Qualifier and modify the attributes. Only when all attributes match will they be added to the candidate list.

// Define combined annotation @ MovieQualifier
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface MovieQualifier {
    String genre();
    Format format();
}

// Limit the matching range of automatic injection. Matching can only be performed when format=Format.VHS, genre="Action"
@Autowired
@MovieQualifier(format=Format.VHS, genre="Action")
private MovieCatalog actionVhsCatalog;

<bean class="example.SimpleMovieCatalog">
    <qualifier type="MovieQualifier">
        <attribute key="format" value="VHS"/>
        <attribute key="genre" value="Comedy"/>
    </qualifier>
</bean>

<bean class="example.SimpleMovieCatalog">
    <meta key="format" value="DVD"/>
    <meta key="genre" value="Action"/>
</bean>

You can also use customautowireconfigurator to declare custom qualifier annotations instead of combining annotations.

<bean id="customAutowireConfigurer" class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
    <property name="customQualifierTypes">
        <set>
            <value>example.CustomQualifier</value>
        </set>
	</property>
</bean>
  1. Use JSR-330 annotation (understand)

JSR-330 annotations are dependency injection annotations defined by the Java standard. Spring has supported these annotations since version 3.0.

<!--introduce JSR-330 Dependency of annotations-->
<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>
  • @ManagedBean/@Named: component tag

javax.annotation.ManagedBean or javax.inject.Named can be used for Component scanning, which is similar to @ Component.

import javax.inject.Inject;
import javax.inject.Named;

@Named("movieListener")  // @ManagedBean("movieListener") could be used as well
public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Inject
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}

In addition, component scanning can be configured in exactly the same way as Spring annotation, as shown in the following example:

@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig  {
    ...
}
  • @Inject/@Named: dependency injection

javax.inject.Inject can be used for attribute injection, which is similar to @ Autowired.

import javax.inject.Inject;

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Inject
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    public void listMovies() {
        this.movieFinder.findMovies(...);
        ...
    }
}

If you want to use qualified names for the injected dependencies, you can also use the @ Named annotation.

import javax.inject.Inject;
import javax.inject.Named;

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Inject
    public void setMovieFinder(@Named("main") MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}

@Inject does not have the required attribute, but it can be used with java.util.Optional or @ Nullable to set whether the attribute is required.

public class SimpleMovieLister {

    @Inject
    public void setMovieFinder(Optional<MovieFinder> movieFinder) {
        ...
    }
}
    
public class SimpleMovieLister {

    @Inject
    public void setMovieFinder(@Nullable MovieFinder movieFinder) {
        ...
    }
}
  • Limitations of JSR-330 standard annotation

When using standard annotations, you should know that some important functions are not available, as shown in the following table:

Spring javax.inject.* javax.inject restrictions / annotations
@Autowired @Inject @Inject does not have a required attribute. It can be used with Java 8's Optional.
@Component @Named/@ManagedBean JSR-330 does not provide a composable model, but only provides a method to identify named components.
@The default Scope of Scope("Singleton") @ Singleton JSR-330 is similar to Spring's prototype. However, in order to keep it consistent with the general default settings of Spring, by default, the JSR-330 bean declared in the Spring container is a singleton. In order to use scopes other than singleton, you should use Spring's @ Scope annotation. javax.inject also provides @ Scope annotations. However, this is only used to create your own annotations.
@Qualifier @ Qualifier/@ Named javax.inject.Qualifier is just a meta annotation for building custom qualifiers. Specific String qualifiers (such as Spring's @ qualifier with value) can be associated through javax.inject.Named.
@Value - no equivalent
@Required - no equivalent
@Lazy - no equivalent
ObjectFactory Provider javax.inject.Provider is a direct alternative to Spring's ObjectFactory, but uses a shorter get() method name. It can also be used in conjunction with Spring's @ Autowired or uncommented constructor and setter methods.

Section 7 Java based container configuration

Java based container Configuration refers to the use of annotations in Java code to configure Spring containers. The two core annotations are @ Configuration and @ Bean. Where @ Configuration marks a class as a Configuration class component of Spring as the source of Bean definition, and @ Bean defines beans through public methods in the marked class.

  1. Define a Bean using @ Bean

By default, the bean name is the method name, the value is the returned object, and the type is the return value type.

@Configuration
public class AppConfig {
	// Define a Bean: transferservice - > com.acme.transferserviceimpl
    @Bean
    public TransferServiceImpl transferService() {
        return new TransferServiceImpl();
    }
}

The equivalent XML configuration is as follows:

<beans>
    <bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
  1. Configure the properties of the Bean
  • Modify the name and description of the Bean

You can use the name attribute to specify the name of the bean, or you can set an alias by specifying multiple names.

@Configuration
public class AppConfig {

    // Define a Bean with the specified name: myThing
    @Bean(name = "myThing")
    public Thing thing() {
        return new Thing();
    }
}

@Configuration
public class AppConfig {
	// Define a Bean and specify multiple names
    @Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"})
    public DataSource dataSource() {
        // instantiate, configure and return DataSource bean...
    }
}

If necessary, you can also provide a more detailed text Description through @ Description.

@Configuration
public class AppConfig {

    // Define a Bean and add description information
    @Bean
    @Description("Provides a basic example of a bean")
    public Thing thing() {
        return new Thing();
    }
}
  • Set lifecycle callback

@Bean annotation supports specifying arbitrary initialization and destruction callback methods, just like the init method and destroy method attributes in XML configuration.

public class BeanOne {

    public void init() {
        // initialization logic
    }
}

public class BeanTwo {

    public void cleanup() {
        // destruction logic
    }
}

@Configuration
public class AppConfig {

    // Specify initialization method
    @Bean(initMethod = "init")
    public BeanOne beanOne() {
        return new BeanOne();
    }

    // Specify destruction method
    @Bean(destroyMethod = "cleanup")
    public BeanTwo beanTwo() {
        return new BeanTwo();
    }
}

You can also call the init() method directly during construction.

@Configuration
public class AppConfig {
    
    @Bean
    public BeanOne beanOne() {
        BeanOne beanOne = new BeanOne();
        beanOne.init();
        return beanOne;
    }
}

In addition to the above, any class defined with the @ bean annotation supports regular Lifecycle callbacks, and you can use the @ PostConstruct and @ PreDestroy annotations in JSR-250. Similarly, if the bean implements InitializingBean, DisposableBean, or Lifecycle, the container will call their respective methods.

Note: by default, when using Java configuration, the public close or shutdown method will be automatically registered as a destroy callback, which can be removed in the following way.

@Bean(destroyMethod="")
public DataSource dataSource() throws NamingException {
	return (DataSource) jndiTemplate.lookup("MyDS");
}
  • Modify the scope of the Bean

Using Java configuration, the default Scope of the generated Bean is singleton. You can modify it with the @ Scope annotation.

@Configuration
public class MyConfiguration {

    @Bean
    @Scope("prototype")
    public Encryptor encryptor() {
        // ...
    }
}

Tip: please refer to the official document for the use of scoped proxy attribute!

  1. Dependency injection of Bean
  • Method parameter mode

The method of creating a bean can have any number of parameters, which describe the dependencies required to build the bean. The resolution mechanism is almost the same as the dependency injection based on constructor.

@Configuration
public class AppConfig {

    // transferService -> com.acme.TransferServiceImpl
    @Bean
    public TransferService transferService(AccountRepository accountRepository) {
        return new TransferServiceImpl(accountRepository);
    }
}
  • Method call mode

In addition to using method parameters to define dependencies, you can also define dependencies between beans through method calls.

@Configuration
public class AppConfig {

    // Define a beanOne and rely on beanTwo through method call
    @Bean
    public BeanOne beanOne() {
        return new BeanOne(beanTwo());
    }

    // Define a beanTwo
    @Bean
    public BeanTwo beanTwo() {
        return new BeanTwo();
    }
}

The equivalent XML configuration is as follows:

<beans>
    <bean id="beanOne" class="com.acme.services.BeanOne">
    	<constructor-arg name="beanTwo" ref="beanTwo"/>
    </bean>
    
    <bean id="beanTwo" class="com.acme.services.BeanTwo"></bean>
</beans>

Note: this method for declaring inter Bean dependencies is valid only when the @ Bean method is declared in the @ Configuration class. You cannot declare dependencies between beans by using the normal @ Component class.

  • Further instructions on defining Bean dependencies for method calls

See the following example. clientDao() was called once in clientService1() and once in clientService2(), but the two calls returned the same instance.

@Configuration
public class AppConfig {

    // Define a Bean: clientService1
    @Bean
    public ClientService clientService1() {
        ClientServiceImpl clientService = new ClientServiceImpl();
        // First call to clientDao() 
        clientService.setClientDao(clientDao());
        return clientService;
    }

    // Define a Bean: clientService2
    @Bean
    public ClientService clientService2() {
        ClientServiceImpl clientService = new ClientServiceImpl();
        // The second call to clientDao() 
        clientService.setClientDao(clientDao());
        return clientService;
    }

    // Define a singleton Bean: clientDao
    @Bean
    public ClientDao clientDao() {
        return new ClientDaoImpl();
    }
}

Because all @ Configuration classes are subclassed with CGLIB at startup. In the subclass, the child method first checks whether there are any cached (scoped) bean s in the container before calling the parent method and creating a new instance.

  • Class member injection

The Configuration class is also a Bean, so @ Autowired or @ Value can be used to inject the required dependencies in the method.

@Configuration
public class ServiceConfig {

    // Injection dependency: accountRepository
    @Autowired
    private AccountRepository accountRepository;

    // Define a bean: transferService
    @Bean
    public TransferService transferService() {
        // Using injected dependencies
        return new TransferServiceImpl(accountRepository);
    }
}

@Configuration
public class RepositoryConfig {
    private final DataSource dataSource;

    // Injection dependency: dataSource
    @Autowired
    public RepositoryConfig(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    // Define a Bean: accountRepository
    @Bean
    public AccountRepository accountRepository() {
        // Using injected dependencies
        return new JdbcAccountRepository(dataSource);
    }
}

// Total configuration class
@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {

    @Bean
    public DataSource dataSource() {
        // return new DataSource
    }
}

// test
public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
    TransferService transferService = ctx.getBean(TransferService.class);
    transferService.transfer(100.00, "A123", "C456");
}

However, this method is not recommended because the Configuration class is handled very early during context initialization, and dependencies forcibly injected in this way (such as dataSource and accountRepository) may lead to unexpected early initialization.

Note: similarly, if the created Bean is a BeanPostProcessor or beanfactoryprocessor, the method should be defined as a static method to prevent the Configuration class from being instantiated prematurely.

  • Lookup method injection

When the beans of the singleton scope depend on the beans of the prototype scope, you can create a subclass of CommandManager by using Java configuration. In this subclass, the abstract createCommand() method is overridden, so that you can find a new (prototype) command object.

public abstract class CommandManager {
    public Object process(Object commandState) {
        // Get a new instance and initialize
        Command command = createCommand();
        command.setState(commandState);
        // Use the new instance to execute the business logic
        return command.execute();
    }

    // okay... but where is the implementation of this method?
    protected abstract Command createCommand();
}

// Dependent prototype Bean: asyncCommand
@Bean
@Scope("prototype")
public AsyncCommand asyncCommand() {
    AsyncCommand command = new AsyncCommand();
    return command;
}

// Singleton Bean dependent on prototype Bean: commandManager
@Bean
public CommandManager commandManager() {
    // Return the singleton Bean instance and implement the lookup method: createCommand()
    return new CommandManager() {
        protected Command createCommand() {
            return asyncCommand();
        }
    }
}
  1. Import other configuration classes

Like the import tag in XML configuration, the @ import annotation can be used in Java configuration to import other configuration classes (or general component classes).

@Configuration
public class ConfigA {

    @Bean
    public A a() {
        return new A();
    }
}

@Configuration
@Import({ConfigA.class,UserService.class})
public class ConfigB {

    @Bean
    public B b() {
        return new B();
    }
}

@Component
public class UserService {
}

If you need to import a large number of classes, you can also use the ImportSelector or ImportBeanDefinitionRegistrar interface to assist in import.

public class Myclass implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        // Returns an array of full class names (note that null cannot be returned)
        return new String[]{"com.yc.Test.TestDemo3"};
    }
}

If you want to selectively import some configuration classes or beans, that is, import the specified class in dev environment and another class in test environment, you can use @ Profile annotation. For specific usage, please refer to: https://blog.csdn.net/ysl19910806/article/details/91646554 .

  1. Mixed use of Java and XML configuration
  • XML centric

That is, the ClassPathXmlApplicationContext is used to create the container. The Configuration in the annotation is introduced into XML. The main idea is to register the Configuration class as a Bean in the container. The container will recognize the @ Configuration annotation and correctly handle the @ Bean methods declared in the Configuration class.

<beans>
    <!-- Turn on the annotation configuration switch -->
    <context:annotation-config/>

    <!-- Define the configuration class as a Bean -->
    <bean class="com.acme.AppConfig"/>
</beans>

You can also use annotation scanning to define the Bean of the Configuration class, because @ Configuration uses @ Component for meta annotation.

<beans>
    <!-- Configure annotation scanning(Implicitly turn on annotation configuration switch) -->
    <context:component-scan base-package="com.acme"/>
</beans>
  • Annotation centric configuration

That is, use the AnnotationConfigApplicationContext to create the container, and import the external XML Configuration file by using the @ ImportResource annotation on the @ Configuration class.

@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
public class AppConfig {

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dataSource() {
        return new DriverManagerDataSource(url, username, password);
    }
}

<!-- properties-config.xml -->
<beans>
    <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
</beans>
  1. Instructions on defining beans in components

You can use the @ Bean annotation in @ Component to define other beans, but these beans are limited.

@Component
public class FactoryMethodComponent {
    private static int i;

    @Bean
    @Qualifier("public")
    public TestBean publicInstance() {
        return new TestBean("publicInstance");
    }

    // use of a custom qualifier and autowiring of method parameters
    @Bean
    protected TestBean protectedInstance( @Qualifier("public") TestBean spouse, 
                                          @Value("#{privateInstance.age}") String country) {
        TestBean tb = new TestBean("protectedInstance", 1);
        tb.setSpouse(spouse);
        tb.setCountry(country);
        return tb;
    }

    @Bean
    private TestBean privateInstance() {
        return new TestBean("privateInstance", i++);
    }

    @Bean
    @RequestScope
    public TestBean requestScopedInstance() {
        return new TestBean("requestScopedInstance", 3);
    }
}

The processing of the @ bean method in the regular Spring component is different from that of the corresponding method in the Spring @Configuration class. The difference is that cglib does not enhance the @ component class to intercept calls to methods and fields. Cglib proxy is a method that calls a method in the @ bean method or a field in the field in the @ Configuration class, which is used to create bean metadata references to collaborate objects. Such methods are not called with ordinary Java semantics, but through containers to provide the usual life cycle Management and proxy of Spring bean s, even when other beans are referenced by calling @ bean methods programmatically. On the contrary, calling methods or fields in normal @Component class @Bean methods has standard Java semantics without special CGLIB processing or other constraints.

You can declare @ Bean methods as static, allowing them to be called without creating their contained configuration classes as instances. This is particularly meaningful when defining a post processor Bean (for example, type BeanFactoryPostProcessor or BeanPostProcessor) because such beans are initialized early in the container life cycle and should avoid triggering other parts of the configuration at that time.

Due to technical limitations, calls to static @ Bean methods will never be intercepted by the container, even in the @ Configuration class (as described earlier in this section). Due to technical limitations, CGLIB subclasses can only override non static methods. Therefore, calling another @ Bean method directly has standard Java semantics, resulting in returning a separate instance directly from the factory method itself.

@The Java language visibility of bean methods does not have a direct impact on the final bean definition in the Spring container. You can declare your own factory methods at will to suit non @ Configuration classes, or you can declare static methods anywhere. However, the regular @ bean methods in the @ Configuration class must be rewritable -- that is, they must not be declared private or final.

The @ Bean method can also be found on the Base Class of a given component or configuration class and on the Java 8 default method declared in the interface implemented by the component or configuration class. This provides great flexibility for combining complex configuration arrangements. Starting from Spring 4.2, multiple inheritance can even be realized through the Java 8 default method.

Finally, a single class can reserve multiple @ Bean methods for the same Bean, depending on the dependencies available at run time, so multiple factory methods can be used. This is the same as the algorithm for selecting the "greediest" constructor or factory method in other configuration schemes: variables with the maximum number of dependencies will be selected during construction, similar to the way the container selects between multiple @ Autowired constructors.

You can also declare factory method parameters of type injectionpoint (or its more specific subclass: DependencyDescriptor) to access the request injection point that triggers the creation of the current bean. Note that this applies only to actually creating bean instances, not to injecting existing instances. Therefore, this feature makes the most sense for prototype scoped beans.

@Component
public class FactoryMethodComponent {

    @Bean @Scope("prototype")
    public TestBean prototypeInstance(InjectionPoint injectionPoint) {
        return new TestBean("prototypeInstance for " + injectionPoint.getMember());
    }
}

Section 8 environmental abstraction

The Environment interface is used to represent the Environment of the entire application runtime. It is an abstraction of the current Bean collection and related attributes in the container. It defines the following two important concepts.

  1. Profile

Profile is used to control which beans are registered and which are not. Only active beans will be registered@ Profile is a function provided by Spring that can dynamically activate and switch a series of components according to the current environment.

  • Configure environment for Bean

@The Profile annotation can act on the configuration class or method, indicating that only when the corresponding environment is activated, the annotated Bean will be registered with the Spring container.

@Configuration
@Profile("development")
public class StandaloneDataConfig {

    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.HSQL)
            .addScript("classpath:com/bank/config/sql/schema.sql")
            .addScript("classpath:com/bank/config/sql/test-data.sql")
            .build();
    }
}

@Configuration
@Profile("production")
public class JndiDataConfig {

    @Bean(destroyMethod="")
    public DataSource dataSource() throws Exception {
        Context ctx = new InitialContext();
        return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
    }
}


@Configuration
public class AppConfig {

    @Bean("dataSource")
    @Profile("development")
    public DataSource standaloneDataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.HSQL)
            .addScript("classpath:com/bank/config/sql/schema.sql")
            .addScript("classpath:com/bank/config/sql/test-data.sql")
            .build();
    }

    @Bean("dataSource")
    @Profile("production")
    public DataSource jndiDataSource() throws Exception {
        Context ctx = new InitialContext();
        return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
    }
}

Tips

  1. You can use some basic operators to configure the environment of a Bean. Such as production & (US East | EU central).
  2. Multiple environments can be configured at the same time. For example, @ Profile({"p1", "! p2"}) indicates registration in p1 active state or p2 inactive state.
  3. If the @ Profile annotation acts on the @ Bean method (method overload) with the same name, the configuration between them should preferably be the same.

In XML configuration, you can use the profile attribute of the beans tag to configure the Bean environment, but there may be some limitations.

<beans profile="development"
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xsi:schemaLocation="...">

    <jdbc:embedded-database id="dataSource">
        <jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
        <jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
    </jdbc:embedded-database>
</beans>
  • Activate environment

When starting the container, we need to specify the active environment, otherwise NoSuchBeanDefinitionException will be thrown. The most direct way is to configure it programmatically through the Environment API.

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("profile1", "profile2");
ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);
ctx.refresh();

Of course, you can also use declarative methods to set the value of spring.profiles.active property through system environment variables and JVM system properties.

-Dspring.profiles.active="profile1,profile2"

servlet context parameters in web.xml can also be set in a specific environment, such as WEB development, or declared through @ ActiveProfiles annotation in the test environment.

  • Default environment

When spring.profiles.active is not set, Spring will activate the Profile according to the corresponding value of spring.profiles.default property. The default value of the spring.profiles.default property is default, which can be modified by using the setDefaultProfiles() method or the spring.profiles.default property.

@Configuration
@Profile("default")
public class DefaultDataConfig {

    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.HSQL)
            .addScript("classpath:com/bank/config/sql/schema.sql")
            .build();
    }
}
  1. @PropertySource

Property indicates the property configuration in the current Environment. The properties may come from the properties file, JVM properties, system Environment variables, JNDI, servlet context parameters, context parameters, special properties objects, Maps, etc@ The PropertySource annotation is used to load the specified properties file (properties/xml/yml) into the Spring Environment.

  • Extract properties from environment

We can use ${} or env.getProperty("") to extract these values from the Environment.

@Configuration
@PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties")
public class AppConfig {

    @Autowired
    Environment env;

    @Bean
    public TestBean testBean() {
        TestBean testBean = new TestBean();
        testBean.setName(env.getProperty("testbean.name"));
        return testBean;
    }
}
  • Combined with @ Value

@PropertySource is used in combination with @ Value to inject the property variable Value in the custom property file into the member variable annotated with @ Value of the current class.

@Component
@PropertySource(value = {"demo/props/demo.properties"})
public class ReadByPropertySourceAndValue {

    @Value("${demo.name}")
    private String name;

    @Value("${demo.sex}")
    private int sex;

    @Value("${demo.type}")
    private String type;

    @Override
    public String toString() {
        return "ReadByPropertySourceAndValue{" +
                "name='" + name + '\'' +
                ", sex=" + sex +
                ", type='" + type + '\'' +
                '}';
    }
}
  • Used in combination with @ ConfigurationProperties (SpringBoot)

Combined with @ ConfigurationProperties, you can bind the property file to a Java class and inject the variable value in the property file into the member variable of the Java class.

@Component
@PropertySource(value = {"demo/props/demo.properties"})
@ConfigurationProperties(prefix = "demo")
public class ReadByPropertySourceAndConfProperties {

    private String name;

    private int sex;

    private String type;

    public void setName(String name) {
        this.name = name;
    }

    public void setSex(int sex) {
        this.sex = sex;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getName() {
        return name;
    }

    public int getSex() {
        return sex;
    }

    public String getType() {
        return type;
    }

    @Override
    public String toString() {
        return "ReadByPropertySourceAndConfProperties{" +
                "name='" + name + '\'' +
                ", sex=" + sex +
                ", type='" + type + '\'' +
                '}';
    }
}

Section IX other functions

  1. Load time Weaver

If you need to dynamically convert a class when it is loaded into the Java virtual machine (JVM), you can turn on load time weaving in the following way.

@Configuration
@EnableLoadTimeWeaving
public class AppConfig {
}

The equivalent XML configuration is as follows:

<beans>
    <context:load-time-weaver/>
</beans>

Once the ApplicationContext is configured, any bean within the ApplicationContext can implement loadtimeweaveaware, thus receiving a reference to the weaver instance at load time.

  1. Internationalization using MessageSource

  2. Standard and custom events

Event handling in ApplicationContext is provided through ApplicationEvent class and ApplicationListener interface. If the bean implementing the ApplicationListener interface is deployed to the context, the bean will be notified every time the ApplicationEvent is published to the ApplicationContext. In essence, this is the standard Observer design pattern.
The following table describes the standard events provided by Spring:

Event Explanation
ContextRefreshedEvent is published when the ApplicationContext is initialized or refreshed (for example, by using the refresh() method on the ConfigurableApplicationContext interface). Here, "initialized" means that all beans have been loaded, the post processor beans have been detected and activated, the singleton has been instantiated in advance, and the ApplicationContext object is ready to be used. The refresh can be triggered multiple times as long as the context has not been closed and as long as the selected ApplicationContext actually supports this "hot" refresh. For example, XmlWebApplicationContext supports hot refresh, but GenericApplicationContext does not.
ContextStartedEvent is published when the ApplicationContext is started using the start() method on the ConfigurableApplicationContext interface. Here, "start" means that all lifecycle beans receive an explicit start signal. Typically, this signal is used to restart the Bean after an explicit stop, but it can also be used to start components that have not been configured to start automatically (for example, components that have not been started at initialization).
ContextStoppedEvent is published when the ApplicationContext is stopped using the stop() method on the ConfigurableApplicationContext interface. Here, "stopped" means that all lifecycle beans receive an explicit stop signal. The stopped context can be restarted through the start() call.
ContextClosedEvent is published when the ApplicationContext is closed using the close() method on the ConfigurableApplicationContext interface. Here, "closed" means that all singleton bean s are destroyed. The closed situation has come to the end of life. Unable to refresh or restart.
RequestHandledEvent is a Web specific event that tells all Bean HTTP requests that have been serviced. This event will be published when the request is completed. This event applies only to Web applications that use Spring's dispatcher servlet.

You can also create and publish your own custom events. The following example shows a simple class that extends Spring's ApplicationEventBase Class:

public class BlackListEvent extends ApplicationEvent {

    private final String address;
    private final String content;

    public BlackListEvent(Object source, String address, String content) {
        super(source);
        this.address = address;
        this.content = content;
    }

    // accessor and other methods...
}

To publish a custom ApplicationEvent, call the publishEvent() method in ApplicationEventPublisher. Typically, this is done by creating a class that implements ApplicationEventPublisherAware and registering it as a Spring bean. The following example shows this class:

public class EmailService implements ApplicationEventPublisherAware {

    private List<String> blackList;
    private ApplicationEventPublisher publisher;

    public void setBlackList(List<String> blackList) {
        this.blackList = blackList;
    }

    public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    public void sendEmail(String address, String content) {
        if (blackList.contains(address)) {
            publisher.publishEvent(new BlackListEvent(this, address, content));
            return;
        }
        // send email...
    }
}

During configuration, the Spring container detects that the EmailService implements ApplicationEventPublisherAware and automatically calls setApplicationEventPublisher(). In fact, the parameter passed in is the Spring container itself. You are interacting with the application context through its ApplicationEventPublisher interface.

To receive custom applicationevents, you can create a class that implements ApplicationListener and register it as a Spring Bean. The following example shows this class:

public class BlackListNotifier implements ApplicationListener<BlackListEvent> {

    private String notificationAddress;

    public void setNotificationAddress(String notificationAddress) {
        this.notificationAddress = notificationAddress;
    }

    public void onApplicationEvent(BlackListEvent event) {
        // notify appropriate parties via notificationAddress...
    }
}

Note that the ApplicationListener is usually parameterized with the type of your custom event (blacklistevent in the previous example). This means that the onApplicationEvent() method can remain type safe, thus avoiding any need for downward conversion. You can register as many event listeners as you need, but note that by default, event listeners receive events synchronously. This means that the publishEvent() method will block until all listeners have finished processing the event. One advantage of this synchronous and single threaded approach is that when the listener receives an event, if there is a transaction context available, it will operate within the publisher's transaction context.

The following example shows the Bean definition used to register and configure each of the above classes:

<bean id="emailService" class="example.EmailService">
    <property name="blackList">
        <list>
            <value>[emailprotected]</value>
            <value>[emailprotected]</value>
            <value>[emailprotected]</value>
        </list>
    </property>
</bean>

<bean id="blackListNotifier" class="example.BlackListNotifier">
    <property name="notificationAddress" value="[emailprotected]"/>
</bean>

Put all the contents together. When the sendEmail() method of the emailService bean is called, if any e-mail message should be blacklisted, a custom event of BlackListEvent type will be published. The blackListNotifier bean registers as an ApplicationListener and receives a BlackListEvent, at which point it can notify the appropriate participants.

Spring's event mechanism aims at simple communication between spring beans within the same application context. However, for more complex enterprise integration requirements, the separately maintained Spring Integration project provides complete support for building an event driven lightweight pattern oriented architecture based on the well-known spring programming model.

Starting with Spring 4.2, you can register event listeners on any public method that hosts a Bean using the EventListener annotation.

public class BlackListNotifier {

    private String notificationAddress;

    public void setNotificationAddress(String notificationAddress) {
        this.notificationAddress = notificationAddress;
    }

    @EventListener
    public void processBlackListEvent(BlackListEvent event) {
        // notify appropriate parties via notificationAddress...
    }
}

The method signature again declares the event type it listens on, but this time it uses a flexible name and does not implement a specific listener interface. As long as the actual event type resolves your common parameters in its implementation hierarchy, you can also narrow the event type through the common type.

If your method should listen to multiple events, or if you want to define it without any parameters at all, the event type can also be specified on the annotation itself. The following example shows how to do this:

@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {
    ...
}

You can also add other runtime filters by using the condition attribute that defines the annotation of spiel expression, which should match to actually call the method for a specific event.

The following example shows that our notifier can be rewritten to call only when the content attribute of the event is equal to my event:

@EventListener(condition = "#blEvent.content == 'my-event'")
public void processBlackListEvent(BlackListEvent blEvent) {
    // notify appropriate parties via notificationAddress...
}

Each SpEL expression is evaluated against a specific context. The following table lists the items available for context so that you can use them for conditional event processing:

Name Location Description Example
Event root object the actual ApplicationEvent# root.event
Arguments array root object is used to call the parameters of the target (as an array)# root.args[0]
Argument name evaluation context the name of any method parameter. If the name is not available for some reason (for example, because there is no debugging information), the parameter name can also be obtained under #a < #arg >, where #arg represents the parameter index (starting from 0)# blEvent or #a0 (you can also use #p0 or #p < #arg > notation as an alias)

Note that even if your method signature actually references any published object, #root.event gives you access to the underlying event.

If you need to publish an event due to processing another event, you can change the method signature to return the event that should be published, as shown in the following example:

@EventListener
public ListUpdateEvent handleBlackListEvent(BlackListEvent event) {
    // notify appropriate parties via notificationAddress and
    // then publish a ListUpdateEvent...
}

This feature is not supported by asynchronous listeners

This new method publishes a new ListUpdateEvent for each BlackListEvent processed by the above method. If you need to publish multiple events, you can return the Collection event.

  • Asynchronous event listening
    If you want a particular listener to handle events asynchronously, you can reuse regular @ Async support. The following example shows how to do this:

    @EventListener
    @Async
    public void processBlackListEvent(BlackListEvent event) {
    // BlackListEvent is processed in a separate thread
    }

When using asynchronous events, note the following limitations:

  1. If the event listener throws an Exception, it is not propagated to the caller. For more details, see AsyncUncaughtExceptionHandler.
  2. This type of event listener cannot send a reply. If you need to send another event as a result of processing, inject ApplicationEventPublisher to send the event manually.
  • Listener call order
    If you need to call a listener first, you can add the @ Order annotation to the method declaration, as shown in the following example:

    @EventListener
    @Order
    public void processBlackListEvent(BlackListEvent event) {
    // notify appropriate parties via notificationAddress...
    }

  • Listener generics
    You can also use generics to further define the structure of events. Consider using EntityCreatedEvent, where T is the type of the actual entity that has been created. For example, you can create the following listener definition to receive only_ EntityCreatedEvent for 4:

    @EventListener
    public void onPersonCreated(EntityCreatedEvent event) {
    ...
    }

Due to type erasure, this method works only if the triggered event resolves the general parameters on which the event listener is based, such as class personcreatedevent extensions entitycreatedevent {...}.

In some cases, this can become tedious if all events follow the same structure (just like the events in the previous example). In this case, you can implement ResolvableTypeProvider to guide the framework beyond the scope provided by the runtime environment. The following event shows how to do this:

public class EntityCreatedEvent<T> extends ApplicationEvent implements ResolvableTypeProvider {

    public EntityCreatedEvent(T entity) {
        super(entity);
    }

    @Override
    public ResolvableType getResolvableType() {
        return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(getSource()));
    }
}

Tags: Java Spring

Posted on Sat, 25 Sep 2021 02:13:21 -0400 by j.smith1981