Java SSM Notes (1) Reset Version

image-20221008182958877

Spring core technology

**Prerequisite course requirements:** Please complete the "JavaWeb" and "Java 9-17 New Features" video tutorials before watching this tutorial.

**Recommendation:** For students who are not very familiar with Java development, it is best to spend half a month to a month writing a large number of small projects. It is not recommended to learn it all in one go. The following content is almost the same as the previous content. It is a dimensionality reduction attack. It is easy to forget the basic knowledge learned before after learning it in one go, especially the content in the JavaSE stage.

Different from the 2021 version of the SSM tutorial, this video is a reprinted version. The Spring framework version studied in the video is: 6.0

Congratulations to everyone for successfully entering the SSM (Spring+SpringMVC+Mybatis) stage of learning. You can also successfully leave the Java Newbie Village. Since we have already studied Mybatis before, the time schedule for this tutorial will be shorter than before. . From here on, it will be a little more difficult to understand many concepts. Because you have not been exposed to enterprise development scenarios, it is difficult to realize the benefits of that kind of thinking. Even in the later stage, almost all the concepts you come into contact with are based on cloud computing and The framework for realizing big data theory (the most popular and cutting-edge technology at the moment) is gradually no longer related to computer basics, but to how to work efficiently.

In the JavaWeb stage, we have learned how to use Java for Web application development. We now have the ability to build Web websites. However, during the development process, we found that there are many inconveniences. In the final actual programming of the library management system , we found that although we have a clear idea and know how to write the corresponding interface, such development efficiency is really too slow, and there are many deficiencies in the management of object creation. Therefore, we have to continue Learn more framework technologies to simplify and standardize our Java development.

