Spring Introduction

The Spring framework was developed by Rod Johnson, and the first version was released in 2004. Spring is a framework extracted from actual development, so it handles many common steps in development, leaving only the application-specific parts to developers, greatly improving the development efficiency of enterprise applications.

The advantages of Spring are summarized as follows:

  • Low-invasive design with very low code pollution.
  • Independent of various application servers. Applications based on the Spring framework can truly achieve the "Write Once, Run Anywhere" promise.
  • Spring's IoC container reduces the complexity of replacing business objects and improves decoupling between components.
  • Spring's AOP support allows common tasks such as security, transactions, and logging to be managed centrally, thus providing better reuse.
  • Spring's ORM and DAO provide good integration with third-party persistence frameworks and simplify low-level database access.
  • Spring is highly open and does not force applications to completely rely on Spring. Developers are free to choose part or all of the Spring framework.

The composition structure diagram of the Spring framework is as follows:

spring-overview

Spring's Core Mechanism

Managing Beans

Programs mainly access the Beans in the container through the Spring container. ApplicationContext is the most commonly used interface of the Spring container. This interface has the following two implementation classes:

  • ClassPathXmlApplicationContext: Searches for configuration files from the class loading path and creates the Spring container based on the configuration files.
  • FileSystemXmlApplicationContext: Searches for configuration files from a relative or absolute path in the file system and creates the Spring container based on the configuration files.
public class BeanTest{
    public static void main(String args[]) throws Exception{
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
        Person p = ctx.getBean("person", Person.class);
        p.say();
    }
}

Using Spring in Eclipse

In IDE tools such as Eclipse, users can create their ownUser Library, and then put all Spring JAR packages into it. Of course, they can also put JAR packages directly in the project's/WEB-INF/libdirectory. However, if usingUser Library, when the project is published, the JAR files referenced by the user library need to be published with the application, that is, copy the JAR files used by the User Library to the/WEB-INF/libdirectory. This is because for a Web application, when Eclipse deploys a Web application, it will not copy the JAR files from the user library to the/WEB-INF/libdirectory, and manual copying is required.

Dependency Injection

The Spring framework has two core functions:

  • The Spring container acts as a super factory, responsible for creating and managing all Java objects. These Java objects are called Beans.
  • The Spring container manages the dependencies between Beans in the container. Spring uses a method called "dependency injection" to manage the dependencies between Beans.

Using dependency injection, you can not only inject ordinary property values into Beans, but also inject references to other Beans. Dependency injection is an excellent decoupling method that allows Beans to be organized together through configuration files, rather than being coupled together in a hard-coded manner.

Understanding Dependency Injection

Rod Johnson was the first to attach great importance to managing the collaborative relationships of Java instances through configuration files. He gave this method a name: Inversion of Control (IoC). Later, Martine Fowler gave it another name: Dependency Injection. Therefore, whether it is dependency injection or inversion of control, the meaning is exactly the same. When a Java object (the caller) needs to call a method of another Java object (the dependent object), there are usually two approaches in the traditional model:

  1. Original approach: the calleractivelycreates the dependent object, and then calls the method of the dependent object.
  2. Simple factory pattern: the caller first finds the factory of the dependent object, thenactivelyobtains the dependent object through the factory, and finally calls the method of the dependent object.

Note the"actively"in the above. This will inevitably lead to hard-coded coupling between the caller and the implementation class of the dependent object, which is very unfavorable for project upgrade and maintenance. After using the Spring framework, the caller does not need toactivelyobtain the dependent object. The caller only needs topassivelyaccept the Spring container assigning values to the caller's member variables. It can be seen that after using Spring, the way the caller obtains the dependent object changes from active acquisition to passive acceptance—so Rod Johnson called it Inversion of Control.

In addition, from the perspective of the Spring container, the Spring container is responsible for assigning the dependent object to the caller's member variables—equivalent to injecting the instance it depends on into the caller. Therefore, Martine Fowler called it dependency injection.

Setter Injection

