Spring basic topics-Chapter 4 (Controlling the number of objects created by Spring + object life cycle)

Foreword: Last year, there has been no good time to complete the blog goal of spring basic + source code. Last year was relatively lazy, so this year I hope that my knowledge can be shared with Internet developers who are struggling, and what I want to do in the future Friends of the architects, let’s make progress together, from an Internet workplace novice to a Husband wet person, let me know how important sharing is all the way, anyway, point out what’s wrong, let’s walk through the code together The ocean!

 

I’m here to make the premise of each chapter. It’s not necessarily standard to apply some official terms. The purpose is to make everyone understand and clearer. If the description is not appropriate, you can leave a message. Thank you very much.

1. How to control the number of times a simple object is created

We mentioned in the previous section that if we create a complex object, we need to implement the FactoryBean interface. If isSingleton returns true, it means that we only need to create it once. If it is false, we need to create multiple complex objects. Each object is different!

We define an Account class and write this in xml

<!-- 如果我们想对这个简单对象,控制次数,加个scope标签,为singleton,那么这个scope对象只会被创建一次-->
    <bean id="account" scope="singleton" class="com.chenxin.spring5.scope.Account"></bean>

If the scope is singleton, it means that it is only created once. This label is not written by default, that is, it is created only once by default, so Spring creates singletons (singleton) by default. If it is prototype, it creates multiple different objects.

 <!-- 如果我们想对这个简单对象,控制次数,加个scope标签并且等于prototype,那么这个scope对象只会被创建多次-->
    <bean id="account" scope="prototype" class="com.chenxin.spring5.scope.Account"></bean>

2. How to control the number of times a complex object is created

If we create a complex object, we need to implement the FactoryBean interface. If isSingleton returns true, it only needs to be created once. If it is false, we need to create multiple complex objects.

Then some students will ask, if it is an instance factory or a static factory, and they did not implement the isSingleton method, they actually still use the scope to control the number of object creations!

 

3. Why do we need to control the number of object creations

Because some objects can be shared, then only need to be created once; some objects cannot be shared by everyone, so we have to manage this object, according to the characteristics of the object, to create conditions

Benefits: save unnecessary waste of resources

  • What kind of object only needs to be created once

1、SqlSessionFactory

2. DAO (because you insert data, I also insert data, we are all insert methods, we have no difference)

3. Service (you have to log in and I have to log in. The difference between us is the user name or other input parameters, but the method is the same, doing the same thing)

  • What kind of objects need to be created new

1、Connection

2. Sqlsession, Session session

3、Struts2 Action

4. The life cycle of the object

1. What is the cycle of the object

Refers to a complete process of creation, survival, and extinction of an object

2. Why study the life cycle of the object

We know that at the beginning, the creation of an object was created by new. This object will always live in the virtual machine when it is always referenced. Our code and the virtual machine will manage the life cycle of this object.

So the survival and death process of the object we are talking about today is not controlled by us, but handed over to Spring to control. If we understand the life cycle of the object better, we will actually make better use of the objects that Spring creates for us. Come do things for us

3. The three stages of the life cycle

  • Creation phase

When does the Spring factory help us create objects? By situation

1. If the object is created only once, that is, when scope="singleton", the object will be created at the same time as the Spring factory is created

2. If the object is created multiple times, that is, when scope="prototype", the Spring factory will obtain the object (ctx.getBean) and create the object at the same time

If it is created once:

<bean id="account" class="com.chenxin.spring5.Account">

    </bean>

Entity class

public class Account {

    private Integer id;
    private String name;

    public Account() {
        System.out.println("Account.Account");
    }

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

 Test, if scope="singleton" or bean scope tag is not written, the default object is only created once, then when the factory is new, the constructor must be called to create the object

    @Test
    public void test1(){
        ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
//        Account account = (Account) ctx.getBean("account");
//        System.out.println("account = " + account);
    }
Account.Account

