Notes on Java back-end development theory Spring practice

1, Introduction to Spring

1.1 simplify Java development

The most fundamental mission of Spring application development is to simplify the open source framework.
  in order to reduce the complexity of Java development, Spring adopts the following four key strategies:

  1. Lightweight and minimally invasive programming based on POJO;
  2. Loose coupling is realized through dependency injection and interface oriented;
  3. Declarative programming based on facets and conventions;
  4. Reduce boilerplate code through facets and templates.

1.1.1 dependency injection

  Spring can do many things. The bottom layer of these functions depends on its two core features, namely dependency injection (DI) and aspect oriented programming (AOP).
  dependency injection will automatically hand over the dependent relationship to the target object, rather than letting the object get the dependency by itself.
  the act of creating collaboration between application components is often called wiring. Spring has many ways to assemble beans, and XML is a very common way to assemble beans. Spring also supports using Java to describe the configuration if the XML configuration does not meet your preference.

1.1.2 application section

   DI can keep the software components that cooperate with each other loosely coupled, while aspect oriented programming (AOP) allows you to separate the functions throughout the application to form reusable components.
  aspect oriented programming is often defined as a technology that promotes the separation of concerns in software systems. The system consists of many different components, each responsible for a specific function. In addition to implementing their core functions, these components often assume additional responsibilities. System services such as logging, transaction management and security are often integrated into their own components with core business logic. These system services are often called crosscutting concerns because they span multiple components of the system.
  if you spread these concerns among multiple components, your code will bring double complexity:

  1. The code that implements the system focus function will appear repeatedly in multiple components. This means that if you want to change the logic of these concerns, you must modify the relevant implementation in each module. Even if you Abstract these concerns into an independent module and other modules only call its methods, the method calls will still appear repeatedly in each module.
  2. Components can become chaotic because of code that has nothing to do with their core business. A method of adding an address entry to an address book should only focus on how to add an address, not whether it is secure or whether it needs to support transactions.

  in the whole system, the calls of concerns (such as log and security) are often scattered in each module, and these concerns are not the core business of the module.
  AOP can modularize these services and apply them declaratively to the components they need to affect. The result is that these components will have higher cohesion and pay more attention to their own business, without understanding the complexity caused by system services. In short, AOP can ensure the simplicity of POJO.

1.2 accommodate your Bean

  in Spring based applications, your application objects exist in the Spring container. The Spring container is responsible for creating objects, assembling them, configuring them, and managing their entire life cycle, from survival to death.
  container is the core of Spring framework. The Spring container uses DI to manage the components that make up the application, and it will create associations between the components that cooperate with each other. There is no doubt that these objects are simpler, cleaner, easier to understand, easier to reuse, and easier to unit test.

1.2.1 application context

  Spring comes with many types of application contexts. Here are some of the things you are most likely to encounter:

  1. AnnotationConfigApplicationContext: loads the Spring application context from one or more Java based configuration classes.
  2. AnnotationConfigWebApplicationContext: loads the Spring Web application context from one or more Java based configuration classes.
  3. ClassPathXmlApplicationContext: load the context definition from one or more XML configuration files under the classpath, and take the definition file of the application context as the class resource.
  4. FileSystemXmlapplicationcontext: loads context definitions from one or more XML configuration files under the file system.
  5. XmlWebApplicationContext: load the context definition from one or more XML configuration files under the Web application.

1.3 Spring scenery

1.3.1 Spring Portfolio

  Spring is far from what the Spring framework downloads. If we only stay at the core level of Spring framework, we will miss the huge wealth provided by Spring Portfolio. The whole Spring Portfolio includes multiple frameworks and class libraries built on the core Spring framework. To sum up, the whole Spring Portfolio provides a Spring programming model for Java development in almost every field.
   the Spring Portfolio mainly includes the following items, which will not be introduced one by one here: Spring Web Flow, Spring Web Service, Spring Security, Spring Integration, Spring Batch, Spring Data, Spring Social, Spring Mobile, Spring for Android, Spring Boot, etc. interested partners can study by themselves.

  1. Spring Security
      security is a critical aspect for many applications. Using Spring AOP, Spring Security provides a declarative security mechanism for Spring applications.
  2. Spring Integration
      many enterprise applications need to interact with other applications. Spring Integration provides a spring declarative style implementation of a variety of general application integration patterns.
  3. Spring Data
      Spring Data makes it easy to use any database in Spring. Although relational databases have dominated enterprise applications for many years, modern applications are recognizing that not all data is suitable for rows and columns in a table. A new kind of database, commonly known as NoSQL database, provides new methods of using data, which will be more suitable than traditional relational database.
  4. Spring Boot
       spring greatly simplifies many programming tasks and reduces or even eliminates a lot of template code. Without spring, you have to write such template code in your daily work. Spring Boot is a new and exciting project. It is committed to simplifying spring itself from the perspective of spring.
       Spring Boot relies heavily on automatic configuration technology, which can eliminate most (or even all) Spring configuration. It also provides multiple Starter projects, whether you use Maven or Gradle, which can reduce the size of the Spring project build file.

1.4 new features of spring

1.4.1 new features of spring 4.0

  spring 4.0 was the latest release at the time of writing this book. Spring 4.0 includes many exciting new features, including:

  1. Spring provides support for WebSocket programming, including JSR-356 -- Java API for WebSocket;
  2. Since WebSocket only provides a low-level API and needs high-level abstraction, Spring 4.0 provides a high-level message oriented programming model based on WebSocket, which is based on SockJS and includes support for STOMP protocol;
  3. Many types of the new messaging module come from the Spring Integration project. This message module supports Spring's SockJS/STOMP function, and provides a template based way to publish messages;
  4. Spring is the first (if not the first) Java framework to support Java 8 features, such as lambda expressions. Not to mention anything else, this first makes the use of specific callback interfaces (such as RowMapper and JdbcTemplate) more concise and the code more readable;
  5. JSR-310, the date and Time API, is also supported with Java 8. When dealing with date and time, it provides developers with more advantages than Java util. Date or Java util. Calendar has richer APIs;
  6. It provides a smoother programming experience for applications developed by Groovy, especially supporting the development of Spring applications completely using Groovy. Along with these are BeanBuilder from Grails, which can configure Spring applications through Groovy;
  7. Added the function of conditionally creating beans. Here, the declared beans will be created only when the conditions defined by the developer are met;
  8. Spring 4.0 includes a new asynchronous implementation of Spring RestTemplate, which will return immediately and allow callback after the operation is completed;
  9. Added support for several JEE specifications, including JMS 2.0, JTA 1.2, JPA 2.1 and Bean Validation 1.1.

