SpringIOC随笔(六)-依赖注入(DI)
-
DI(Dependency Injection)
-
概念:对象之间的关系的装配交给容器来管理。
-
注入的方式:
-
构造注入:
-
顾名思义,构造注入就是使用构造方法注入,所以使用构造注入肯定要写对应的构造方法。下面直接上例子:
-
public class User implements Serializable { private static final long serialVersionUID = 84601116866236075L; private String username; private String password; private Department department; public User() { } public User(String username, String password) { this.username = username; this.password = password; } public User(String username, String password, Department department) { this.username = username; this.password = password; this.department = department; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Department getDepartment() { return department; } public void setDepartment(Department department) { this.department = department; } @Override public String toString() { return "User{" + "username='" + username + '\'' + ", password='" + password + '\'' + ", department=" + department + '}'; } } public class Department implements Serializable { private static final long serialVersionUID = -7390074405145933835L; private Integer id; private String departmentName; private String address; public Department() { } public Department(Integer id, String departmentName, String address) { this.id = id; this.departmentName = departmentName; this.address = address; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getDepartmentName() { return departmentName; } public void setDepartmentName(String departmentName) { this.departmentName = departmentName; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "Department{" + "id=" + id + ", departmentName='" + departmentName + '\'' + ", address='" + address + '\'' + '}'; } }
-
<?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="user" class="com.fxyh.spring.di.User"> <constructor-arg index="0" value="zhangsan"/> <constructor-arg index="1" value="123456"/> <!--ref方式--> <!--<constructor-arg index="2" ref="department"/>--> <constructor-arg index="2"> <!--内部bean方式--> <bean class="com.fxyh.spring.di.Department"> <constructor-arg index="0" value="2"/> <constructor-arg index="1" value="Ali"/> <constructor-arg index="2" value="hangzhou"/> </bean> </constructor-arg> </bean> <bean id="department" class="com.fxyh.spring.di.Department"> <constructor-arg index="0" value="1"/> <constructor-arg index="1" value="Baidu"/> <constructor-arg index="2" value="shengzhen"/> </bean> </beans>
-
@ContextConfiguration("classpath*:applicationContext-di.xml") @RunWith(SpringJUnit4ClassRunner.class) public class UserTest { @Autowired private User user; @Test public void test() { System.out.println(this.user); } }
-
这里使用的是Spring-test,然后省去了很多代码。
-
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>4.3.21.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency>
-
-
-
setter注入
- 使用setXxx方法注入的方式,这里不提了。
-
集合注入
-
array,list,set,map,properties注入。
-
<bean id="collectionDemo" class="com.fxyh.spring.di.CollectionDemo"> <property name="arrayValue"> <array> <value>zhangsan</value> <value>lisi</value> <value>wangwu</value> </array> </property> <property name="listValue"> <list> <value>beijin</value> <value>shanghai</value> <value>hangzhou</value> <value>shenzhen</value> </list> </property> <property name="setValue"> <set> <value>aaa</value> <value>bbb</value> <value>ccc</value> <value>ddd</value> </set> </property> <property name="mapValue"> <map> <entry key="key1" value="111"/> <entry key="key2" value="222"/> <entry key="key3" value="333"/> <entry key="key4" value="444"/> </map> </property> <property name="properties"> <props> <prop key="driverClassName">com.mysql.jdbc.Driver</prop> <prop key="url">jdbc:mysql:///fxyh</prop> <prop key="username">root</prop> <prop key="password">root</prop> </props> </property> </bean>
-
public class CollectionDemo implements Serializable { private static final long serialVersionUID = -4477381744799533624L; private String[] arrayValue; private List<String> listValue; private Set<String> setValue; private Map<String, Object> mapValue; private Properties properties; public String[] getArrayValue() { return arrayValue; } public void setArrayValue(String[] arrayValue) { this.arrayValue = arrayValue; } public List<String> getListValue() { return listValue; } public void setListValue(List<String> listValue) { this.listValue = listValue; } public Set<String> getSetValue() { return setValue; } public void setSetValue(Set<String> setValue) { this.setValue = setValue; } public Map<String, Object> getMapValue() { return mapValue; } public void setMapValue(Map<String, Object> mapValue) { this.mapValue = mapValue; } public Properties getProperties() { return properties; } public void setProperties(Properties properties) { this.properties = properties; } @Override public String toString() { return "CollectionDemo{" + "arrayValue=" + Arrays.toString(arrayValue) + ", listValue=" + listValue + ", setValue=" + setValue + ", mapValue=" + mapValue + ", properties=" + properties + '}'; } }
-
-
抽象Bean
-
把公共的东西抽象出来,然后需要用到的使用parent。
-
<bean id="abstractDemo" abstract="true"> <property name="id" value="1"/> <property name="username" value="zhangsan"/> </bean> <bean id="abstractDemo1" class="com.fxyh.spring.di.AbstractDemo1" parent="abstractDemo"> <property name="password" value="123456"/> </bean> <bean id="abstractDemo2" class="com.fxyh.spring.di.AbstractDemo2" parent="abstractDemo"> <property name="age" value="22"/> </bean> <bean id="abstractDemo3" class="com.fxyh.spring.di.AbstractDemo3" parent="abstractDemo"> <property name="address" value="jiangxi"/> <property name="id" value="2"/> </bean>
-
这里abstractDemo是抽象Bean,然后使用parent对应这个则对应的属性就会被赋值,并且在自己的bean中重新给这个属性赋值了,那么这个属性将会把抽象bean中的属性的值给覆盖掉。
-
-
自定义属性编辑器
-
no matching editors or conversion strategy found
-
比如我注入的时候注入时间,我直接写字符串的时间,然后这肯定是会报错的,就上面说的那个错误,无法注入。
-
所以我们需要自定义属性编辑器。
-
import org.apache.commons.lang3.time.DateUtils; import java.beans.PropertyEditorSupport; import java.text.ParseException; import java.util.Date; public class CustomDatePropertyEditor extends PropertyEditorSupport { @Override public void setAsText(String text) throws IllegalArgumentException { Date date = null; try { date = DateUtils.parseDate(text, "yyyy-MM-dd"); } catch (ParseException e) { e.printStackTrace(); } this.setValue(date); } }
-
<bean id="person" class="com.fxyh.spring.di.Person"> <property name="id" value="1"/> <property name="username" value="zhangsan"/> <property name="salary" value="#{10*new java.util.Random().nextInt(10)}"/> <property name="createDate" value="2019-06-30"/> </bean> <bean id="customEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer"> <property name="customEditors"> <map> <entry key="java.util.Date" value="com.fxyh.spring.di.CustomDatePropertyEditor"/> </map> </property> </bean>
-
自定义属性编辑器我们需要继承PropertyEditorSupport类,然后重写它的setAsText方法。现在已经不推荐使用了,推荐使用自定义转换器。
-
-
自定义转换器
-
实现Converter接口,注意:
org.springframework.core.convert.converter.Converter
包路径。 -
然后实现convert方法,在里面写对应的逻辑。
-
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.DateUtils; import org.springframework.core.convert.converter.Converter; import java.text.ParseException; import java.util.Date; public class CustomDateConverter implements Converter<String, Date> { private String[] parsePatterns; public void setParsePatterns(String[] parsePatterns) { this.parsePatterns = parsePatterns; } @Override public Date convert(String source) { Date date = null; if (StringUtils.isBlank(source)){ throw new IllegalArgumentException(""); } try { date = DateUtils.parseDate(source, this.parsePatterns); } catch (ParseException e) { e.printStackTrace(); } return date; } }
-
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <property name="converters"> <set> <bean class="com.fxyh.spring.di.CustomDateConverter"> <property name="parsePatterns"> <array> <value>yyyy-MM-dd</value> <value>yyyy/MM/dd</value> </array> </property> </bean> </set> </property> </bean>
-
这里注意这个转换器bean的id,我用其他的试都是没用,只有把id改成conversionService才能使用。
-
-
-
ByName&ByType
-
ByName:根据名称匹配
-
ByType:根据类型匹配
-
这里有个比较经典的异常
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.fxyh.spring.dao.UserDao' available: expected single matching bean but found 2: userDao111111,userDao
-
这个接口有两个实现类,然后现在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" default-autowire="byType"> <bean id="userService" class="com.fxyh.spring.service.impl.UserServiceImpl" autowire="byName"/> <bean id="userDao111111" class="com.fxyh.spring.dao.impl.UserDaoMyBatisImpl"/> <bean id="userDao" class="com.fxyh.spring.dao.impl.UserDaoHibernateImpl"/> </beans>
-
这里在beans上使用了default-autowire为byType,所以这个beans下的bean都会根据类型自动注入,而此时有userDao和userDao111111都是实现了UserDao接口,所以两个都符合要求,然后bean就不知道要注入哪一个了,而这里我们又在userService配置了autowire为byName,此时userService是根据名称来匹配的,则只有userDao一个匹配成功。(这里提供setter方法,然后把setter方法set去掉,然后第一个字母小写,就是名称)
-
同时在配置中局部配置优先于全局配置!
-
-
注解编程
-
@Autowired
- 属于Spring框架的注解
- 先按类型匹配,如果匹配不到再按名称匹配
-
@Resource
- 属于JavaEE规范的注解
- 先按名称匹配,如果匹配不到再按类型匹配
-
@Qualifier
- 和@Autowired结合使用的时候,按照名称匹配,不按类型匹配。
- @Resource原本就是按名称所以在它按名称找不到的时候,使用@Qualifier设置的名称匹配。
-
@Component
- 把这个类注解为一个组件
-
@Repository
- 数据库交互层使用的注解(Dao)
-
@Service
- 服务层使用的注解
-
@Controller
- 控制层使用的注解
-
@Configuration
- 配置类注解
-
@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" 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"> <!-- bean definitions here --> <!--<context:annotation-config/>--> <context:component-scan base-package="com.fxyh.spring"/> </beans>
-
-
这里不贴测试用例了,自己写写测试用例测一测就ok了。
-