[学习笔记] Spring-DI

3 篇文章 0 订阅

依赖注入

Spring Ioc简单来讲就是让spring框架(或者说容器, 工厂)帮我们创建bean, 而DI(依赖注入)则是spring帮我们注入属性

注入是什么意思? 简单来讲就是给对象设置值

注入的方式:

  • 方式1: 调用对象的setter方法
  • 方式2: 创建对象时(即调用对象构造器时)设置对象的属性值

xml自动装配(不推荐)

自动装配对应bean元素的autowire属性, 让spring按照一定的规则自己去找对应的对象, 并完成DI操作.

可选值有:

  • default: 不要自动注入, 相当于no
  • no: 不要自动注入
  • byName: 按照属性的id在spring中寻找bean, 类似factory.getBean(String beanName);
  • byType: 按照依赖对象的类型注入, 类似factory.getBean(Class requiredType)
  • constructor: 按照对象的构造器上面的参数类型注入

bean:

package _02_DI._01_xml_autowire;

public class SomeBean {
	
	private OtherBean otherBean;

	public void setOtherBean(OtherBean otherBean) {
		this.otherBean = otherBean;
	}
	@Override
	public String toString() {
		return "SomeBean [otherBean=" + otherBean + "]";
	}
}

class OtherBean {
	
}

配置文件:

	<bean id="otherBean" class="_02_DI._01_xml_autowire.OtherBean" />
	<bean id="someBean" class="_02_DI._01_xml_autowire.SomeBean" autowire="byType" />

测试代码:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:_02_DI/_01_xml_autowire/AppTest.xml")
public class App {
	
	@Autowired
	private SomeBean someBean;
	
	@Test
	public void testXmlAutoWire() throws Exception {
		System.out.println(someBean);
	}
}

通过setter方法注入

使用bean元素中的property子元素, 通过对象的setter方法完成设置操作

注入值类型:

  • 常量值(简单类型), value属性
  • 对象, ref属性
  • 集合, 对应集合类型元素

value属性给简单类型设置值

bean:

package _02_DI._02_setter_wire;

public class Obj {
	private String name;
	private Integer age;

	public void setName(String name) {
		this.name = name;
	}
	public void setAge(Integer age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "Obj [name=" + name + ", age=" + age + "]";
	}
}

xml:

	<bean id="obj" class="_02_DI._02_setter_wire.Obj">
		<property name="name">
			<value>Fighter</value>
		</property>
		
		<property name="age" value="21" />
	</bean>

注意:

  • name属性的值是bean中被注入的属性的名称; value属性(value子元素)的值是被注入的值
  • 两种设置值的方式等价
  • 传入时都传入的是字符串, spring会自动转换类型

测试代码:

	@Autowired
	private Obj obj;
	
	@Test
	public void test01() throws Exception {
		System.out.println(obj);
	}

ref属性给对象类型设置值

bean:

package _02_DI._02_setter_wire._02_obj_setter;

public class Obj {
	private Wired wired;

	public void setWired(Wired wired) {
		this.wired = wired;
	}

	@Override
	public String toString() {
		return "Obj [wired=" + wired + "]";
	}
}

class Wired {

} 

xml:

	<bean id="wired" class="_02_DI._02_setter_wire._02_obj_setter.Wired" />
	<bean id="obj" class="_02_DI._02_setter_wire._02_obj_setter.Obj">
		<property name="wired" ref="wired" />
	</bean>

ref属性与被注入的对象的id属性相同

给集合类型设置值

bean:

package _02_DI._02_setter_wire._03_collection_wire;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

public class CollectionBean {
	private Set<String> s;
	private List<String> l;
	private String[] array;
	private Map<String, String> map;
	private Properties p;
	
	public void setS(Set<String> s) {
		this.s = s;
	}
	public void setL(List<String> l) {
		this.l = l;
	}
	public void setArray(String[] array) {
		this.array = array;
	}
	public void setMap(Map<String, String> map) {
		this.map = map;
	}
	public void setP(Properties p) {
		this.p = p;
	}

