Spring基于注解的配置bean的练习

1. 半注解配置bean

前面几篇博客主要练习了在Spring里面通过xml来配置bean的操作。本篇主要练习如何在Spring框架里面,使用注解来进行配置。

https://www.cnblogs.com/liwanliangblog/tag/spring

本节主要介绍和练习的是半注解配置,也就是同时包含了xml配置和注解配置的一种配置形式。

1.1 创建和初始化项目

开发环境在kvm部署的centos8虚拟机node99里面。

  • 操作系统:CentOS Linux release 8.4.2105
  • JDK:java version "17" 2021-09-14 LTS
  • maven:Apache Maven 3.8.2
  • Tomcat:apache-tomcat-8.5.70
  • spring:5.3.15
  • 开发工具:vscode

在node99里面,项目放置路径:/root/liwldev/java/web/spring

首先是命令行创建项目:

mvn archetype:generate -DgroupId=com.liwl.dev -DartifactId=SpringAnnotation -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
通过vscode远程连接node99以后,修改项目配置文件pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.liwl.dev</groupId>
  <artifactId>SpringAnnotation</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>SpringAnnotation</name>
  <url>http://maven.apache.org</url>

  <properties>
      <spring.version>5.3.15</spring.version>
  </properties>

  <dependencies>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${spring.version}</version>
    </dependency>
  
     <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
     </dependency>
      
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.0</version>
        <configuration>
          <source>17</source>
          <target>17</target>
          <encoding>UTF8</encoding>
        </configuration>
      </plugin>
    </plugins>
  </build>

</project>

创建资源配置目录:mkdir -p /root/liwldev/java/web/spring/SpringAnnotation/src/main/resources,在该目录下创建文件beans.xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd 
        http://www.springframework.org/schema/context 
        https://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 
        注册扫描器
    -->
    <context:component-scan base-package="com.liwl.dev"/>

</beans>

该配置文件标签<context:component-scan base-package="com.liwl.dev"/>,其功能就是指定Spring在启动时,扫描包com.liwl.dev

1.2 创建项目文件和测试

/root/liwldev/java/web/spring/SpringAnnotation/src/main/java/com/liwl/dev目录下创建Person.java和PersonImpl.java文件

Person.java

package com.liwl.dev;

public interface Person {
    
}

PersonImpl.java

package com.liwl.dev;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component(value = "liwl")
public class PersonImpl implements Person {

    @Value(value = "liwanliang")
    private String name;
    @Value(value = "30")
    private int age;


    public PersonImpl(){
        System.out.println("PersonImpl类对象无参构造方法调用");
    }
    
    public PersonImpl(String name,int age){
        System.out.println("PersonImpl类对象有参构造方法调用");
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "[PersonImpl]" + ",name:" + name + ",age:" + age;
    }
}

/root/liwldev/java/web/spring/SpringAnnotation/src/test/java/com/liwl/dev目录下创建MyTest.java

package com.liwl.dev;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        PersonImpl liwl = (PersonImpl) context.getBean("liwl");
        System.out.println(liwl);
        context.close();
    }
}

F5运行MyTest.java,结果如下:

PersonImpl类对象无参构造方法调用
[PersonImpl],name:liwanliang,age:30

总结:

  • 在beans.xml里面通过标签引入了注解,以及配置了Spring要扫描的包
  • 在PersonImpl.java里面通过注解@Compent(Value = "liwl")向Spring注册了id=liwl的bean
  • 在PersonImpl.java里面通过@Value注解,在基本数据类型String和int上注解了name和age的初始值
  • 在MyTest.java里面或者到了bean的值

1.3 引用类型的注解

/root/liwldev/java/web/spring/SpringAnnotation/src/main/java/com/liwl/dev目录下创建School.java和SchoolImpl.java

School.java

package com.liwl.dev;

public interface School {
    
}

SchoolImpl.java

package com.liwl.dev;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component(value="school")
public class SchoolImpl implements School {
    
    @Value(value = "jiangnan")
    private String name;
    @Value(value = "wuxi")
    private String address;

    public SchoolImpl(){
        System.out.println("SchoolImpl类对象无参构造方法调用");
    }

    public SchoolImpl(String name,String address){
        System.out.println("SchoolImpl类对象有参构造方法调用");
        this.name = name;
        this.address = address;
    }