1.4.2 new features of spring 5.0

   the new features of Spring 5 can be basically classified into the following categories:

  1. JDK version upgrade
  2. Core framework revision, core container update
  3. Kotlin functional programming
  4. Responsive programming model
  5. Test improvement
  6. Additional library support

2, Assemble Bean

   the behavior of creating collaborative relationships between application objects is often called wiring, which is also the essence of dependency injection (DI).
   Spring has great flexibility. It provides three main assembly mechanisms:

  1. Explicit configuration in XML.
  2. Explicit configuration in Java.
  3. Implicit bean discovery mechanism and automatic assembly.

2.1 automatic assembly

  Spring realizes automatic assembly from two perspectives:

  1. Component scanning: Spring will automatically discover the bean s created in the application context.
  2. Automatic assembly: Spring automatically satisfies the dependencies between bean s.

  example: CD and CD player. Define CD interface:

package com.springinaction;

public interface CompactDisc {
    void play();
}

   an implementation class of CD, which uses @ Component to tell Spring to create bean s for this class.

package com.springinaction;
import org.springframework.stereotype.Component;

@Component
public class SgtPeppers implements CompactDisc {

    private String title = "Sgt. Pepper's Lonely Hearts Club Band";
    private String artist = "The Beatles";

    @Override
    public void play() {
        System.out.println("Playing " + title + " by " + artist);
    }
}

  configure with Java based Spring, and use @ ComponentScan to scan the same package as the configuration class by default.

package com.springinaction;
import org.springframework.context.annotation.*;

@Configuration
@ComponentScan
public class CDPlayerConfig {
}

   if you use XML to enable component scanning, you can use the following code:

<?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">
    <Context:component-scan base-package="com.springinaction" />
</beans>

   use JUnit to test whether the implementation class of CD is automatically created by Spring:

package com.springinaction;

import static org.junit.Assert.*;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=CDPlayerConfig.class)
public class CDPlayerTest {

    @Autowired
    private CompactDisc sgtPeppers;

    @Test
    public void cdShouldNotBeNull(){
        assertNotNull(sgtPeppers);
    }
}

  the result shows that the bean was created successfully:

   the above shows the process and test of Spring creating beans. The bean of sgtpeppers does not have an explicit ID, but Spring will automatically specify it. The default is sgtpeppers and the initial letter is lowercase. If you want to set a different ID for this bean, you can use the following method:

@Component("lonelyHeartsClub")
public class SgtPeppers implements CompactDisc {
    .......
}

   so far, no properties have been set for @ ComponentScan. This means that according to the default rule, it will scan components with the package where the configuration class is located as the base package. If you want to put the configuration class in a separate package, distinguish it from other application code. Then you can set it in the following ways:

@Configuration
@ComponentScan("com.springinaction")
public class CDPlayerConfig { }

   or more clearly, the basic package is set:

@Configuration
@ComponentScan(basePackages="com.springinaction")
public class CDPlayerConfig { }

  you can also set multiple basic packages:

@Configuration
@ComponentScan(basePackages={"com.springinaction", "video"})
public class CDPlayerConfig { }

  you can also specify it as the class or interface contained in the package:

@Configuration
@ComponentScan(basePackageClasses={CDPlayer.class, DVDPlayer.class})
public class CDPlayerConfig { }

  most classes in the system still depend on each other, so we need to understand automatic assembly.
   in short, automatic assembly is a way for Spring to automatically meet bean dependencies. In the process of meeting dependencies, it will find other beans matching the requirements of a bean in the Spring application context. To declare automatic assembly, we can use Spring's @ Autowired annotation.
   for example, for the class implementing CD player, the player interface has a play method:

package com.springinaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class CDPlayer implements MediaPlayer {
    private CompactDisc compactDisc;

    @Autowired
    public CDPlayer(CompactDisc compactDisc){
        this.compactDisc = compactDisc;
    }

    @Override
    public void play() {
        compactDisc.play();
    }
}

   the above uses an automatic assembly constructor, which can also be used on the attribute Setter method:

@Autowired
public void setCompactDisc(CompactDisc compactDisc){
    this.compactDisc = compactDisc;
}

  if there is no matching bean, Spring will throw an exception when the application context is created. To avoid exceptions, you can set the required property of @ Autowired to false:

@Autowired(required=false)
public CDPlayer(CompactDisc compactDisc){
    this.compactDisc = compactDisc;
}

  if there are multiple bean s that can satisfy the dependency, Spring will throw an exception.
   now, we have added the @ Autowired annotation in the constructor of CDPlayer. Spring will automatically inject a bean that can be assigned to CompactDisc type. For verification, we will test:

package com.springinaction;

import static org.junit.Assert.*;

import org.junit.Test;
import org.junit.Rule;
import org.junit.contrib.java.lang.system.SystemOutRule;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=CDPlayerConfig.class)
public class CDPlayerTest {

    @Rule
    public final SystemOutRule log = new SystemOutRule().enableLog();

    @Autowired
    private CompactDisc sgtPeppers;

    @Autowired
    private CDPlayer cdPlayer;

    @Test
    public void cdShouldNotBeNull(){
        assertNotNull(sgtPeppers);
    }

    @Test
    public void play(){
        cdPlayer.play();
        assertEquals(
                "Playing Sgt. Pepper's Lonely Hearts Club Band " +
                        "by The Beatles\n",
                log.getLog()
        );
    }
}

  the test result shows OK:

2.2 assembling bean s through Java code

  when the automation scheme does not work, the way of display assembly must be adopted. There are two options for display assembly: JavaConfig and XML. JavaConfig is a better solution because it is more powerful, type safe and refactoring friendly.
   continue with the above example code, first create the configuration class:

package com.springinaction;
import org.springframework.context.annotation.*;

@Configuration
public class CDPlayerConfig {
}

The     @ Configuration annotation indicates that this class is a Configuration class, which should contain the details of how to create bean s in the Spring application context.
  we remove the previous @ ComponentScan and use the display configuration:

@Bean
public CompactDisc sgtPeppers(){
    return new SgtPeppers();
}

    @ bean annotation will tell Spring that this method will return an object, which should be registered as a bean in the Spring application context. The method body contains the logic that ultimately generates the bean instance.
   by default, the ID of the Bean is the same as the method name with @ Bean annotation. In this case, the name of the Bean will be sgtPeppers. You can also rename:

@Bean(name="lonelyHeartsClubBand")
public CompactDisc sgtPeppers(){
    return new SgtPeppers();
}

  insert CD into CDPlayer:

@Bean
public CDPlayer cdPlayer(CompactDisc compactDisc){
    return new CDPlayer(compactDisc);
}

  there are two points to note:

  1. The constructor cannot create an object with new (the class of this object has been declared in Spring), because Spring will intercept all calls to the declared object and ensure that the bean created by this method is returned directly. This is because the beans created by Spring are singleton.
  2. Compactdisc will look for bean s that have implemented compactdisc in Spring.

2.3 assembling bean s through XML

  let's start with the simplest spring XML configuration:

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

  create XML configuration files with Spring Tool Suite. A simple way to create and manage Spring XML configuration files is to simply declare a bean using Spring Tool Suite:

<bean id="compactDisc" class="com.springinaction.SgtPeppers" />

  look at some features:

  1. You no longer need to be directly responsible for creating an instance of SgtPeppers, but in the JavaConfig based configuration. When Spring finds this element, it will call the default constructor of SgtPeppers to create the bean.
  2. In this bean, we set the bean type in the class attribute in the form of string. There is no type check.

   when XML declares DI, there are many optional configuration schemes and styles. For constructor injection, there are two basic configuration schemes to choose from:

  • 1. < constructor Arg > element
  • 2. Use spring 3 c-namespace introduced by 0

  constructor injects bean reference:

<bean id="cdPlay" class="com.springinaction.CDPlayer" >
	<constructor-arg ref="compactDisc"/>
</bean>

  when Spring encounters this < bean > element, it will create a CDPlayer instance< The constructor Arg > element tells Spring to pass a bean reference with ID compactDisc to the constructor of CDPlayer. As an alternative, you can also use Spring's c-namespace. The following is the top declaration of XML and its schema:

<?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:c="http://www.springframework.org/schema/c"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
      ....
</beans>

   after c-namespace and schema declaration, we can use it to declare constructor parameters, as follows:

<bean id="cdPlay" class="com.springinaction.CDPlayer" c:cd-ref="compactDisc" />

The properties of     c are described as follows:

2.4 import and hybrid configuration

  you want to reference XML configuration or import other configuration classes in JavaConfig, as shown below:

package com.springinaction;
import org.springframework.context.annotation.*;

@Configuration
@Import(CDConfig.class)     // Import a single configuration class
@Import({CDConfig.class, CDPlayingConfig.class})   // Import multiple configuration classes
@ImportResource("classpath:soundsystem.xml") // Import xml file
public class CDPlayerConfig {
}

   if you want to reference JavaConfig in XML configuration, as shown below, sgtPeppers is configured in JavaConfig, and CDPlayer continues to be configured 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: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 class="com.springinaction.CDConfig" />
        <bean id="cdPlay" class="com.springinaction.CDPlayer" c:cd-ref="sgtPeppers" />
</beans>

   if a part of the XML configuration is written into an XML file independently, the following references can be made:

<?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: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 class="com.springinaction.CDConfig" />
    <import resource="cdplayer-config.xml"/>
</beans>

2.5 assembly summary

  there are three main ways to assemble bean s in Spring: automatic configuration, explicit configuration based on Java and explicit configuration based on XML. I also suggest using automated configuration as much as possible to avoid the maintenance cost of explicit configuration. However, if you really need to explicitly configure Spring, you should give priority to Java based configuration, which is more powerful, type safe and easy to refactor than XML based configuration.

3, Advanced assembly

3.1 environment and profile

  in the development environment, we may use embedded database and pre load test data. There are three connection configurations for data sources:

// An embedded Hypersonic database will be built through embedded database builder
@Bean(destroyMethod = "shutdown")
@Profile("dev")
public DataSource embeddedDataSource() {
    return new EmbeddedDatabaseBuilder()
    .setType(EmbeddedDatabaseType.H2)
    .addScript("classpath:schema.sql")
    .addScript("classpath:test-data.sql")
    .build();
}

// Obtaining a DataSource through JNDI allows the container to decide how to create the DataSource
@Bean
@Profile("prod")
public DataSource jndiDataSource() {
    JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
    jndiObjectFactoryBean.setJndiName("jdbc/myDS");
    jndiObjectFactoryBean.setResourceRef(true);
    jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
    return (DataSource) jndiObjectFactoryBean.getObject();
}

// It can also be configured as a common DBCP connection pool, and the BasicDataSource can be replaced by Alibaba's DruidDataSource connection pool
@Bean(destroyMethod = "close")
@Profile("qa")
public DataSource datasource(){
    BasicDataSource datasource = new BasicDataSource();
    datasource.setUrl("jdbc:h2:tcp://dbserver/~/test");
    datasource.setDriverClassName("org.h2.Driver");
    datasource.setUsername("sa");
    datasource.setPassword("password");
    datasource.setInitialSize(20);
    datasource.setMaxActive(30);

    return dataSource;
}

  the solution provided by spring for environment related beans is not to make a decision at the time of construction, but to wait for the runtime to determine. Spring introduces the function of bean Profile, adds @ Profile to each bean configured for database connection, and specifies which Profile this bean belongs to.
Spring3.1 you need to specify @ Profile on the configuration class, spring 3 2 can be specified in the method.
   we can also specify in XML through the profile attribute of the < bean > element. For example:

<?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:jdbc="http://www.springframework.org/schema/jdbc"
  xmlns:jee="http://www.springframework.org/schema/jee" xmlns:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="
    http://www.springframework.org/schema/jee
    http://www.springframework.org/schema/jee/spring-jee.xsd
    http://www.springframework.org/schema/jdbc
    http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd">

  <beans profile="dev">
    <jdbc:embedded-database id="dataSource" type="H2">
      <jdbc:script location="classpath:schema.sql" />
      <jdbc:script location="classpath:test-data.sql" />
    </jdbc:embedded-database>
  </beans>
  
  <beans profile="prod">
    <jee:jndi-lookup id="dataSource"
      lazy-init="true"
      jndi-name="jdbc/myDatabase"
      resource-ref="true"
      proxy-interface="javax.sql.DataSource" />
  </beans>
