1. The introduction of the Spring

When we say Spring, we actually mean the Spring Framework, which is just a branch of the Spring family. So what does the Spring family have?

Spring was created to address the complexities of enterprise application development. Before Spring, there was a heavyweight tool called EJB, which used Spring to effectively decouple Java beans from each other in a way that was previously only possible with EJBs, which were bloated and rarely used. Spring is not limited to server-side development, but is very good at both testability and loose coupling.

Generally speaking, there are four main aspects of Spring that beginners can master:

  • Ioc/DI
  • AOP
  • The transaction
  • JdbcTemplate

2. Download the Spring

Normally, we can use Spring directly by adding Maven dependencies to the project. If we need to download the JAR separately, we can download it from the following address:

  • Repo. Spring. IO/libs – releas…

After successful download, the components in Spring provide the following functions:

3.1 the Ioc

3.1.1 Ioc concept

Ioc is an Inversion of Control. This is a concept and an idea. Inversion of control is actually a reversal of control over an object. For example, the following code:

public class Book {
    private Integer id;
    private String name;
    private Double price;
/ / omit getter/setter
}
public class User {
    private Integer id;
    private String name;
    private Integer age;

    public void doSth(a) {
        Book book = new Book();
        book.setId(1);
        book.setName("A new story");
        book.setPrice((double) 20); }}Copy the code

In this case, the control of the Book object resides with the User object, so the Book object is highly coupled to the User object. If the Book object is used in other objects, it must be recreated. In other words, the creation, initialization, and destruction of the object are all done by the developer. By handing these operations over to the container, developers are greatly relieved of the need to create objects.

With Spring, you can hand over the creation, initialization, and destruction of objects to the Spring container. That is, at project startup, all beans register themselves with the Spring container (if necessary), and then if other beans need to use the Bean, they don’t need to go to New themselves, but directly to the Spring container.

Look at this process with a simple example.

3.1.2 Initial Ioc experience

First create a normal Maven project and then introduce the Spring-Context dependency as follows:

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

Next, create a Spring configuration file in the Resources directory (note that the dependency must be added before the configuration file is created, otherwise there will be no template options when creating the configuration file) :

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

</beans>
Copy the code

In this file, we can configure all beans that need to be registered with the Spring container:

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

    <bean class="org.javaboy.Book" id="book"/>
</beans>
Copy the code

The class attribute represents the full path of the bean to be registered. The ID attribute represents the unique tag of the bean. The name attribute can also be used as the tag of the bean.

Next, load the configuration file:

public class Main {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); }}Copy the code

Using the main method, the configuration file is automatically loaded, which initializes a Book instance in Spring. At this point, we explicitly specify the no-argument constructor for the Book class and print a log in the no-argument constructor. We can see that the no-argument constructor executes, thus proving that the object has been initialized in the Spring container.

Finally, we can get objects from the container using the getBean method:

public class Main {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        Book book = (Book) ctx.getBean("book"); System.out.println(book); }}Copy the code

Loading way, besides ClassPathXmlApplicationContext (under the classpath to find the configuration file), also can use FileSystemXmlApplicationContext, FileSystemXmlApplicationContext will go down to find the configuration file from the operating system path.

public class Main {
    public static void main(String[] args) {
        FileSystemXmlApplicationContext ctx = new FileSystemXmlApplicationContext("F:\\workspace5\\workspace\\spring\\spring-ioc\\src\\main\\resources\\applicationContext.xml");
        Book book = (Book) ctx.getBean("book"); System.out.println(book); }}Copy the code

3.2 Bean acquisition

In the previous section, we used the ctx.getBean method to get the Bean from the Spring container, passing in the Bean’s name or ID attribute. In addition to this approach, you can also fetch a Bean directly through the Class.

public class Main {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); Book book = ctx.getBean(Book.class); System.out.println(book); }}Copy the code

One big disadvantage of this approach is that it does not work if there are multiple instances, such as two beans in an XML file:

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

    <bean class="org.javaboy.Book" id="book"/>
    <bean class="org.javaboy.Book" id="book2"/>
</beans>
Copy the code

If you use Class to find beans, you will get the following error:

Therefore, it is generally recommended to use name or ID to obtain the instance of the Bean.

3.3 Attribute injection

There are several ways to inject attributes in XML configuration.

3.3.1 Constructor injection

The Bean’s constructor injects values into the Bean’s properties.

1. Add the corresponding constructor to the Bean:

public class Book {
    private Integer id;
    private String name;
    private Double price;

    public Book(a) {
        System.out.println("-------book init----------");
    }

    public Book(Integer id, String name, Double price) {
        this.id = id;
        this.name = name;
        this.price = price; }}Copy the code

2. Inject beans into the 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 http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean class="org.javaboy.Book" id="book"> <constructor-arg index="0" value="1"/> <constructor-arg index="1" value="1"/> <constructor-arg index="2" value="30"/>  </bean> </beans>Copy the code

Note here that the index in constructor-arg corresponds to the constructor parameters in Book. The write order can be reversed, but the values of index and value must correspond one to one.

In another constructor, attribute injection is done by specifying the parameter name directly:

<bean class="org.javaboy.Book" id="book2"> <constructor-arg name="id" value="2"/> <constructor-arg name="name" Value =" /> <constructor-arg name="price" value="40"/> </bean>Copy the code

If there are multiple constructors, the constructor is automatically matched to the corresponding constructor based on the given parameter number and parameter type, and then an object is initialized.

3.3.2 Set method injection

In addition to the constructor, we can also inject values through the set method.

<bean class="org.javaboy.Book" id="book3">
    <property name="id" value="3"/>
    <property name="name" value="Water Margin"/>
    <property name="price" value="30"/>
</bean>
Copy the code

Set method injection, there is a very important problem, is the property name. Many people have the illusion that the property name is the property name you defined, which is not true. In all frameworks, property names that involve reflected injected values are not defined in the Bean, but are analyzed by introspection in Java, or simply by get/set methods.

3.3.3 P namespace injection

P namespace injection, less used, is also essentially a call to the set method.

<bean class="org.javaboy.Book" id="book4" p:id="4" p:bookName=Journey to the West p:price="33"></bean>
Copy the code