Setter injection means that the IoC container injects dependent objects through the setter methods of member variables. This injection method is simple and intuitive, so it is widely used in Spring's dependency injection.

Constructor Injection

The way to set dependencies using constructors is called constructor injection. In simple terms, it drives Spring to execute a constructor with specified parameters at the bottom through reflection. When the parameterized constructor is executed, the constructor parameters can be used to initialize member variables—this is the essence of constructor injection.

Comparison of the Two Injection Methods

Setter injection has the following advantages:

  • It is more similar to traditional JavaBean writing, making it easier for developers to understand and accept. Setting dependencies through setter methods is more intuitive and natural.
  • For complex dependencies, if constructor injection is used, the constructor may become too bloated and difficult to read. When Spring creates a Bean instance, it also needs to instantiate all its dependent instances at the same time, thus causing performance degradation. Setter injection can avoid these problems.
  • Especially when some member variables are optional, constructors with multiple parameters are even more cumbersome.

The advantages of constructor injection are as follows:

  • Constructor injection can determine the injection order of dependencies in the constructor, with higher-priority dependencies injected first.
  • For Beans whose dependencies do not need to change, constructor injection is more useful. Because there are no setter methods, all dependencies are set in the constructor, so there is no need to worry about subsequent code damaging the dependency relationships.
  • If dependencies can only be set in the constructor, then only the creator of the component can change the component's dependencies. For the caller of the component, the internal dependency relationships of the component are completely transparent, which is more in line with the principle of high cohesion.

Note:
It is recommended to use setter injection as the main approach and constructor injection as a supplement. For injections where the dependency relationship does not need to change, try to use constructor injection; for other dependency injections, consider using setter injection.

Beans in the Spring Container

For developers, using the Spring framework mainly involves two things: ① developing Beans; ② configuring Beans. For the Spring framework, what it needs to do is to create Bean instances according to the configuration file and call the methods of the Bean instances to complete "dependency injection"—this is the essence of the so-called IoC.

Scopes of Beans in the Container

When creating a Bean instance through the Spring container, you can not only instantiate the Bean instance, but also specify a specific scope for the Bean. Spring supports the following five scopes:

  1. singleton: Singleton mode. In the entire Spring IoC container, only one instance will be created for a Bean with the singleton scope.
  2. prototype: Each time a prototype-scoped Bean is obtained through the container's getBean() method, a new Bean instance will be created.
  3. request: For one HTTP request, a request-scoped Bean will generate only one instance. This means that within the same HTTP request, every time the program requests this Bean, it always gets the same instance. This scope is only truly effective when Spring is used in a web application.
  4. session: This scope limits the definition of the bean to an HTTP session. It is only valid in the context of a web-aware Spring ApplicationContext.
  5. global session: Each global HTTP Session corresponds to one Bean instance. In typical cases, it is only effective when using a portlet context, and it is likewise only valid in web applications.

If the Bean scope is not specified, Spring uses the singleton scope by default. Creating and destroying prototype-scoped Beans is relatively costly. In contrast, once a singleton-scoped Bean instance is created, it can be reused. Therefore, you should try to avoid setting Beans to prototype scope.

Injecting Collaborating Beans with Autowiring

Spring can automatically wire the dependencies between Beans. That is, there is no need to explicitly specify the dependent Bean using ref. Instead, the Spring container inspects the contents of the XML configuration file and injects the depended-on Bean into the caller Bean according to certain rules.
Spring autowiring can be specified via the<beans/>element'sdefault-autowireattribute. This attribute applies to all Beans in the configuration file. It can also be specified via the<bean/>element'sautowireattribute, which only affects that Bean.

