set注入复杂类型ref属性
测试准备
准备一个UserDao类和UserService类,其中UserService类关联UserDao类型的属性
public class UserDao {
public void insert(){
System.out.println("正在保存用户数据");
}
}
public class UserService {
private UserDao userDao;
// 使用set方式注入必须提供set方法,反射机制要调用这个方法给属性赋值的
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void save(){
userDao.insert();
}
}
注入外部Bean(常用)
注入外部Bean的方式: 在外部注册一个bean, 然后通过外部bean的id配合property标签的ref引用属性
进行注入
- 对于property标签来说还可以使用子标签ref的bean属性方式引入外部bean(实际开发中一般直接使用ref属性)
<?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">
<!--配置dao-->
<bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>
<!--配置service-->
<!--name属性的属性值怎么写:属性set方法的方法名去掉set,然后把剩下的单词首字母变小写-->
<bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
<property name="userDao" ref="userDaoBean"/>
</bean>
<!--对于property标签来说,ref属性也可以采用标签的方式,但使用ref属性是多数的-->
<bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
<property name="userDao">
<ref bean="userDaoBean"/>
</property>
</bean>
</beans>
注入的bean相当于是从容器中拿的,我们修改从容器中获取的bean或者通过访问被注入对象的属性修改bean都会引起容器中的bean变化
public void testFirst(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
// 从容器中获取要注入的UserDao然后修改属性值,此时注入到UserService的UserDao属性值也发生了变化
UserDao userDaoBean = applicationContext.getBean("userDaoBean", UserDao.class);
userDaoBean.setId(9);
UserService userServiceBean = (UserService)applicationContext.getBean("userServiceBean");
System.out.println(userServiceBean.getUserDao().getId());
// 通过UserService修改UserDao的属性值,此时从容器中获取的UserDao的属性值也发生改变
userServiceBean.getUserDao().setId(6);
System.out.println(userDaoBean.getId());
}
注入内部Bean(了解)
注入内部Bean的方式: 在bean标签中嵌套bean标签
直接注入,注意通过内部bean的id并不能通过容器获取到bean对象,所以不用写id
<?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>
set注入简单类型value属性
简单类型的注入和分类
Spring的源码来分析有哪些简单类型: BeanUtils类
(按两次shift键搜索)
- String类或其他的CharSequence子类,Number子类,Date子类,Enum子类,URI,URL,Temporal子类,Locale,Class,包括以上简单值类型对应的数组类型
public class BeanUtils{
//........
//ctrl + F12搜索isSimpleValueType方法
public static boolean isSimpleValueType(Class<?> type) {
return (Void.class != type && void.class != type &&
(ClassUtils.isPrimitiveOrWrapper(type) ||Enum.class.isAssignableFrom(type) ||
//String实现了CharSequence
CharSequence.class.isAssignableFrom(type) || Number.class.isAssignableFrom(type) ||
Date.class.isAssignableFrom(type) ||
//java8提供的时间和时区类型
Temporal.class.isAssignableFrom(type) ||
URI.class == type || URL.class == type ||
//语言类型
Locale.class == type || Class.class == type));
}
//........
}
注入部分简单类型: 实际开发中我们一般不会把Date当做简单类型而是采用ref属性给Date类型的属性赋值
- 如果把Date当做简单类型的话,日期字符串格式必须符合Date的toString()方法格式
Fri Sep 30 15:26:38 CST 2022
,否则无法赋值给Date类型的属性
// 实体类
public class A {
private String str;
private Date date;
// 枚举类型的属性
private Season season;
// spring6之后要求注入的URL必须存在有效,如果不存在则报错
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="str" value="zhangsan"/>
<!--value后面的日期字符串格式不能随便写,必须是Date对象toString()方法执行的结果,否则报错提示无法转换成Date类型-->
<!--如果想使用其他格式的日期字符串就需要进行特殊处理了,具体怎么处理后面再说-->
<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>
手写数据源并自动注入属性值
我们都知道所有的数据源都要实现javax.sql.DataSource
接口,并且数据源中应该有连接数据库的信息例如driver、url、username、password
等
// 手写一个数据源并交给Spring取管理
public class MyDataSource implements DataSource {
private String driver;
private String url;
private String username;
private String password;
//提供属性的set和toString方法....
@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;
}
}
使用Spring提供的依赖注入完成数据源对象的创建和driver,url,username,password属性的赋值(要求属性提供了setter方法)
<?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">
<!--让Spring管理我们的数据源-->
<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>
构造注入
测试准备
准备一个OrderDao类和OrderService类 , 其中OrderService类关联OrderDao类型的属性
public class OrderDao {
public void deleteById(){
System.out.println("正在删除订单");
}
}
public class UserDao {
public void insert(){
System.out.println("正在添加用户");
}
}
public class OrderService {
private OrderDao orderDao;
private UserDao userDao;
// 通过反射机制调用构造方法给属性赋值(构造方法有两个参数)
public OrderService(OrderDao orderDao, UserDao userDao) {
this.orderDao = orderDao;
this.userDao = userDao;
}
// 构造方法重载
public OrderService(Integer a, UserDao userDao) {
this.userDao = userDao;
}
public void delete(){
orderDao.deleteById();
userDao.insert();
}
}
构造注入的核心原理
通过调用构造方法来给属性赋值,每个constructor-arg
标签对应构造器的一个形参,Spring先通过constructor-arg标签的个数确定调用哪个构造方法
如果构造方法有多个参数,通过index和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="orderDaoBean" class="com.powernode.spring6.dao.OrderDao"/>
<bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>
<!--这里利用构造方法上参数的下标为属性赋值-->
<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
<!--指定为构造方法的第一个参数赋值,第一个参数下标是0-->
<constructor-arg index="0" ref="orderDaoBean"/>
<!--指定为构造方法的第二个参数赋值,第二个参数下标是1-->
<constructor-arg index="1" ref="userDaoBean"/>
</bean>
<!--这里使用了构造方法上参数的名字-->
<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
<constructor-arg name="orderDao" ref="orderDaoBean"/>
<constructor-arg name="userDao" ref="userDaoBean"/>
</bean>
</beans>
如果既不指定下标也不指定参数名字,Spring也会根据实参类型自动推断出要给哪个形参赋值,使用type
属性指定形参的类型让Spring可以快速精确到具体形参
<!--这里没有指定下标也没有指定参数名字,默认按照constructor-arg标签从上到下的顺序给构造方法中对应的形参赋值-->
<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
<constructor-arg ref="orderDaoBean"/>
<constructor-arg ref="userDaoBean"/>
</bean>
<!--这里标签的顺序已经和构造方法的参数顺序不同了,Spring也可以根据实参类型自动推断给哪个形参赋值-->
<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
<constructor-arg ref="userDaoBean"/>
<constructor-arg ref="orderDaoBean"/>
</bean>
<!--这里没有指定下标也没有指定参数名字,利用type属性指定参数的类型-->
<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
<constructor-arg ref="userDaoBean" type="com.powernode.spring6.dao.UserDao"/>
<constructor-arg ref="orderDaoBean" type="com.powernode.spring6.dao.OrderDao"/>
</bean>
set和构造注入的核心实现原理
set注入是对象创建好后注入的,构造注入是对象实例化过程当中注入
set注入
set注入简单类型和复杂类型的常见方式
- 利用property标签的value属性给对象的属性注入简单类型,ref属性给对象的属性注入复杂类型
set注入是基于setter方法实现的,底层会通过反射机制调用属性对应的setter方法然后给属性赋值
- set注入要求实体类的属性必须对外提供setter方法, 并且想让Spring调用对应的setter方法还需要设置property标签
- Spring先拿到property标签中的name属性值 ,然后首字母大写前面加上set , 最后通过反射机制调用实体类中对应的setXxx方法为该属性赋值
- set注入只会调用实体类属性对应的setter方法为属性赋值与属性名无关,如果所有getter/setter都自动生成,那么property标签的name属性的值就可以写属性名
- 对于实体类中没有赋值的属性,遵循属性默认赋值规范
构造注入
构造注入简单类型和复杂类型的常见方式
- 利用constructor-arg标签的value属性给形参注入简单类型,ref属性注入复杂类型
构造注入是基于构造方法实现的,底层会通过反射机制调用对应的构造方法给属性赋值
- 通过constructor-arg标签个数确定调用哪个构造方法,即使形参的个数相同但类型一定不都相同,Spring会根据实参类型自动推断出要给哪个构造方法的形参赋值
- 如果构造方法有多个参数,通过
index和name
属性指定为构造方法的哪个形参赋值, 对于没有赋值的形参遵循属性的默认赋值规范