3.3.4 Injection of external beans

In some cases, we use external beans that may not have a constructor but are constructed with the Builder. In this case, we cannot use the above method to inject values into the beans.

For example, in OkHttp’s web request, it would be written as follows:

public class OkHttpMain {
    public static void main(String[] args) {
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .build();
        Request request = new Request.Builder()
                .get()
                .url("http://b.hiphotos.baidu.com/image/h%3D300/sign=ad628627aacc7cd9e52d32d909032104/32fa828ba61ea8d3fcd2e9ce9e0a304e241f580 3.jpg")
                .build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                System.out.println(e.getMessage());
            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                FileOutputStream out = new FileOutputStream(new File("E:\\123.jpg"));
                int len;
                byte[] buf = new byte[1024];
                InputStream is = response.body().byteStream();
                while((len = is.read(buf)) ! = -1) {
                    out.write(buf, 0, len); } out.close(); is.close(); }}); }}Copy the code

The OkHttpClient and Request instances of this Bean are not directly new. They are configured with default parameters during the call to the Builder method. In this case, we can use static factory injection or instance factory injection to provide an instance to OkHttpClient.

Static factory injection

First we provide a static factory for OkHttpClient:

public class OkHttpUtils {
    private static OkHttpClient OkHttpClient;
    public static OkHttpClient getInstance(a) {
        if (OkHttpClient == null) {
            OkHttpClient = new OkHttpClient.Builder().build();
        }
        returnOkHttpClient; }}Copy the code

In the XML file, configure the static factory:

<bean class="org.javaboy.OkHttpUtils" factory-method="getInstance" id="okHttpClient"></bean>
Copy the code

This configuration means that getInstance in the OkHttpUtils class is the instance we need. The instance name is okHttpClient. Then, in Java code, you get the instance and you can use it directly.

public class OkHttpMain {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        OkHttpClient okHttpClient = ctx.getBean("okHttpClient", OkHttpClient.class);
        Request request = new Request.Builder()
                .get()
                .url("http://b.hiphotos.baidu.com/image/h%3D300/sign=ad628627aacc7cd9e52d32d909032104/32fa828ba61ea8d3fcd2e9ce9e0a304e241f580 3.jpg")
                .build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                System.out.println(e.getMessage());
            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                FileOutputStream out = new FileOutputStream(new File("E:\\123.jpg"));
                int len;
                byte[] buf = new byte[1024];
                InputStream is = response.body().byteStream();
                while((len = is.read(buf)) ! = -1) {
                    out.write(buf, 0, len); } out.close(); is.close(); }}); }}Copy the code

2. Example factory injection

An instance factory means that a factory method is an instance method, so that a factory class must be instantiated before it can call a factory method.

The factory category this time is as follows:

public class OkHttpUtils {
    private OkHttpClient OkHttpClient;
    public OkHttpClient getInstance(a) {
        if (OkHttpClient == null) {
            OkHttpClient = new OkHttpClient.Builder().build();
        }
        returnOkHttpClient; }}Copy the code

At this point, in the XML file, you need to provide an instance of the factory method before you can call the factory method:

<bean class="org.javaboy.OkHttpUtils" id="okHttpUtils"/>
<bean class="okhttp3.OkHttpClient" factory-bean="okHttpUtils" factory-method="getInstance" id="okHttpClient"></bean>
Copy the code

Self-written beans generally do not use either method for injection, but if you need to introduce an external JAR and initialize the class of the external JAR, you may need to use both methods.

3.4 Injection of complex attributes

3.4.1 Object Injection

<bean class="org.javaboy.User" id="user">
    <property name="cat" ref="cat"/>
</bean>
<bean class="org.javaboy.Cat" id="cat">
    <property name="name" value="White"/>
    <property name="color" value="White"/>
</bean>
Copy the code

You can inject objects through XML and reference an object through a REF.

3.4.2 Array injection

Array injection and collection injection are configured the same way in XML. As follows:

<bean class="org.javaboy.User" id="user">
    <property name="cat" ref="cat"/>
    <property name="favorites">
        <array>
            <value>football</value>
            <value>basketball</value>
            <value>Table tennis</value>
        </array>
    </property>
</bean>
<bean class="org.javaboy.Cat" id="cat">
    <property name="name" value="White"/>
    <property name="color" value="White"/>
</bean>
Copy the code

Note that the array node can also be replaced by a list node.

Of course, an array or list node can also be an object.

<bean class="org.javaboy.User" id="user"> <property name="cat" ref="cat"/> <property name="favorites"> <list> <value> soccer </value> <value> basketball </value> <value> ping-pong </value> </list> </property> <property name="cats"> <list> <ref Bean ="cat"/> <ref bean="cat2"/> <bean class="org.javaboy. cat" id="cat3"> <property name="name" value=" floret "/> <property Name ="color" value=" color" /> </bean> </list> </property> </bean> <bean class="org.javaboy.Cat" id=" Cat" > <property Name ="name" value=" white "/> <property name="color" value=" white "/> </bean> <bean class="org.javaboy.Cat" id="cat2"> <property /> <property name="color" value=" black "/> </bean>Copy the code

Note that you can either use externally defined beans through a ref or define beans directly in a list or array node.

Rule 3.4.3 Map injection

<property name="map">
    <map>
        <entry key="age" value="99"/>
        <entry key="name" value="javaboy"/>
    </map>
</property>
Copy the code

3.4.4 Properties into

<property name="info">
    <props>
        <prop key="age">99</prop>
        <prop key="name">javaboy</prop>
    </props>
</property>
Copy the code

In the above Demo, the User is defined as follows:

public class User {
    private Integer id;
    private String name;
    private Integer age;
    private Cat cat;
    private String[] favorites;
    private List<Cat> cats;
    private Map<String,Object> map;
    private Properties info;

    @Override
    public String toString(a) {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\' ' +
                ", age=" + age +
                ", cat=" + cat +
                ", favorites=" + Arrays.toString(favorites) +
                ", cats=" + cats +
                ", map=" + map +
                ", info=" + info +
                '} ';
    }

    public Properties getInfo(a) {
        return info;
    }

    public void setInfo(Properties info) {
        this.info = info;
    }

