set注入专题

本篇文章是set注入专题,上篇说了构造注入

​https://blog.51cto.com/u_15485663/6132498​

配合视频教程观看,更易理解吸收,动力节点老杜的Spring6教程采用难度逐步递进的方式,从入门的第一个程序到手写Spring框架,真正的能够让小白成为老手。如果你是老程序员不妨看看手写Spring框架,也会让你受益颇多。

1. 注入外部Bean

在之前4.2.1中使用的案例就是注入外部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="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>

<bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
<property name="userDao" ref="userDaoBean"/>
</bean>

</beans>

跟老杜手撕Spring6教程(十)set注入专题-1_xml

外部Bean的特点:bean定义到外面,在property标签中使用ref属性进行注入。通常这种方式是常用。

2. 注入内部Bean

内部Bean的方式:在bean标签中嵌套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="userServiceBean" class="com.powernode.spring6.service.UserService">
<property name="userDao">
<bean class="com.powernode.spring6.dao.UserDao"/>
</property>
</bean>

</beans>
@Test
public void testInnerBean(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-inner-bean.xml");
UserService userService = applicationContext.getBean("userServiceBean", UserService.class);
userService.save();
}

跟老杜手撕Spring6教程(十)set注入专题-1_java_02

执行测试程序:

跟老杜手撕Spring6教程(十)set注入专题-1_java_03

​这种方式作为了解。

3. 注入简单类型

我们之前在进行注入的时候,对象的属性是另一个对象。

public class UserService{

private UserDao userDao;

public void setUserDao(UserDao userDao){
this.userDao = userDao;
}

}

那如果对象的属性是int类型呢?

public class User{

private int age;

public void setAge(int age){
this.age = age;
}

}

可以通过set注入的方式给该属性赋值吗?

  • 当然可以。因为只要能够调用set方法就可以给属性赋值。

编写程序给一个User对象的age属性赋值20:

第一步:定义User类,提供age属性,提供age属性的setter方法。

package com.powernode.spring6.beans;

/**
* @author 动力节点
* @version 1.0
* @className User
* @since 1.0
**/
public class User {
private int age;

public void setAge(int age) {
this.age = age;
}

@Override
public String toString() {
return "User{" +
"age=" + age +
'}';
}
}

第二步:编写spring配置文件:spring-simple-type.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">
<bean id="userBean" class="com.powernode.spring6.beans.User">
<!--如果像这种int类型的属性,我们称为简单类型,这种简单类型在注入的时候要使用value属性,不能使用ref-->
<!--<property name="age" value="20"/>-->
<property name="age">
<value>20</value>
</property>
</bean>
</beans>

第三步:编写测试程序

@Test
public void testSimpleType(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-simple-type.xml");
User user = applicationContext.getBean("userBean", User.class);
System.out.println(user);
}

第四步:运行测试程序

跟老杜手撕Spring6教程(十)set注入专题-1_xml_04

需要特别注意:如果给简单类型赋值,使用value属性或value标签。而不是ref。

简单类型包括哪些呢?可以通过Spring的源码来分析一下:BeanUtils类

public class BeanUtils{

//.......

/**
* Check if the given type represents a "simple" property: a simple value
* type or an array of simple value types.
* <p>See {@link #isSimpleValueType(Class)} for the definition of <em>simple
* value type</em>.
* <p>Used to determine properties to check for a "simple" dependency-check.
* @param type the type to check
* @return whether the given type represents a "simple" property
* @see org.springframework.beans.factory.support.RootBeanDefinition#DEPENDENCY_CHECK_SIMPLE
* @see org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#checkDependencies
* @see #isSimpleValueType(Class)
*/
public static boolean isSimpleProperty(Class<?> type) {
Assert.notNull(type, "'type' must not be null");
return isSimpleValueType(type) || (type.isArray() && isSimpleValueType(type.getComponentType()));
}

/**
* Check if the given type represents a "simple" value type: a primitive or
* primitive wrapper, an enum, a String or other CharSequence, a Number, a
* Date, a Temporal, a URI, a URL, a Locale, or a Class.
* <p>{@code Void} and {@code void} are not considered simple value types.
* @param type the type to check
* @return whether the given type represents a "simple" value type
* @see #isSimpleProperty(Class)
*/
public static boolean isSimpleValueType(Class<?> type) {
return (Void.class != type && void.class != type &&
(ClassUtils.isPrimitiveOrWrapper(type) ||
Enum.class.isAssignableFrom(type) ||
CharSequence.class.isAssignableFrom(type) ||
Number.class.isAssignableFrom(type) ||
Date.class.isAssignableFrom(type) ||
Temporal.class.isAssignableFrom(type) ||
URI.class == type ||
URL.class == type ||
Locale.class == type ||
Class.class == type));
}

//........
}

跟老杜手撕Spring6教程(十)set注入专题-1_java_05通过源码分析得知,简单类型包括:

  • 基本数据类型
  • 基本数据类型对应的包装类
  • String或其他的CharSequence子类
  • Number子类
  • Date子类
  • Enum子类
  • URI
  • URL
  • Temporal子类
  • Locale
  • Class
  • 另外还包括以上简单值类型对应的数组类型。

