@TOC# Spring概念
Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson创建。简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。
Spring安装配置
环境搭建
创建工程,添加Maven依赖
<dependencies>
<!-- Spring IOC最小依赖是beans、context,我们引入context依赖,maven会自动将beans依赖一并引入 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
</dependencies>
编写配置文件
<?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">
<context:annotation-config/>
<context:component-scan base-package="com.john.spring" />
</beans>
依赖注入
/**
* Service
*/
public interface StudentService {
List<Student> queryStudents();
}
public class StudentServiceImpl implements StudentService {
// 通过Spring DI自动注入
@Autowired
private StudentDao studentDao;
@Override
public List<Student> queryStudents() {
return studentDao.queryStudents();
}
}
/**
* dao
*/
public interface StudentDao {
List<Student> queryStudents();
}
public class StudentDaoImpl implements StudentDao {
@Override
public List<Student> queryStudents() {
return new ArrayList<>();
}
}
/**
* 实体类
*/
@Setter
@Getter
@Component
public class Student {
private Integer id;
private String sname;
private Integer age;
private String gender;
private String nickName;
}
加载配置文件,获取SpringContext管理的Bean
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
Student stu = ctx.getBean(Student.class);
System.out.println(stu);
}
ApplicationContext实现类
ClassPathXmlApplicationContext通过XML方式
用于加载类路径下的spring配置文件,通常用于控制台程序
// ClassPathXmlApplicationContext用于初始化ioc容器(基于类路径下基于xml方式的配置)
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
StudentService ss = ctx.getBean(StudentService.class);
System.out.println(ss);
}
AnnotationConfigApplicationContext通过注解方式
用于初始化通过注解方式配置的ioc容器
// AnnotationApplicationContext用于初始化ioc容器(注解方式配置文件(用@Configuration注解的类))
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
ctx.registerShutdownHook();//设置一个钩子
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
}
通过XML方式管理Bean
优点:
对代码没有任何侵入性, 改了配置不需要重新编译、打包
缺点:
配置相比注解的方式要繁琐很多,工程量比较大
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
">
需要注意的是schema约束地址不能用https,否则每次都会从spring加载,没有网络时无法运行
如果不改的话,运行时会报找不到文件的错误
通过注解方式管理Bean
优点:
配置简单。由于Java类中已经包含很上下文信息,所有在Java类上直接加注解可以省略很多属性。
缺点:
对代码有侵入性,如果改了是基于注解的配置信息改变了,需要重新编译、打包
开启注解支持
开启注解支持有两种方式
在xml配置文件中通过context命名空间中的annotation-config标签开启注解配置
<?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">
<!-- 开启注解支持 -->
<context:annotation-config/>
<!-- 开启注解支持,同时指定扫描的包路径(指定了这个就不用指定上面的) -->
<context:component-scan base-package="com.john.spring" />
</beans>
通过注解的方式开启注解配置支持
@Configuration
@ComponentScan("com.john.spring")
public class AppConfig {
@Bean
public StudentDao studentDao() {
return new StudentDao();
}
// 其他bean配置....
public static void main(String[] args) {
// 如果在非web工程中使用这种方式开启注解支持,需要使用下面的方式初始化ioc容器,否则@ComponentScan注解会被忽略
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
}
}
一个用@Configuration
注解标注的类就相当于一个xml配置文件,我们可以给其添加一个@ComponentScan
注解来开启注解扫描支持,同时指定扫描包根路径
注解扫描过滤
注解扫描过滤也有两种方式
@Configuration
@ComponentScan(basePackages = "org.example",
includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
excludeFilters = @Filter(Repository.class))
public class AppConfig {
...
}
xml配置方式
<beans>
<context:component-scan base-package="org.example">
<context:include-filter type="regex"
expression=".*Stub.*Repository"/>
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
</beans>
XML和注解方式混合使用
@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
public class AppConfig {
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource dataSource() {
return new DriverManagerDataSource(url, username, password);
}
}
properties-config.xml
jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=sa
使用入口
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
TransferService transferService = ctx.getBean(TransferService.class);
// ...
}
在一个配置中导入另一个配置
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"
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
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 导入其他spring配置文件 -->
<import resource="classpath:XXXX.xml" />
在另一个XXXX.xml里
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
注解方式导入
通过@ImportResource注解导入spring配置xml文件
applicationContext.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"
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
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 开启Spring注解配置支持 -->
<!-- <context:annotation-config /> -->
<!-- 配置了扫描包路径后就无需再配置上面那行了 -->
<!-- 配置注解扫描包路径 -->
<context:component-scan base-package="com.lanou3g.spring" />
通过@Import注解导入其他注解配置
@Configuration 相当于创建一个xml文件
@ComponentScan(basePackages = "com.lanou3g.spring") 扫描包路径 @ImportResource("applicationContext.xml") 导入xml配置 public class App {
@Autowired
@Qualifier("ap") //这里写的apple对应的是bean的id或者name
Fruit fruit;
public void getFrult(){
frult.eatFrult();
}
public static void main( String[] args ) {
ApplicationContext ac = new AnnotationConfigApplicationContext(App.class);
App app = ac.getBean(App.class);
app.getFrult();
}
}
通过注解导入其他注解配置
@Configuration 相当于创建一个xml文件
@ComponentScan(basePackages = "com.lanou3g.spring") 扫描包路径
@Import(MyConf.class) 导入其他注解类
@ImportResource("applicationContext.xml") 导入xml配置 public class App {
@Autowired
@Qualifier("ap") //这里写的apple对应的是bean的id或者name
Fruit fruit;
public void getFrult(){
frult.eatFrult();
}
public static void main( String[] args ) {
ApplicationContext ac = new AnnotationConfigApplicationContext(App.class);
App app = ac.getBean(App.class);
app.getFrult();
}
}
@Configuration // <beans>
public class MyConf {
@Bean // <bean id="bigPear"> // 默认 <bean id="方法名">
public Frult bigPear(){
return new Pear();
} // </bean>
} // </beans>
管理Bean的作用域
singleton | 单例。在整个ioc容器中只有一个此类型的示例。(默认值) |
---|---|
prototype | 原型。每次使用都用创建一个新的对象。 |
request | 对象的实例仅在一个request请求周期内有效,仅限在web环境中使用。 |
session | 对象的实例仅在一个session会话周期内有效,仅限在web环境中使用。 |
application | 对象的实例在整个application生命周期内有效,仅限在web环境中使用。 |
websocket | 对象的实例仅在一个websocket生命周期内有效,仅限在web环境中使用。 |
singleton
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--
我们可以通过scope指定bean的作用域
singleton(默认): 单例
prototype: 每调用一次getBean,都是一个新的对象
-->
<bean id="messageDao" class="com.lanou3g.spring.dao.MessageDaoImpl2" />
</beans>
public void testScope(ApplicationContext ctx) {
MessageDao messageDao1 = ctx.getBean("md2", MessageDao.class);
System.out.println(messageDao1);
MessageDao messageDao2 = ctx.getBean("md2", MessageDao.class);
System.out.println(messageDao2);
MessageDao messageDao3 = ctx.getBean("md2", MessageDao.class);
System.out.println(messageDao3);
}
控制台打印的三个对象地址是相同的,这个bean就是一个单例,即每次调用getBean()方法,获取到的都是同一个bean实例。
prototype
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--
我们可以通过scope指定bean的作用域
prototype: 每调用一次getBean,都是一个新的对象
-->
<bean id="messageDao" class="com.lanou3g.spring.dao.MessageDaoImpl2" scope="prototype" />
</beans>
public void testScope(ApplicationContext ctx) {
MessageDao messageDao1 = ctx.getBean("md2", MessageDao.class);
System.out.println(messageDao1);
MessageDao messageDao2 = ctx.getBean("md2", MessageDao.class);
System.out.println(messageDao2);
MessageDao messageDao3 = ctx.getBean("md2", MessageDao.class);
System.out.println(messageDao3);
}
控制台打印的三个对象地址是不同的,这个bean就是一个非单例,即每次调用getBean()方法,获取到的都是不同bean实例。
管理Bean的生命周期
public class Pear implements Frult{
public void myInit(){
System.out.println("init" +创建了);
}
public void myDestroy(){
System.out.println("destroy"+销毁了);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="md" class="com.lanou3g.spring.dao.MessageDaoImpl"
init-method="myInit" destroy-method="myDestroy" />
public class Launcher {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(App.class);
ctx.registerShutdownHook();
public void testLifeCycle(ApplicationContext ctx) {
MessageDao messageDao = ctx.getBean("md", MessageDao.class);
System.out.println(messageDao);
}
}
}
实例化bean的方式
通过构造方法实例化
这种方式是最常用的方式,适合绝大多数的javabean,因为我们的java类无需继承任何父类或实现任何接口。但是我们通常需要提供一个无参的构造方法。
<bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
通过静态工厂方法实例化
<!-- 通过静态工厂创建bean -->
<bean id="big_apple" class="com.lanou3g.spring.bean.FruitFactory" factory-method="produceFruit">
<constructor-arg name="name" value="banana" />
</bean>
public class FruitFactory {
/**
* 静态工厂方法
* @param name
* @return
*/
public static Fruit produceFruit(String name) {
switch (name) {
case "apple":
return new Apple();
case "banana":
return new Banana();
default:
return null;
}
}
}
public class Launcher {
public void testBeanInit(ApplicationContext ctx) {
//通过静态工厂方法来初始化bean
Object obj = ctx.getBean("big_apple");
System.out.println(obj);
}
}
通过非静态工厂方法实例化
<!-- 通过非静态工厂方法创建bean -->
<bean id="fruitFactory" class="com.lanou3g.spring.bean.FruitFactory" />
<bean id="big_banana" factory-bean="fruitFactory" factory-method="produceFruitByInstrance">
<constructor-arg name="name" value="banana" />
</bean>
public class FruitFactory {
/**
* (非静态)普通工厂方法
* @param name
* @return
*/
public Fruit produceFruitByInstrance(String name) {
switch (name) {
case "apple":
return new Apple();
case "banana":
return new Banana();
default:
return null;
}
}
}
public class Launcher {
public void testBeanInit(ApplicationContext ctx) {
// 通过非静态工厂方法来初始化bean
Object obj = ctx.getBean("big_banana");
System.out.println(obj);
System.out.println(obj.getClass());
}
}
Spring懒加载
Spring IOC容器默认会在启动的时候初始化我们配置的所有bean,这在大多数情况下是适合的,如果有什么错误可以在启动时及时发现。但是在特定的业务场景下,这种行为可能并不适合,我们可能需要让某些bean延迟初始化的时机,在我们真正使用时才去初始化。
我们可以在bean上面添加lazy-init属性,属性值可以是true、false、default。 默认情况下是false。
<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.something.AnotherBean"/>
设置Bean全局懒加载默认值
<beans default-lazy-init="true">
<!-- 下面配置的所有bean默认都会开启懒加载 -->
</beans>
构造方法注入
public class Student {
private String sname;
private String nickName;
private Fruit fruit;
public Student() {}
public Student(String sname, String nickName, Fruit fruit) {
this.sname = sname;
this.nickName = nickName;
this.fruit = fruit;
}
}
<bean id="student" class="com.xxx.spring.bean.Student">
<constructor-arg name="nickName" value="三哥" />
<constructor-arg name="sname" value="张三" />
<constructor-arg name="fruit" ref="banana" />
</bean>
注入匿名内部Bean
注入匿名内部bean这种方式相当于给对象赋值,给对象赋值的方式有两种,一种是通过new对象,然后set对象的属性给对象的属性赋值,一种是通过带参构造函数传递参数给对象赋值,在配置文件中通过bean创建对象也可以给对象赋值
<bean id="outer" class="...">
<!-- 像set一样实例化对象的方式用这样的注入方式 -->
<property name="target">
<bean class="com.example.Person"> <!-- this is the inner bean -->
<property name="name" value="Fiona Apple"/>
<property name="age" value="25"/>
</bean>
</property>
<!-- 像带参构造函数实例化对象的方式用这样的注入 -->
<constructor-arg name="target">
<bean class="com.example.Person"> <!-- this is the inner bean -->
<property name="name" value="Fiona Apple"/>
<property name="age" value="25"/>
</bean>
</constructor-arg>
</bean>
Spring 注入集合集合类型属性和注入null,空字符串的属性值
<!-- 注入集合、Map类型参数 -->
<bean id="aa" class="com.XXX.spring.bean.XXX">
<property name="hobbies">
<list>
<value>游泳</value>
<value type="java.lang.Integer">0034</value>
<value>写代码</value>
<value>玩游戏</value>
</list>
</property>
<property name="gameTitles">
<map>
<entry key="LOL" value="嘴强王者"></entry>
<entry key="王者农药" value="甩锅大神"></entry>
<entry key="和平精英">
<null />
</entry>
</map>
</property>
<property name="nickName">
<null />
</property>
/**
* 演示注入集合、map类型
*/
@Setter
@Getter
public class xxx {
private List<Object> hobbies;
private Map<String, Object> gameTitles;
private String nickName;
}
public void testInjectListMap(ApplicationContext ctx) {
XXX xxx = ctx.getBean(XXX.class);
// 获取注入的list属性
List<Object> hobbies = saisai.getHobbies();
for(Object hobby : hobbies) {
System.out.println("类型:" + hobby.getClass()+", 值:" + hobby);
}
// 获取注入的map属性
System.out.println(xxx.getGameTitles());
// 获取普通属性nickName
System.out.println("nickName: " + xxx.getNickName());
}
Spring 注入复合属性值
<!-- 注入集合、Map类型参数 -->
<bean id="aa" class="com.XXX.spring.bean.XXX">
<property name="hobbies">
<list>
<value>游泳</value>
<value type="java.lang.Integer">0034</value>
<value>写代码</value>
<value>玩游戏</value>
</list>
</property>
<property name="gameTitles">
<map>
<entry key="LOL" value="嘴强王者"></entry>
<entry key="王者农药" value="甩锅大神"></entry>
<entry key="和平精英">
<null />
</entry>
</map>
/**
* 演示注入关联属性
*/
@Setter
@Getter
public class YYY {
private int age;
}
/**
* 演示注入集合、map类型
*/
@Setter
@Getter
public class xxx {
private List<Object> hobbies;
private Map<String, Object> gameTitles;
private String nickName;
private YYY yyy;
}
public void testInjectListMap(ApplicationContext ctx) {
// 获取关联对象中的属性值
System.out.println("YYY中的age: " + XXX.getYYY().getAge());
}
Spring 注入外部properties文件中的属性值
有两种方式,推荐使用使用第二种
<!--
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:jdbc.properties" />
</bean>
-->
<!-- 通过下面这种方式和上面等效 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
p:driverClassName="${jdbc.driver.className}"
p:url="${jdbc.url}"
p:username="${jdbc.username}"
p:password="${jdbc.password}"
p:maxIdle="${jdbc.maxIdle}"
p:minIdle="${jdbc.minIdle}"
p:maxActive="${jdbc.maxActive}"
/>
jdbc.driver.className=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mydb
jdbc.username=root
jdbc.password=root
jdbc.maxIdle=3
jdbc.minIdle=1
jdbc.maxActive=10
<!-- 将外部properties文件中的属性注入到bean中 -->
<bean id="jdbcConf" class="com.lanou3g.spring.bean.JDBCConf">
<property name="url" value="${jdbc.url}" />
<property name="driver" value="${jdbc.driver}" />
<property name="userName" value="${jdbc.user}" />
<property name="password" value="${jdbc.password}" />
</bean>
Spring 通过p and c 命名空间注入属性
C命名空间和像带参构造函数实例化对象的方式的传统注入方式本质一样,只需要修改一下配置文件的头部信息
就可以通过C:注入属性值,比传统注入方式简单方便
@Getter
@Setter
public class YunJie {
private String sname;
public YunJie() {}
public YunJie(String sname) {
this.sname = sname;
}
public static void main(String[] args) {
YunJie yunJie1 = new YunJie("云姐");
yunJie1.getSname();
YunJie yunJie2 = new YunJie();
yunJie2.setSname("云姐");
yunJie2.getSname();
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<!-- 这里需要配置 -->
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 通过c命名空间来注入构造参数 -->
<<bean id="yunjie2" class="com.lanou3g.spring.bean.YunJie">
<constructor-arg name="sname" value="云姐2" />
</bean>
</beans>
P命名空间和像set一样实例化对象的方式的传统注入方式本质一样,只需要修改一下配置文件的头部信息
就可以通过P:注入属性值,比传统注入方式简单方便
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<!-- 这里需要配置 -->
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 通过p命名空间来注入属性 -->
<!--<bean id="yunjie1" class="com.lanou3g.spring.bean.YunJie">
<property name="sname" value="云姐1" />
</bean>-->
</beans>
/**
* 测试通过p和c命名空间注入属性和构造参数
*/
public void testInjectPAndC(ApplicationContext ctx) {
YunJie yunJie1 = ctx.getBean("yunjie1", YunJie.class);
System.out.println("yunjie1(通过p注入属性setter), sname: " + yunJie1.getSname());
YunJie yunJie2 = ctx.getBean("yunjie2", YunJie.class);
System.out.println("通过c注入构造参数, sname: " +yunJie2.getSname());
}
自动装配(autowire)
自动装配允许我们不用显示给某个bean注入依赖的属性或者构造参数,而交由Spring自动帮我们注入所需的依赖。
Spring给我们提供了以下四种选项
自动装配模式 | 说明 |
---|---|
no | (默认值) 禁止Spring帮我们自动装配依赖,所有依赖由开发者显示注入。 |
byName | 按照bean的名称注入。比如beanA有一个属性beanB需要注入,如果beanA配置了自动装配模式是byName,那么Spring会在容器中找bean的名称为beanB的bean,找到后自动帮我们注入到beanA中。如果没找到,则不会注入。 |
byType | 按照bean的类型注入。byType模式需要保证容器中符合注入的类型的bean只有一个,如果匹配的类型有不止一个bean,那么会直接抛出运行时异常。如果没有一个匹配类型的bean,则不会注入。 |
constructor | 类似于byType。但这种模式应用的对象是构造参数。如果构造参数需要注入参数类型的bean有不止一个,同样会抛出运行时异常。 |
byName 和 byType,constructor
public class Student {
private String sname;
private String nickName;
private Fruit fruit;
public String getSname() {
return this.sname;
}
public String getNickName() {
return this.nickName;
}
public Fruit getFruit() {return this.fruit;}
public void setFruit(Fruit fruit) {
this.fruit = fruit;
}
}
/**
* 自动装配属性
*/
@Getter
@Setter
public class AutoInjectByNameTeacher {
private String tname;
private Student student;
}
/**
* 自动装配属性
*/
@Getter
@Setter
public class AutoInjectByTypeTeacher {
public AutoInjectByTypeTeacher() {}
public AutoInjectByTypeTeacher(LazyHeJinJie lazyHeJinJie) {
System.out.println("构造参数传入: " + lazyHeJinJie);
this.lazyStudent = lazyHeJinJie;
}
private String tname;
private LazyHeJinJie lazyStudent;
}
<?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="teacherByName" class="com.lanou3g.spring.bean.AutoInjectByNameTeacher"
autowire="byName">
<property name="tname" value="John" />
<bean id="teacherByType" class="com.lanou3g.spring.bean.AutoInjectByTypeTeacher"
autowire="byType">
<property name="tname" value="John" />
</bean>
<bean id="teacherByConstructor" class="com.lanou3g.spring.bean.AutoInjectByConstructorTeacher"
autowire="constructor">
<property name="tname" value="John" />
</bean>
</bean>
public void testAutoInject(ApplicationContext ctx) {
// 按照名称自动注入属性
AutoInjectByNameTeacher teacher = ctx.getBean("teacherByName" , AutoInjectByNameTeacher.class);
System.out.println("教师名称: " + teacher.getTname());
System.out.println("所教学生:" + teacher.getStudent().getSname());
// 按照类型自动注入属性(容器中符合此类型的bean只能有一个,否则报错)
AutoInjectByTypeTeacher teacherByType = ctx.getBean("teacherByType" , AutoInjectByTypeTeacher.class);
System.out.println("教师名称(类型): " + teacherByType.getTname());
teacherByType.getLazyStudent().destroy();
// 构造器参数自动注入(按照类型)(容器中符合此类型的bean只能有一个,否则报错)
AutoInjectByConstructorTeacher teacherByConstructor = ctx.getBean("teacherByConstructor" , AutoInjectByConstructorTeacher.class);
System.out.println("教师名称(构造参数): " + teacherByConstructor.getTname());
teacherByConstructor.getLazyStudent().destroy();