    public Map<String, Object> getMap(a) {
        return map;
    }

    public void setMap(Map<String, Object> map) {
        this.map = map;
    }

    public List<Cat> getCats(a) {
        return cats;
    }

    public void setCats(List<Cat> cats) {
        this.cats = cats;
    }

    public String[] getFavorites() {
        return favorites;
    }

    public void setFavorites(String[] favorites) {
        this.favorites = favorites;
    }

    public User(a) {}public User(Integer id, String name, Integer age, Cat cat) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.cat = cat;
    }

    public Cat getCat(a) {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

    public Integer getId(a) {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName(a) {
        return name;
    }

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

    public Integer getAge(a) {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age; }}Copy the code

3.5 Java configuration

In Spring, there are generally three different ways to register a Bean into the Spring container.

  • XML injection, as mentioned earlier
  • Java configuration (Registering beans with the Spring container through Java code)
  • Automatic scanning

Here we look at Java configuration.

Java configuration was rarely used before Spring Boot, when Java configuration development was widely used because there was no single line of XML configuration in Spring Boot.

For example, I have the following Bean:

public class SayHello {
    public String sayHello(String name) {
        return "hello "+ name; }}Copy the code

In Java configuration, we replace the applicationContext.xml file with a Java configuration class.

@Configuration
public class JavaConfig {
    @Bean
    SayHello sayHello(a) {
        return newSayHello(); }}Copy the code

First, there is an @Configuration annotation on the Configuration class. This annotation indicates that the class is not a normal class, but a Configuration class that acts as applicationContext.xml. Then, you define a method that returns an object and add an @bean annotation to the method to inject the return value of that method into the Spring container. That is, the @bean corresponds to a method that is equivalent to the Bean node in applicationContext.xml.

Since it is a configuration class, we need to load the configuration class when the project starts.

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);
        SayHello hello = ctx.getBean(SayHello.class);
        System.out.println(hello.sayHello("javaboy")); }}Copy the code

Note that the configuration of load, is the use of AnnotationConfigApplicationContext to implement.

Here’s one thing to note about Java configuration: What is the name of the Bean?

The default name of the Bean is the method name. In the example above, the Bean name is sayHello. If developers want to customize the method name, they can do so directly in the @bean annotation. To change the name of the Bean to Javaboy:

@Configuration
public class JavaConfig {
    @Bean("javaboy")
    SayHello sayHello(a) {
        return newSayHello(); }}Copy the code

3.6 Automatic Configuration

In our actual development, automatic configuration is used a lot.

Automated configuration can be implemented either through Java configuration or XML configuration.

3.6.1 Preparations

For example, if I have a UserService and I want the class to be automatically registered with the Spring container during automated scanning, I can add @Service as a tag to the class.

There are four annotations similar to @service annotations:

  • @Component
  • @Repository
  • @Service
  • @Controller

Of these four, the other three are based on @Component, and are functionally consistent from the current source code, so why three? This is mainly for the convenience of adding on top of different classes.

  • At the Service layer, when adding annotations, use @service
  • In the Dao layer, when adding annotations, use @repository
  • At the Controller layer, when adding annotations, use @Controller
  • When adding annotations to other components, use @Component
@Service
public class UserService {
    public List<String> getAllUser(a) {
        List<String> users = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            users.add("javaboy:" + i);
        }
        returnusers; }}Copy the code

Once added, automated scanning can be configured in either Java code or XML files.

3.6.2 Configuring Automatic Scanning in Java Code

@Configuration
@ComponentScan(basePackages = "org.javaboy.javaconfig.service")
public class JavaConfig {}Copy the code

The configuration class is then loaded in the project startup, where the @ComponentScan annotation specifies the package to scan (if not, the beans downloaded from the package that the configuration class belongs to and the classes under the subpackages of the package that the configuration class belongs to are scanned by default). Then you can get an instance of UserService:

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = newAnnotationConfigApplicationContext(JavaConfig.class); UserService userService = ctx.getBean(UserService.class); System.out.println(userService.getAllUser()); }}Copy the code

Here are a few things to note:

1. What’s the name of the Bean?

By default, the Bean’s name is lowercase. For example, UserService, whose instance name is UserService by default. If developers want to customize the name, they can add it directly in the @service annotation.

2. How many scanning modes are available?

In the configuration above, we scan by packet location. That is, the Bean must be scanned at the specified location, otherwise, even if you have the @service annotation, it will not be scanned.

In addition to scanning by package location, there is another way to scan by annotations. For example:

@Configuration
@ComponentScan(basePackages = "org.javaboy.javaconfig",useDefaultFilters = true,excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class)})
public class JavaConfig {}Copy the code

This configuration means scanning all beans under org.javaboy.javaconfig except Controller.

3.6.3 Automatic XML Configuration Scanning

<context:component-scan base-package="org.javaboy.javaconfig"/>
Copy the code

The above configuration scans all beans under org.javaboy.javaconfig. You can also scan by class.

After the XML configuration is complete, load the XML configuration in Java code.

public class XMLTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService userService = ctx.getBean(UserService.class); List<String> list = userService.getAllUser(); System.out.println(list); }}Copy the code

It is also possible to scan by annotation type in the XML configuration:

<context:component-scan base-package="org.javaboy.javaconfig" use-default-filters="true">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
Copy the code

3.6.4 Object Injection

There are three ways to inject objects in automatic scanning:

  1. @Autowired
  2. @Resources
  3. @Injected

@autowired is looking up by type and assigning, so there is a requirement that this type can only have one object, otherwise an error will be reported. @resources is a look-up by name. By default, the name of the variable defined is the look-up name, although developers can specify it manually in the @Resources annotation. So, if there are multiple instances of a class, @resources should be used for injection. If @autowired is used, you can use another annotation, @qualifier, where variable names can be specified. The two are used together (@qualifier and @Autowired) to find variables by their names.

@Service
public class UserService {

    @Autowired
    UserDao userDao;
    public String hello(a) {
        return userDao.hello();
    }

    public List<String> getAllUser(a) {
        List<String> users = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            users.add("javaboy:" + i);
        }
        returnusers; }}Copy the code

3.7 Conditional Notes

