1. The background

Spring was created to simplify enterprise development and is an absolutely indispensable technology in the Spring framework family.

2. Basic concepts

To reduce the complexity of Java development, Spring employs the following four key strategies:

  • Pojo-based (Java Ben) lightweight and minimally invasive programming.
  • Loose coupling is achieved through dependency injection and interface orientation.
  • Declarative programming based on aspects and conventions.
  • Reduce boilerplate code based on cuts and boilerplate.

Everything Spring does revolves around these points.

Dependency Injection (DI) sounds daunting, but it’s not as complicated as it sounds.

IoC (IoC) is also known as Dependency Injection (DI). The process by which an object is created by defining the parameters of its constructor or factory method (that is, the objects it uses) and then injecting these dependencies when the container creates the bean.

Contrast difference:

  • The traditional approach is: Class A uses Class B, so you need to create an object of B in the code of A.
  • Dependency injection is to define A and B, use XML to describe the relationship between A and B, and inject THE B object into the sample object of A when the container creates A. It can be created from the container and used directly, no need to create a New one.

Aspect Oriented Programming (AOP)

AOP (Aspect Oriented Programming), section-oriented Programming. Aspect refers to Aspect, which can be understood as “dimension” in Chinese.

AOP is a “separation of concerns” technique in which software systems are often composed of multiple components/modules, each responsible for a specific piece of functionality. These components often assume additional responsibilities, such as logging, transactions, security controls, and system service logic, mixed with business functions. These system service logic can exist in multiple components/modules and are referred to as “crosscutting concerns.”

The system service logic called in these modules is spread across multiple components/modules, resulting in the complexity of maintaining code for multiple components. Even if these concerns are separated into a separate module, method calls still occur in each module.

AOP, on the other hand, modularizes these facets of concern and applies them declaratively to specific business components/modules, making them more cohesive and business-focused.

Use modules to eliminate boilerplate code. Boilerplate code is repetitive code, such as opening database connections, constructing preprocessed statements, and so on in traditional JDBC. Help eliminate boilerplate code and simplify complexity by using Template encapsulation, which makes your code more relevant to its business responsibilities.

Spring container, Dependency Injection (DI), and aspect-Orientd Programming (AOP) are the core of Spring framework. The following are introduced respectively.

3. Container (ApplicationContext)

3.1 Introduction to containers

Org. Springframework. Context. On behalf of the Spring IoC container ApplicationContext interface, is responsible for the instantiation, configure, and assemble the bean.

  • The container reads “configuration metadata” to learn how to create and assemble objects.
  • “Configuration metadata” can be represented in XML configuration files, Java annotations, or Java code.

ApplicationContext is built on the BeanFactory, which provides the configuration framework and basic functionality, while ApplicationContext adds more enterprise-specific functionality. We use the ApplicationContext more.

Spring provides several ApplicationContext implementations

  • From the classpath ClassPathXmlApplicationContext loading XML configuration files
  • FileSystemXmlApplicationContext loading XML configuration files from the file system
  • AnnotationConfigApplicationContext annotation-based context from the annotations to load

Example:

ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
Copy the code

3.2 Bean life cycle

With containers, which are responsible for creating and managing beans, we need to take a closer look at the Bean lifecycle:

3.3 Code Examples

Add spring-Context dependencies when the dependency libraries are in Maven mode.

<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> The < version > 5.3.4 < / version > < / dependency > < / dependencies >Copy the code

Spring supports a variety of ways to load configuration

  • (1) XML
  • (2) the Java way

XML using the ClassPathXmlApplicationContext or FileSystemXmlApplicationContext initialization container object from an XML file.

Example: XML file

<? The 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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="hero" class="cn.zyfvir.demo.Hero"> <constructor-arg ref="swordAction"/> </bean> <bean id="swordAction" class="cn.zyfvir.demo.SwordAction"> <constructor-arg  name="printStream" value="#{T(System).out}"/> </bean> </beans>Copy the code
Private static void demoXmlSpring() {ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); Hero bean = context.getBean(Hero.class); bean.play(); }Copy the code

AnnotationConfigApplicationContext context supports from the annotation and the way of Java code configuration object.

Example:

@Configuration public class HeroConfig { @Bean public Hero hero() { return new Hero(action()); } @Bean public Action action() { return new SwordAction(System.out); }}Copy the code
/ / use the Java way private static void demoJavaSpring () {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); // Enable Component scanning to find any class context.scan("cn.zyfvir.demo") with @Component, @Configuration annotations; context.refresh(); Hero bean = context.getBean(Hero.class); bean.play(); }Copy the code

My sample demo code is at: github.com/vir56k/java…

3. Dependency Injection DI (Assembly Bean)

3.1 Wiring (Wiring)

Wiring: In Spring, an object does not need to look up and create other objects associated with it. The container is responsible for assigning objects to which it needs to collaborate. The behavior of creating a collaboration between objects is called Wiring. This is also the essence of DI

Spring provides three ways to assemble beans:

  • Configure in XML
  • Configuration in Java mode
  • Automatically.

How to choose? Some suggestions:

  • Use the autoload approach as much as possible, which is easier to use because it does not display the dependency configuration for each Bean.
  • Second, use Java configuration, which is type-safe and more powerful and intuitive than XML.
  • XML is the last option.

3.2 Automatic assembly of beans

Why declare it in XML, etc., when Spring can assemble itself? Automatic assembly brings a lot of convenience.

Spring implements autowiring from two perspectives:

  • Component Scanning: Spring automatically scans and discovers beans that need to be created
  • Autowiring: Spring automatically satisfies dependencies between beans

The @Component annotation can be applied to a class to declare a bean object. The @ComponentScan annotation is used to enable component scanning. Classes in the same package are scanned by default. It can also specify specific packages through the basePackages property. The @autoWired annotation declares automatic assembly, and Spring will select a Bean that matches the appropriate Bean to assemble. It applies to constructors and set methods.

3.3 Configuration using Java Code

@Configuration declares that this class is a Configuration class, which is not required. Declaring this method via the @bean annotation returns an object to be registered in the Spring context.

Such as:

@Configuration public class AppConfig { @Bean public MyService myService() { return new MyServiceImpl(); }}Copy the code

3.4 Assembly through XML

Description by label.

  • The ID attribute is the bean’s identifier
  • The class attribute defines the type of the bean and uses the fully qualified class name
<? The 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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="..." class="..." > </bean> <bean id="myService" class="com.acme.services.MyServiceImpl"/> </beans>Copy the code

3.5 Mixed Use

Use @import or @importResource annotations. IO /spring-fram…

3.6 Code Examples

An example of autowiring is written below:

/** * @description: CD * @author: zhangyunfei * @date: 2021/7/3 22:22 */ public interface CompactDisc {void play(); } /** * @description: VCD * @author: zhangyunfei * @date: 2021/7/3 22:25 */ @component public class VcdCompactDisc implements CompactDisc {private String title = "implements CompactDisc "; Public void play() {system.out.println (String. Format (" %s is playing... , title)); }}Copy the code

When used, the key is Autowired’s annotations, which automatically find the right object to inject into it.

/** * @description: * @author: zhangyunfei * @date: 2021/7/3 22:16 */ @Component public class Player { private CompactDisc compactDisc; @autowired void insertCompactDisc(CompactDisc action) {this.pactdisc = action; } /** * start playing */ void startPlay() {compactdisc.play (); }}Copy the code

You also need to configure Automatic Scanning for components to assemble. ComponentScan is used to declare a search for the current package.

@Configuration
@ComponentScan
public class PlayerConfig {
}

Copy the code

The main method shows how to call:

public class MainClass { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(PlayerConfig.class); Player bean = context.getBean(Player.class); bean.startPlay(); }}Copy the code

Examples of my code can be found at: github.com/vir56k/java…

4. Aspect Oriented (AOP)

AOP (Aspect Oriented Programming, AOP) Aspect Oriented Programming, refers to the compilation time way or the run-time dynamic proxy way, the code into the specified location (specific class method) on a Programming idea, is the Aspect Oriented Programming.

Why use AOPIt depends on the scene and timing, as shown below:

Functions such as logging and transactions face several options for modularity, such as object inheritance and delegation. Inheritance has the same base class throughout the application, often resulting in a fragile object system, and delegation may require complex calls to delegate objects.

Writing calls in each business module is also too cumbersome to maintain. While aspect is an alternative, using AOP you can apply it externally in a declarative manner without modifying (affecting) specific business function modules. This is also a case of “separation of concerns”, where each concern is concentrated in one place rather than scattered code.

AOP is a programming paradigm that can be implemented in a number of ways:

  • Proxy approaches, such as Spring AOP
  • Compile-time approach, such as AspectJ
  • Class loading period

The proxy can be divided into static proxy and dynamic proxy. Static proxy can be understood as self-written proxy or bytecode proxy. Dynamic proxy is a way of generating a proxy class (implementation class) at run time to access the target object.

Spring AOP is AOP implemented through dynamic proxies

  • Spring AOP uses JDK Proxies to create Proxy objects that declare that they implement an interface. Create a proxy class and a new target proxy implementation that is accessed through the proxy class when invoked.
  • When there is no interface declaration, Spring AOP uses Cglib to generate a subclass of the propped object as a proxy.

AspectJ is woven in when compiled to class and has more power.

The class loading period is woven when the target class is loaded into the JVM and requires special class loaders such as The load-time Weaving in AspectJ 5, which LTW supports.

AOP terms:

  • Advise: A message received and at what time it is known.
  • Pointcut: Describes where to cut, such as a method for a name.
  • Join point: Cut to that point, such as the three concrete methods called getSome.

Types of Spring AOP advice:

  • Before: Notification “Before” the target method is called.
  • After notification: notification “After” is called.
  • Post-returning notification: notification After “successful” execution.
  • After-throwing: Notification After “throwing an exception.”
  • Circular notification (Around): A method that is “fully wrapped” so that the execution of the target method can be obtained, thus allowing for custom timing before and after invocation, or even multiple times to implement the retry mechanism.

Code example (1) references the class library

< the dependency > < groupId > org. Springframework < / groupId > < artifactId > spring - aspects < / artifactId > < version > 5.3.4 < / version > </dependency>Copy the code

(2) Declare the automatic proxy configuration that enables Aspect

// Enable: ComponentScan @componentScan @enableAspectJAutoProxy public class MySrpingConfig {}Copy the code

A pointcut to write AOP advice

Public void postBlog(String blogContet); } @Component class BlogServiceImpl implements BlogService { public void postBlog(String blogContet) { System.out.println(" publish a blog "); @component class LogAspect {// Pointcut expression @pointcut ("execution(*) cn.zyfvir.demo.BlogService.postBlog(..) )") public void doPostPoint() { } @Before("doPostPoint()") public void before() { System.out.println("## before..." ); } @After("doPostPoint()") public void after() { System.out.println("## after..." ); } @AfterReturning("doPostPoint()") public void afterReturning() { System.out.println("## AfterReturning..." ); } @AfterThrowing("doPostPoint()") public void afterThrowing() { System.out.println("## AfterThrowing..." ); }}Copy the code

Through AOP, no impact on the actual business call is achieved, just normal use, example:

public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MySrpingConfig.class); BlogService bean = context.getBean(BlogService.class); Bean.postblog (" From beginner to Master "); System.out.println(" end of execution, "+ bean); }Copy the code

During execution, the logging classes are triggered and work. Examples of my code can be found at: github.com/vir56k/java…

5. Extension: Advanced assembly

5.1 Environment and @Profile annotations

There are often multiple environments in real development, such as the DEV development environment, the Test test environment, and the Product formal environment. Spring provides a layer of abstraction for the environment, allowing you to define multiple environments and activate one in use.

The key points are:

  • Declare an environment and objects that are used only in that environment
  • Activating an environment

You can use @profile to declare that a bean is only available (active) under certain circumstances. For example, the following example uses the @profile annotation to declare that the class is only available in the Development environment.

@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();
    }
}
Copy the code

To activate the configured environment, you can use code methods such as:

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("development");
ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);
ctx.refresh();
Copy the code

You can also specify when starting from the Java command line: declare the active environment using the environment variable “spring.profiles.active”, as shown in the following example:

    -Dspring.profiles.active="profile1,profile2"
Copy the code

5.2 Conditional selection Bean with @Conditional annotation

In fact, the @profile mentioned above is also implemented through the @Conditional annotation. The @Conditional annotation can be used to indicate that a Bean should only be registered under certain circumstances.

Example:

@Configuration public class PersonConfig { @Bean() @Conditional({ConditionalDemo1.class}) public Person person1(){ return new Person("Bill Gates",62); }}Copy the code