autowireanddefault-autowireIt can accept the following values:

  • no: Autowiring is not used. Bean dependencies must be defined through the ref element. This is the default configuration. In larger deployment environments, changing this configuration is discouraged; explicitly configuring collaborators can produce clearer dependency relationships.
  • byName: Autowiring based on setter method names. The Spring container searches all Beans in the container, finds a Bean whose id matches the setter method name with the "set" prefix removed and the first letter lowercased, and completes the injection. If no matching Bean instance is found, Spring will not perform any injection.
  • byType: Autowiring based on the parameter type of the setter method. The Spring container looks up all Beans in the container. If there is exactly one Bean whose type matches the parameter type of the setter method, that Bean is automatically injected; if multiple such Beans are found, an exception is thrown; if no such Bean is found, nothing happens and the setter method will not be called.
  • constructor: Similar to byType, except that it is used to automatically match constructor parameters. If the container cannot find exactly one Bean matching the constructor parameter type, an exception will be thrown.
  • autodetect: The Spring container decides by itself whether to use the constructor or byType strategy based on the Bean's internal structure. If a default constructor is found, the byType strategy will be applied.

When a Bean uses both autowired dependencies and explicitly specified dependencies via ref, the explicitly specified dependencies override the autowired dependencies. For large applications, autowiring is discouraged. Although autowiring can reduce the workload of configuration files, it greatly reduces the clarity and transparency of dependency relationships. Dependency assembly relies on property names and property types in source files, causing the coupling between Beans to be reduced to the code level, which is not conducive to high-level decoupling.

<!--通过设置可以将Bean排除在自动装配之外-->
<bean id="" autowire-candidate="false"/>

<!--除此之外,还可以在beans元素中指定,支持模式字符串,如下所有以abc结尾的Bean都被排除在自动装配之外-->
<beans default-autowire-candidates="*abc"/>

Three Ways to Create Beans

Creating Bean Instances with Constructors

Using a constructor to create a Bean instance is the most common case. If constructor injection is not used, Spring will internally call the Bean class's no-argument constructor to create the instance. Therefore, the Bean class is required to provide a no-argument constructor.

Using the default constructor to create a Bean instance, Spring performs default initialization on all properties of the Bean instance, i.e., all primitive-type values are initialized to 0 or false, and all reference-type values are initialized to null.

Creating Beans with Static Factory Methods

When using a static factory method to create a Bean instance, the class attribute must also be specified. However, at this time the class attribute does not specify the implementation class of the Bean instance, but rather the static factory class. Spring uses this attribute to know which factory class will create the Bean instance.

In addition, the factory-method attribute must be used to specify the static factory method. Spring will call the static factory method to return a Bean instance. Once the specified Bean instance is obtained, Spring's subsequent processing steps are exactly the same as when creating a Bean instance using an ordinary method. If the static factory method requires parameters, use the<constructor-arg.../>element to specify the parameters of the static factory method.

Creating Beans by Calling Instance Factory Methods

The only difference between an instance factory method and a static factory method is that calling a static factory method only requires a factory class, while calling an instance factory method requires a factory instance. When using an instance factory method, the<bean.../>element does not need the class attribute. To configure an instance factory method, usefactory-beanto specify the factory instance.
The<bean.../>element for creating a Bean using an instance factory method requires specifying the following two attributes:

  • factory-bean: The value of this attribute is the id of the factory Bean.
  • factory-method: This attribute specifies the factory method of the instance factory.

If parameters need to be passed when calling the instance factory method, use the<constructor-arg.../>element to determine the parameter values.

Coordinating Beans with Different Scopes

When a singleton-scoped Bean depends on a prototype-scoped Bean, a synchronization problem occurs. This is because when the Spring container initializes, it pre-initializes all thesingleton Bean, and sincesingleton Beandepends onprototype Bean, when Spring initializessingleton Beanbefore, it will first createprototypeBean— and only then createsingleton Bean, and thenprototype Beaninjectsingleton Bean。
There are two ways to solve the synchronization problem:

  • Abandoning dependency injection: whenever a singleton-scoped Bean needs a prototype-scoped Bean, it actively requests a new Bean instance from the container, thereby ensuring that each injectedprototype Beaninstance is the latest instance.
  • Using method injection: method injection usually uses lookup method injection. Lookup method injection allows the Spring container to override abstract or concrete methods of Beans in the container, returning the result of looking up other Beans in the container. The looked-up Bean is usually anon-singleton Bean. Spring achieves the above requirement by using JDK dynamic proxies or the cglib library to modify the client's binary code.