</beans>

  the next step is to activate a profile.
  when determining which profile is active, spring needs to rely on two independent properties: spring profiles. And. Spring active profiles. default. If spring is set profiles. If the active attribute is set, its value will be used to determine which profile is active, but if spring. Is not set profiles. If the active attribute is used, spring will find spring profiles. The value of default. If spring profiles. And. Spring active profiles. If default is not set, there will be no active profile, so only those bean s that are not defined in the profile will be created.
  there are many ways to set these two properties:

  1. As the initialization parameter of DispatcherServlet;
  2. As the context parameter of Web application;
  3. As a JNDI entry;
  4. As an environmental variable;
  5. As the system attribute of the JVM;
  6. On the integration test class, use the @ ActiveProfiles annotation to set.

  for example, in a web application, set spring profiles. Default web The XML file will look like this:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring/root-context.xml</param-value>
  </context-param>
  <!--Set default for context profile-->
  <context-param>
    <param-name>spring.profiles.default</param-name>
    <param-value>dev</param-value>
  </context-param>

  <listener>
    <listener-class>
      org.springframework.web.context.ContextLoaderListener
    </listener-class>
  </listener>
  
  <servlet>
    <servlet-name>appServlet</servlet-name>
    <servlet-class>
      org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <init-param>
      <!--by Servlet Set default profile-->
      <param-name>spring.profiles.default</param-name>
      <param-value>dev</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>appServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

3.2 scope of bean

  by default, all bean s in the Spring application context are created as singletons.
   Spring defines a variety of scopes, and bean s can be created based on these scopes, including:

  1. Singleton: in the whole application, only one instance of the bean is created.
  2. Prototype: a new bean instance will be created every time it is injected or obtained through the Spring application context.
  3. Session: in a Web application, create a bean instance for each session.
  4. Request: in a Web application, create a bean instance for each request.

   for example, if you use component scanning, you can use the @ Scope annotation on the bean class to declare it as a prototype bean:

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Notepad { ... }

   or declare on JavaConfig:

@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Notepad notepad { 
    return new Notepad();
}

  or declare on XML:

<bean id="notepad" class="com.myapp.Notepad" scope="prototype" />

  in web applications, it will be valuable if you can instantiate shared bean s within the scope of sessions and requests. For example, in the shopping cart of e-commerce, the conversation scope is most suitable for:

@Component
@Scope(value=WebApplicationContext.SCOPE_SESSION,
               proxyMode=ScopedProxyMode.TARGET_CLASS)
public class ShoppingCart  { ... }

  inject a service class:

@Component
public class StoreService {
    @Autowired
    public void setShoppingCart (ShoppingCart shoppingCart) {
        this.shoppingCart = shoppingCart;
    }
}

  because StoreService is a singleton bean, it will be created when the Spring application context is loaded. When it is created, Spring will try to inject the ShoppingCart bean into the setShoppingCart() method. However, the ShoppingCart bean is session scoped and does not exist at this time. The ShoppingCart instance will not appear until a user enters the system and creates a session.
  in addition, there will be multiple ShoppingCart instances in the system: one for each user. We don't want Spring to inject a fixed ShoppingCart instance into StoreService. What we hope is that when StoreService handles the shopping cart function, the instance of ShoppingCart it uses is exactly the one corresponding to the current session.
   spring will not inject the actual ShoppingCart bean into the StoreService. Spring will inject a proxy to the ShoppingCart bean, as shown in the following figure. This agent will expose the same method as ShoppingCart, so StoreService will think it is a shopping cart. However, when StoreService calls the method of ShoppingCart, the agent will lazy parse it and delegate the call to the real ShoppingCart bean in the session scope.
  if ShoppingCart is an interface rather than a class, use scopedproxymode TARGET_ Interfaces (proxy using JDK). If it is a class instead of an interface, you must use CGLib to generate a class based proxy, so you need to use scopedproxymode TARGET_ CLASS.
  the scope principle of the request is the same as that of the session scope:

   the scope agent can delay the injection of request and session scope bean s, and can also be configured with XML:

<bean id="cart" class="com.myapp.ShoppingCart" scope="session" >
  <aop:scoped-proxy />
</bean>

  < AOP: scoped proxy / > is a Spring XML configuration element with the same function as the proxy attribute of the @ Scope annotation. It tells Spring to create a Scope proxy for the bean. By default, it uses CGLib to create a proxy for the target class. We can also set the proxy targe class attribute to false, and then require the generation of an interface based proxy:

<bean id="cart" 
           class="com.myapp.ShoppingCart" 
           scope="session" >
  <aop:scoped-proxy proxy-targe-class="false"/>
</bean>

3.3 plant injection during operation

  we previously configured BlankDisc in javaConfig configuration:

@Bean
public CompactDisc sgtPeppers() {
    return new BlankDisc (
             "Sgt. Pepper's Lonely Hearts Club Band",
             "The Beatles"
    );
}

   this hard coding implements the requirements, but sometimes we want to avoid, but we want these values to be determined at run time. To achieve these functions, Spring provides two ways to evaluate at run time:

  1. Property placeholder.
  2. Spring expression language (spiel).

  in Spring, the simplest way is to declare the attribute source and retrieve the attribute through the Spring Environment.

package com.springinaction;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;

@Configuration
@ComponentScan("com.springinaction")
@PropertySource("app.properties")
public class AppConfig {
    
    @Autowired
    Environment environment;
    
    @Bean
    public BlankDisc disc(){
        return new BlankDisc(
            environment.getProperty("disc.title"),
            environment.getProperty("disc.artist"));
    }
}

   in this example, @ PropertySource refers to a class path named app Properties, as follows:

disc.title=Sgt. Peppers Lonely Hearts Club Band
disc.artist=The Beatles

   getProperty in Environment has four overloaded methods:

String getProperty(String key);
String getProperty(String key, String defaultValue);
T getProperty(String key, Class<T> type);
T getProperty(String key, Class<T> type, T defaultValue);

  the difference between the second method and the first method is that there is a default value. The third and fourth methods do not treat all values as strings and can be converted to other types, such as:

int connectionCount = env.getProperty("db.connection.count", Integer.class, 30);

  other methods include:

// If there is no key, an IllegalStateException is thrown
String getRequiredProperty(String key); 
// Check whether the value of key exists
boolean containsProperty(String key)
// Resolve property to class
Class<T>  getPropertyAsClass(String key, Class<T> type);
// Returns an array of active profile names
String[] getActiveProfiles();
// Returns an array of default profile names
String[] getDefaultProfiles()
// Returns true if the environment supports the given profile
boolean acceptsProfiles(String... profiles)

   we can also inject with attribute placeholders in the form of attribute names wrapped with "${...}".

<bean id="sgtPeppers" class="soundsystem.BlankDisc"
           c:_title="${disc.title}"
           c:_artist="${disc.artist}" />

  if we rely on component scanning and automatic assembly to create initialization:

public BlankDisc (
          @Value("disc.title") String title,
          @Value("disc.artist") String artist) {
    this.title = title;
    this.artist = artist;
}

  in order to use placeholders, we must configure a PropertyPlaceholderConfigurer bean or propertysourcespocecholderconfigurer bean. Recommend the latter. If you declare in the javaConfig configuration file:

@Bean
public static PropertySourcesPlaceholderConfigurer placeholderConfigurer(){
    return new PropertySourcesPlaceholderConfigurer();
}

  if declared in the XML configuration file:

<context: property-placeholder />

   the SpEl expression evaluates at run time. SpEl has many features, including:

  1. Use the ID of the bean to reference the bean;
  2. Calling methods and accessing properties of objects;
  3. Arithmetic, relational and logical operations on values;
  4. Regular expression matching;
  5. Collection operation.

  common usage:

1. SpEL Expression to be placed in“# { ... }", For example: #{1}
2. '# {T(System).currentTimeMillis()} ', its final result is the number of milliseconds of the current time at the moment when the expression is calculated. The T () expression will put Java Lang. system is regarded as the corresponding type in Java, so you can call its static modified currentTimeMillis() method.
3. SpEL Expressions can reference other expressions bean Or other bean Properties of.
   For example, reference sgtPeppers of bean
   '# { sgtPeppers }'
   For example, the following expression can be calculated ID by sgtPeppers of bean of artist Properties:
   '# { sgtPeppers.artist }'
4. You can also pass systemProperties Object reference system properties:
   '# { systemProperties['disc.title'] }'
5. Indicates literal value:
   '# { 3.1415926 } '
   '# { 9.87E4 } '
   '# { 'Hello' } '
   '# { false }'
6. Reference other bean Method of
   '# { artistSelector.selectArtist () }'
   To prevent the method value from being null,Throw an exception, you can use“?."
   '# { artistSelector.selectArtist ()?.toUpperCase() }'
   no null,Normal return; If it is null,Return directly without executing the following methods null
7. If you want to SpEL To access the methods and constants of the class scope, you need to rely on T() This key operator.
   '# { T(java.lang.Math).PI }'
   '# { T(java.lang.Math).random() }'
8. You can also use operators on expressions, such as:
   '# { 2 * T(java.lang.Math).PI * circle.radius }'
   '# { disc.title + ' by ' + disc.artist }'
9. Compare the writing of equal numbers
   '# { counter.total == 100 }'
   '# { counter.total eq 100 }'
10. Ternary operator 
    '# { scoreboard.score > 1000 ? "Winner!" : "Loser" }'
    '# {disc.title?: 'rate and hum'} '/ / if disc If the value of title is null, 'rate and hum' will be returned
11. regular expression 
    '# { admin.email matches '[a-zA-Z0-9.*%+-]+@[a-zA-Z0-9.*]+\.com' }'
12. Expressions related to collections and arrays are supported
    '# { jukebox.songs[4].title }'
    '# { jukebox.songs[T(java.lang.Math).random() * jukebox.songs.size()].title }'
    '# {'This is a test' [3]} '/ / reference the fourth character - "s"
13. Support query operators
    For example, you want to get jukebox in artist Attribute is Aerosmith All songs:
    '# { jukebox.songs.?[artist eq 'Aerosmith'] }'
    Find the first in the list artist Attribute is Aerosmith Songs:
    '# { jukebox.songs.^[artist eq 'Aerosmith'] }'
    Find the last in the list artist Attribute is Aerosmith Songs:
    '# { jukebox.songs.$[artist eq 'Aerosmith'] }'
14. Support projection operator
    Suppose we don't want a collection of song objects, but a collection of all song names. The following expression will title Property is projected to a new String In a collection of types:
    '# { jukebox.songs.![title]}'
    obtain Aerosmith All songs title
    '# { jukebox.songs.?[artist eq 'Aerosmith'].![title] }'

4, Aspect oriented Spring

  in software development, functions scattered in applications are called cross cutting concerns. Generally speaking, these crosscutting concerns are conceptually separated from the application business logic (but often directly embedded into the application business logic). Separating these crosscutting concerns from business is the problem to be solved by aspect oriented programming (AOP).
   application scenarios include logging, security and transaction management.

4.1 what is aspect oriented programming

  when using aspect oriented programming, we still define common functions in one place, but we can define how and where this function is to be applied by declaration without modifying the affected classes. Crosscutting concerns can be modularized into special classes called aspects. Common terms used to describe aspects include advice, pointcut, and join point.

  • 1. Advice
       in AOP terminology, the work of the section is called notification.
       Spring aspect can apply five types of notifications:
        1> pre notification: call the notification function Before the target method is called;
        2> post notification (After): the notification is called After the target method is completed, and the output of the method is not concerned at this time;
        3> after returning: call the notification after the target method is successfully executed;
        4> after throwing: call the notification after the target method throws an exception;
        5 > surround notification: the notification wraps the notified method and executes custom behavior before and after the notified method is called.
  • 2. Join point
      apps may have thousands of opportunities to apply notifications. These opportunities are called connection points. The connection point is a point that can be inserted into the section during the execution of the application. This point can be when calling a method, throwing an exception, or even modifying a field. Slice code can use these points to insert into the normal process of the application and add new behavior.
  • 3. Pointcut
       the definition of tangent point will match one or more connection points to be woven into the notification. We usually specify these pointcuts by using explicit class and method names, or by using regular expression definitions to match class and method names.
  • 4. Aspect
      aspect is the combination of notification and cut-off point. Notification and pointcut together define the entire content of the facet.
  • 5. Introduction
      the introduction allows us to add new methods or properties to existing classes.
  • 6. Weaving
      weaving is the process of applying facets to the target object and creating a new proxy object. The cut plane is woven into the target object at the specified connection point. How many points can be woven in the life cycle of the target object:
        1 > compile time: the aspect is woven in when the target class is compiled. AspectJ's weaving compiler weaves facets in this way.
        2 > class loading period: the facet is woven when the target class is loaded into the JVM. A special class loader is required, which can enhance the bytecode of the target class before it is introduced into the application. AspectJ5 supports weaving facets in this way when loading.
        3 > operation period: the section is woven at a certain time of application operation. Generally, when weaving in the cut plane, the AOP container will dynamically create a proxy object for the target object. Spring AOP is woven into the section in this way.

  Spring provides four types of AOP support:

  1. Classic spring AOP based on agent;
  2. Pure POJO section;
  3. @AspectJ annotation driven facets;
  4. Injected AspectJ section.

  Spring notifies objects at runtime
  by wrapping the aspect in the proxy class, Spring weaves the aspect into Spring managed beans at run time. The proxy encapsulates the target class, intercepts the call of the notified method, and then forwards the call to the real target bean. When the proxy intercepts a method call, the aspect logic is executed before calling the target bean method.
   Spring does not create proxy objects until the application needs the proxy bean. If ApplicationContext is used, Spring will only create the proxied object when ApplicationContext loads all beans from BeanFactory. Because the proxy object is created only by Spring runtime, we don't need a special compiler to weave into the aspect of Spring AOP.
  Spring only supports method level join points
  because spring is based on dynamic proxy, spring only supports method join points. Spring lacks support for field join points, and it does not support constructor join points. Method, we can use Aspect to supplement it.

