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


  4.0.0
  com.liwl.dev
  SpringAnnotation
  jar
  1.0-SNAPSHOT
  SpringAnnotation
  http://maven.apache.org

  
      5.3.15
  

  

    
      junit
      junit
      3.8.1
      test
    

    
      org.springframework
      spring-core
      ${spring.version}
    
  
     
      org.springframework
      spring-context
      ${spring.version}
     
      
  

  
    
      
        org.apache.maven.plugins
        maven-compiler-plugin
        3.8.0
        
          17
          17
          UTF8
        
      
    
  


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


    
    


该配置文件标签,其功能就是指定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
    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


    
    
    
    
    


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

总结:

  • 标签定义了从属性配置文件all.properties加载属性
  • all.properties里面自定义的属性以Key-Value的形式,在java源码注解里面以@Value=(value="${Key}")的形式存在

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



    
    


修改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


    
    


修改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里面仅剩下一行配置。这一行配置可以通过创建配置类来代替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

3. 总结

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

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

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