It is recommended to use the second method, method injection. To use lookup method injection, roughly the following two steps are needed:

  1. Define the implementation class of the caller Bean as an abstract class, and define an abstract method to obtain the depended-on Bean.
  2. In<bean.../>Add the<lookup-method.../>child element to the element, letting Spring implement the specified abstract method for the implementation class of the caller Bean.

Note:

Spring will use runtime dynamic enhancement to implement the<lookup-method.../>abstract method specified by the element. If the target abstract class has implemented an interface, Spring will use JDK dynamic proxy to implement the abstract class and implement the abstract method for it; if the target abstract class has not implemented an interface, Spring will use cglib to implement the abstract class and implement the abstract method for it. The cglib library has already been integrated into the spring-core-xxx.jar package in Spring 4.0.

Two Types of Post-Processors

Spring provides two commonly used post-processors:

  • Bean post-processor: This post-processor performs post-processing on Beans in the container, providing additional enhancement to the Beans.
  • Container post-processor: This post-processor performs post-processing on the IoC container to enhance container functionality.

Bean Post-Processor

A Bean post-processor is a special kind of Bean. This special Bean does not provide services to the outside, and it can even omit the id attribute. It is mainly responsible for performing post-processing on other Beans in the container, such as generating proxies for target Beans in the container. This kind of Bean is called a Bean post-processor. After a Bean instance is successfully created, the Bean post-processor performs further enhancement processing on the Bean instance. A Bean post-processor must implement theBeanPostProcessorinterface, and must implement both methods of that interface.

  1. Object postProcessBeforeInitialization(Object bean, String name) throws BeansException: The first parameter of this method is the Bean instance about to be post-processed by the system, and the second parameter is the configuration id of the Bean.
  2. Object postProcessAfterinitialization(Object bean, String name) throws BeansException: The first parameter of this method is the Bean instance about to be post-processed by the system, and the second parameter is the configuration id of the Bean.

Once a Bean post-processor is registered in the container, it will start automatically and work automatically when each Bean is created in the container. The callback timing of the two methods of the Bean post-processor is shown in the following figure:

bean-post-process

Note that if you useBeanFactoryas the Spring container, you must manually register the Bean post-processor. The program must obtain a Bean post-processor instance and then register it manually.

BeanPostProcessor bp = (BeanPostProcessor)beanFactory.getBean("bp");
beanFactory.addBeanPostProcessor(bp);
Person p = (Person)beanFactory.getBean("person");

Container Post-Processor

The Bean post-processor is responsible for processing all Bean instances in the container, while the container post-processor is responsible for processing the container itself. The container post-processor must implement theBeanFactoryPostProcessorInterface, and implement a method of the interface.postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)The method body that implements this method is the processing performed on the Spring container. This processing can be a custom extension of the Spring container, or of course, it can perform no processing on the Spring container at all.

similar toBeanPostProcessor,ApplicationContextCan automatically detect container post-processors in the container, and automatically register container post-processors. But if usingBeanFactoryAs the Spring container, it must manually call this container post-processor for processing.BeanFactoryContainer.

Spring's "Zero Configuration" Support

Searching for Bean Classes

Spring provides the following annotations to mark Spring Beans:

  • @Component: Marks an ordinary Spring Bean class.
  • @Controller: Annotates a controller component class.
  • @Service: Annotates a business logic component class.
  • @Repository: Annotates a DAO component class.

Make the following configuration in the Spring configuration file to specify the package to be automatically scanned:

<context:component-scan base-package="edu.shu.spring.domain"/>

Configuring Dependencies with @Resource

@ResourceLocated atjavax.annotationLocated under the package, it is one from the JavaEE specification.Annotation, Spring directly borrowed thisAnnotation, by using thisAnnotationSpecify collaborator Beans for the target Bean. Use@Resourceand<property.../>The ref attribute of the element has the same effect.
@Resourcecan not only annotate setter methods, but also directly annotate instance variables. If using@ResourceAnnotating instance variables will be even simpler; in this case, Spring will directly use the JavaEE standard field injection, and even setter methods are not needed.