 If it is scope="prototype", then the object will be created multiple times, and the factory will help us create the object when we getBean.

  <bean id="account" scope="prototype" class="com.chenxin.spring5.Account">

    </bean>
    @Test
    public void test1(){
        ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
        Account account = (Account) ctx.getBean("account");
//        System.out.println("account = " + account);
    }

 So we can clearly understand when the factory helps us create objects.

Then join me at this time, scope="singleton", but I don't want to help us create the object when the factory is created. I need to create the object when I get Bean. How can I do it this time?

So let’s do this, we only need to add a label lazy-init="true", which means lazy loading

<bean id="account" scope="prototype" class="com.chenxin.spring5.Account" lazy-init="true">

    </bean>

In this way, we can call getBean on the premise of singleton, and the factory will start to create objects for us!

  • Initialization phase

What is the initialization phase?

It means that after the Spring factory has created the object, it calls the object's initialization method to complete the corresponding initialization operation!

1. Who will provide the initialization: The programmer provides the initialization method according to the needs to complete the initialization operation.

2. Who calls the initialization method: Spring factory calls

 

Spring provides two ways for initialization:

1. InitializingBean interface: You need to implement the afterPropertiesSet() method of InitializingBean. The code to complete the initialization operation can be written in this method; because you implement the Spring interface, Spring can find the interface you implement, and you can find this method;

public class Product implements InitializingBean {

    public Product() {
        System.out.println("Product.Product");
    }

    /*
    *  这个就是初始化方法:做一些初始化的操作
    * Spring会进行调用
    */
    public void afterPropertiesSet() throws Exception {
        System.out.println("Product.afterPropertiesSet");
    }
}
<bean id="product" class="com.chenxin.spring5.Product"></bean>
    @Test
    public void test2(){
        ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
    }

 So the output result is that the object is created first, the constructor is called, and Spring initializes the object. When it is found to be initialized, I implement the afterPropertiesSet method of the InitializingBean interface, which will then help us call the initialization method for initialization.

But there is a problem here. Students who have read the previous chapter can know that this InitializingBean is similar to the FactoryBean interface, which is coupled with the Spring framework, which is not good for code scalability. If I leave the Spring framework, I think These initialization codes can no longer be used, so we have another strategy for initialization in order to be flexible;

2. Provide a common method in the class

public class Cat {

    public Cat() {
        System.out.println("Cat.Cat");
    }

    /**
     * 提供一个普通方法
     */
    public void myInit(){
        System.out.println("Cat.myInit");
    }
}

If you want Spring to know the call of your initialization method, can you only tell Spring that you want to call my initialization method in the configuration file, so that I can not use your InitializingBean interface, even if you change to another framework later, you The code is still usable, at least it will not report an error can not find InitializingBean

   <bean id="cat" class="com.chenxin.spring5.Cat" init-method="myInit"></bean>
Cat.Cat
Cat.myInit

 In this way, you can control the initialization of the object more flexibly!

Detailed analysis:

If an object not only implements InitializingBean, but also provides the normal initialization method myInit(), what is the order of execution? We test it

public class Product implements InitializingBean {//不仅实现了InitializingBean

    public Product() {
        System.out.println("Product.Product");
    }

    //还提供了普通的初始化方法
    public void myInit(){
        System.out.println("Product.myInit");
    }
    /*
    *  这个就是初始化方法:做一些初始化的操作
    * Spring会进行调用
    */
    public void afterPropertiesSet() throws Exception {
        System.out.println("Product.afterPropertiesSet");
    }
}

The configuration file also points to the initialization method

<bean id="product" class="com.chenxin.spring5.Product" init-method="myInit"></bean>

turn out:

Product.Product
Product.afterPropertiesSet
Product.myInit

 Obviously, the afterPropertiesSet initialization is executed before the custom initialization myInit;

So we know that after the object is created, the initialization method will be called; at this time, we have overlooked some details. If properties need to be injected at this time, then you said that this injection operation should be injected first after the object is created. , Or the initialization operation first?

We test it

public class Product implements InitializingBean {