A conditional annotation is a configuration that takes effect if a condition is met.

3.7.1 Conditional notes

How do I get operating system information in Windows first? On Windows, the command to view a folder’s directory is dir, and on Linux, the command to view a folder’s directory is ls. Now I want to automatically print the directory display command on Windows when the system is running on Windows, and when Linux is running, Automatically displays directory display commands on Linux.

First define an interface to display the folder directory:

public interface ShowCmd {
    String showCmd(a);
}
Copy the code

Then, implement the Windows and Linux instances respectively:

public class WinShowCmd implements ShowCmd {
    @Override
    public String showCmd(a) {
        return "dir"; }}public class LinuxShowCmd implements ShowCmd {
    @Override
    public String showCmd(a) {
        return "ls"; }}Copy the code

Next, define two conditions, one for Windows and one for Linux.

public class WindowsCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return context.getEnvironment().getProperty("os.name").toLowerCase().contains("windows"); }}public class LinuxCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return context.getEnvironment().getProperty("os.name").toLowerCase().contains("linux"); }}Copy the code

Next, when defining the Bean, you can configure the conditional annotations.

@Configuration
public class JavaConfig {
    @Bean("showCmd")
    @Conditional(WindowsCondition.class)
    ShowCmd winCmd(a) {
        return new WinShowCmd();
    }

    @Bean("showCmd")
    @Conditional(LinuxCondition.class)
    ShowCmd linuxCmd(a) {
        return newLinuxShowCmd(); }}Copy the code

Here, it is important to give both beans the same name so that they will automatically match when called. Each Bean is then annotated with a condition in which the Bean definition takes effect when the matches method returns true.

public class JavaMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);
        ShowCmd showCmd = (ShowCmd) ctx.getBean("showCmd"); System.out.println(showCmd.showCmd()); }}Copy the code

A very typical use scenario for conditional annotations is multi-context switching.

3.7.2 Switching between Multiple Environments

How do you quickly switch between development/production/test environments during development? Spring provides profiles to solve this problem, and the underlying layer of profiles is conditional annotations. This can be seen from the @profile annotation definition:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {

	/** * The set of profiles for which the annotated component should be registered. */
	String[] value();

}
class ProfileCondition implements Condition {

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

We define a DataSource:

public class DataSource {
    private String url;
    private String username;
    private String password;

    @Override
    public String toString(a) {
        return "DataSource{" +
                "url='" + url + '\' ' +
                ", username='" + username + '\' ' +
                ", password='" + password + '\' ' +
                '} ';
    }

    public String getUrl(a) {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getUsername(a) {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword(a) {
        return password;
    }

    public void setPassword(String password) {
        this.password = password; }}Copy the code

Then, when configuring the Bean, specify different environments via the @profile annotation:

@Bean("ds")
@Profile("dev")
DataSource devDataSource(a) {
    DataSource dataSource = new DataSource();
    dataSource.setUrl("JDBC: mysql: / / 127.0.0.1:3306 / dev." ");
    dataSource.setUsername("root");
    dataSource.setPassword("123");
    return dataSource;
}
@Bean("ds")
@Profile("prod")
DataSource prodDataSource(a) {
    DataSource dataSource = new DataSource();
    dataSource.setUrl("JDBC: mysql: / / 192.158.222.33:3306 / dev." ");
    dataSource.setUsername("jkldasjfkl");
    dataSource.setPassword("jfsdjflkajkld");
    return dataSource;
}
Copy the code

Finally, before loading the configuration class, note that you need to set the current environment first, and then load the configuration class:

public class JavaMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
        ctx.getEnvironment().setActiveProfiles("dev");
        ctx.register(JavaConfig.class);
        ctx.refresh();
        DataSource ds = (DataSource) ctx.getBean("ds"); System.out.println(ds); }}Copy the code

This is configured in Java code. Context switching can also be configured in an XML file. The following configuration must be placed after other nodes in the XML file.

<beans profile="dev">
    <bean class="org.javaboy.DataSource" id="dataSource">
        <property name="url" value="jdbc:mysql:///devdb"/>
        <property name="password" value="root"/>
        <property name="username" value="root"/>
    </bean>
</beans>
<beans profile="prod">
    <bean class="org.javaboy.DataSource" id="dataSource">
        <property name="url" value="JDBC: mysql: / / 111.111.111.111 devdb"/>
        <property name="password" value="jsdfaklfj789345fjsd"/>
        <property name="username" value="root"/>
    </bean>
</beans>
Copy the code

Set the current environment and load the configuration in the startup class:

public class Main {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext();
        ctx.getEnvironment().setActiveProfiles("prod");
        ctx.setConfigLocation("applicationContext.xml");
        ctx.refresh();
        DataSource dataSource = (DataSource) ctx.getBean("dataSource"); System.out.println(dataSource); }}Copy the code

3.8 other

3.8.1 Scope of beans

If I fetch beans registered in an XML configuration, or beans registered with a Java configuration, do I get the same object?

public class Main {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        User user = ctx.getBean("user", User.class);
        User user2 = ctx.getBean("user", User.class); System.out.println(user==user2); }}Copy the code

As shown above, when you get the same Bean from the Spring container multiple times, you actually get the same instance by default. Of course we can configure it manually.

<bean class="org.javaboy.User" id="user" scope="prototype" />
Copy the code

We can adjust the default number of instances by setting the scope attribute in the XML node. The scope value is singleton (the default), which means that the Bean is a singleton in the Spring container. If the scope value is prototype, it means that the Bean is not a singleton in the Spring container. Fetching multiple times will result in multiple different instances.

In addition to Singleton and Prototype, there are two values, request and session. The two values are valid in the Web environment. This is configured in XML, but we can also configure it in Java.

@Configuration
public class JavaConfig {
    @Bean
    @Scope("prototype")
    SayHello sayHello(a) {
        return newSayHello(); }}Copy the code

In Java code, we can specify the Scope of the Bean through the @Scope annotation.

Of course, it is also possible to specify the scope of the Bean in an automatic scan configuration.

@Repository
@Scope("prototype")
public class UserDao {
    public String hello(a) {
        return "userdao"; }}Copy the code

3.8.2 Difference between ID and Name

In the XML configuration, we can see that a Bean can be assigned a unique identifier by id or by name. In most cases, the two functions are the same, with one small difference:

Multiple names are supported. Multiple names are separated by comma:

<bean class="org.javaboy.User" name="user,user1,user2,user3" scope="prototype"/>
Copy the code

In this case, the current object can be obtained by user, user1, user2, user3:

public class Main {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        User user = ctx.getBean("user", User.class);
        User user2 = ctx.getBean("user2", User.class); System.out.println(user); System.out.println(user2); }}Copy the code