4.2 select the connection point through the tangent point

  in Spring AOP, you need to use AspectJ's pointcut expression language to define pointcuts.
   because Sring is agent-based, and some tangent expressions are independent of agent-based AOP. The following table lists the AspectJ pointcut indicators supported by Spring AOP.

  when trying to use other AspectJ indicators in Spring, an illegalargument exception will be thrown.
  when we look at the indicators supported by Spring as shown above, only the execution indicator is the actual execution match, while the other indicators are used to limit the match.

4.2.1 write cut points

  to illustrate the aspects in Spring, we define a Performance interface:

package concert;

public interface Performance {
     public void perform() ;
}

   suppose we want to write the notification triggered by the perform() method of Performance, the following shows the writing method of the tangent expression:

  use the AspectJ pointcut expression to select the perform() method of Performance. Now let's assume that the pointcut we need to configure only matches the concert package. We can use the within() indicator to restrict matching, as shown in the following figure:

4.2.2 select bean in pointcut

  in addition to the indicators listed in the table, Spring also introduces a new bean() indicator, which allows us to use the bean ID to identify the bean in the pointcut expression. bean() uses beanID or bean name as a parameter to restrict the pointcut to match only a specific bean.
  for example, consider the following tangent points:

execution(* concert.Performance.perform()) 
            and bean('woodstock')

   you can also use non operations to apply notifications for bean s other than specific ID S:

execution(* concert.Performance.perform()) 
            and !bean('woodstock')

4.3 creating facets with annotations

  we have defined the Performance interface, which is the target object of the tangent point in the aspect. Now, let's use the AspectJ annotation to define the aspect. AspectJ provides five annotations to define notifications, as shown in the following table:

4.3.1 define section

  it is wise for us to define the Audience as one aspect and apply it to the performance. The following is the code of Audience class:

package com.springinaction.perf;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

//Cut POJO
@Aspect
public class Audience {

    //Define named tangent points
    @Pointcut("execution(** com.springinaction.perf.Performance.perform(..))")
    public void performance(){
    }

    //Define notification
    @Before("performance()")   // Before the performance
    public void silenceCellPhones(){
        System.out.println("Silencing cell phones");
    }

    @Before("performance()") // Before the performance
    public void takeSeats(){
        System.out.println("Taking seats");
    }

    @AfterReturning("performance()")  // After the performance
    public void applause(){
        System.out.println("CLAP CLAP CLAP");
    }

    @AfterThrowing("performance()")   // After the performance failed
    public void demandRefund(){
        System.out.println("Demanding a refund");
    }

    @Around("performance()")  // Surround notification method
    public void watchPerformance(ProceedingJoinPoint jp){
        try {
            System.out.println("Silencing cell phones Again");
            System.out.println("Taking seats Again");
            jp.proceed();
            System.out.println("CLAP CLAP CLAP Again");
        }
        catch (Throwable e){
            System.out.println("Demanding a refund Again");
        }
    }
}

  with regard to the surround notification, we first notice that it accepts the ProceedingJoinPoint as a parameter. This object is necessary because it is used to call the notified method in the notification.
  it should be noted that in general, don't forget to call the proceed() method. If not called, the notification will actually block the call to the notified method, which may be the desired effect. Of course, it can also be called multiple times. For example, to implement a scenario is to implement the retry logic.
  in addition to the annotation and the performance() method without actual operation, the Audience class is still a POJO and can be assembled as a bean in Spring:

@Bean
public Audience audience(){   // Statement Audience
    return new Audience();
}

   in addition to defining the section, you also need to start the automatic agent to make these annotations parse. If JavaConfig is used, the following configuration is required:

package com.springinaction.perf;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan
@EnableAspectJAutoProxy   //Start AspectJ automatic agent
public class AppConfig {

    @Bean
    public Audience audience(){   // Statement Audience
        return new Audience();
    }
}

  if you want to use XML to assemble bean s in Spring, you need to use the < AOP: AspectJ AutoProxy > element in the Spring aop namespace.

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       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">

    <context:component-scan base-package="com.springinaction.perf" />
    <aop:aspectj-autoproxy />
    <bean id="audience" class="com.springinaction.perf.Audience" />
</beans>

   whether JavaConfig or XML is used, the Aspect automatic proxy will create a proxy using the bean annotated by @ Aspect, which will surround all the beans matched by the tangent point of the Aspect. In this case, a proxy will be created for the Concert bean, and the notification method in the Audience class will be executed before and after the perform() call.
  what we need to remember is that Spring's AspectJ automatic proxy only uses @ AspectJ as a guide for creating aspects, which are still agent-based. In essence, it is still a proxy based aspect of Spring.

4.3.2 parameters in processing notification

   so far, there are no parameters for other notifications except surround notifications. What if the method notified by the aspect does have parameters? Can the facet access and use the parameters passed to the notified method?
  to illustrate this problem, let's revisit the BlankDisc sample. Suppose you want to record the number of times each track is played. To record the number of times, we created the TrackCounter class, which is an aspect of notifying the playTrack() method.

package com.springinaction.disc;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

import java.util.HashMap;
import java.util.Map;

@Aspect
public class TrackCounter {

    private Map<Integer, Integer> trackCounts = new HashMap<>();

    @Pointcut("execution(* com.springinaction.disc.CompactDisc.playTrack(int))" +
     "&& args(trackNumber)")  // Notify playTrack() method
    public void trackPlayed(int trackNumber){}

    @Before("trackPlayed(trackNumber)")  // Count the track before playing
    public void countTrack(int trackNumber){
        int currentCount = getPlayCount(trackNumber);
        trackCounts.put(trackNumber, currentCount + 1);
    }