    private String name;

    public void setName(String name) {
        System.out.println("Product.setName");
        this.name = name;
    }

    public Product() {
        System.out.println("Product.Product");
    }


    public void myInit(){
        System.out.println("Product.myInit");
    }
    /*
    *  这个就是初始化方法:做一些初始化的操作
    * Spring会进行调用
    */
    public void afterPropertiesSet() throws Exception {
        System.out.println("Product.afterPropertiesSet");
    }
}
<bean id="product" class="com.chenxin.spring5.Product" init-method="myInit">
        <property name="name" value="chenxin"></property>
    </bean>

Our code adds a sentence setName to the set method

Product.Product
Product.setName
Product.afterPropertiesSet
Product.myInit

 So obviously, set injection is after the object is created and before the initialization, so it is to create the object -> attribute injection -> initialization

In fact, you find that the meaning of this method of initialization interface afterPropertiesSet is after the property set is injected, and it also expresses that the initialization is before the set injection! !

So what is the initialization operation?

In fact, it is the initialization of resources, database resources, io resources, network, etc... So these functions are generally written in this afterPropertiesSet

In fact, there are still relatively few application initialization phases later!

  • Destruction phase

Before Spring destroys the object, it will call the destruction method of the object to complete the destruction operation

1. When does Spring destroy the created object?

ctx.close(); means that the factory is closed

The destruction method is that the programmer defines the destruction method according to his own needs, which is called by the Spring factory!

What methods does Spring provide us to destroy objects? (Actually, it is very similar to initialization)

1. Implement the DisposableBean interface

public class Product implements InitializingBean, DisposableBean {

    private String name;

    public void setName(String name) {
        System.out.println("Product.setName");
        this.name = name;
    }

    public Product() {
        System.out.println("Product.Product");
    }


    public void myInit(){
        System.out.println("Product.myInit");
    }
    /*
    *  这个就是初始化方法:做一些初始化的操作
    * Spring会进行调用
    */
    public void afterPropertiesSet() throws Exception {
        System.out.println("Product.afterPropertiesSet");
    }

    @Override
    public String toString() {
        return "Product{" +
                "name='" + name + '\'' +
                '}';
    }

    /**
     * 实现销毁方法,资源释放的操作
     * @throws Exception
     */
    public void destroy() throws Exception {
        System.out.println("Product.destroy");
    }
}

 

    @Test
    public void test3(){
        ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
        Product product = (Product) ctx.getBean("product");
        System.out.println("product = " + product);
    }

 As a result, you will find that the object is created, injected, and initialized, but why is it not destroyed?

Product.Product
Product.setName
Product.afterPropertiesSet
Product.myInit

Because the destruction of the object is found when the factory is closed, ctx.close() is needed, because the close method is implemented in AbstractApplicatonContext, the parent class of ClassPathXmlApplicationContext, which is not available in the ApplicationContext interface, so we need to change it