Id does not support holding multiple values. If forced, separated, it’s still a value. For example:

<bean class="org.javaboy.User" id="user,user1,user2,user3" scope="prototype" />
Copy the code

This configuration indicates that the Bean name is user,user1,user2,user3, and the call is as follows:

public class Main {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        User user = ctx.getBean("user,user1,user2,user3", User.class);
        User user2 = ctx.getBean("user,user1,user2,user3", User.class); System.out.println(user); System.out.println(user2); }}Copy the code

3.8.3 Mixed Configuration

Mixed configuration is Java configuration +XML configuration. For intermixing, you can introduce XML configuration into your Java configuration.

@Configuration
@ImportResource("classpath:applicationContext.xml")
public class JavaConfig {}Copy the code

In a Java configuration, you can import an XML configuration through the @importResource annotation.

4. Aware interface

The Aware interface is literally perceptual capture. A Bean alone is insensible.

In the scenario described in Section 3.6.4, the UserDao can be injected into the UserService only if both are managed by the Spring container. If you simply new a UserService, this is useless because the UserService is not managed by the Spring container, so it will not inject beans into it.

In real life development, we might encounter classes that need to get container details, which can be done through the Aware interface.

Aware is an empty interface with many implementation classes:

These implemented interfaces have some common features:

  1. They all end with Aware
  2. Both inherited from Aware
  3. Each interface defines a set method

Each subinterface provides a set method whose parameters are what the Bean needs to be aware of, so we need to declare relevant member variables in the Bean to accept this parameter. Once this parameter is received, the container details can be retrieved from this parameter.

@Component
public class SayHello implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    public String sayHello(String name) {
        // Determine whether a Bean exists in the container
        boolean userDao = applicationContext.containsBean("userDao333");
        System.out.println(userDao);
        return "hello " + name;
    }
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext; }}Copy the code

5.1 Aop

Aop (Aspect Oriented Programming), section-oriented Programming, which is a supplement to object-oriented thought.

Section-oriented programming is the function of dynamically enhancing the method without changing the source code while the program is running. There are many common use scenarios:

  1. The log
  2. The transaction
  3. Database operations
  4. .

In all of these operations, there is a lot of template code, and Aop excels at tackling template code and eliminating bloatiness.

In Aop, there are several common concepts:

concept instructions
Point of tangency The point where you want to add code is called the cut point
Notification (enhanced) Notifications are dynamically added code to pointcuts
section Pointcut + notification
The join Definition of tangent point

5.1.1 Implementation of Aop

Aop is actually implemented based on Java dynamic proxy.

Dynamic proxies in Java can be implemented in two ways:

  • cglib
  • jdk

5.2 Dynamic Proxy

Dynamic proxy based on JDK.

1. Define a calculator interface:

public interface MyCalculator {
    int add(int a, int b);
}
Copy the code

2. Define the implementation of computer interface:

public class MyCalculatorImpl implements MyCalculator {
    public int add(int a, int b) {
        returna+b; }}Copy the code

3. Define proxy classes

public class CalculatorProxy {
    public static Object getInstance(final MyCalculatorImpl myCalculator) {
        return Proxy.newProxyInstance(CalculatorProxy.class.getClassLoader(), myCalculator.getClass().getInterfaces(), new InvocationHandler() {
            / * * *@paramProxy Proxy object *@paramMethod Method of the proxy *@paramParameter * to the args method@return
             * @throws Throwable
             */
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println(method.getName()+"Method started executing...");
                Object invoke = method.invoke(myCalculator, args);
                System.out.println(method.getName()+"Method execution completed...");
                returninvoke; }}); }}Copy the code

The proxy. newProxyInstance method takes three parameters, the first is a classLoader, the second is the interface to Proxy multiple implementations, and the third is the handler of the Proxy object method. All additional behaviors to be added are implemented in the Invoke method.

5.3 Five types of notifications

There are five types of advice for Aop in Spring:

  • Pre notice
  • The rear notice
  • Abnormal notice
  • Return to inform
  • Surrounding the notification

The implementation, in this case as in 5.2, is to enhance the calculator method.

First, in the project, introduce Spring dependencies (this time with aOP-related dependencies) :

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.9. RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.5</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.9.5</version>
    </dependency>
</dependencies>
Copy the code

Next, define the pointcut. Here are two ways to define pointcut:

  • Use custom annotations
  • Use rules

The use of custom annotations to mark pointcuts is intrusive, so this method is not recommended in practical development, just as another way to understand the rules to define pointcuts, non-intrusive, generally recommended to use this method.

Custom annotations

Start with a custom annotation:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Action {
}
Copy the code

This annotation is then added to the method that needs to be intercepted. The @Action annotation is added to the add method to indicate that this method will be intercepted by Aop, while other methods that do not have this annotation are not affected.

@Component
public class MyCalculatorImpl {
    @Action
    public int add(int a, int b) {
        return a + b;
    }

    public void min(int a, int b) {
        System.out.println(a + "-" + b + "="+ (a - b)); }}Copy the code

Next, define enhancements (notifications, Advice) :

@Component
@Aspect// indicates that this is a cut surface
public class LogAspect {

    / * * *@paramJoinPoint contains key information about the target method *@BeforeThe annotation indicates that this is a pre-notification, that is, before the target method is executed. In the annotation, you need to fill in the pointcut */
    @Before(value = "@annotation(Action)")
    public void before(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println(name + "Method started executing...");
    }

    /** * after notification *@paramJoinPoint contains all the key information for the target method *@AfterIndicates that this is a post-notification, that is, the */ is executed after the target method is executed
    @After("@annotation(Action)")
    public void after(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println(name + "Method execution completed...");
    }