    public String getName() {
        return name;
    }

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

    public String getAddress() {
        return address;
    }

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


    @Override
    public String toString() {
        return "[SchoolImpl]" + ",name:" + name + ",address:" + address;
    }

}

修改PersonImpl.java

package com.liwl.dev;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component(value = "liwl")
public class PersonImpl implements Person {

    @Value(value = "liwanliang")
    private String name;
    @Value(value = "30")
    private int age;
    @Autowired
    @Qualifier(value="school")
    private School school;


    public PersonImpl(){
        System.out.println("PersonImpl类对象无参构造方法调用");
    }
    
    public PersonImpl(String name,int age,School school){
        System.out.println("PersonImpl类对象有参构造方法调用");
        this.name = name;
        this.age = age;
        this.school = school;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }


    public School getSchool() {
        return school;
    }

    public void setSchool(School school) {
        this.school = school;
    }

    @Override
    public String toString() {
        return "[PersonImpl]" + ",name:" + name + ",age:" + age + ",school:" + school;
    }
}

F5运行MyTest.java,结果如下:

PersonImpl类对象无参构造方法调用
SchoolImpl类对象无参构造方法调用
[PersonImpl],name:liwanliang,age:30,school:[SchoolImpl],name:jiangnan,address:wuxi

总结:

  • SchoolImpl.java里面通过@Compent注解向Spring注册了bean,其id=school
  • PersonImpl.java里面通过@Autowired注解注注入了school,而@Qualifier表示按照name类注入,即autowire=byName

1.4 引入外部属性配置文件

修改beans.xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd 
        http://www.springframework.org/schema/context 
        https://www.springframework.org/schema/context/spring-context.xsd">
    
    <!-- 注册扫描器 -->
    <context:component-scan base-package="com.liwl.dev"/>
    <!-- 引入外部属性配置文件 -->
    <context:property-placeholder location="classpath:all.properties" file-encoding="utf-8"/>

</beans>

/root/liwldev/java/web/spring/SpringAnnotation/src/main/resources目录下,创建all.properties文件

liwl_name="liwanliang.property"
liwl_age=30
school_name="jiangnan.property"
school_address="wuxi.property"

F5运行MyTest.java,结果运行

PersonImpl类对象无参构造方法调用
SchoolImpl类对象无参构造方法调用
[PersonImpl],name:"liwanliang.property",age:30,school:[SchoolImpl],name:"jiangnan.property",address:"wuxi.property"

总结:

  • <context:property-placeholder location="classpath:all.properties" file-encoding="utf-8"/>标签定义了从属性配置文件all.properties加载属性
  • all.properties里面自定义的属性以Key-Value的形式,在java源码注解里面以@Value=(value="${Key}")的形式存在

此时我们再通过注解的方式引入外部属性配置文件。修改beans.xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd 
        http://www.springframework.org/schema/context 
        https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 注册扫描器 -->
    <context:component-scan base-package="com.liwl.dev"/>

</beans>

修改PersonImpl.java

package com.liwl.dev;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

@PropertySource(value = {"classpath:all.properties"},encoding = "utf-8")
@Component(value = "liwl")
public class PersonImpl implements Person {

    @Value(value = "${liwl_name}")
    private String name;
    @Value(value = "${liwl_age}")
    private int age;
    @Autowired
    @Qualifier(value = "school")
    private School school;


    public PersonImpl(){
        System.out.println("PersonImpl类对象无参构造方法调用");
    }
    
    public PersonImpl(String name,int age,School school){
        System.out.println("PersonImpl类对象有参构造方法调用");
        this.name = name;
        this.age = age;
        this.school = school;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }


    public School getSchool() {
        return school;
    }

    public void setSchool(School school) {
        this.school = school;
    }

    @Override
    public String toString() {
        return "[PersonImpl]" + ",name:" + name + ",age:" + age + ",school:" + school;
    }
}

F5运行MyTest.java,结果如下

PersonImpl类对象无参构造方法调用
SchoolImpl类对象无参构造方法调用
[PersonImpl],name:"liwanliang.property",age:30,school:[SchoolImpl],name:"jiangnan.property",address:"wuxi.property"