经典案例:给数据源的属性注入值:

假设我们现在要自己手写一个数据源,我们都知道所有的数据源都要实现javax.sql.DataSource接口,并且数据源中应该有连接数据库的信息,例如:driver、url、username、password等。

package com.powernode.spring6.beans;

import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;

/**
* @author 动力节点
* @version 1.0
* @className MyDataSource
* @since 1.0
**/
public class MyDataSource implements DataSource {
private String driver;
private String url;
private String username;
private String password;

public void setDriver(String driver) {
this.driver = driver;
}

public void setUrl(String url) {
this.url = url;
}

public void setUsername(String username) {
this.username = username;
}

public void setPassword(String password) {
this.password = password;
}

@Override
public String toString() {
return "MyDataSource{" +
"driver='" + driver + '\'' +
", url='" + url + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}

@Override
public Connection getConnection() throws SQLException {
return null;
}

@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}

@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}

@Override
public void setLogWriter(PrintWriter out) throws SQLException {

}

@Override
public void setLoginTimeout(int seconds) throws SQLException {

}

@Override
public int getLoginTimeout() throws SQLException {
return 0;
}

@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}

@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}

@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
}

我们给driver、url、username、password四个属性分别提供了setter方法,我们可以使用spring的依赖注入完成数据源对象的创建和属性的赋值吗?看配置文件

<?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="dataSource" class="com.powernode.spring6.beans.MyDataSource">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>

</beans>

测试程序:

@Test
public void testDataSource(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-datasource.xml");
MyDataSource dataSource = applicationContext.getBean("dataSource", MyDataSource.class);
System.out.println(dataSource);
}

执行测试程序:

跟老杜手撕Spring6教程(十)set注入专题-1_spring_06

​你学会了吗?

接下来,我们编写一个程序,把所有的简单类型全部测试一遍:

编写一个类A:

package com.powernode.spring6.beans;

import java.net.URI;
import java.net.URL;
import java.time.LocalDate;
import java.util.Date;
import java.util.Locale;

/**
* @author 动力节点
* @version 1.0
* @className A
* @since 1.0
**/
public class A {
private byte b;
private short s;
private int i;
private long l;
private float f;
private double d;
private boolean flag;
private char c;

private Byte b1;
private Short s1;
private Integer i1;
private Long l1;
private Float f1;
private Double d1;
private Boolean flag1;
private Character c1;

private String str;

private Date date;

private Season season;

private URI uri;

private URL url;

private LocalDate localDate;

private Locale locale;

private Class clazz;

// 生成setter方法
// 生成toString方法
}

enum Season {
SPRING, SUMMER, AUTUMN, WINTER
}
<?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="a" class="com.powernode.spring6.beans.A">
<property name="b" value="1"/>
<property name="s" value="1"/>
<property name="i" value="1"/>
<property name="l" value="1"/>
<property name="f" value="1"/>
<property name="d" value="1"/>
<property name="flag" value="false"/>

<property name="c" value="a"/>
<property name="b1" value="2"/>
<property name="s1" value="2"/>
<property name="i1" value="2"/>
<property name="l1" value="2"/>
<property name="f1" value="2"/>
<property name="d1" value="2"/>
<property name="flag1" value="true"/>
<property name="c1" value="a"/>

<property name="str" value="zhangsan"/>
<!--注意:value后面的日期字符串格式不能随便写,必须是Date对象toString()方法执行的结果。-->
<!--如果想使用其他格式的日期字符串,就需要进行特殊处理了。具体怎么处理,可以看后面的课程!!!!-->
<property name="date" value="Fri Sep 30 15:26:38 CST 2022"/>
<property name="season" value="WINTER"/>
<property name="uri" value="/save.do"/>
<!--spring6之后,会自动检查url是否有效,如果无效会报错。-->
<property name="url" value="http://www.baidu.com"/>
<property name="localDate" value="EPOCH"/>
<!--java.util.Locale 主要在软件的本地化时使用。它本身没有什么功能,更多的是作为一个参数辅助其他方法完成输出的本地化。-->
<property name="locale" value="CHINESE"/>
<property name="clazz" value="java.lang.String"/>
</bean>
</beans>

编写测试程序:

@Test
public void testAllSimpleType(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-all-simple-type.xml");
A a = applicationContext.getBean("a", A.class);
System.out.println(a);
}

执行结果如下:

跟老杜手撕Spring6教程(十)set注入专题-1_spring_07

需要注意的是:

  • 如果把Date当做简单类型的话,日期字符串格式不能随便写。格式必须符合Date的toString()方法格式。显然这就比较鸡肋了。如果我们提供一个这样的日期字符串:2010-10-11,在这里是无法赋值给Date类型的属性的。
  • spring6之后,当注入的是URL,那么这个url字符串是会进行有效性检测的。如果是一个存在的url,那就没问题。如果不存在则报错。