    / * * *@param joinPoint
     * @@AfterReturningIt is a return notification that is triggered when a returning method is returning a value. The RETURNING attribute in the annotation is the name of the variable returning the value of the target method. The return type of the target method must be the same as that of the return parameter of the method, otherwise it cannot be intercepted. If you want to intercept all (including void), the return parameter of the method can be Object */
    @AfterReturning(value = "@annotation(Action)",returning = "r")
    public void returing(JoinPoint joinPoint,Integer r) {
        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println(name + "Method returns:"+r);
    }

    /** * Exception notification *@param joinPoint
     * @paramE The exception thrown by the target method. Note that this parameter must be either the exception thrown by the target method or the parent of the exception thrown by the target method. If you want to intercept all, declare the parameter type as Exception */
    @AfterThrowing(value = "@annotation(Action)",throwing = "e")
    public void afterThrowing(JoinPoint joinPoint,Exception e) {
        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println(name + Method throws exception:+e.getMessage());
    }

    /** * Wrap notification is an aggregator that can be used to implement all four of the above. The core of this method is somewhat similar to the implementation of the method * through reflection here@param pjp
     * @returnNote that the return value should preferably be Object, matching the intercepted method */
    @Around("@annotation(Action)")
    public Object around(ProceedingJoinPoint pjp) {
        Object proceed = null;
        try {
            // This is equivalent to the method.invoke method, and we can add logs before and after this method, which is equivalent to pre/post notifications
            proceed = pjp.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        returnproceed; }}Copy the code

After the notification definition is complete, next in the configuration class, enable packet scanning and automatic proxy:

@Configuration
@ComponentScan
@EnableAspectJAutoProxy// Enable automatic proxy
public class JavaConfig {}Copy the code

Then, in the Main method, turn on the call:

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);
        MyCalculatorImpl myCalculator = ctx.getBean(MyCalculatorImpl.class);
        myCalculator.add(3.4);
        myCalculator.min(3.4); }}Copy the code

If we look at the LogAspect aspect, we find that the definition of pointcuts is not flexible. The pointcuts were written directly in annotations, so that if we want to change pointcuts, we have to change pointcuts in each method. Therefore, we can define pointcuts uniformly and then call pointcuts uniformly.

@Component
@Aspect// indicates that this is a cut surface
public class LogAspect {

    /** ** can define the pointcut */
    @Pointcut("@annotation(Action)")
    public void pointcut(a) {}/ * * *@paramJoinPoint contains key information about the target method *@BeforeThe annotation indicates that this is a pre-notification, that is, before the target method is executed. In the annotation, you need to fill in the pointcut */
    @Before(value = "pointcut()")
    public void before(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println(name + "Method started executing...");
    }

    /** * after notification *@paramJoinPoint contains all the key information for the target method *@AfterIndicates that this is a post-notification, that is, the */ is executed after the target method is executed
    @After("pointcut()")
    public void after(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println(name + "Method execution completed...");
    }

    / * * *@param joinPoint
     * @@AfterReturningIt is a return notification that is triggered when a returning method is returning a value. The RETURNING attribute in the annotation is the name of the variable returning the value of the target method. The return type of the target method must be the same as that of the return parameter of the method, otherwise it cannot be intercepted. If you want to intercept all (including void), the return parameter of the method can be Object */
    @AfterReturning(value = "pointcut()",returning = "r")
    public void returing(JoinPoint joinPoint,Integer r) {
        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println(name + "Method returns:"+r);
    }

    /** * Exception notification *@param joinPoint
     * @paramE The exception thrown by the target method. Note that this parameter must be either the exception thrown by the target method or the parent of the exception thrown by the target method. If you want to intercept all, declare the parameter type as Exception */
    @AfterThrowing(value = "pointcut()",throwing = "e")
    public void afterThrowing(JoinPoint joinPoint,Exception e) {
        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println(name + Method throws exception:+e.getMessage());
    }

    /** * Wrap notification is an aggregator that can be used to implement all four of the above. The core of this method is somewhat similar to the implementation of the method * through reflection here@param pjp
     * @returnNote that the return value should preferably be Object, matching the intercepted method */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint pjp) {
        Object proceed = null;
        try {
            // This is equivalent to the method.invoke method, and we can add logs before and after this method, which is equivalent to pre/post notifications
            proceed = pjp.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        returnproceed; }}Copy the code

However, you will also notice that using annotations is intrusive, and we can optimize it to be non-intrusive. Redefine the pointcut so that the @Action annotation is no longer required for the definition of the pointcut, nor for the target method to be intercepted. The following is a more general interception method:

@Component
@Aspect// indicates that this is a cut surface
public class LogAspect {