此时SchoolImpl实现类作为PersonImpl的依赖,并没有通过@PropertySource(value = {"classpath:all.properties"},encoding = "utf-8")进行外部属性配置文件的导入,看起来只需要被依赖的bean注解此项即可

注意:@Qualifier(value = "school")的使用

1.5 注解引入外部属性配置文件

修改beans.xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd 
        http://www.springframework.org/schema/context 
        https://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 
        注册扫描器
    -->
    <context:component-scan base-package="com.liwl.dev"/>

</beans>

修改PersonImpl.java

package com.liwl.dev;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@PropertySource(value = {"classpath:all.properties"},encoding = "utf-8")
@Component(value = "liwl")
public class PersonImpl implements Person {
    

    @Value(value = "${liwl_name}")
    private String name;
    @Value(value = "${liwl_age}")
    private int age;
    @Autowired
    @Qualifier(value = "school")
    private School school;

    public PersonImpl(){
        System.out.println("PersonImpl 无参构造方法调用");
    }

    public PersonImpl(String name,int age){
        this.name = name;
        this.age = age;
        System.out.println("PersonImpl 有参构造方法调用");
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public School getSchool() {
        return school;
    }

    public void setSchool(School school) {
        this.school = school;
    }


    @Override
    public String toString() {
        return "[PersonImpl]" + ",name:" + name + ",age:" + age + ",school:" + school;
    }
}

运行结果:

PersonImpl类对象无参构造方法调用
SchoolImpl类对象无参构造方法调用
[PersonImpl],name:"liwanliang.property",age:30,school:[SchoolImpl],name:"jiangnan.property",address:"wuxi.property"

2. 全注解配置bean

在第一节《半注解配置bean》中,beans.xml里面仅剩下<context:component-scan base-package="com.liwl.dev"/>一行配置。这一行配置可以通过创建配置类来代替xml配置,从而彻底脱离xml配置。

本节主要介绍使用全注解方式代替xml来配置bean。

注:本节重新部署了环境,代码有变动
源码目录:/root/liwldev/java/web/springframe/SpringAnnotation/

源文件:PersonImpl.java换成了PersonImpl01.java

toString方法里面的写法也变了

2.1 @Configuration注解配置类

Person.java

package com.liwl.dev;

public interface Person {
    
}

PersonImpl01.java

package com.liwl.dev;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;


@PropertySource(value = {"classpath:all.properties"},encoding = "utf-8")
@Component(value = "liwl")
public class PersonImpl01 implements Person {
    

    @Value(value = "${liwl_name}")
    private String name;
    @Value(value = "${liwl_age}")
    private int age;
    @Autowired
    @Qualifier(value = "school")
    private School school;

    public PersonImpl01(){
        System.out.println("PersonImpl01 无参构造方法调用");
    }

    public PersonImpl01(String name,int age){
        this.name = name;
        this.age = age;
        System.out.println("PersonImpl01 有参构造方法调用");
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public School getSchool() {
        return school;
    }

    public void setSchool(School school) {
        this.school = school;
    }


    @Override
    public String toString() {
        return "PersonImpl01->" + "name:" + name + ",age:" + age + ",school:" + school;
    }
}

School.java

package com.liwl.dev;

public interface School {
    
}

SchoolImpl.java

package com.liwl.dev;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component(value = "school")
public class SchoolImpl implements School {

    @Value(value = "${school_name}")
    private String name;
    @Value(value = "${school_address}")
    private String address;

    public SchoolImpl(){
        System.out.println("SchoolImpl 无参构造方法调用");
    }

    public SchoolImpl(String name,String address){
        this.name = name;
        this.address = address;
        System.out.println("SchoolImpl 有参构造方法调用");
    }

    public String getName() {
        return name;
    }

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

    public String getAddress() {
        return address;
    }

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

    @Override
    public String toString() {
        return "SchoolImpl->" + "name:" + name + ",address:" + address;
    }
}

/root/liwldev/java/web/springframe/SpringAnnotation/src/main/java/com/liwl目录下,创建源文件SpringConfig.java

package com.liwl.dev;

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

@Configuration
@ComponentScan(value = "com.liwl.dev")
public class SpringConfig {
    public void showMe(){
        System.out.println("我是配置类的showMe方法");
    }
}

修改MyTest.java

package com.liwl.dev;

import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        SpringConfig config = context.getBean(SpringConfig.class);
        config.showMe();
        ((ConfigurableApplicationContext) context).close();
    }
}