Spring is such a framework (document: https://docs.spring.io/spring-framework/docs/6.0.4/reference/html/core.html#spring-core), which is designed to simplify development. It is a lightweight IoC and AOP container framework. It is mainly a lightweight container that manages the life cycle of beans , and its ecology has developed extremely large. So, first of all, what are IoC and AOP , and what are Beans ? Don’t be afraid, these concepts just sound full of high-end, but are not actually that advanced (this is true of many things, the names sound awesome, but in fact they are just something that is easy to understand)

IoC container basics

The core of the Spring framework is actually its IoC container, which is the first stop for us to start learning Spring.

Introduction to IoC theory

In our previous library management system web application, we found that the entire program actually relied on various parts to cooperate with each other to complete an operation. For example, if you want to display a list of borrowing information, you first need to use Servlet to process the request and response data. Then all the requested data is handed over to the corresponding Service (business layer) for processing. When the Service finds that it needs to obtain data from the database, it initiates a request to the corresponding Mapper.

They are like gears connected together, and no one can live without the other:

img

Just like a team, everyone has a clear division of labor, and a set of operations on the assembly line must be interlocked. This is a highly coupled system.

Although the logic of such a system is very clear and the entire process can be understood quickly, there is a very serious problem. Our current era is actually an era of high-speed iteration of software projects. We find that many apps come and go every other day. As long as it is updated, and which function is the most popular at the moment, we will follow up the development non-stop. Therefore, it is easy to happen that the previously written code and implemented functions need to be overturned and changed into new functions. Then we have to It is not necessary to modify some modules on the pipeline, but such a modification will directly lead to a large-scale update of the reference relationships of the entire pipeline.

For example, the following situation:

class A{
   
    
    
    private List<B> list;
    public B test(B b){
   
    
    
        return null;
    }
}

class C{
   
    
    
    public C(B b){
   
    
    }
}

class B{
   
    
     }

It can be seen that A and C directly use B in large quantities, but one day, the implementation of B is outdated. At this time, D comes to implement the function better. We need to use this new class to complete the business. Got:

image-20221122135859871

As you can see, because the correlation between classes is too strong, errors will begin to appear on a large scale. All classes that used B before have to be modified one by one, and all of them will be changed to D. This is simply a disaster!

Including the actual projects we wrote in the JavaWeb stage before, if we don’t want to use a certain Service implementation class, and I want to use other implementation classes to perform these functions with different logic, then at this time, we can only do it for each class one by one. Modification, when the project is particularly large, the associated modifications caused by just changing the class name are enough for you to modify for a day.

Therefore, the shortcomings caused by high coupling are obvious and are also a fatal problem in modern software development. If we want to improve this situation, we can only decouple each module so that the dependence between modules is no longer so strong. In other words, the implementation class of Service is no longer decided by us, but is decided by the program itself. All implementation class objects are managed by the program, and the relationship between all objects is also dynamically determined by the program. In this way The IoC theory was introduced.

IOC is the abbreviation of Inversion of Control, which translates as: "Inversion of Control". It decomposes complex systems into cooperating objects. After these object classes are encapsulated, the internal implementation is transparent to the outside, thereby reducing the complexity of solving problems. , and can be flexibly reused and extended.

img

We can hand over the object to the IoC container for management. For example, when we need the implementation of an interface, it will decide which implementation class to give us based on the configuration file. In this way, we no longer need to worry about which one we want to use. We only need to care about the implementation class. What we are given must be an implementation class that can be used normally. If it can be used, that's it. Anyway, I only need to adjust the interface definition. In this way, we can safely let one person go. If you write the code of the view layer and one person writes the code of the business layer, the development efficiency will be very high.

It’s still the same code as before, but with the support of the IoC container:

public static void main(String[] args) {
   
    
    
		A a = new A();
  	a.test(IoC.getBean(Service.class));   //瞎编的一个容器类,但是是那个意思
  	//比如现在在IoC容器中管理的Service的实现是B,那么我们从里面拿到的Service实现就是B
}

class A{
   
    
    
    private List<Service> list;   //一律使用Service,具体实现由IoC容器提供
    public Service test(Service b){
   
    
    
        return null;
    }
}

interface Service{
   
    
     }   //使用Service做一个顶层抽象

class B implements Service{
   
    
    }  //B依然是具体实现类,并交给IoC容器管理

When the specific implementation class is modified, we also only need to hand over the new implementation class to the IoC container management, so that we do not need to modify any previous code:

interface Service{
   
    
     }

class D implements Service{
   
    
    }   //现在实现类变成了D,但是之前的代码并不会报错

In this way, even if our underlying implementation class is modified, it will not cause errors in the classes associated with it. Instead, large-scale modifications will be made. By defining abstraction + container management, we can dissolve the original strong association.

High cohesion and low coupling are the design goals of modern software development, and the Spring framework provides us with such an IoC container for object management. An object is instantiated, assembled and managed by the Spring IoC container. We Call it a Bean.

The first Spring project

First of all, it must be clear that the primary purpose of using Spring is to decouple software projects, not to simplify the code! Through it, we can better manage our beans. In this part, we will experience the basic use of Spring.

Spring is not an independent framework, it actually contains many modules:

image-20221121233807593

The first thing we have to learn is Core Container, which is the core container module. Only by understanding the core technology of Spring can we truly understand the convenience that this framework brings to us.

Spring is a non-intrusive framework, just like a tool library. It can be easily added to our existing projects. Therefore, we only need to directly import its dependencies to use it. The Maven dependencies of the Spring core framework coordinate:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>6.0.4</version>
</dependency>

**Note:** Different from the old version of the tutorial, Spring 6 requires you to use a Java version of 1710 or above, including when we learn SpringMvc later, the Tomcat version must be 10 or above. This dependency contains the following dependencies:

image-20221122133820198

What appears here are Spring core-related content, such as Beans, Core, Context, SpEL and the very critical AOP framework. In this chapter, we will explain them all.

If the following warning appears when using the Spring framework:

12月 17, 2022 3:26:26 下午 org.springframework.core.LocalVariableTableParameterNameDiscoverer inspectClass
警告: Using deprecated '-debug' fallback for parameter name resolution. Compile the affected code with '-parameters' instead or avoid its introspection: XXXX

-parametersThis is because LocalVariableTableParameterNameDiscoverer has been marked as obsolete in Spring 6.0.1 version and will be removed soon. Please add compilation parameters for the compilation plug-in in the Maven configuration file :

<build>
 <pluginManagement>
     <plugins>
         <plugin>
             <artifactId>maven-compiler-plugin</artifactId>
             <version>3.10.1</version>
             <configuration>
                 <compilerArgs>
                     <arg>-parameters</arg>
                 </compilerArgs>
             </configuration>
         </plugin>
     </plugins>
 </pluginManagement>
</build>

If you don't have this problem, please ignore this part.

Here we will try to write the simplest Spring project. As we have said before, Spring will provide us with an IoC container to manage beans, but we must first write a configuration file for this container. We can pass the configuration file Tell the container which beans need to be managed and the properties, dependencies, etc. of the beans.

First we need to create a Spring configuration file in the resource (the file created in the resource will be placed in the classpath during compilation), named test.xml, and you can create it by right-clicking:

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

At this time, IDEA will prompt us that we have not configured an application context for this file. Here we only need to specify it as the current project. Of course, configuring this is just for quick viewing of code prompts and dependencies. If we do not configure it, it will not affect anything. The program Still works fine:

image-20221121234427739

Here we just confirm according to the default configuration points:

image-20221121234447213

Spring provides us with an IoC container to store the objects we need to use. We can hand over the objects to the IoC container for management. When we need to use the objects, we can ask the IoC container for it and let it decide. Which object is given to us. If we need to use the IoC container provided by Spring, we need to create an application context, which represents the IoC container, which will be responsible for instantiating, configuring and assembling beans:

public static void main(String[] args) {
   
    
    
  	//ApplicationContext是应用程序上下文的顶层接口,它有很多种实现,这里我们先介绍第一种
  	//因为这里使用的是XML配置文件,所以说我们就使用 ClassPathXmlApplicationContext 这个实现类
    ApplicationContext context = new ClassPathXmlApplicationContext("test.xml");  //这里写上刚刚的名字
}

For example, now we want the IoC container to help us manage a Student object (Bean), and apply for it when we need this object. Then we need to do this. First, define the Student class:

package com.test.bean;

public class Student {
   
    
    
    public void hello(){
   
    
    
        System.out.println("Hello World!");
    }
}

Since we now want others to help manage the object, we can no longer create the new object ourselves, but write the corresponding configuration. We open the test.xmlfile we just created for editing and add:

<bean name="student" class="com.test.bean.Student"/>

Here we have written the corresponding Bean information in the configuration file, and then the container will process it according to the configuration here.

Now, this object does not need to be created by us anymore, but is automatically created and provided by the IoC container. We can directly get the object it created for us from the context:

public static void main(String[] args) {
   
    
    
    ApplicationContext context = new ClassPathXmlApplicationContext("test.xml");
    Student student = (Student) context.getBean("student");   //使用getBean方法来获取对应的对象(Bean)
    student.hello();
}

In fact, the Student object obtained here is created by Spring through the reflection mechanism. Beginners will be very confused as to why the object is created in this way. Wouldn't it be nice if we just create a new one? Why leave it to IoC container management? In the following study, we will slowly understand it.

image-20221122153946251

Bean registration and configuration

Previously, we used a simple example to experience how to use Spring to manage our objects and request the managed objects from the IoC container. In this lesson, we will learn in detail how to register a Bean with Spring and the related configuration of the Bean.

In fact, we can have many configuration files, and these configuration files can be imported into each other:

<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
    <import resource="test.xml"/>
</beans>

But for the sake of simplicity, we will start with a single configuration file. First we need to know how to configure and register the Bean.

To configure a bean, just add:

<bean/>

But if written like this, Spring cannot know which class the Bean we want to configure is, so we have to specify the corresponding class:

<bean class="com.test.bean.Student"/>

image-20221122164149924

You can see that the Bean icon appears next to the class, indicating that our Bean has been successfully registered. In this way, we can ask the container for the Bean instance object according to the type:

public static void main(String[] args) {
   
    
    
    ApplicationContext context = new ClassPathXmlApplicationContext("test.xml");
  	//getBean有多种形式,其中第一种就是根据类型获取对应的Bean
  	//容器中只要注册了对应类的Bean或是对应类型子类的Bean,都可以获取到
    Student student = context.getBean(Student.class);
    student.hello();
}

However, in some cases, there may be ambiguity in the acquisition of Beans. We can register Beans of two subclasses separately:

public class ArtStudent extends Student{
   
    
    
  	public void art(){
   
    
    
        System.out.println("我爱画画");
    }
}
public class SportStudent extends Student{
   
    
    
		public void sport(){
   
    
    
        System.out.println("我爱运动");
    }
}
<bean class="com.test.bean.ArtStudent"/>
<bean class="com.test.bean.SportStudent"/>

But at this time when we get the beans, we ask for their parent class:

Student student = context.getBean(Student.class);
student.hello();

When running, I get the following error:

image-20221122164100561

There is an exception that the Bean definition is not unique. Obviously, the type we need is Student, but there are two Bean definitions that meet this type. They are both subclasses of Student. At this time, the IoC container does not know what to give us. Which Bean is returned, so you can only throw an exception.

Therefore, if we need a bean and retrieve it using a type, the type must be specified and there can be no ambiguity:

ArtStudent student = context.getBean(ArtStudent.class);
student.art();

What if the two Bean types are the same?

<bean class="com.test.bean.Student"/>
<bean class="com.test.bean.Student"/>

In this case, Class cannot be used to distinguish. In addition to specifying the corresponding type for the Bean, we can also specify a name for the Bean for differentiation:

<bean name="art" class="com.test.bean.ArtStudent"/>
<bean name="sport" class="com.test.bean.SportStudent"/>

The name attribute is to set a unique name for this Bean (the id attribute can also be used, which has the same function as name, but it will check whether the naming is standardized, otherwise a yellow mark will be displayed). Different Bean names cannot be the same, otherwise an error will be reported:

<bean name="a" class="com.test.bean.Student"/>
<bean name="b" class="com.test.bean.Student"/>

In this way, we can distinguish these two Beans:

Student student = (Student) context.getBean("a");
student.hello();

Although the current two Bean definitions are exactly the same and there is no difference, they are indeed two different Beans, just of the same type. Later, we can also set different other properties for these two Beans.

We can give the Bean a name or an alias. In addition to having a name, we may also have our own nickname at home:

<bean name="a" class="com.test.bean.Student"/>
<alias name="a" alias="test"/>

In this way, we can also get the corresponding Bean using aliases:

Student student = (Student) context.getBean("test");
student.hello();

So now there is a new question. Is there only one Bean created by the IoC container, or will it give us a new object every time we ask for it? We now obtain the Bean object twice in a row in the main method:

Student student1 = context.getBean(Student.class);
Student student2 = context.getBean(Student.class);
System.out.println(student1 == student2);   //默认为单例模式,对象始终为同一个

We found that the final result is true, which means that the object obtained from the IoC container is always the same. By default, the beans managed through the IoC container are singleton mode. This object will only is created once.

If we want the object we get every time to be a new one, we can also modify its scope:

image-20221122175719997

There are two kinds of scopes here. The first one is singletonthis one by default. Of course, there is also prototypethe prototype mode (it is also called multiple instance mode for convenience). This mode gets an object every time. new:

Student student1 = context.getBean(Student.class);  //原型模式下,对象不再始终是同一个了
Student student2 = context.getBean(Student.class);
System.out.println(student1 == student2);

In fact, when the scope of the Bean is singleton mode, it will be created at the beginning (when the container loads the configuration), and all we get later is this object. In prototype mode, it will only be created when it is obtained. In other words, in singleton mode, the Bean will be stored by the IoC container. As long as the container is not destroyed, the object will always exist, and the prototype mode is quite Because if you directly new an object when you want to use it, it will not be saved.

Of course, if we want the Bean in singleton mode to not be loaded at the beginning, but to be loaded again when needed (it will still be stored by the container after loading, and this object will be used from now on, and no new one will be created) we You can also enable lazy loading:

<bean class="com.test.bean.Student" lazy-init="true"/>

After lazy loading is turned on, the object will only be created when it is actually used for the first time.

Because beans are loaded by the IoC container in singleton mode, but we don’t know the loading order. If we need to maintain the loading order of beans (for example, a bean must be created before another bean), then we can depends-onuse Set the loaded Bean, so that the dependent Bean will be loaded before, for example, Teacher should be loaded before Student:

<bean name="teacher" class="com.test.bean.Teacher"/>
<bean name="student" class="com.test.bean.Student" depends-on="teacher"/>

This ensures the loading order of beans.

dependency injection

Dependency Injection (DI) is a design pattern and one of the core concepts of the Spring framework. Now we have understood how to register and use a Bean, but this is not enough. Remember what we said at the beginning, eliminate the strong association between classes? For example, there is now a teacher interface:

public interface Teacher {
   
    
    
    void teach();
}

There are two specific implementations:

public class ArtTeacher implements Teacher{
   
    
    
    @Override
    public void teach() {
   
    
    
        System.out.println("我是美术老师,我教你画画!");
    }
}
public class ProgramTeacher implements Teacher{
   
    
    
    @Override
    public void teach() {
   
    
    
        System.out.println("我是编程老师,我教你学Golang!");
    }
}

Our students first have a teacher to teach them, such as an art teacher:

public class Student {
   
    
    
    private Teacher teacher = new ArtTeacher();   
  	//在以前,如果我们需要制定哪个老师教我们,直接new创建对应的对象就可以了
    public void study(){
   
    
    
        teacher.teach();
    }
}

But we found that if the art teacher no longer teaches, and now another teacher comes to teach the students, then we need to modify the definition of the Student class:

public class Student {
   
    
    
    private Teacher teacher = new ProgramTeacher();
  	...

You can imagine that if various classes emerge now and need to use Teacher in this way, then once the implementation of Teacher changes, it will cause us to modify the classes that previously used Teacher one by one, which is very uncomfortable.

With dependency injection, the Teacher member variable in Student can be assigned by the IoC container to select a suitable Teacher object. In other words, when the IoC container creates an object, it needs to inject our pre-given attributes into the object. , very simple, we can use propertytags to achieve it, we expand the bean tag:

<bean name="teacher" class="com.test.bean.ProgramTeacher"/>
<bean name="student" class="com.test.bean.Student">
    <property name="teacher" ref="teacher"/>
</bean>

At the same time, we also need to modify the Student class. Dependency injection requires that the corresponding attribute must have a set method:

public class Student {
   
    
    
    private Teacher teacher;
  	//要使用依赖注入,我们必须提供一个set方法(无论成员变量的访问权限是什么)命名规则依然是驼峰命名法
    public void setTeacher(Teacher teacher) {
   
    
    
        this.teacher = teacher;
    }
    ...

image-20221122191025279

Use it propertyto specify the value or a Bean that needs to be injected. Here we choose ProgramTeacher. Then when used, what is obtained in the Student class is the object of this Bean:

Student student = context.getBean(Student.class);
student.study();

image-20221122191109690

As you can see, in our Java code now, no specific implementation class information appears (ArtTeacher and ProgramTeacher do not appear). Instead, there is a bunch of xml configurations. In this way, even if we switch the implementation of the teacher to another class, There is no need to adjust the code, just change the bean type:

<!--  只需要修改这里的class即可,现在改为ArtTeacher  -->
<bean name="teacher" class="com.test.bean.ArtTeacher"/>
<bean name="student" class="com.test.bean.Student">
    <property name="teacher" ref="teacher"/>
</bean>

In this way, the Bean class becomes a new type, and we do not need to adjust the code in other locations and start the program again:

image-20221122191427776

Through dependency injection, have you begun to gradually feel the convenience that Spring brings to us? Of course, dependency injection does not necessarily have to inject other beans, it can also be a simple value:

<bean name="student" class="com.test.bean.Student">
    <property name="name" value="卢本伟"/>
</bean>

Direct use valuecan directly pass in a specific value.

In fact, in many cases, some parameters in the class are initialized in the constructor rather than after creation, such as:

public class Student {
   
    
    
    private final Teacher teacher;   //构造方法中完成,所以说是一个final变量

    public Student(Teacher teacher){
   
    
       //Teacher属性是在构造方法中完成的初始化
        this.teacher = teacher;
    }
  	...

As we said before, Beans are actually created by the IoC container, but now we have modified the default no-parameter structure, and you can see that an error is reported in the configuration file:

image-20221122174328107

Obviously, because we modified the constructor, the IoC container will only call the parameterless constructor by default. Therefore, we need to specify a usable constructor. We expand the bean label and add a label constructor-arg:

<bean name="teacher" class="com.test.bean.ArtTeacher"/>
<bean name="student" class="com.test.bean.Student">
    <constructor-arg name="teacher" ref="teacher"/>
</bean>

What is here constructor-argis a parameter of the construction method. You can write many parameters for this parameter, and it will automatically match the construction method that matches the number of parameters inside. What is matched here is the construction method we just wrote that requires one parameter.

image-20221122191427776

In this way, we can also implement dependency injection, but now we advance the timing of dependency injection to object construction.

What if this happens? Now our Student class is defined like this:

public class Student {
   
    
    
    private final String name;
    public Student(String name){
   
    
    
        System.out.println("我是一号构造方法");
        this.name = name;
    }

    public Student(int age){
   
    
    
        System.out.println("我是二号构造方法");
        this.name = String.valueOf(age);
    }
}

At this time, we want to use the No. 2 construction method, so how can we specify it? There are 2 ways we can add types to tags:

<constructor-arg value="1" type="int"/>

It can also be specified as the corresponding parameter name:

<constructor-arg value="1" name="age"/>

Anyway, as long as we can ensure that the parameters we specify match the target construction method.

Now a special type appears in our class, which is a collection type:

public class Student {
   
    
    
    private List<String> list;

    public void setList(List<String> list) {
   
    
    
        this.list = list;
    }
}

There is special support for this collection type:

<bean name="student" class="com.test.bean.Student">
  	<!--  对于集合类型,我们可以直接使用标签编辑集合的默认值  -->
    <property name="list">
        <list>
            <value>AAA</value>
            <value>BBB</value>
            <value>CCC</value>
        </list>
    </property>
</bean>

Not only List, but also commonly used collection classes such as Map and Set, including arrays, support writing in this way. For example, the Map type can also be injected using entry:

<bean name="student" class="com.test.bean.Student">
    <property name="map">
        <map>
            <entry key="语文" value="100.0"/>
            <entry key="数学" value="80.0"/>
            <entry key="英语" value="92.5"/>
        </map>
    </property>
</bean>

So far, we have completed the study of two types of dependency injection:

  • Setter dependency injection: Injection is completed through the set method corresponding to the member attribute.
  • Constructor dependency injection: Injection is completed through the constructor.

automatic assembly

Previously, if we needed to use dependency injection, we needed to propertyconfigure the parameters:

<bean name="student" class="com.test.bean.Student">
    <property name="teacher" ref="teacher"/>
</bean>

But sometimes for convenience, we can also turn on automatic assembly. Autowiring is to let the IoC container find the values ​​that need to be filled in by itself. We only need to provide the set method. Here we need to add the autowire attribute:

<bean name="student" class="com.test.bean.Student" autowire="byType"/>

autowireThe attribute has two common values, one is byName, and the other is byType. As the name suggests, one is to find the appropriate Bean for automatic assembly based on the type, and the other is to find it based on the name, so we do not need to specify it explicitly property.

image-20221122221936559

At this time, an automatic assembly icon will appear next to the set method, and the effect is the same as above.

For dependency injection completed using the constructor method, automatic assembly is also supported. We only need to modify autowire to:

<bean name="student" class="com.test.bean.Student" autowire="constructor"/>

In this way, we only need to provide a constructor with corresponding parameters (in this case, byType is also looking for it by default):

image-20221122230320004

This can also complete automatic injection:

image-20221122191427776

Although automated things save trouble, they are too mechanical. Sometimes, automatic assembly may encounter some problems, such as the following situations:

image-20221122223048820

At this time, because autowirethe rule is byType, there are two candidate Beans, but we actually hope that the ProgramTeacher Bean will not participate in automatic assembly under any circumstances. At this time, we can turn off its automatic assembly candidates:

<bean name="teacher" class="com.test.bean.ArtTeacher"/>
<bean name="teacher2" class="com.test.bean.ProgramTeacher" autowire-candidate="false"/>
<bean name="student" class="com.test.bean.Student" autowire="byType"/>

When autowire-candidatefalse is set, this Bean will no longer be a candidate Bean for automatic assembly. At this time, there is only one unique Bean left for automatic assembly, the error disappears, and the program can run normally.

In addition to this method, we can also set the primary attribute to indicate that this Bean is the main Bean. When ambiguity occurs, it will be selected first:

<bean name="teacher" class="com.test.bean.ArtTeacher" primary="true"/>
<bean name="teacher2" class="com.test.bean.ProgramTeacher"/>
<bean name="student" class="com.test.bean.Student" autowire="byType"/>

The program written in this way can still run normally, and the ArtTeacher is selected (I just don’t know why IDEA has a red mark, BUG?)

Lifecycle and inheritance

In addition to modifying the constructor method, we can also specify the initialization method and destruction method for the Bean to perform some other tasks when the object is created and destroyed:

public void init(){
   
    
    
    System.out.println("我是对象初始化时要做的事情!");    
}

public void destroy(){
   
    
    
    System.out.println("我是对象销毁时要做的事情!");
}

We can specify by init-methodand :destroy-method

<bean name="student" class="com.test.bean.Student" init-method="init" destroy-method="destroy"/>

So when is it initialized and when is it destroyed?

//当容器创建时,默认情况下Bean都是单例的,那么都会在一开始就加载好,对象构造完成后,会执行init-method
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("test.xml");
//我们可以调用close方法关闭容器,此时容器内存放的Bean也会被一起销毁,会执行destroy-method
context.close();

So, the final result is:

image-20221123132604262

Note that if the Bean is not in singleton mode, but in prototype mode, it will only be created when it is obtained, and the init-method will be called, and the corresponding destruction method will not be called (therefore, for Beans in prototype mode , Spring cannot take into account its complete life cycle, and in singleton mode, Spring can manage from the creation of the Bean object to the destruction of the object) The original text of the official document is as follows:

In contrast to the other scopes, Spring does not manage the complete lifecycle of a prototype bean. The container instantiates, configures, and otherwise assembles a prototype object and hands it to the client, with no further record of that prototype instance. Thus, although initialization lifecycle callback methods are called on all objects regardless of scope, in the case of prototypes, configured destruction lifecycle callbacks are not called. The client code must clean up prototype-scoped objects and release expensive resources that the prototype beans hold. To get the Spring container to release resources held by prototype-scoped beans, try using a custom bean post-processor, which holds a reference to beans that need to be cleaned up.

Beans also have inheritance relationships, but the inheritance here is not the inheritance of classes, but the inheritance of attributes, such as:

public class SportStudent {
   
    
    
    private String name;

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

At this point, we first register ArtStudent as a Bean:

<bean name="artStudent" class="com.test.bean.ArtStudent">
    <property name="name" value="小明"/>
</bean>

Here we will inject an initial value of name. At this time we have created a SportStudent Bean. We hope that the properties of this Bean are the same as the properties of the Bean we just created. Then we can write an identical one:

<bean class="com.test.bean.SportStudent">
    <property name="name" value="小明"/>
</bean>

But if there are too many attributes, will it be a bit troublesome to write? In this case, we can configure the inheritance relationship between beans. We can let the SportStudent Bean directly inherit the properties configured by the ArtStudent Bean:

<bean class="com.test.bean.SportStudent" parent="artStudent"/>

In this way, the properties configured in the ArtStudent Bean will be directly inherited to the SportStudent Bean (note that all configured properties must also exist in the sub-Bean and can be injected, otherwise an error will occur). Of course, if something in the sub-class Some properties are special and can also be configured individually on the basis of inheritance:

<bean name="artStudent" class="com.test.bean.ArtStudent" abstract="true">
    <property name="name" value="小明"/>
    <property name="id" value="1"/>
</bean>
<bean class="com.test.bean.SportStudent" parent="artStudent">
    <property name="id" value="2"/>
</bean>

If we just want a certain Bean to be used only as a configuration template for other Beans to inherit and use, then we can configure it as abstract, so that the container will not create an object of this Bean:

<bean name="artStudent" class="com.test.bean.ArtStudent" abstract="true">
    <property name="name" value="小明"/>
</bean>
<bean class="com.test.bean.SportStudent" parent="artStudent"/>

Note that once declared as an abstract bean, its instantiated object cannot be obtained through the container.

image-20221123140409416

However, the frequency of use of Bean inheritance is not very high, just master it.

One last thing to mention here, we have already learned about various Bean configuration properties. If we want all beans in the entire context to adopt a certain configuration, we can make the default configuration in the outermost beans tag:

image-20221123141221259

In this way, even if the Bean is not configured with a certain attribute, as long as the default configuration is written in the outermost layer, it will also take effect unless the Bean configures itself to override the default configuration.

Factory pattern and factory beans

Earlier we introduced the Bean creation mechanism of the IoC container. By default, the container will call the constructor of the corresponding type of the Bean to create objects. However, at some times, we may not want the outside world to use the constructor of the class to complete object creation, such as in In the factory method design pattern (for details, please watch the video tutorial "Java Design Patterns"), we prefer that Spring not directly use the reflection mechanism to create Bean objects through the construction method, but use the reflection mechanism to first find the corresponding factory class, and then use the factory class To generate the required Bean objects:

public class Student {
   
    
    
    Student() {
   
    
    
        System.out.println("我被构造了");
    }
}
public class StudentFactory {
   
    
    
    public static Student getStudent(){
   
    
    
      	System.out.println("欢迎光临电子厂");
        return new Student();
    }
}

At this time, Student has a factory. Normally we need to use the factory to get the Student object. Now we hope that Spring will do the same. Instead of directly using reflection to create the constructor method, we can specify it through factory-method:

<bean class="com.test.bean.StudentFactory" factory-method="getStudent"/>

Note that the Bean type here needs to be filled in as the factory class of the Student class, and factory-method is added to specify the corresponding factory method. However, the last thing registered is the return type of the factory method, so it is still the Bean of Student:

image-20221123143302785

At this point we go to get it again, and what we get is also the object obtained through the factory method:

image-20221123143347376

There is a misunderstanding here. Don't think that we registered the StudentFactory Bean. The class is filled in as this class just to tell Spring where our factory method is. What is really registered is what the factory method provides.

It can be found that when we adopt the factory mode, we can no longer perform dependency injection and other operations on the Bean through the configuration file, but can only complete it in the factory method. This seems to run counter to Spring's design philosophy?

Of course, some factory classes may need to construct objects before they can be used. We can also directly register a factory class as a factory bean:

public class StudentFactory {
   
    
    
    public Student getStudent(){
   
    
    
        System.out.println("欢迎光临电子厂");
        return new Student();
    }
}

Now we need the StudentFactory object to get the Student. At this time, we can only register it as a Bean first:

<bean name="studentFactory" class="com.test.bean.StudentFactory"/>

Register the factory class as a Bean like this, we call it a factory bean, and then use factory-beanto specify the factory bean of the bean:

<bean factory-bean="studentFactory" factory-method="getStudent"/>

Note that after using factory-bean, you are no longer required to specify the class, we can use it directly:

image-20221123164134470

At this time, you can see that the factory method also has an icon. In this way, since the factory class is registered as a Bean, we can configure dependency injection and other content for the factory Bean in the configuration file.

There is also a very detailed operation here. If we want to get the beans provided by the factory bean, we can directly enter the name of the factory bean. This will not get the instance of the factory bean, but the instance of the bean produced by the factory bean:

Student bean = (Student) context.getBean("studentFactory");

Of course, if we need to get an instance of the factory class, we can add a symbol in front of the name &:

StudentFactory bean = (StudentFactory) context.getBean("&studentFactory");

Another small detail.

Develop using annotations

We have completed most of the configuration file learning before, but we found that using configuration files for configuration seems a bit too tiring, right? You can imagine that if our project is very large, the entire configuration file will be filled with Bean configurations and will continue to grow. Is there a more efficient way to omit configuration? Remember the very convenient thing we used in the JavaWeb stage? That's right, it's an annotation.

Since we are now going to use annotations for development, let's delete the previous xml configuration file and see how convenient it is to use annotations.

ApplicationContext context = new AnnotationConfigApplicationContext();

Now we use AnnotationConfigApplicationContext as the context implementation, which is annotated and configured.

Now that we are using annotations, we need to use classes to write configuration files. Before, if we wanted to write a configuration, we needed:

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

Now we just need to create a configuration class:

@Configuration
public class MainConfiguration {
   
    
    
}

The two are equivalent. Similarly, we will be prompted at the beginning that there is no configuration context:

image-20221123175555433

Just configure it as required here, same as above, this just affects

Guess you like

Origin blog.csdn.net/qq_25928447/article/details/129067303