主要存在三种方法
(1).在XML文件中进行显式配置
(2).在java中进行显示配置
(3).隐式的bean发现机制和自动装配
1.自动化装配bean
下面举一个例子进行自动化装配bean
这些代码都在同一个文件夹下面
下面代码是一个CD接口,提供播放的功能
package com.example.demo.project;
public interface CompactDisc {
void play();
}
下面是接口的实现
这个类添加了@Component注解,它表明自己是一个bean,即使是这样,它依然不会被自动装配,必须让Spring发现它,那么就需要一个发现这个bean的类,在Spring中,这种类叫做配置类,它使用@Configuration注解实现
package com.example.demo.project;
import org.springframework.stereotype.Component;
@Component
public class CompactDiscImpl implements CompactDisc {
private String title = "title";
private String artist = "artist";
@Override
public void play() {
System.out.println("Playing " + title + " by " + artist);
}
}
下面是配置类,它可以发现被声明为bean的类
package com.example.demo.project;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
//自动扫描和配置类相同的包以及下面的子包
@Configuration
@ComponentScan
public class CDPlayerConfig {
}
这个类有两个注解,一个是@Configuration,一个是@ConponentScan
第一个注解表明它是一个配置类,第二个注解指的是扫描范围,如果不在里面写一点什么,那么它默认扫描同级目录以及子目录下的bean,它可以通过多种方式指定扫描的路径和文件。
除了这种注解方式的扫描指定,还可以使用xml文件指定。
下面我们测试一下上面的bean能不能自动装配
package com.example.demo.project;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes= CDPlayerConfig.class)
public class TestOne {
@Autowired
private CompactDisc cd;
@Test
public void test() {
// cd = new CompactDiscImpl();
// cd.play();
assertNotNull(cd);
}
}
正常运行,但是有点奇怪,@Autowired居然标红,输出也乱七八糟的。总之,可以装配成功。如果装配失败,我们的cd对象应该是null。因为没有new。@Autowored是自动导入bean的意思,不用你new了。
1.1补充
1.1.1 bean的名字
@Component还可以给bean取名字,这个名字就是bean的id
package com.example.demo.project;
import org.springframework.stereotype.Component;
@Component("mybean")//给bean取名
public class CompactDiscImpl implements CompactDisc {
private String title = "title";
private String artist = "artist";
@Override
public void play() {
System.out.println("Playing " + title + " by " + artist);
}
}
1.1.2 设定扫描范围
@ComponentScan
刚刚说过,如果不指定任何范围,这个注解只会扫描同级目录下以及子目录的bean,可以通过两种方法指定范围
(1).通过包地址扫描
package com.example.demo.project;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
//自动扫描和配置类相同的包以及下面的子包
@Configuration
@ComponentScan(basePackages="com.example.demo.project")//通过包地址扫描
public class CDPlayerConfig {
}
(2).通过类或者接口扫描
package com.example.demo.project;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
//自动扫描和配置类相同的包以及下面的子包
@Configuration
@ComponentScan(basePackageClasses = CompactDisc.class)
public class CDPlayerConfig {
}
上面的例子都只用了一个包,如果想要同时扫描多个,可以使用数组
1.2 通过为bean添加注解实现自动装配
使用@Autowired进行自动装配的时候,可以在任意对象和方法上。
package com.example.demo.project;
public interface MediaPlayer {
void play();
}
package com.example.demo.project;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class CDPlayer implements MediaPlayer {
private CompactDisc compactDisc;
//构造器上使用自动装配
@Autowired
public CDPlayer(CompactDisc compactDisc) {
this.compactDisc = compactDisc;
}
//setter方法上使用自动装配
@Autowired
public void setCompactDisc(CompactDisc compactDisc) {
this.compactDisc = compactDisc;
}
public void play() {
compactDisc.play();
}
}
在方法上使用Autowired的时候,它会根据情况自动创建compaactDisc对象,而不需要传入一个这样的对象进行初始化。
Autowired可以设置参数required=false,这样可以避免因为没有bean而抛出异常,但是如果你这么写,当确实没有bean可以依赖的时候,那么就会抛出空指针异常。但是如果有多个bean都满足依赖关系,那么也会抛出异常。
和@Autowired类似的注解是@Inject,都可以使用(一般情况下)。
测试一下
package com.example.demo.project;
import org.junit.Test;
import org.junit.runner.Computer;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes= CDPlayerConfig.class)
public class TestOne {
@Autowired
private MediaPlayer mediaPlayer;
@Autowired
private CompactDisc cd;
@Test
public void test() {
assertNotNull(cd);
}
@Test
public void play() {
mediaPlayer.play();
}
}
2.通过java代码装配bean
如果你需要用其他人提供的包,那你肯定没办法在别人的代码里面写上面的那些注解,那么没办法使用自动化装配了,这个时候就需要使用显示装配。
显式装配有java代码装配和xml装配两种方法。下面介绍java装配法。
在上一节中,我使用了CDPlayerConfig类来扫描bean实现自动装配,主要归功@ComponentScan注解,这一节我们要删除这个注解了,后面要使用显式装配。删除这个注解,后面运行test就会出错。
2.1 在java的配置类中装配bean
假设有这么两个 类,它是第三方提供的,我们也没办法对它们进行修改,如果我们想要对这两个类进行装配,该怎么做?
public class Address {
private String city;
public Address() {
}
public Address(String city) {
this.city = city;
}
// Getter and Setter
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
}
public class Person {
private String name;
private Address address;
public Person() {
}
public Person(String name) {
this.name = name;
}
// Getter and Setter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
写一个配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public Address address() {
return new Address("New York");
}
@Bean
public Person person(Address address) {
Person person = new Person("John Doe");
person.setAddress(address);
return person;
}
}
这里我们使用了@Bean注解,需要指出的是,存在很多形式。比如不写传递参数直接返回new的对象,这个其实也没啥问题,就像address类。
上面两个类之间存在依赖关系,person类有一个成员对象是address,那么初始化的时候就需要传递address实例对象,但是@Bean会自动跟踪他需要的依赖,只要你装配了所需要的bean,它就可以替你管理好了。
说一个题外话,其实Spring管理的这些bean,默认情况下是同一个实例对象,因为它是单例模式实现的Spring 容器管理。
package com.example.demo.wx;
import com.example.demo.project.CDPlayerConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes= JavaConfig.class)
public class Test2 {
@Autowired
private Address address;
@Autowired
private Person person;
@Test
public void myTest(){
System.out.println(address);
System.out.println(person);
}
}
上面是测试类,可以正常运行。
3.通过xml文件进行显式装配
xml文件配置只是多种文件配置的其中一种,还可以用yml格式等。
3.1 xml配置的范式
下面这段代码是xml配置必须的
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
这段代码还没有加入任何bean配置,如果想加入,就可以在<bean></bean>之间设置。
3.1.2 声明一个bean
首先我们先声明一个Student类,构造函数要注意了!!!
为什么呢?因为xml文件创建bean的时候,会根据类的构造进行创建bean。
package com.example.demo.project;
public class Student {
private int id;
private String name;
public Student(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Student(){
}
}
下面声明一个bean
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="com.example.demo.project.Student"/>
</beans>
这里只比最开始多了一行代码而已
<bean id="student" class="com.example.demo.project.Student"/>
id就是bean的名字,之后可以通过这个id引用,如果不指定id,它也会自动生成一个带有序号的id,比如这里它的默认名字就是 com.example.demo.project.Student#0,如果出现相同的配置对象,就会往下技术1,2,3等等,这里和上面java代码配置bean的name意义。
class需要指定需要配置的对象的具体包路径。所以xml的配置要小心对象的名字变更或者路径变更。
与java配置bean相比,xml生成bean更简单,因为它什么也不做,自动生成了,而java配置还需要new一个对象,然后才会被容器接管。
3.1.3 借助构造器注入bean
其实除了构造器注入,还可以用c-命名空间,我们重点介绍常用的构造器注入
如果我们想给上面的student的id和name注入变量
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="com.example.demo.project.Student">
<constructor-arg value="123"/>
<constructor-arg value="hello world"/>
</bean>
</beans>
这里多了下面两行代码
<constructor-arg value="123"/> <constructor-arg value="hello world"/>
构造器注入就是这样,需要指定constructor-arg这个标识,然后value指的是对象的成员要赋予的值,按代码顺序进行赋值。其实,如果不写这个也没关系,最多是被当作无参构造而已。
arg其实就是参数的缩写。value代表值。如果你的对象成员是其他对象呢?那么value就是替换成ref关键字了。
下面是测试代码
package com.example.demo.project;
import org.junit.Test;
import org.junit.runner.Computer;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.*;
import org.springframework.context.ApplicationContext;
@RunWith(SpringJUnit4ClassRunner.class)
public class TestOne {
@Test
public void test() {
String xmlPath = "classpath:bean.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(xmlPath);
CompactDisc cd = (CompactDisc) context.getBean("beanTest");
cd.play();
}
}
上面的测试代码需要注意,这是指定读取xml文件的意思,xml文件需要放在资源文件中。
ClassPathXmlApplicationContext这个类是通过类的类路径读取,而不是文件路径读取。类的路径就是指的是com.XXX.XXX这样的路径,文件路径就是C:\\UserXXXXX这样的路径。
上面是使用构造函数配置成员,而这个成员只是普通的字符串或者数字,如果成员变量是列表、数组、集合等等呢?又该如何编写xml的配置?
下面简单提一下,将来有机会再写代码补充。因为都挺简单的。
如果是集合List,那么就是使用<list>标签
如果是集合set,那么就用标签<set>
依次类推。
3.2 设置属性
上面讲的是通过构造函数进行配置,但是如果我们需要通过set函数呢?
比如我们有下面这个类,它没有构造器,只有一个setter,如何使用xml进行装配?
package com.example.demo.project;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class CDPlayer implements MediaPlayer {
private CompactDisc compactDisc;
//setter方法上使用自动装配
@Autowired
public void setCompactDisc(CompactDisc compactDisc) {
this.compactDisc = compactDisc;
}
public void play() {
compactDisc.play();
}
}
xml的装配可以这么写,相比于上一个,只多了对CDPlayer的装配
<?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
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.example.demo.project" />
<bean id="beanTest" class="com.example.demo.project.CompactDiscImpl">
<constructor-arg name="title" value="123"/>
<constructor-arg name="artist" value="hello world"/>
</bean>
<bean id="CDPlayer" class="com.example.demo.project.CDPlayer">
<property name="compactDisc" ref="beanTest"></property>
</bean>
</beans>
测试代码:
package com.example.demo.project;
import org.junit.Test;
import org.junit.runner.Computer;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.*;
import org.springframework.context.ApplicationContext;
@RunWith(SpringJUnit4ClassRunner.class)
public class TestOne {
@Test
public void test() {
String xmlPath = "classpath:bean.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(xmlPath);
// CompactDisc cd = (CompactDisc) context.getBean("beanTest");
// cd.play();
CDPlayer cdPlayer = (CDPlayer) context.getBean("CDPlayer");
cdPlayer.play();
}
}
bean只自动产生的,所以我屏蔽了compactDisc的读取,CDPlayer虽然依赖compactDisc,但是会自动装配。
3.3 混合注入
通过xml同时注入属性和成员,也就是使用setter和constructor
下面这个类有设置器,成员中有List,如何通过xml配置它的bean呢?
package com.example.demo.project;
import java.util.List;
public class CompactDiscImpl implements CompactDisc {
private String title;
private String artist;
private List<String> tracks;
public void setTitle(String title) {
this.title = title;
}
public void setArtist(String artist) {
this.artist = artist;
}
public void setTracks(List<String> tracks) {
this.tracks = tracks;
}
@Override
public void play() {
System.out.println("Playing " + title + " by " + artist);
}
}
xml的配置文件
由于只有setter,所以全都是<property>标签完成,list对象的装配放在<list>标签中。
<?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
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.example.demo.project" />
<bean id="beanTest" class="com.example.demo.project.CompactDiscImpl">
<property name="title" value="123"/>
<property name="artist" value="hello world"/>
<property name="tracks">
<list>
<value>first</value>
<value>second</value>
<value>third</value>
</list>
</property>
</bean>
<bean id="CDPlayer" class="com.example.demo.project.CDPlayer">
<property name="compactDisc" ref="beanTest"></property>
</bean>
</beans>
4.混合装配bean
上面我们介绍了自动化装配、java显式装配,以及xml装配,实际上这几种装配方法并不是相互排斥的。
4.1 有关CD的类
(1)CD类
package com.example.demo.project;
public interface CompactDisc {
void play();
}
package com.example.demo.project;
import org.springframework.stereotype.Component;
public class CompactDiscImpl implements CompactDisc {
private String title;
private String artist;
public CompactDiscImpl() {
}
public void setTitle(String title) {
this.title = title;
}
public void setArtist(String artist) {
this.artist = artist;
}
@Override
public void play() {
System.out.println("Playing " + title + " by " + artist);
}
}
(2)CD类的配置类CDConfig
所有的配置信息都写在resources文件夹中的bean.xml文件中,所有为了自动读取,就加了@ImportResourece,让它去读取指定的配置文件
package com.example.demo.project;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
@Configuration
@ImportResource("classpath:bean.xml")
public class CDConfig {
@Bean
public CompactDisc compactDisc() {
return new CompactDiscImpl();
}
}
4.2 有关CDPlayer的类
下面是CDPlayer类
package com.example.demo.project;
public interface MediaPlayer {
void play();
}
package com.example.demo.project;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
public class CDPlayer implements MediaPlayer {
private CompactDisc compactDisc;
public CDPlayer(CompactDisc compactDisc) {
this.compactDisc = compactDisc;
}
public void setCompactDisc(CompactDisc compactDisc) {
this.compactDisc = compactDisc;
}
public void play() {
System.out.println("Playing cd.");
compactDisc.play();
}
}
CDPlayer的配置类,让它自动读取配置信息,生成bean
@Configuration
@Import(CDConfig.class)
@ImportResource("classpath:bean.xml")
public class CDPlayerConfig {
@Bean
public CDPlayer cdPlayer(CompactDisc compactDisc) {
return new CDPlayer(compactDisc);
}
}
由于CDPlayer与CD类有关,因此使用@Import导入了它的配置类
4.3 XML中的配置信息
它CD类和CDPlayer的信息进行了配置,自己生产bean,所有上面的两个类都不需要添加@Component
<?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
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.example.demo.project" />
<bean id="cd" class="com.example.demo.project.CompactDiscImpl">
<property name="title" value="123"/>
<property name="artist" value="hello world"/>
</bean>
<bean id="CDPlayer" class="com.example.demo.project.CDPlayer">
<property name="compactDisc" ref="cd"></property>
</bean>
</beans>
4.4 测试文件
package com.example.demo.project;
import org.junit.Test;
import org.junit.runner.Computer;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.*;
import org.springframework.context.ApplicationContext;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes= {CDPlayerConfig.class, CDConfig.class})
public class TestOne {
@Autowired
private CompactDisc cd;
@Autowired
private CDPlayer cdPlayer;
@Test
public void test() {
cd.play();
cdPlayer.play();
}
}
@ContextConfiguration(classes= {CDPlayerConfig.class, CDConfig.class})
引入两个配置文件
结构情绪,所以没什么可说的。
4.5 补充
4.5.1 配置类的配置类
如果写一个配置类,管理上面了两个配置类,那么这个配置类可以这么写:
@Configuration
@Import({CDConfig.class, CDPlayer.class})
@ImportResource("classpath:bean.xml")
public class JavaConfig{
}
这样其他两个配置类就不用写@Import和@ImportResoure
4.5.2 配置xml的xml
配置类的bean太多了,所以可以拆分多个,然后使用@Import进行组合
同样的xml需要管理的bean太多,也会导致文件混乱,所以xml也可拆分之后进行管理,依赖关系可使用<import>管理
比如CD类的配置文件为:
<?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
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.example.demo.project" />
<import resource="cd.xml"></import>
<bean id="CDPlayer" class="com.example.demo.project.CDPlayer">
<property name="compactDisc" ref="cd"></property>
</bean>
</beans>
而CDPlayer配置依赖它,就使用了这个标签。
<?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
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.example.demo.project" />
<import resource="cd.xml"></import>
<bean id="CDPlayer" class="com.example.demo.project.CDPlayer">
<property name="compactDisc" ref="cd"></property>
</bean>
</beans>