Customizing Lifecycle Behavior with @PostConstruct and @PreDestroy

@PostConstructand@PreDestroyAlso located under the javax.annotation package, these are two Annotations from the JavaEE specification. Spring directly borrowed them to customize the lifecycle behavior of Beans in the Spring container. Both are used to modify methods and require no attributes. The method modified by the former is the Bean's initialization method; the method modified by the latter is the method before the Bean is destroyed.

Enhanced Autowiring and Precise Wiring in Spring 4.0

Spring provides@Autowiredannotation to specify autowiring,@Autowiredcan annotate setter methods, regular methods, instance variables, constructors, and so on. When using@AutowiredAnnotating a setter method, by default the byType autowiring strategy is adopted. Under this strategy, there are often multiple candidate bean instances matching the autowiring type, which may cause exceptions. To achieve precise autowiring, Spring provides@Qualifierannotation, by using@Qualifier, allows automatic assembly to be performed based on the Bean's id.

Spring's AOP

Why AOP Is Needed

AOP (Aspect-Oriented Programming), also known as aspect-oriented programming, as a supplement to object-oriented programming, has become a relatively mature programming approach. In fact, AOP has not been around for very long. AOP and OOP complement each other; aspect-oriented programming decomposes the program execution process into various aspects.

AOP is specifically used to handle cross-cutting concerns distributed across various modules (different methods) in a system. In JavaEE applications, AOP is often used to handle system-level services with cross-cutting characteristics, such as transaction management, security checks, caching, and object pool management. AOP has become a very common solution.

Using AspectJ to Implement AOP

AspectJ is an AOP framework based on the Java language, providing powerful AOP functionality. Many other AOP frameworks have borrowed or adopted some of its ideas. It mainly consists of two parts: one part defines the syntax specification for expressing and defining AOP programming. With this syntax specification, AOP can conveniently solve cross-cutting concerns in the Java language; the other part is the tooling part, including compilation and debugging tools, etc.

AOP implementations can be divided into two categories:

  1. Static AOP implementation: The AOP framework modifies the program during the compilation phase, that is, it enhances the target class and generates a static AOP proxy class, represented by AspectJ.
  2. Dynamic AOP implementation: The AOP framework dynamically generates AOP proxies at runtime to enhance target objects, represented by Spring AOP.

Generally speaking, static AOP implementation has better performance, but requires a special compiler. Dynamic AOP implementation is a pure Java implementation, so it does not require a special compiler, but its performance is usually slightly worse.

Basic Concepts of AOP

Some terminology about aspect-oriented programming:

  • Aspect: An aspect is used to organize multiple Advice, and Advice is defined in the aspect.
  • Joinpoint: A clear point during program execution, such as a method call or an exception throw. In Spring AOP, a joinpoint is always a method call.
  • Advice: Enhanced processing executed by the AOP framework at specific pointcuts. The processing has types such as "around", "before", and "after".
  • Pointcut: A joinpoint where enhanced processing can be inserted. In short, when a joinpoint meets specified requirements, enhanced processing will be added to that joinpoint, and that joinpoint becomes a pointcut.

Spring's AOP Support

AOP proxies in Spring are generated and managed by Spring's IoC container, and their dependencies are also managed by the IoC container.
In order to use in the application@AspectJTo support it, Spring needs to add three libraries:

  • aspectjweaver.jar
  • aspectjrt.jar
  • aopalliance.jar

And make the following configuration in the Spring configuration file:

<!--启动@AspectJ支持-->
<aop:aspectj-autoproxy/>

<!--指定自动搜索Bean组件、自动搜索切面类-->
<context:component-scan base-package="edu.shu.sprint.service">
    <context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/>
</context:component-scan>

Source:http://codepub.cn/2015/06/21/Basic-knowledge-summary-of-Spring/