	@Override
	public String toString() {
		return "CollectionBean [\ns=" + s + ", \nl=" + l + ", \narray="
				+ Arrays.toString(array) + ", \nmap=" + map + ", \np=" + p + "\n]";
	}
}

配置文件:

<bean id="cb" class="_02_DI._02_setter_wire._03_collection_wire.CollectionBean">
		<!-- 给属性注入值一律用property子元素 -->
		<property name="s">
			<set>
				<value>set1</value>
				<value>set2</value>
			</set>
		</property>
		
		<property name="l">
			<list>
				<value>list1</value>
				<value>list2</value>
			</list>
		</property>
		
		<property name="array">
			<array>
				<value>array1</value>
				<value>array2</value>
			</array>
		</property>
		
		<property name="map">
			<map>
				<entry key="key1" value="value1" />
				<entry key="key2" value="value2" />
			</map>
		</property>
		
		<property name="p">
			<value>
				p1=v1
				p2=v2
			</value>
		</property>
	</bean>

说明:

  • 给属性注入值一律用property子元素, 不管是简单类型还是对象类型还是集合类型
  • map类型由键值对组成, 所以需要使用entry子元素, 在entry中设置key和value
  • properties类型其实本质上属于map类型, 所以使用和map一样的注入方式也是可以的. 但更推荐使用演示的这种写法
  • 经过测试, 发现set, list和array这三个其实是可以乱用的, 原因查明了再回来补, 留个坑

构造器注入

以上演示的是setter方法注入, 底层调用对象的setter方法, 下面的案例演示通过构造器注入(bean不用提供setter方法)

setter注入使用的是<property>元素, 表示bean的属性; 而构造器注入使用的是<constructor-arg>元素

构造器注入有多钟方式, 如按照默认顺序, 使用index, 使用type等, 但不推荐, 推荐使用name, 与<property>元素里的name一样代表属性的名字, 下面案例很容易看懂其用法

bean:

package _02_DI._02_setter_wire._04_constructor_setter;

import java.util.List;
import java.util.Properties;

public class Bean {
	private String name;
	private Integer age;
	private OtherBean otherBean;
	private List<String> list;
	private Properties p;

	public Bean(String name, Integer age, OtherBean otherBean,
			List<String> list, Properties p) {
		this.name = name;
		this.age = age;
		this.otherBean = otherBean;
		this.list = list;
		this.p = p;
	}

	@Override
	public String toString() {
		return "Bean [name=" + name + ", age=" + age + ", otherBean="
				+ otherBean + ", list=" + list + ", p=" + p + "]";
	}
}

class OtherBean{
	
}

配置文件:

	<bean id="ob" class="_02_DI._02_setter_wire._04_constructor_setter.OtherBean" /> 
	<bean id="bean" class="_02_DI._02_setter_wire._04_constructor_setter.Bean">
		<constructor-arg name="name" value="Fighter" />
		<constructor-arg name="age" value="21" />
        
		<constructor-arg name="otherBean" ref="ob" />
        
		<constructor-arg name="list">
			<list>
				<value>list1</value>
				<value>list2</value>
			</list>
		</constructor-arg>
        
		<constructor-arg name="p">
			<value>
				k1=v1
				k2=v2
			</value>
		</constructor-arg>
	</bean>

bean元素的继承

先看一下这个案例(普通的bean注入的演示):

bean:

package _02_DI._03_inheritance;

public class Bean1 {
	private String name;
	private Integer age;
	private Integer id;
	
	public void setName(String name) {
		this.name = name;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	
	@Override
	public String toString() {
		return "Bean1 [name=" + name + ", age=" + age + ", id=" + id + "]";
	}
}
//---------------------------------------------
package _02_DI._03_inheritance;

public class Bean2 {
	private String name;
	private Integer age;
	private String color;
	
