标签:print 无法 one 加载 oid username cal beans 名称
本教程源码请访问:tutorial_demo
之前我们学习了如何使用注解实现IOC和DI,接下来我们学习一下如何完全抛弃XML配置,完全使用注解配置。
在学习纯注解之前先看一个完全用XML配置的案例,就当一个回顾。
需求:设计一个Person对象,包含username、age、gender、birthday四个属性,并使用Spring创建Person对象,并注入属性。
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.codeaction</groupId>
<artifactId>anno</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
</project>
package org.codeaction.bean;
import java.io.Serializable;
import java.time.LocalDateTime;
public class Person implements Serializable {
private String username;
private int age;
private String gender;
//注意birthday的类型,思考如何为其注入值
private LocalDateTime birthday;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public LocalDateTime getBirthday() {
return birthday;
}
public void setBirthday(LocalDateTime birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "Person{" +
"username=‘" + username + ‘\‘‘ +
", age=" + age +
", gender=‘" + gender + ‘\‘‘ +
", birthday=" + birthday +
‘}‘;
}
}
XML文件在resource目录下。
<?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">
<!-- 配置person -->
<bean id="person" class="org.codeaction.bean.Person">
<property name="username" value="Tom"></property>
<property name="age" value="20"></property>
<property name="gender" value="男"></property>
<property name="birthday" ref="birthday"></property>
</bean>
<!-- 配置birthday -->
<bean id="birthday" class="java.time.LocalDateTime" factory-method="of">
<constructor-arg name="date" ref="date"/>
<constructor-arg name="time" ref="time"/>
</bean>
<bean id="date" class="java.time.LocalDate" factory-method="of">
<constructor-arg name="year" value="2000"/>
<constructor-arg name="month" value="OCTOBER"/>
<constructor-arg name="dayOfMonth" value="15"/>
</bean>
<bean id="time" class="java.time.LocalTime" factory-method="of">
<constructor-arg name="hour" value="10" />
<constructor-arg name="minute" value="10" />
<constructor-arg name="second" value="20" />
</bean>
</beans>
这里配置birthday比肩复杂,需要引用其它配置。
package org.codeaction.test;
import org.codeaction.bean.Person;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Person person = (Person) context.getBean("person");
System.out.println(person);
}
}
运行main方法,输出如下:
Person{username=‘Tom‘, age=20, gender=‘男‘, birthday=2000-10-15T10:10:20}
上面就是我们完全使用XML,配置了Person,并注入了属性。
结合上一篇教程,我们可以对这给程序使用基于注解的配置改造。有些步骤和上面是完全一样的,下面只列出关键代码。
package org.codeaction.bean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.Serializable;
import java.time.LocalDateTime;
@Component
public class Person implements Serializable {
@Value("Bob")
private String username;
@Value("20")
private int age;
@Value("男")
private String gender;
@Autowired
private LocalDateTime birthday;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public LocalDateTime getBirthday() {
return birthday;
}
public void setBirthday(LocalDateTime birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "Person{" +
"username=‘" + username + ‘\‘‘ +
", age=" + age +
", gender=‘" + gender + ‘\‘‘ +
", birthday=" + birthday +
‘}‘;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 告知spring创建容器时要扫描的包 -->
<context:component-scan base-package="org.codeaction"></context:component-scan>
<!-- 配置birthday -->
<bean id="birthday" class="java.time.LocalDateTime" factory-method="of">
<constructor-arg name="date" ref="date"/>
<constructor-arg name="time" ref="time"/>
</bean>
<bean id="date" class="java.time.LocalDate" factory-method="of">
<constructor-arg name="year" value="2000"/>
<constructor-arg name="month" value="OCTOBER"/>
<constructor-arg name="dayOfMonth" value="15"/>
</bean>
<bean id="time" class="java.time.LocalTime" factory-method="of">
<constructor-arg name="hour" value="10" />
<constructor-arg name="minute" value="10" />
<constructor-arg name="second" value="20" />
</bean>
</beans>
由于类似于LocalDateTime这种类在JDK自带的包中,我们无法使用@Component注解让Spring容器去管理它的对象,以我们目前学习的知识,这里我们只能用XML去配置。
运行测试类中的main方法,控制台输出如下:
Person{username=‘Bob‘, age=20, gender=‘男‘, birthday=2000-10-15T10:10:20}
以上就是使用XML和注解进行配置,我们既然学习了注解,有没有什么办法能完全使用注解进行配置,抛弃XML?我们来看接下来的内容。
有些步骤和上面是完全一样的,下面只列出关键代码。Person类和2.1中完全相同。由于我们完全使用注解进行配置,我们删除beans.xml。
删除了beans.xml,就要有内容替代它,这里我们使用Java类(配置类)代替XML文件。
package org.codeaction.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import java.time.LocalDateTime;
import java.time.Month;
@Configuration
@ComponentScan(basePackages = "org.codeaction.bean")
public class MainConfig {
@Bean("birthday")
public LocalDateTime dateTime() {
return LocalDateTime.of(1980, Month.OCTOBER, 11, 12, 20, 15);
}
}
@Configuration
作用:指定当前类是一个配置类。
注意:当配置类作为AnnotationConfigApplicationContext对象(3.2中会用到)创建的参数时,该注解可以不写。
@ComponentScan
作用:用于通过注解指定spring在创建容器时要扫描的包。这个注解等同于XML中的context:component-scan。
属性:value和basePackages的作用是一样的,都是用于指定创建容器时要扫描的包,用哪个都可以。
@Bean
作用:用于把当前方法的返回值作为bean对象存入Spring的IOC容器中。
属性:name,用于指定bean的id,当不写时,默认值是当前方法的名称。
注意:使用注解此配置方法时,如果方法有参数,Spring会去容器中查找有没有可用的bean对象。查找的方式和@Autowired注解的作用相同。
接下来我们看一下@Bean修饰的方法传参的情况,代码如下:
package org.codeaction.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
@Configuration
@ComponentScan(basePackages = "org.codeaction.bean")
public class MainConfig {
//方法有参数传入
@Bean("birthday")
public LocalDateTime dateTime(LocalDate date, LocalTime time) {
//return LocalDateTime.of(1980, Month.OCTOBER, 11, 12, 20, 15);
return LocalDateTime.of(date, time);
}
@Bean("date")
public LocalDate date() {
return LocalDate.of(1980, Month.OCTOBER, 11);
}
@Bean("time")
public LocalTime time() {
return LocalTime.of(12, 20, 15);
}
}
通过上面的例子,我们可以看出@Bean修饰带参数的方法,Spring会去容器中查找有没有可用的bean对象。查找的方式和@Autowired注解的作用相同。
package org.codeaction.test;
import org.codeaction.bean.Person;
import org.codeaction.config.MainConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
Person person = (Person) context.getBean("person");
System.out.println(person);
}
}
运行main方法,控制台输出如下:
Person{username=‘Bob‘, age=20, gender=‘男‘, birthday=1980-10-11T12:20:15}
以上完全使用注解配置的大部分内容都学完了,接下来我们结合案例来看一些其他常用的内容。
在3.1中,我们将所有的配置都写在了一个配置类中,如果有很多不同种类的配置,都写在同一个配置类中,代码会很庞大,难以维护,我们可以对配置进行拆分,然后通过导入的方式将所有配置整合在一起。
package org.codeaction.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
@Configuration
public class BirthdayConfig {
//方法有参数传入
@Bean("birthday")
public LocalDateTime dateTime(LocalDate date, LocalTime time) {
//return LocalDateTime.of(1980, Month.OCTOBER, 11, 12, 20, 15);
return LocalDateTime.of(date, time);
}
@Bean("date")
public LocalDate date() {
return LocalDate.of(1980, Month.OCTOBER, 11);
}
@Bean("time")
public LocalTime time() {
return LocalTime.of(12, 20, 15);
}
}
我们将配置birthday的内容提取了出来,新建了一个配置类。
我自己起的名字,大家把它当成汇总所有配置类的配置类就可以了
package org.codeaction.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@ComponentScan(basePackages = "org.codeaction.bean")
@Import(BirthdayConfig.class)
public class MainConfig {
}
@Import:
作用:用于导入其他的配置类。
属性:value,用于指定其他配置类的字节码。当我们使用Import的注解之后,有Import注解的类就父配置类,而导入的都是子配置类。
运行3.2中的main方法,控制台输出如下:
Person{username=‘Bob‘, age=20, gender=‘男‘, birthday=1980-10-11T12:20:15}
在之前的学习中,我们学习了@Qualifier注解,作用如下:
在属性上的使用已经学过了,我们看一下@Qualifier在方法上的使用,在学习之前,我们修改一下3.3.1中的配置类,代码如下:
package org.codeaction.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
@Configuration
public class BirthdayConfig {
//方法有参数传入
@Bean("birthday")
public LocalDateTime dateTime(LocalDate date, LocalTime time) {
//return LocalDateTime.of(1980, Month.OCTOBER, 11, 12, 20, 15);
return LocalDateTime.of(date, time);
}
@Bean("date1")
public LocalDate date1() {
return LocalDate.of(1980, Month.OCTOBER, 11);
}
@Bean("date2")
public LocalDate date2() {
return LocalDate.of(1980, Month.OCTOBER, 11);
}
@Bean("time1")
public LocalTime time1() {
return LocalTime.of(12, 20, 15);
}
@Bean("time2")
public LocalTime time2() {
return LocalTime.of(12, 20, 15);
}
}
我们配置了两个LocalDate和两个LocalTime,运行测试类的main方法,报错,原因是因为Spring容器中有多个相同类型的bean对象,@Bean修饰的方法,无法通过自动注入的方式注入。控制台信息如下:
警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘person‘: Unsatisfied dependency expressed through field ‘birthday‘; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘birthday‘ defined in org.codeaction.config.BirthdayConfig: Unsatisfied dependency expressed through method ‘dateTime‘ parameter 0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type ‘java.time.LocalDate‘ available: expected single matching bean but found 2: date1,date2
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘person‘: Unsatisfied dependency expressed through field ‘birthday‘; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘birthday‘ defined in org.codeaction.config.BirthdayConfig: Unsatisfied dependency expressed through method ‘dateTime‘ parameter 0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type ‘java.time.LocalDate‘ available: expected single matching bean but found 2: date1,date2
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:643)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:130)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1422)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:594)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:895)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:89)
at org.codeaction.test.MyTest.main(MyTest.java:11)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘birthday‘ defined in org.codeaction.config.BirthdayConfig: Unsatisfied dependency expressed through method ‘dateTime‘ parameter 0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type ‘java.time.LocalDate‘ available: expected single matching bean but found 2: date1,date2
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:798)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:539)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1338)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1177)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1306)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1226)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640)
... 14 more
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type ‘java.time.LocalDate‘ available: expected single matching bean but found 2: date1,date2
at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:220)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1284)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1226)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:885)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789)
... 27 more
使用@Qualifier修改如下:
package org.codeaction.config;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
@Configuration
public class BirthdayConfig {
//方法有参数传入
@Bean("birthday")
public LocalDateTime dateTime(@Qualifier("date1") LocalDate date, @Qualifier("time1") LocalTime time) {
//return LocalDateTime.of(1980, Month.OCTOBER, 11, 12, 20, 15);
return LocalDateTime.of(date, time);
}
@Bean("date1")
public LocalDate date1() {
return LocalDate.of(1980, Month.OCTOBER, 11);
}
@Bean("date2")
public LocalDate date2() {
return LocalDate.of(1980, Month.OCTOBER, 11);
}
@Bean("time1")
public LocalTime time1() {
return LocalTime.of(12, 20, 15);
}
@Bean("time2")
public LocalTime time2() {
return LocalTime.of(12, 20, 15);
}
}
这里我们在方法上使用@Qualifier,告诉Spring应该注入哪个bean的对象。
文件名:person.properties
位置:resource目录下
person.username=Tom
person.age=20
person.gender=男
person.year=2000
person.month=10
person.dayOfMonth=15
person.hour=10
person.minute=10
person.second=20
package org.codeaction.config;
import org.springframework.context.annotation.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
@Configuration
@ComponentScan(basePackages = "org.codeaction.bean")
@Import(BirthdayConfig.class)
@PropertySource("classpath:person.properties")
public class MainConfig {
// //方法有参数传入
// @Bean("birthday")
// public LocalDateTime dateTime(LocalDate date, LocalTime time) {
// //return LocalDateTime.of(1980, Month.OCTOBER, 11, 12, 20, 15);
// return LocalDateTime.of(date, time);
// }
//
// @Bean("date")
// public LocalDate date() {
// return LocalDate.of(1980, Month.OCTOBER, 11);
// }
//
// @Bean("time")
// public LocalTime time() {
// return LocalTime.of(12, 20, 15);
// }
}
@PropertySource
作用:用于加载.properties文件中的配置。
属性:value,用于指定properties文件位置。如果在类路径下,需要写上classpath:。
package org.codeaction.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
@Configuration
public class BirthdayConfig {
@Value("${person.year}")
private int year;
@Value("${person.month}")
private int month;
@Value("${person.dayOfMonth}")
private int dayOfMonth;
@Value("${person.hour}")
private int hour;
@Value("${person.minute}")
private int minute;
@Value("${person.second}")
private int second;
//方法有参数传入
@Bean("birthday")
public LocalDateTime dateTime(LocalDate date, LocalTime time) {
//return LocalDateTime.of(1980, Month.OCTOBER, 11, 12, 20, 15);
return LocalDateTime.of(date, time);
}
@Bean("date")
public LocalDate date() {
return LocalDate.of(year, month, dayOfMonth);
}
@Bean("time")
public LocalTime time() {
return LocalTime.of(hour, minute, second);
}
}
package org.codeaction.bean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.Serializable;
import java.time.LocalDateTime;
@Component
public class Person implements Serializable {
@Value("${person.username}")
private String username;
@Value("${person.age}")
private int age;
@Value("${person.gender}")
private String gender;
@Autowired
private LocalDateTime birthday;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public LocalDateTime getBirthday() {
return birthday;
}
public void setBirthday(LocalDateTime birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "Person{" +
"username=‘" + username + ‘\‘‘ +
", age=" + age +
", gender=‘" + gender + ‘\‘‘ +
", birthday=" + birthday +
‘}‘;
}
}
运行3.2中的main方法,控制台输出如下:
Person{username=‘Tom‘, age=20, gender=‘男‘, birthday=2000-10-15T10:10:20}
标签:print 无法 one 加载 oid username cal beans 名称
原文地址:https://www.cnblogs.com/codeaction/p/12977617.html