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>外部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>@Testpublic void testInnerBean(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-inner-bean.xml"); UserService userService = applicationContext.getBean("userServiceBean", UserService.class); userService.save();}执行测试程序:
这种方式作为了解。
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>第三步:编写测试程序
@Testpublic void testSimpleType(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-simple-type.xml"); User user = applicationContext.getBean("userBean", User.class); System.out.println(user);}第四步:运行测试程序
需要特别注意:如果给简单类型赋值,使用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)); } //........}通过源码分析得知,简单类型包括:
- 基本数据类型
- 基本数据类型对应的包装类
- 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>测试程序:
@Testpublic void testDataSource(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-datasource.xml"); MyDataSource dataSource = applicationContext.getBean("dataSource", MyDataSource.class); System.out.println(dataSource);}执行测试程序:
你学会了吗?
接下来,我们编写一个程序,把所有的简单类型全部测试一遍:
编写一个类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>编写测试程序:
@Testpublic void testAllSimpleType(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-all-simple-type.xml"); A a = applicationContext.getBean("a", A.class); System.out.println(a);}执行结果如下:
需要注意的是:
- 如果把Date当做简单类型的话,日期字符串格式不能随便写。格式必须符合Date的toString()方法格式。显然这就比较鸡肋了。如果我们提供一个这样的日期字符串:2010-10-11,在这里是无法赋值给Date类型的属性的。
- spring6之后,当注入的是URL,那么这个url字符串是会进行有效性检测的。如果是一个存在的url,那就没问题。如果不存在则报错。