    /** ** can define the pointcut */
    @Pointcut("@annotation(Action)")
    public void pointcut2(a) {}/ * * * can be unified definition of tangent point * first * said to intercept the target method return value (can also explicitly specify any return value type * * the second said in any class (can also explicitly specify the third class * * class methods of * the final two arbitrary point representation parameters, the arbitrary number, Any type */
    @Pointcut("execution(* org.javaboy.aop.commons.*.*(..) )")
    public void pointcut(a) {}/ * * *@paramJoinPoint contains key information about the target method *@BeforeThe annotation indicates that this is a pre-notification, that is, before the target method is executed. In the annotation, you need to fill in the pointcut */
    @Before(value = "pointcut()")
    public void before(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println(name + "Method started executing...");
    }

    /** * after notification *@paramJoinPoint contains all the key information for the target method *@AfterIndicates that this is a post-notification, that is, the */ is executed after the target method is executed
    @After("pointcut()")
    public void after(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println(name + "Method execution completed...");
    }

    / * * *@param joinPoint
     * @@AfterReturningIt is a return notification that is triggered when a returning method is returning a value. The RETURNING attribute in the annotation is the name of the variable returning the value of the target method. The return type of the target method must be the same as that of the return parameter of the method, otherwise it cannot be intercepted. If you want to intercept all (including void), the return parameter of the method can be Object */
    @AfterReturning(value = "pointcut()",returning = "r")
    public void returing(JoinPoint joinPoint,Integer r) {
        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println(name + "Method returns:"+r);
    }

    /** * Exception notification *@param joinPoint
     * @paramE The exception thrown by the target method. Note that this parameter must be either the exception thrown by the target method or the parent of the exception thrown by the target method. If you want to intercept all, declare the parameter type as Exception */
    @AfterThrowing(value = "pointcut()",throwing = "e")
    public void afterThrowing(JoinPoint joinPoint,Exception e) {
        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println(name + Method throws exception:+e.getMessage());
    }

    /** * Wrap notification is an aggregator that can be used to implement all four of the above. The core of this method is somewhat similar to the implementation of the method * through reflection here@param pjp
     * @returnNote that the return value should preferably be Object, matching the intercepted method */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint pjp) {
        Object proceed = null;
        try {
            // This is equivalent to the method.invoke method, and we can add logs before and after this method, which is equivalent to pre/post notifications
            proceed = pjp.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        returnproceed; }}Copy the code

5.4 XML to configure Aop

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.1.9. RELEASE</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.5</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.9.5</version>
</dependency>
Copy the code

Next, define notifications/enhancements, but simply define your own behavior without annotations:

public class LogAspect {

    public void before(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println(name + "Method started executing...");
    }
    
    public void after(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println(name + "Method execution completed...");
    }

    public void returing(JoinPoint joinPoint,Integer r) {
        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println(name + "Method returns:"+r);
    }
    
    public void afterThrowing(JoinPoint joinPoint,Exception e) {
        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println(name + Method throws exception:+e.getMessage());
    }
    
    public Object around(ProceedingJoinPoint pjp) {
        Object proceed = null;
        try {
            // This is equivalent to the method.invoke method, and we can add logs before and after this method, which is equivalent to pre/post notifications
            proceed = pjp.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        returnproceed; }}Copy the code

Next, configure Aop in Spring:

<bean class="org.javaboy.aop.LogAspect" id="logAspect"/>
<aop:config>
    <aop:pointcut id="pc1" expression="execution(* org.javaboy.aop.commons.*.*(..) )"/>
    <aop:aspect ref="logAspect">
        <aop:before method="before" pointcut-ref="pc1"/>
        <aop:after method="after" pointcut-ref="pc1"/>
        <aop:after-returning method="returing" pointcut-ref="pc1" returning="r"/>
        <aop:after-throwing method="afterThrowing" pointcut-ref="pc1" throwing="e"/>
        <aop:around method="around" pointcut-ref="pc1"/>
    </aop:aspect>
</aop:config>
Copy the code

Finally, load the configuration file in the Main method:

public class Main {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        MyCalculatorImpl myCalculator = ctx.getBean(MyCalculatorImpl.class);
        myCalculator.add(3.4);
        myCalculator.min(5.6); }}Copy the code

6. JdbcTemplate

The JdbcTemplate is a JDBC manipulation tool that Spring encapsulates using Aop ideas.

6.1 to prepare

Create a new project and add the following dependencies:

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.1.9. RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.9. RELEASE</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.17</version>
    </dependency>
</dependencies>
Copy the code

Preparing the database:

CREATE DATABASE / *! 32312 IF NOT EXISTS*/`test01` / *! 40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci */ / *! 80016 DEFAULT ENCRYPTION='N' */;

USE `test01`;

/*Table structure for table `user` */

DROP TABLE IF EXISTS `user`;

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL.`address` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
  PRIMARY KEY (`id`))ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
Copy the code

Prepare an entity class:

public class User {
    private Integer id;
    private String username;
    private String address;

    @Override
    public String toString(a) {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\' ' +
                ", address='" + address + '\' ' +
                '} ';
    }

    public Integer getId(a) {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername(a) {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getAddress(a) {
        return address;
    }

    public void setAddress(String address) {
        this.address = address; }}Copy the code

6.2 Java configuration

Provide a configuration class in which to configure the JdbcTemplate:

@Configuration
public class JdbcConfig {
    @Bean
    DataSource dataSource(a) {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUsername("root");
        dataSource.setPassword("123");
        dataSource.setUrl("jdbc:mysql:///test01");
        return dataSource;
    }
    @Bean
    JdbcTemplate jdbcTemplate(a) {
        return newJdbcTemplate(dataSource()); }}Copy the code

DataSource and JdbcTemplate. JdbcTemplate is very easy to configure. We just need to create a new Bean. Then configure DataSource.

public class Main {

    private JdbcTemplate jdbcTemplate;

    @Before
    public void before(a) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JdbcConfig.class);
        jdbcTemplate = ctx.getBean(JdbcTemplate.class);
    }

    @Test
    public void insert(a) {
        jdbcTemplate.update("insert into user (username,address) values (? ,?) ;"."javaboy"."www.javaboy.org");
    }
    @Test
    public void update(a) {
        jdbcTemplate.update("update user set username=? where id=?"."javaboy123".1);

    }
    @Test
    public void delete(a) {
        jdbcTemplate.update("delete from user where id=?".2);
    }

    @Test
    public void select(a) {
        User user = jdbcTemplate.queryForObject("select * from user where id=?".new BeanPropertyRowMapper<User>(User.class), 1); System.out.println(user); }}Copy the code

If the BeanPropertyRowMapper is used in the query, the fields must correspond to the property name of the Bean. If not, do not use BeanPropertyRowMapper. In this case, you need to customize the RowMapper or alias the fields in the query.

  1. Alias the columns in the query:
@Test
public void select2(a) {
    User user = jdbcTemplate.queryForObject("select id,username as name,address from user where id=?".new BeanPropertyRowMapper<User>(User.class), 1);
    System.out.println(user);
}
Copy the code

2. Customize the RowMapper

@Test
public void select3(a) {
    User user = jdbcTemplate.queryForObject("select * from user where id=?".new RowMapper<User>() {
        public User mapRow(ResultSet resultSet, int i) throws SQLException {
            int id = resultSet.getInt("id");
            String username = resultSet.getString("username");
            String address = resultSet.getString("address");
            User u = new User();
            u.setId(id);
            u.setName(username);
            u.setAddress(address);
            returnu; }},1);
    System.out.println(user);
}
Copy the code

You can query multiple records using the following methods:

@Test
public void select4(a) {
    List<User> list = jdbcTemplate.query("select * from user".new BeanPropertyRowMapper<>(User.class));
    System.out.println(list);
}
Copy the code

6.3 the XML configuration

The above configuration can also be implemented through XML files. The XML file implementation simply provides the JdbcTemplate instance, and the rest of the code remains Java code, with JdbcConfig replaced by an XML file.

<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource">
    <property name="username" value="root"/>
    <property name="password" value="123"/>
    <property name="url" value="jdbc:mysql:///test01? serverTimezone=Asia/Shanghai"/>
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
</bean>
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
    <property name="dataSource" ref="dataSource"/>
</bean>
Copy the code

After the configuration is complete, load the configuration file and start:

public class Main {