    public int getPlayCount(int trackNumber){
        return trackCounts.containsKey(trackNumber) ? trackCounts.get(trackNumber) : 0;
    }
}

   tangent point expression decomposition:

   declare a parameter in the pointcut expression, which is passed into the notification method. The args(trackNumber) qualifier indicates that the int type parameter passed to the playTrack() method will also be passed to the notification. trackNumber also matches the parameters in the tangent method signature. The parameter name in the tangent point definition is the same as that in the tangent point method.
  let's start AspectJ automatic proxy and define bean:

package com.springinaction.disc;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

import java.util.ArrayList;
import java.util.List;

@Configuration
@EnableAspectJAutoProxy
public class TrackCounterConfig {

    @Bean
    public CompactDisc sgtPeppers(){
        BlankDisc cd = new BlankDisc();
        cd.setTitle("Sgt. Pepper's Lonely Hearts Club Band");
        cd.setArtist("The Beatles");
        List<String> tracks = new ArrayList<>();
        tracks.add("Sgt. Pepper's Lonely Hearts Club Band");
        tracks.add("With a Little Help from My Friends");
        tracks.add("Luck in the Sky with Diamonds");
        tracks.add("Getting Better");
        tracks.add("Fixing a Hole");
        tracks.add("Feel My Heart");
        tracks.add("L O V E");
        cd.setTracks(tracks);
        return cd;
    }

    @Bean
    public TrackCounter trackCounter(){
        return new TrackCounter();
    }
}

  test:

package com.springinaction;

import static org.junit.Assert.*;
import com.springinaction.disc.CompactDisc;
import com.springinaction.disc.TrackCounter;
import com.springinaction.disc.TrackCounterConfig;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.contrib.java.lang.system.StandardOutputStreamLog;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TrackCounterConfig.class)
public class TrackCounterTest {

    @Rule
    public final StandardOutputStreamLog log = new StandardOutputStreamLog();

    @Autowired
    private CompactDisc cd;

    @Autowired
    private TrackCounter counter;

    @Test
    public void testTrackCounter(){
        cd.playTrack(1);
        cd.playTrack(2);
        cd.playTrack(3);
        cd.playTrack(3);
        cd.playTrack(3);
        cd.playTrack(3);
        cd.playTrack(7);
        cd.playTrack(7);

        assertEquals(1,counter.getPlayCount(1));
        assertEquals(1,counter.getPlayCount(2));
        assertEquals(4,counter.getPlayCount(3));
        assertEquals(0,counter.getPlayCount(4));
        assertEquals(0,counter.getPlayCount(5));
        assertEquals(0,counter.getPlayCount(6));
        assertEquals(2,counter.getPlayCount(7));
    }
}

4.3.3 introduction of new functions through annotations

  in addition to adding new functions to existing methods, we can also add some additional functions.
  recall that in Spring, aspects only implement the same interface proxy as the beans they wrap. In addition to implementing these interfaces, the agent can also expose new interfaces. Even if the underlying implementation class does not implement these interfaces, the beans notified by the aspect can implement new interfaces. The figure below shows how they work.

  using Spring AOP, we can introduce new methods for bean s. The proxy intercepts the call and delegates it to other objects that implement the method.
  it should be noted that when the method introducing the interface is called, the agent will delegate the call to some other object that implements the new interface. In fact, the implementation of a bean is split into multiple classes.
  in order to verify that it works, we introduce encorable interface for all Performance implementations:

package com.springinaction.perf;

public interface Encoreable {
    void performEncore();
}

  with the help of AOP, we create a new section:

package com.springinaction.perf;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;

@Aspect
public class EncoreableIntroducer {   // Additional method implementations need to be added to Performance and its implementation classes
    @DeclareParents(value = "com.springinaction.perf.Performance+",
                    defaultImpl = DefaultEncoreable.class)
    public static Encoreable encoreable;
}

  the @ DeclareParents annotation introduces the encorable interface into the Performance bean.
@The DeclareParents annotation consists of three parts:

  1. The value attribute specifies which type of bean is to be introduced into the interface. (in this case, it is Performance, and the plus sign indicates all subtypes of Performance)
  2. The defaultImpl property specifies the class that provides the implementation for the incoming function.
  3. @The static attribute marked by the DeclareParents annotation indicates the interface to be introduced.

    then you need to declare the bean of encorableintroducer in the configuration:

@Bean
public EncoreableIntroducer encoreableIntroducer(){
    return new EncoreableIntroducer();
}

   when calling the bean entrusted to the proxy or the introduced implementation, it depends on the method attribute of the call, the proxy bean or the interface in which the attribute is introduced.
  in Spring, annotations and automatic proxies provide a convenient way to create facets. But there is a disadvantage: you must be able to annotate the notification class and have the source code.
  if there is no source code or you don't want to annotate your code, you can choose to declare the aspect in the Spring XML configuration file.

4.4 declaring facets in XML

   if you declare aspects but cannot add annotations to the notification class, you need to turn to XML configuration. In the aop namespace of Spring, multiple elements are provided to declare aspects in XML, as shown in the following table:

  take a look at the Audience class again. This time, we remove all its AspectJ annotations:

package com.springinaction.perf;

public class Audience {

    public void silenceCellPhones(){
        System.out.println("Silencing cell phones");
    }

    public void takeSeats(){
        System.out.println("Taking seats");
    }

    public void applause(){
        System.out.println("CLAP CLAP CLAP");
    }

    public void demandRefund(){
        System.out.println("Demanding a refund");
    }

    public void watchPerformance(ProceedingJoinPoint jp){
        try {
            System.out.println("Silencing cell phones Again");
            System.out.println("Taking seats Again");
            jp.proceed();
            System.out.println("CLAP CLAP CLAP Again");
        }
        catch (Throwable e){
            System.out.println("Demanding a refund Again");
        }
    }
}

4.4.1 declaration of pre -, post -, and surround notices

   the required XML is shown below:

<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="audience" class="com.springinaction.perf.Audience" />
    <bean id="performance" class="com.springinaction.perf.Concert"/>

    <aop:config>
        <aop:aspect ref="audience">
            <aop:pointcut id="perf" expression="execution(* com.springinaction.perf.Performance.perform(..))" />
            <aop:before pointcut-ref="perf" method="silenceCellPhones" />
            <aop:before pointcut-ref="perf" method="takeSeats" />
            <aop:after-returning pointcut-ref="perf" method="applause" />
            <aop:after-throwing pointcut-ref="perf" method="demandRefund"/>
            <aop:around pointcut-ref="perf" method="watchPerformance"/>
        </aop:aspect>
    </aop:config>

</beans>

