Spring基于注解的配置bean的练习
1. 半注解配置bean
前面几篇博客主要练习了在Spring里面通过xml来配置bean的操作。本篇主要练习如何在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的注解很多,需要更深入的学习和练习。