    @Test
    public void test3(){
//        ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
        Product product = (Product) ctx.getBean("product");
        System.out.println("product = " + product);
        ctx.close();
    }

As a result, the destruction method can be printed out!

2. Define a common method

We can also define a destruction method in Product called myDestory():

public void myDestory(){
        System.out.println("Product.myDestory");
    }
<bean id="product" class="com.chenxin.spring5.Product" init-method="myInit" destroy-method="myDestory">
        <property name="name" value="chenxin"></property>
    </bean>

In the same way, the printing order is also Spring's own family first, and then customize!

Product.Product
Product.setName
Product.afterPropertiesSet
Product.myInit
product = Product{name='chenxin'}
Product.destroy
Product.myDestory

Detailed analysis:

The operation of the destruction method is only applicable to the method with scope="singleton". If it is prototype, it does not have any effect; because the object is created every time, I don't know which one you need to destroy, so I understand it as a Spring factory. I won’t destroy it for you.

In fact, the destruction operation is very rarely used in development, so you can understand it.

The object life cycle is summarized in the next picture:

1. First create a Spring factory, check whether your bean tag is a singleton object or non-singleton, and then start calling the object's construction method according to different timing (whether it is lazy), and reflect to create the object

2. Perform DI operation and attribute injection after creation

3. Then start the call of the initialization method

4. Close the factory and call the destroy method

5. Configuration file parameterization

What is configuration file parameterization? Refers to the transfer of string information that needs to be frequently modified in the Spring configuration file to a smaller configuration file!

Let me ask the question first:

Are there strings that need to be modified frequently in the Spring configuration file?

Of course there are, such as database connection-related parameters. In the early days of Spring, the configuration in applicationContext.xml was as long as several thousand lines. If there is a certain configuration of the database, a certain piece of code in it, for developers, is it right? Is it easy to know this situation?

Okay, if you say that you don’t know who else knows about development, then I think you have less business contact, and the structure is small. If this is a product for the customer, the customer needs to modify a certain configuration, you let him go Look for these configuration files, find a specific configuration, and say I want to change the database configuration information. Where is it?

Or leave it to O&M to modify it. O&M doesn’t understand Spring. Once he makes a mistake, who is the pitfall, or does it not develop? So can you transfer this modified string to a small configuration file. In this case, is it easy to modify and maintain?

 

So for the sake of convenience, we will pull out a small configuration file (db.properties) to store this configuration information separately. You can put this file as you like. I put it here under Resources

jdbc.driverClassName = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/users?useSSL=false
jdbc.username = root
jdbc.password = root

Then you said how can Spring find your configuration file?

We have to write such a paragraph in xml

<context:property-placeholder location="classpath:/db.properties"></context:property-placeholder>
<context:property-placeholder location="classpath:/db.properties"></context:property-placeholder>

    <bean id="conn" class="com.chenxin.spring5.factorybean.ConnectionFactoryBean">
        <property name="driverName" value="${jdbc.driverClassName}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

This completes the configuration file parameterization operation! If we want to change the related configuration parameters later, we don't need to change it in the Spring configuration file, right?

6. Type converter

We have talked about property injection through the Spring configuration file. For the value of Integer property, why can we inject into the property of Integer by injecting the <value></value> tag of the string?

In fact, Spring helped us complete the type conversion at the bottom layer, which is actually Spring's type converter (Converter interface). Because multiple types of conversion are involved, the interface is defined to shield this difference.

So when Spring gives a string to Integer, it is done through a type converter called StringToNumber

6.1, custom type converter

When Spring does not provide a specific type converter, and the programmer needs to use it in the application process, then the programmer needs to define the type converter.

Let's try to define a Person class with an attribute of Date type

public class Person implements Serializable {

    private String name;

    private Date birthday;

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

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", birthday=" + birthday +
                '}';
    }
}

xml placement

 <bean id="person" class="com.chenxin.spring5.converter.Person">
        <property name="name" value="chenxin"></property>
        <property name="birthday" value="2020-04-05"></property>
    </bean>

 Test category:

    @Test
    public void test4(){
        ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
        Person person = (Person) ctx.getBean("person");
        System.out.println("person = " + person);
    }

An error was reported Failed to convert property value of type'java.lang.String' to required type'java.util.Date' for property'birthday', indicating that Spring cannot help us convert the string to Date type, which Spring did not provide. because the format, different date formats in different countries, so that Spring does not turn up, this time we will custom type converter of

 

Development steps:

  • The class implements the Converter interface
public class MyDateConverter implements Converter<String, Date> {
    
    public Date convert(String s) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Date date = null;
        try {
            date = simpleDateFormat.parse(s);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }
}