    private JdbcTemplate jdbcTemplate;

    @Before
    public void before(a) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        jdbcTemplate = ctx.getBean(JdbcTemplate.class);
    }

    @Test
    public void insert(a) {
        jdbcTemplate.update("insert into user (username,address) values (? ,?) ;"."javaboy"."www.javaboy.org");
    }
    @Test
    public void update(a) {
        jdbcTemplate.update("update user set username=? where id=?"."javaboy123".1);

    }
    @Test
    public void delete(a) {
        jdbcTemplate.update("delete from user where id=?".2);
    }

    @Test
    public void select(a) {
        User user = jdbcTemplate.queryForObject("select * from user where id=?".new BeanPropertyRowMapper<User>(User.class), 1);
        System.out.println(user);
    }
    @Test
    public void select4(a) {
        List<User> list = jdbcTemplate.query("select * from user".new BeanPropertyRowMapper<>(User.class));
        System.out.println(list);
    }

    @Test
    public void select2(a) {
        User user = jdbcTemplate.queryForObject("select id,username as name,address from user where id=?".new BeanPropertyRowMapper<User>(User.class), 1);
        System.out.println(user);
    }

    @Test
    public void select3(a) {
        User user = jdbcTemplate.queryForObject("select * from user where id=?".new RowMapper<User>() {
            public User mapRow(ResultSet resultSet, int i) throws SQLException {
                int id = resultSet.getInt("id");
                String username = resultSet.getString("username");
                String address = resultSet.getString("address");
                User u = new User();
                u.setId(id);
                u.setName(username);
                u.setAddress(address);
                returnu; }},1); System.out.println(user); }}Copy the code

7. The transaction

Transactions in Spring take advantage of Aop ideas to simplify the configuration of transactions, either through Java or XML.

Preparations:

Let’s look at transaction configuration in Spring through a transfer operation.

Start with SQL:

CREATE DATABASE / *! 32312 IF NOT EXISTS*/`test01` / *! 40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci */ / *! 80016 DEFAULT ENCRYPTION='N' */;

USE `test01`;

/*Table structure for table `account` */

DROP TABLE IF EXISTS `account`;

CREATE TABLE `account` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL.`money` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`))ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

/*Data for the table `account` */

insert  into `account`(`id`.`username`.`money`) values (1.'zhangsan'.1000), (2.'lisi'.1000);
Copy the code

Then configure the JdbcTemplate as you did in Section 6.

Then, provide the method of transfer operation:

@Repository
public class UserDao {
    @Autowired
    JdbcTemplate jdbcTemplate;

    public void addMoney(String username, Integer money) {
        jdbcTemplate.update("update account set money=money+? where username=?", money, username);
    }

    public void minMoney(String username, Integer money) {
        jdbcTemplate.update("update account set money=money-? where username=?", money, username); }}@Service
public class UserService {
    @Autowired
    UserDao userDao;
    public void updateMoney(a) {
        userDao.addMoney("zhangsan".200);
        int i = 1 / 0;
        userDao.minMoney("lisi".200); }}Copy the code

Finally, in the XML file, turn on automated scanning:

<context:component-scan base-package="org.javaboy"/>
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource">
    <property name="username" value="root"/>
    <property name="password" value="123"/>
    <property name="url" value="jdbc:mysql:///test01? serverTimezone=Asia/Shanghai"/>
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
</bean>
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
    <property name="dataSource" ref="dataSource"/>
</bean>
Copy the code

7.1 the XML configuration

There are three steps to configuring transactions in XML:

1. Configuration TransactionManager

<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>
Copy the code

2. Configure the method to process the transaction

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="update*"/>
        <tx:method name="insert*"/>
        <tx:method name="add*"/>
        <tx:method name="delete*"/>
    </tx:attributes>
</tx:advice>
Copy the code

Note that once the method name rules are configured, the methods in the service must follow these name rules otherwise the transaction configuration will not take effect

3. The configuration of Aop

<aop:config>
    <aop:pointcut id="pc1" expression="execution(* org.javaboy.service.*.*(..) )"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pc1"/>
</aop:config>
Copy the code

4. Test

@Before
public void before(a) {
    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    jdbcTemplate = ctx.getBean(JdbcTemplate.class);
    userService = ctx.getBean(UserService.class);
}
@Test
public void test1(a) {
    userService.updateMoney();
}
Copy the code

7.2 Java configuration

To enable Java annotation configuration, add the following configuration to the XML configuration:

<tx:annotation-driven transaction-manager="transactionManager" />
Copy the code

This line of configuration can replace the following two configurations:

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="update*"/>
        <tx:method name="insert*"/>
        <tx:method name="add*"/>
        <tx:method name="delete*"/>
    </tx:attributes>
</tx:advice>
<aop:config>
    <aop:pointcut id="pc1" expression="execution(* org.javaboy.service.*.*(..) )"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pc1"/>
</aop:config>
Copy the code

You can then add the @Transactional annotation to any method that requires a transaction to enable it. This annotation can also be placed ona class to enable transactions for all methods in that class.

@Service
public class UserService {
    @Autowired
    UserDao userDao;
    @Transactional
    public void updateMoney(a) {
        userDao.addMoney("zhangsan".200);
        int i = 1 / 0;
        userDao.minMoney("lisi".200); }}Copy the code

Follow the wechat official account “A Little Rain in Jiangnan”, reply to Spring, get the electronic version of this article, or visitspring.javaboy.orgCheck out this ebook.