	public void setName(String name) {
		this.name = name;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	public void setColor(String color) {
		this.color = color;
	}
	
	@Override
	public String toString() {
		return "Bean2 [name=" + name + ", age=" + age + ", color=" + color
				+ "]";
	}
}

这里有两个bean, 两者都有name属性和age属性

配置文件:

	<bean id="bean1" class="_02_DI._03_inheritance.Bean1">
		<property name="name" value="Fighter" />
		<property name="age" value="21" />
		<property name="id" value="201603775" />
	</bean>

	<bean id="bean2" class="_02_DI._03_inheritance.Bean2">
		<property name="name" value="Fighter" />
		<property name="age" value="21" />
		<property name="color" value="deepskyblue" />
	</bean>

可以发现, 两个bean的两个属性是一样的, 而且值也一样, 这样就可以把相同的地方抽取出来

现在配置文件应该这样写:

	<bean id="base_bean" abstract="true">
		<property name="name" value="Fighter" />
		<property name="age" value="21" />
	</bean>

	<bean id="bean1" class="_02_DI._03_inheritance.Bean1" parent="base_bean">
		<property name="id" value="201603775" />
	</bean>

	<bean id="bean2" class="_02_DI._03_inheritance.Bean2" parent="base_bean">
		<property name="color" value="deepskyblue" />
	</bean>

说明:

  • abstract="true"表示spring不需要为我们创建对象
  • bean的继承和java继承不一样, bean继承是把相同的代码抽取出来, 相当于一段xml代码的拷贝
  • bean元素的parent属性表示继承于哪一个bean

配置数据库连接池

之前给数据库连接池设置连接四要素时是这样设置的(以druid为例):

public class App {
	
	private DruidDataSource ds;
	
	@Test
	public void test01() throws Exception {
		ds = new DruidDataSource();
		ds.setDriverClassName("com.mysql.jdbc.Driver");
		ds.setUrl("jdbc:mysql://localhost:3306/jdbcdemo");
		ds.setUsername("root");
		ds.setPassword("1092568516");
		Connection conn = ds.getConnection();
        ...
	}
}

观察一下可以发现, 创建连接池对象实际上就是new出来一个连接池, 然后给它设置属性

现在我们交给spring帮我们管理连接池:

配置文件:

	<bean id="ds" class="com.alibaba.druid.pool.DruidDataSource">
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="jdbc:mysql://localhost:3306/jdbcdemo" />
		<property name="username" value="root" />
		<property name="password" value="1092568516" />
	</bean>

测试代码:

	@Autowired
	private DruidDataSource ds;
	
	@Test
	public void test01() throws Exception {
		Connection conn = ds.getConnection();
        ...
	}

但是上述代码耦合度太高了, 应该把连接数据库的四要素写在properties文件里面, 方便维护(比如哪天不用MySql而用用其他数据库, 不用修改源代码), 然后让xml配置文件加载properties文件
properties文件:

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcdemo
username=root
password=1092568516

现在就该让xml配置文件读取properties文件了:

<?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">
    
    <!-- 加载db.properties配置文件 -->    
    <context:property-placeholder location="classpath:db.properties" />

	<bean id="ds" class="com.alibaba.druid.pool.DruidDataSource">
		<property name="driverClassName" value="${driverClassName}" />
		<property name="url" value="${url}" />
		<property name="username" value="${username}" />
		<property name="password" value="${password}" />
	</bean>
	
</beans>

一运行会发现报错, 原因是因为java中当前系统账户的属性也叫username, 为避免冲突, 在properties文件中所有key值前加上jdbc.

新的properties:

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/jdbcdemo
jdbc.username=root
jdbc.password=1092568516

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">
    
    <!-- 加载db.properties配置文件 -->    
    <context:property-placeholder location="classpath:db.properties" />

