测试准备
准备一个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引用属性
进行注入
<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>
<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
<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>
简单类型的注入和分类
Spring的源码来分析有哪些简单类型: BeanUtils类
(按两次shift键搜索)
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类型的属性赋值
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
}
<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"/>
<property name="date" value="Fri Sep 30 15:26:38 CST 2022"/>
<property name="season" value="WINTER"/>
<property name="uri" value="/save.do"/>
<property name="url" value="http://www.baidu.com"/>
<property name="localDate" value="EPOCH"/>
<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方法)
<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>
测试准备
准备一个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
属性指定为构造方法的哪个形参赋值, 对于没有赋值的形参遵循属性的默认赋值规范
<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">
<constructor-arg index="0" ref="orderDaoBean"/>
<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可以快速精确到具体形参
<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
<constructor-arg ref="orderDaoBean"/>
<constructor-arg ref="userDaoBean"/>
bean>
<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
<constructor-arg ref="userDaoBean"/>
<constructor-arg ref="orderDaoBean"/>
bean>
<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注入是基于set方法实现的,底层会通过反射机制调用属性对应的setter方法然后给属性赋值
构造注入
构造注入简单类型和复杂类型的常见方式
构造注入是基于构造方法实现的,底层会通过反射机制调用对应的构造方法给属性赋值
type
属性指定形参的类型让Spring可以快速精确到具体到哪个形参index和name
属性指定为构造方法的哪个形参赋值, 对于没有赋值的形参遵循属性的默认赋值规范