1. 常量属性DI
概念: <bean> 的子标签 <property> 用于常量属性注入,简单类型如String,Integer等直接使用 name 和 value 属性进行注入,复杂类型使用其子孙标签配合如下:
<array>+<value>用于注入数组类型属性。<list>+<value>用于注入List类型属性。<set>+<value>用于注入Set类型属性。<map>+<entry>用于注入Map类型属性。<props>+<prop>用于注入Properties类型属性。
源码: /spring4/
- res:
classpath:spring/di/constant-field.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 class="com.yap.di.pojo.Emp">
<property name="name" value="赵四"/>
<property name="age" value="56"/>
<property name="hobby">
<array>
<value>抽烟</value>
<value>喝酒</value>
<value>烫头</value>
</array>
</property>
<property name="userList">
<list>
<value>LiLei</value>
<value>HanMeiMei</value>
</list>
</property>
<property name="userSet">
<set>
<value>clerk</value>
<value>manager</value>
</set>
</property>
<property name="userMap">
<map>
<entry key="name" value="rose"/>
<entry key="age" value="18"/>
</map>
</property>
<property name="jdbc">
<props>
<prop key="driver">com.mysql.cj.jdbc.Driver</prop>
<prop key="url">jdbc:mysql://127.0.0.1:3306/joezhou</prop>
<prop key="user">joezhou</prop>
<prop key="password">joezhou</prop>
</props>
</property>
</bean>
</beans>
- src:
c.y.di.pojo.Emp
/**
* @author yap
*/
@Data
public class Emp implements Serializable {
private String name;
private Integer age;
private String[] hobby;
private List<String> userList;
private Set<String> userSet;
private Map<String, Object> userMap;
private Properties jdbc;
}
- tst:
c.y.di.EmpTest.constantField()
/**
* @author yap
*/
public class EmpTest {
private ClassPathXmlApplicationContext app;
@Test
public void constantField() {
app = new ClassPathXmlApplicationContext("spring/di/constant-field.xml");
Emp emp = app.getBean(Emp.class);
System.out.println(emp.getName());
System.out.println(emp.getAge());
System.out.println(Arrays.toString(emp.getHobby()));
System.out.println(emp.getUserList());
System.out.println(emp.getUserSet());
System.out.println(emp.getUserMap());
System.out.println(emp.getJdbc());
}
@After
public void after(){
app.close();
}
}
依赖注入时,spring容器寻找的是属性对应的
set()而非属性本身。
2. 构造形参DI
概念: <bean> 的子标签 <constructor-arg> 用于构造形参注入,可以替代工厂模式:
index:形参角标,从0开始。value:对应角标的变量值。type:对应角标的变量类型,当bean中仅有一个有参构造时可以省略。
源码: /spring4/
- res:
classpath:spring/di/constructor.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 class="com.yap.di.pojo.Manager">
<constructor-arg index="0" value="3" type="double"/>
<constructor-arg index="1" value="4" type="double"/>
</bean>
</beans>
- src:
c.y.di.pojo.Manager
/**
* @author yap
*/
@Data
public class Manager implements Serializable {
public Manager() {
System.out.println("Manager()...");
}
public Manager(int sal, int bonus) {
System.out.println("total: " + (sal + bonus));
}
public Manager(double sal, double bonus) {
System.out.println("total: " + (sal + bonus));
}
}
- tst:
c.y.di.ManagerTest.constructor()
/**
* @author yap
*/
public class ManagerTest {
private ClassPathXmlApplicationContext app;
@Test
public void constructor() {
app = new ClassPathXmlApplicationContext("spring/di/constructor.xml");
}
@After
public void after(){
app.close();
}
}
3. 实体类DI
概念: 在Service层中注入Dao层的实例有两种方式:
- 外部注入:使用
<property>的ref引用Dao的<bean>的id。 - 内部注入:使用
<property>直接包裹Dao的<bean>。
源码: /spring4/
- res:
classpath:spring/di/bean-outer.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="carDao" class="com.yap.di.bean.CarDao"/>
<bean class="com.yap.di.bean.CarService">
<property name="carDao" ref="carDao" />
</bean>
</beans>
- res:
classpath:spring/di/bean-inner.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 class="com.yap.di.bean.CarService">
<property name="carDao">
<bean class="com.yap.di.bean.CarDao"/>
</property>
</bean>
</beans>
- src:
c.y.di.bean.CarDao
/**
* @author yap
*/
public class CarDao {
public void info() {
System.out.println("CarDao.info()...");
}
}
- src:
c.y.di.bean.CarService
/**
* @author yap
*/
@Data
public class CarService {
private CarDao carDao;
public void info() {
carDao.info();
}
}
- tst:
c.y.di.CarTest.beanOuter()
/**
* @author yap
*/
public class CarTest {
private ClassPathXmlApplicationContext app;
@Test
public void beanOuter() {
app = new ClassPathXmlApplicationContext("spring/di/bean-outer.xml");
app.getBean(CarService.class).info();
}
@After
public void after(){
app.close();
}
}
- tst:
c.y.di.CarTest.beanInner()
@Test
public void beanInner() {
app = new ClassPathXmlApplicationContext("spring/di/bean-inner.xml");
app.getBean(CarService.class).info();
}
4. 注解方式DI
概念: 使用注解进行IOC和DI操作需要配置 <context:annotation-config/> 以使得注解生效,此配置相当于一次性对 org.springframework.beans.factory.annotation 包中的三个注解处理器类进行了配置:
..AutowiredAnnotationBeanPostProcessor驱动@Autowired。..RequiredAnnotationBeanPostProcessor驱动@Required。..InitDestroyAnnotationBeanPostProcessor驱动@Resource/@PreDestroy/@PostConstruct。
若核心配置文件不识别
<context:>,在头标签中添加xmlns:context命名空间和两条xsi:schemaLocation约束文件位置即可。
4.1 @Resource注解
概念: @Resource 是J2EE的注解,与spring无关,该注解直接作用在成员属性(可以不配置set/get方法)上,以自动注入该属性,以及自动分析该属性与bean的关系:
@Resource匹配流程:- spring容器初始化时,用注解所在的属性名和容器中所有
<bean>的id进行匹配。 id匹配成功则实例化该bean。id匹配失败,再用注解所在的属性类型的类全名和容器中所有<bean>的class进行匹配。class匹配成功则实例化该bean,注意类与接口的关系也算匹配成功。class匹配失败或匹配到多个,报错。
- spring容器初始化时,用注解所在的属性名和容器中所有
@Resource(name="abc")匹配流程:- spring容器初始化时,用
abc去和容器中的所有<bean>的id进行匹配。 id匹配成功则实例化该bean。id匹配失败,报错。
- spring容器初始化时,用
源码: /spring4/
- res:
classpath:spring/di/resource.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">
<!--
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>
<bean class="org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor"/>
-->
<context:annotation-config/>
<!--无需指定二者关系,@Resource可以自动分析-->
<bean class="com.yap.resource.DeptDao"/>
<bean class="com.yap.resource.DeptService"/>
</beans>
- src:
c.y.di.resource.DeptDao
/**
* @author yap
*/
public class DeptDao {
public void info() {
System.out.println("DeptDao.info()...");
}
}
- src:
c.y.di.resource.DeptService
/**
* @author yap
*/
public class DeptService {
@Resource
private DeptDao deptDao;
public void info() {
deptDao.info();
}
}
- tst:
c.y.di.DeptTest.info()
/**
* @author yap
*/
public class DeptTest {
private ClassPathXmlApplicationContext app;
@Test
public void info() {
app = new ClassPathXmlApplicationContext("spring/di/resource.xml");
app.getBean(DeptService.class).info();
}
@After
public void after(){
app.close();
}
}
4.2 @Autowired注解
概念: @Autowired 是spring的注解,可以作用在对成员属性,成员属性的set方法或构造器上,以完成自动装配工作:
@Autowired匹配流程:- spring容器初始化时,用注解所在的属性类型的类全名和容器中所有
<bean>的class进行匹配。 class匹配成功则实例化该bean,注意类与接口的关系也算匹配成功。class匹配失败,再用注解所在的属性名和容器中所有<bean>的id进行匹配(4.3+支持)。id匹配成功则实例化该bean。id匹配失败,报错。
- spring容器初始化时,用注解所在的属性类型的类全名和容器中所有
@Autowired配合@Qualifier("abc")匹配流程:- spring容器初始化时,用注解所在的属性名和容器中所有
<bean>的id进行匹配。 id匹配成功则实例化该bean。id匹配失败,再用注解所在的属性类型的类全名和容器中所有<bean>的class进行匹配。class匹配成功则实例化该bean,注意类与接口的关系也算匹配成功。class匹配失败,报错。
- spring容器初始化时,用注解所在的属性名和容器中所有
源码: /spring4/
- res:
classpath:spring/di/autowired.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">
<context:annotation-config/>
<!--无需指定二者关系,@Autowired可以自动分析-->
<bean class="com.yap.di.autowired.DogDao"/>
<bean class="com.yap.di.autowired.DogService"/>
</beans>
- src:
c.y.di.autowired.DogDao
/**
* @author yap
*/
public class DogDao {
public void info() {
System.out.println("DogDao.info()...");
}
}
- src:
c.y.di.autowired.DogService
/**
* @author yap
*/
public class DogService {
private DogDao dogDao;
@Autowired
public DogService(DogDao dogDao){
this.dogDao = dogDao;
}
public void info() {
dogDao.info();
}
}
- tst:
c.y.di.DogTest.info()
/**
* @author yap
*/
public class DogTest {
private ClassPathXmlApplicationContext app;
@Test
public void info() {
app = new ClassPathXmlApplicationContext("spring/di/autowired.xml");
app.getBean(DogService.class).info();
app.close();
}
@After
public void after(){
app.close();
}
}
4.3 @Component注解
概念: @Component 标识的类被扫描时可以自动配置对应的 <bean>:
- 子注解:
@Component拥有三个语义化子注解,都支持使用value属性来绑定<bean>的id,默认为该类名的首字母小写形式:@Controller:建议配置在控制器类上。@Service:建议配置在业务类上。@Repository:建议配置在数据类上。
- 包扫描:扫描指定包及其子包中的所有
@Component:- 格式:
<context:component-scan base-package="包路径"/> - 包扫描也可以隐式地向spring容器注册注解对应的
Processor,即此时可以省略<context:annotation-config/>配置。
- 格式:
源码: /spring4/
- res:
classpath:spring/di/component.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">
<context:component-scan base-package="com.yap.dao"/>
<context:component-scan base-package="com.yap.service"/>
</beans>
- src:
c.y.di.component.CatDao
/**
* @author yap
*/
@Repository
public class CatDao {
public void info() {
System.out.println("CatDao.info...");
}
}
- src:
c.y.di.component.CatService
/**
* @author yap
*/
@Service
public class CatService {
private CatDao catDao;
@Autowired
public CatService(CatDao catDao) {
this.catDao = catDao;
}
public void info() {
catDao.info();
}
}
- tst:
c.y.di.CatTest.info()
/**
* @author yap
*/
public class CatTest {
private ClassPathXmlApplicationContext app;
@Test
public void info() {
app = new ClassPathXmlApplicationContext("spring/di/component.xml");
/*((CatService)app.getBean("catService")).info();*/
app.getBean(CatService.class).info();
}
@After
public void after(){
app.close();
}
}
4.4 SpringJUnit4测试
概念: 若Junit测试类中大部分的测试方法都需要管理容器,则建议使用spring提供的与junit4.12+整合的测试方案:
- 引入
spring-test依赖。 - 测试类添加
@RunWith(SpringJUnit4ClassRunner.class)以指定使用整合方案。 - 测试类添加
@ContextConfiguration()以指定配置文件路径:value/locations属性用于指定xml配置文件的位置。classes属性用于指定纯java配置文件的类对象。
- 测试类中使用
@Autowired时建议作用于成员属性上。
源码: /spring4/
- res:
pom.xml
<!--spring-test-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring}</version>
</dependency>
- res:
classpath:spring/di/spring-test.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">
<context:component-scan base-package="com.yap.di.test"/>
</beans>
- src:
c.y.di.test.PigDao
/**
* @author yap
*/
@Repository
public class PigDao {
public void info() {
System.out.println("PigDao.info...");
}
}
- src:
c.y.di.test.PigService
/**
* @author yap
*/
@Service
public class PigService {
private PigDao pigDao;
@Autowired
public PigService(PigDao pigDao) {
this.pigDao = pigDao;
}
public void info() {
pigDao.info();
}
}
- tst:
c.y.di.PigTest.info()
/**
* @author yap
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring/di/spring-test.xml")
public class PigTest {
@Autowired
private PigDao pigDao;
@Test
public void info() {
pigDao.info();
}
}
5. 纯java方式DI
概念: 使用纯java代码代替xml配置容器并实现IOC是Spring4.x和SpringBoot推荐的配置方式:
- 引入
bonecp-spring连接池和mysql-connector-java驱动依赖。 - 开发配置类:添加
@Configuration表示该类是一个配置类。- 类上使用
@ComponentScan进行包扫描。 - 类上使用
@PropertySource读取数据源属性文件。 - 开发属性并添加
@Value配合插值符号读取数据源属性值。
- 类上使用
- 开发配置方法:方法上添加
@Bean表示这个方法用于配置一个<bean>:- 方法名对应
<bean>的id。 - 方法返回值类型对应
<bean>的class。 - 方法的返回值就是该bean的最终实例。
- 方法名对应
源码: /spring4/
- res:
pom.xml
<!--mysql-connector-java-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
<scope>runtime</scope>
</dependency>
<!--spring-aspects-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring}</version>
</dependency>
- res:
db.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/mysql?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
jdbc.user=root
jdbc.password=root
- src:
c.y.di.java.DataSourceConfig
/**
* @PropertySource("classpath:jdbc/db.properties") 相当于
* <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
* <property name="location" value="classpath:jdbc/db.properties"/>
* </bean>
* @author yap
*/
@Configuration
@PropertySource("classpath:jdbc/db.properties")
@ComponentScan("com.joezhou.di.java")
public class DataSourceConfig {
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.user}")
private String user;
@Value("${jdbc.password}")
private String password;
/**
* 相当于:
* <bean id="dataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">
* <property name="driverClass" value="${jdbc.driver}"/>
* <property name="jdbcUrl" value="${jdbc.url}"/>
* <property name="username" value="${jdbc.user}"/>
* <property name="password" value="${jdbc.password}"/>
* <property name="maxConnectionsPerPartition" value="100"/>
* <property name="minConnectionsPerPartition" value="5"/>
* </bean>
*
* @return 数据源对象
*/
@Bean
public DataSource dataSource() {
BoneCPDataSource bonecpDataSource = new BoneCPDataSource();
bonecpDataSource.setDriverClass(driver);
bonecpDataSource.setJdbcUrl(url);
bonecpDataSource.setUsername(user);
bonecpDataSource.setPassword(password);
bonecpDataSource.setMaxConnectionsPerPartition(100);
bonecpDataSource.setMinConnectionsPerPartition(5);
return bonecpDataSource;
}
}
- tst:
c.y.di.DataSourceTest.getConnection()
/**
* @author yap
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = DataSourceConfig.class)
public class DataSourceTest {
@Autowired
private DataSource dataSource;
@Test
public void getConnection() throws SQLException {
System.out.println(dataSource.getConnection().isClosed());
}
}