The code is finished, but how do you tell Spring to find the type converter you defined yourself? Spring must first hand over the converter object you define to Spring for management, create it, and then this converter tells Spring that this is not an ordinary object, this is a converter, you help me register it in yours and I will use it To convert

  • Created by Spring management
<bean id="myDateConverter" class="com.chenxin.spring5.converter.MyDateConverter">

    </bean>
  • Tell Spring, you need to help me register in your converter, the converter type is a Set collection, and is a custom object, you need to use the ref tag
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <ref bean="myDateConverter"></ref>
            </set>
        </property>
    </bean>

The id and conversionService are fixed and cannot be written casually; look at the properties in this ConversionServiceFactoryBean, and understand why it is the set tag, right?

This can help us convert, output: person = Person{name='chenxin', birthday=Sun Apr 05 00:00:00 CST 2020}

detail:

1. ConversionSeviceFactoryBean defines the id attribute value must be conversionService

2. Spring framework built-in date type converter, date format: 2020/05/01 (not supported: 2020-05-01)

7. Post-processing Bean

The full name of post-processing Bean is BeanPostProcessor. This course will first enter the door, and then we will explain in depth when we talk about aop later.

Function: Reprocess the objects created by the Spring factory

Let's look at a picture analysis:

1. For a User user, after Spring creates the factory, it scans the bean id as user, and then gets the full path of this class, and then starts reflection to get the construction method and create the object

2. After the object is created, the injection is complete , and then Spring leaves a hole for you to process this object. The parameter Object bean represents the newly created object User, and the id value will be given to beanName. After you finish processing , Returned your processed object

3. Spring gets your processed object, and then initializes it

4. After the initialization is complete, leave a hole for you, you can process it again, and then return it to Spring to form a Bean

The programmer implements the methods in the BeanPostProcessor specified interface:

1. Object postProcessBeforeInitiallization(Object bean String beanName) Function: After Spring has created an object, and annotated it , you can run the Before method to add it to obtain the object created by Spring: the parameters of the method are finally passed through the return value Hand it over to the Spring framework

2. Object postProcessAfterInitiallization(Object bean String beanName) Function: After Spring completes the initialization of the object, it can run the After method to add it to obtain the object created by Spring: the parameters of the method are finally passed through the return value. Give to the Spring framework 

In actual combat, Spring initialization operations are rarely handled: there is no need to distinguish Before After. You only need to implement one of the After methods.

 

Development steps:

1. The class implements BeanPostProcessor, I modified person.name=modify chexin in the processor

It must be noted here that for BeanPostProcessor, BeanPostProcessor will add all the objects created in Spring! ! ! ! So you have to judge whether it is an object you want to modify

public class MyBeanPostProcessor implements BeanPostProcessor {

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if(bean instanceof Person){
            Person person = (Person) bean;
            person.setName("modify chenxin!");
        }
        return bean;
    }
}

2. The configuration file set name=chenxin, let’s see if this post processor helps us modify the name after initialization?

  <bean id="person" class="com.chenxin.spring5.converter.Person">
        <property name="name" value="chenxin"></property>
        <property name="birthday" value="2020-04-05"></property>
    </bean>

    <bean id="myBeanPostProcessor" class="com.chenxin.spring5.postprocessor.MyBeanPostProcessor">

The result can be a try, it must have been modified to modify chenxin!

Must pay attention to details

1. BeanPostProcessor will add all objects created in Spring! ! ! ! So you have to judge whether it is an object you want to modify

2. Why not operate BeforeInitialization? There is something called drumming and passing flowers. You know, this process must be the process of each object from beginning to end. Then, what you gave me before, I must return to you later, so we will do it here. return bean, it’s just that I didn’t write it in Before in this case

If you are interested, you can try it, normal development, only need to implement After!

Well, we are here for this section, and the next section starts, oriented to the Aop chapter! ! ! !

 

 

 

 

 

Guess you like

Origin blog.csdn.net/qq_31821733/article/details/114108469