	<bean id="ds" class="com.alibaba.druid.pool.DruidDataSource">
		<property name="driverClassName" value="${jdbc.driverClassName}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
	</bean>
	
</beans>

现在就没问题了


注册测试案例

到这里IoC和DI差不多就这些了, 下面讲一个注册的案例, 将之前的知识点融合一下

domain类:

package _02_DI._05_register.domain;

@Getter@Setter@toString
public class User {
	private Long id;
	private String username;
	private int age;

新建dao包, 包下新建IUserDao接口和其实现类:

package _02_DI._05_register.dao;

import _02_DI._05_register.domain.User;

public interface IUserDao {
	void save(User u);
}
package _02_DI._05_register.dao.impl;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import javax.sql.DataSource;

import _02_DI._05_register.dao.IUserDao;
import _02_DI._05_register.domain.User;

public class UserDaoImpl implements IUserDao {

	private DataSource ds;
	public void setDs(DataSource ds) {
		this.ds = ds;
	}

	public void save(User u) {
		Connection conn = null;
		PreparedStatement ps = null;
		try {
			conn = ds.getConnection();
			String sql = "INSERT INTO `t_student` (sname, age) VALUE (?, ?)";
			ps = conn.prepareStatement(sql);
			ps.setString(1, u.getUsername());
			ps.setInt(2, u.getAge());
			ps.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally{
			if(ps != null){
				try {
					ps.close();
				} catch (SQLException e) {
					e.printStackTrace();
				} finally{
					if(conn != null){
						try {
							conn.close();
						} catch (SQLException e) {
							e.printStackTrace();
						} 
					}
				}
			}
		}
	}

}

然后在xml配置文件中配置数据库连接池和dao的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"
    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">
    
    <!-- 加载db.properties配置文件 -->    
    <context:property-placeholder location="classpath:db.properties" />

	<bean id="ds" class="com.alibaba.druid.pool.DruidDataSource">
		<property name="driverClassName" value="${jdbc.driverClassName}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
	</bean>
	
	<bean id="userDao" class="_02_DI._05_register.dao.impl.UserDaoImpl">
		<property name="ds" ref="ds" />
	</bean>

</beans>

新建service包, 包下新建IUserService接口和其实现类, 实现类中调用dao中的save方法

package _02_DI._05_register.service;

import _02_DI._05_register.domain.User;

public interface IUserService {
	void Register(User u);
}
package _02_DI._05_register.service.impl;

import _02_DI._05_register.dao.IUserDao;
import _02_DI._05_register.domain.User;
import _02_DI._05_register.service.IUserService;

public class UserServiceImpl implements IUserService {

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

	public void Register(User u) {
		userDao.save(u);
	}

}

配置文件中添加service的配置:

	<bean id="userService" class="_02_DI._05_register.service.impl.UserServiceImpl">
		<property name="userDao" ref="userDao" />
	</bean>

新建action包, 包下新建UserAction类, 调用service的register方法:

package _02_DI._05_register.action;

import _02_DI._05_register.domain.User;
import _02_DI._05_register.service.IUserService;

public class UserAction {

	private IUserService userService;
	public void setUserService(IUserService userService) {
		this.userService = userService;
	}

	public String execute(){
		userService.Register(new User());
		return "Success";
	}
}

配置文件添加action的bean配置:

	<bean id="userAction" class="_02_DI._05_register.action.UserAction">
		<property name="userService" ref="userService" />
	</bean>

测试代码, 注入UserAction类, 调用其execute方法, execute方法会调用service的register方法, service会调用dao的save方法:

package _02_DI._05_register;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import _02_DI._05_register.action.UserAction;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:_02_DI/_05_register/AppTest.xml")
public class App {
	
	@Autowired
	private UserAction userAction;
	
	@Test
	public void testDao(){
		userAction.execute();
	}
}

最后运行成功, 但数据库新增的一行中没有数据(因为没有给User设置属性)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值