F5运行,结果

PersonImpl01 无参构造方法调用
SchoolImpl 无参构造方法调用
我是配置类的showMe方法

上面结果说明,spring已经成功加载了配置类SpringConfig.java,并且运行了showMe方法

修改MyTest.java

package com.liwl.dev;

import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        PersonImpl01 liwl = (PersonImpl01) context.getBean("liwl");
        System.out.println(liwl);
        ((ConfigurableApplicationContext) context).close();
    }
}

F5运行结果

PersonImpl01 无参构造方法调用
SchoolImpl 无参构造方法调用
PersonImpl01->name:"liwl",age:30,school:SchoolImpl->name:"江南",address:"无锡"

2.1 @Bean注解配置bean

在目录liwldev/java/web/springframe/SpringAnnotation/src/main/java/com/liwl/dev/下创建PersonImpl02.java

package com.liwl.dev;

import org.springframework.beans.factory.annotation.Value;

public class PersonImpl02 implements Person {
   
    @Value(value = "liwl02")
    private String name;
    @Value(value = "30")
    private int age;

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "PersonImpl02->" + "name:" + name + ",age:" + age;
    }
}

修改SpringConfig.java

package com.liwl.dev;

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

@Configuration
@ComponentScan(value = "com.liwl.dev")
public class SpringConfig {
    public void showMe(){
        System.out.println("我是配置类的showMe方法");
    }
    
    @Bean
    public PersonImpl02 liwl02(){
        return new PersonImpl02();
    }
}

修改MyTest.java

package com.liwl.dev;

import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        System.out.println(context.getBean("liwl02"));
        ((ConfigurableApplicationContext) context).close();
    }
}

F5运行,结果

PersonImpl01 无参构造方法调用
SchoolImpl 无参构造方法调用
PersonImpl02->name:liwl02,age:30

修改SpringConfig.java

package com.liwl.dev;

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

@Configuration
@ComponentScan(value = "com.liwl.dev")
public class SpringConfig {
    public void showMe(){
        System.out.println("我是配置类的showMe方法");
    }

    @Bean("liwl001")
    public PersonImpl liwl(){
        return new PersonImpl();
    }

    @Bean({"liwl003","liwl002"})
    public PersonImpl liwl01(){
        return new PersonImpl();
    }
}

修改MyTest.java

package com.liwl.dev;

import java.util.Arrays;

import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        for(String beanName: context.getBeanDefinitionNames()){
            String[] aliases = context.getAliases(beanName);
            System.out.println(String.format("bean名称:%s,别名:%s,bean对象:%s", beanName,Arrays.asList(aliases),context.getBean(beanName)));
        }
        ((ConfigurableApplicationContext) context).close();
    }
}

F5运行结果,最后几行:

bean名称:springConfig,别名:[],bean对象:com.liwl.dev.SpringConfig$$EnhancerBySpringCGLIB$$b3bb86a1@4b013c76
bean名称:liwl,别名:[],bean对象:PersonImpl->name:"liwl",age:30,school:SchoolImpl->name:"江南",address:"无锡"
bean名称:school,别名:[],bean对象:SchoolImpl->name:"江南",address:"无锡"
bean名称:liwl001,别名:[],bean对象:PersonImpl->name:"liwl",age:30,school:SchoolImpl->name:"江南",address:"无锡"
bean名称:liwl003,别名:[liwl002],bean对象:PersonImpl->name:"liwl",age:30,school:SchoolImpl->name:"江南",address:"无锡"

结论:

  • SpringConfig类也是一个bean,被注册到spring容器中
  • @Bean如果没有参数时,bean对象的名称就是方法名。
  • @Bean如果有参数时,bean对象的名称就是第一个名字,其他名字都是别名

3. 总结

第一小节通过xml和注解的形式,完成了bean的配置和使用,并且逐步过渡到注解的形式。

第二小节完全采用了配置类和注解的方式,也就是全注解的方式来注册和使用bean。

本篇主要是简单的练习,spring的注解很多,需要更深入的学习和练习。

posted @ 2022-03-01 13:48  liwldev  阅读(78)  评论(0编辑  收藏  举报