4.4.2 parameters for notification delivery

  we use XML to configure BlankDisc. First, remove all @ AspectJ annotations on TrackCounter.

package com.springinaction.disc;
import java.util.HashMap;
import java.util.Map;

public class TrackCounter {

    private Map<Integer, Integer> trackCounts = new HashMap<>();

    // Count the track before playing
    public void countTrack(int trackNumber){
        int currentCount = getPlayCount(trackNumber);
        trackCounts.put(trackNumber, currentCount + 1);
    }

    public int getPlayCount(int trackNumber){
        return trackCounts.containsKey(trackNumber) ? trackCounts.get(trackNumber) : 0;
    }
}

   the following shows how to configure TrackCounter as a parameterized aspect 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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="trackCounter" class="com.springinaction.disc.TrackCounter" />

    <bean id="cd" class="com.springinaction.disc.BlankDisc" >
        <property name="title" value="Sgt. Pepper's Lonely Hearts Club Band" />
        <property name="artist" value="The Beatles" />
        <property name="tracks">
            <list>
                <value>Sgt. Pepper's Lonely Hearts Club Band</value>
                <value>With a Little Help from My Friends</value>
                <value>Lucy in the Sky with Diamonds</value>
                <value>Getting Better</value>
                <value>Fixing a Hole</value>
                <value>Feel My Heart</value>
                <value>L O V E</value>
            </list>
        </property>
    </bean>

    <aop:config>
        <aop:aspect ref="trackCounter">
            <aop:pointcut id="trackPlayed" expression="
            execution(* com.springinaction.disc.CompactDisc.playTrack(int))
            and args(trackNumber)" />

            <aop:before pointcut-ref="trackPlayed" method="countTrack"/>
        </aop:aspect>
    </aop:config>

</beans>

   note: in XML, the "&" symbol will be parsed as the beginning of the entity, and the "and" keyword will be used.

4.4.3 introduce new functions through section

  using the < AOP: declare parents > element in the Spring aop namespace, we can achieve the same function.

<aop:aspect>
      <aop:declare-parents types-matching="com.springinaction.perf.Performance+"
                                 implement-interface="com.springinaction.perf.Encoreable"
                                 default-impl="com.springinaction.perf.DefaultEncoreable" />
</aop:aspect>

  we can also use the delegate ref attribute to identify:

<aop:aspect>
    <aop:declare-parents types-matching="com.springinaction.perf.Performance+"
                         implement-interface="com.springinaction.perf.Encoreable"
                         delegate-ref="defaultEncoreable" />
</aop:aspect>

  the delegate ref attribute refers to a Spring bean as the incoming delegate.

<bean id="defaultEncoreable" class="com.springinaction.perf.DefaultEncoreable" />

4.5 injection AspectJ section

  AspectJ provides many types of pointcuts that Spring AOP cannot support. For example, the constructor tangent point is very convenient.
  in order to demonstrate, we create a new aspect. We create a role of commentator in an aspect way, and make some criticism after the performance.
  first create such a section:

package com.springinaction.perf;

public aspect CriticAspect {
    public  CriticAspect(){}

    pointcut performance() : execution(* perform(..));

    after() returning : performance() {
       System.out.println(criticismEngine.getCriticism());
    }

    private CriticismEngine criticismEngine;

    public CriticismEngine getCriticismEngine() {
        return criticismEngine;
    }

    public void setCriticismEngine(CriticismEngine criticismEngine) {
        this.criticismEngine = criticismEngine;
    }
}

  then the interface of CriticismEngine:

package com.springinaction.perf;

public interface CriticismEngine {
    String getCriticism();
}

  implementation class of CriticismEngine:

package com.springinaction.perf;

public class CriticismEngineImpl implements CriticismEngine {

    public CriticismEngineImpl(){}

    @Override
    public String getCriticism() {
        int i = (int) (Math.random() * criticismPool.length);
        return criticismPool[i];
    }

    private String[] criticismPool;
    public void setCriticismPool(String[] criticismPool){
        this.criticismPool = criticismPool;
    }
}

  inject list into CriticismEngineImpl:

<bean id="criticismEngine" class="com.springinaction.perf.CriticismEngineImpl">
    <property name="criticismPool">
        <list>
            <value>Worst performance ever!</value>
            <value>I laughed, I cried, then I realized I was at the wrong show.</value>
            <value>A must see show!</value>
        </list>
    </property>
</bean>

  before showing how to implement injection, we must understand that AspectJ aspects can be woven into our applications without Spring at all. If you want to use Spring dependency injection, you need to declare the aspect as a < bean > in the Spring configuration.

<bean class="com.springinaction.perf.CriticAspect" factory-method="aspectOf">
    <property name="criticismEngine" ref="criticismEngine"/>
</bean>

  usually, Spring bean s are initialized by the Spring container, but the AspectJ aspect is created by AspectJ at run time. When Spring has the opportunity to inject CriticismEngine into CriticAspect, CriticAspect has been instantiated.
  because Spring cannot be responsible for creating criticaspectes, you cannot simply declare criticaspectes as bean s in Spring. Instead, we need a way to get the handle of the CriticAspect instance created by AspectJ for Spring, so that we can inject CriticismEngine. Fortunately, all AspectJ facets provide a static aspectOf() method that returns a singleton of the facet. In order to obtain all instances of the facet, we must use the factory method to call aspectOf() instead of calling the constructor method of CriticAspect.
  in short, spring cannot use the < bean > declaration to create a CriticAspect instance as before - it has been created with AspectJ at runtime. Spring needs to obtain the reference of the aspect through the aspectOf() factory method, and then perform dependency injection on the object as specified by the < bean > element.

4.6 summary

  AOP is a powerful complement to object-oriented programming. With AspectJ, we can now put the behaviors previously scattered throughout the application into reusable modules. We explicitly declare where and how the behavior is applied. This effectively reduces code redundancy and allows our classes to focus on their main functions.
   Spring provides an AOP framework that lets us insert aspects around method execution.
  now we've learned how to weave notifications into calls to pre -, post -, and wrap methods, and how to add custom behavior for handling exceptions. There are many choices about how to use facets in Spring applications. By using @ AspectJ annotations and a simplified configuration namespace, assembling notifications and pointcuts in Spring becomes very simple.
  finally, when Spring AOP cannot meet the requirements, we must turn to the more powerful AspectJ. For these scenarios, we learned how to use Spring to inject dependencies into AspectJ aspects.

Tags: Java Spring AOP

Posted by rochakchauhan on Thu, 05 May 2022 00:21:45 +0300