The example above uses the @Conditional annotation, which specifies conditionaldemo1.class. The argument to @Conditional is actually an interface that you can implement according to your needs:

public interface Condition {
    boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}
Copy the code

For example, the @profile annotation actually works like this:

@Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { // Read the @Profile annotation attributes MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName()); if (attrs ! = null) { for (Object value : attrs.get("value")) { if (context.getEnvironment().acceptsProfiles(((String[]) value))) { return true; } } return false; } return true; }Copy the code

5.3 Handle ambiguity in automatic assembly

An exception occurs when more than one selectable object cannot be determined during autowiring. For example, encounter the following situation:

// interface Dessert {String getName(); } @component class IceCream implements Dessert{private String name = "IceCream "; public String getName() { return name; }} @component class Chocolate implements Dessert{private String name = "Chocolate "; public String getName() { return name; }} @component class Lollipop implements Dessert{private String name = "Lollipop "; public String getName() { return name; }}Copy the code

The above example implements multiple dessert classes, and the child does not know which one to eat.

@Component public class Child { Dessert dessert; // want @autowired public void wantDessert(Dessert Dessert) {this. Dessert = Dessert; } public void eating() {system.out.println (String. Format (" eating %s... , dessert.getName())); }}Copy the code

At this point, you can choose the way to deal with ambiguity:

  • Declare a “preferred” with @primary
  • Specify a Bean name with the @qualifier qualified name annotation.

Example:

@autowired @Qualifier("lollipop") // @qualifier declares a preferred qualified name. public void wantDessert(Dessert dessert) { this.dessert = dessert; }Copy the code

Examples of my code can be found at: github.com/vir56k/java…

5.4 Bean Scope Scope

By default, Spring creates instances that are singletons, or singletons.

Sping supports multiple scopes, including:

Scope describe
singleton A single instance
prototype A new instance is created each time
request During a request for a Web application
session Web application session duration
application Web Application duration
websocket Websocket range

Use the @scope annotation to specify a Scope for a Bean, as in:

@Scope("prototype")
@Component
class IceCream implements Dessert{
    ...
}
Copy the code

5.5 Runtime assembly

Run-time scenarios, such as dynamically fetching the contents of a configuration file, or the result of executing a method, or a random number, or the result of an expression.

  • The configuration file can be read using the @propertysource annotation
  • Using the @Value annotation, you can get external property values
  • In the Value annotation you can use ${… } expressions like this read values

The following is an example:

@configuration@propertysource ("classpath:myproperty_config.properties") public class MyPropertyConfig {// Read the Configuration file author.name @Value("${author.name}") public String authorName; }Copy the code

5.6 SpEL

SpEL stands for Spring Expression Language (SpEL), which provides a concise and powerful way to assemble values into Bean properties that are evaluated at run time using expressions.

SpEL features:

  • Reference to the Bean
  • Call a method or access a property
  • Arithmetic operations, relational operations, logical operations
  • Regular expression
  • Set operations

SpEL format: #{.. } It starts with #.

Example:

@value ("#{systemProperties['user.region']}") private String defaultLocale; @configuration@propertysource ("classpath:myproperty_config.properties") public class MyPropertyConfig {// Read the Configuration file author.name @Value("${author.name}") public String authorName; @ Value (" # {} 3.1415 ") public String PI; @Value("#{'xxxxx'}") public String string1; @Value("#{myPropertyConfig.getMyName().toUpperCase()}") public String myName; @Value("#{T(java.lang.Math).PI}") public String PI; @Value("#{T(java.lang.Math).random()}") public String random; @value ("#{mypropertyConfig. PI == 3.14}") public Boolean is3_14; @value ("#{mypropertyConfig. PI == 3.14}") public Boolean is3_14; @Value("#{ myPropertyConfig.pi ? :'333' }") public String stirng2; public String getMyName() { return "zhang3"; } @Value("#{ myPropertyConfig.getArray() }") public String[] array; @Value("#{ myPropertyConfig.array[1] }") public String array1; public String[] getArray() { String[] arr = new String[3]; arr[0] = "#1"; arr[1] = "#2"; arr[2] = "#3"; return arr; }}Copy the code

6. Reference

Docs. Spring. IO/spring – fram…

Github.com/spring-proj…