依赖注入,spring基于注解的IoC,spring整合junit
环境:
Idea:2019.3.1
系统:windows10 家庭版
Jdk: 8
spring:5.0.3 release
spring文档
项目代码
依赖注入
spring中的依赖注入,依赖关系的管理,以后都交给spring来维护,在当前类需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明,依赖关系的维护,就称为依赖注入,但如果是经常变化的数据,并不适用于注入的方式
一.applicationContext.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></bean>
</beans>
二.数据类型
1.基本类型和String
2.其他bean类型(在配置文件中或者注解配置过的bean)
3.复杂类型/集合类型
三.注入方式
1.构造函数注入
标签:<constructor-arg/>
位置:<bean></bean>内部
属性:
type: 用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
index: 用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始的
name: 用于指定给构造函数中指定名称的参数赋值(最常用)
value: 用于提供基本类型和String类型的数据
ref: 用于指定其他的bean类型数据,它指的就是在spring的IoC核心容器中出现的bean对象
优势: 在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功,即当我们需要某个类的实例对象一定拥有值的时候,就可以用这种方式注入
弊端: 改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供
例子:
<bean id="accountService" class="com.cbw.service.impl.AccountServiceImpl">
<constructor-arg name="name" value="test"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
//birthday是date对象,spring无法用字符串转换,所以要创建一个date类型的bean对象, 再用ref属性引用
<constructor-arg name="birthday" ref="birthday"></constructor-arg>
</bean>
//创建一个date类型bean供birthday使用
<bean id="birthday" class="java.util.Date"></bean>
2.set方法注入(常用)
标签:<property/>
位置:<bean></bean>内部
属性:
name: 用于指定注入时所调用的set方法名称
value: 用于提供基本类型和String类型的数据
ref: 用于指定其他的bean类型数据,它指的就是在spring的IoC核心容器中出现的bean对象
优势: 创建对象时没有明确的限制,可以直接使用默认构造函数,即使用无参构造函数都行
弊端: 如果有某个成员必须有值,则获取对象是有可能set方法没有执行,即可能调用的默认构造函数创建对象,然后用完了都没有调用set方法,也就没有值
例子:
<bean id="accountServic" class="com.cbw.service.impl.AccountServiceImpl2">
<property name="name" value="cbw"></property>
<property name="age" value="23"></property>
<property name="birthday" ref="birthday"></property>
</bean>
<bean id="birthday" class="java.util.Date"></bean>
3.注解注入
见下文
四.复杂类型的注入/集合类型的注入(能够使用构造方法和set方法注入,这里我们使用set方法)
1.用于给List结构集合注入的标签:
<list></list>
<array></array>
<set></set>
2.用于给Map结构集合注入的标签:
<map></map>
<props></props>
注:结构相同,标签可以互换,也就是能共用标签,当然只能使用一种,这些标签的位置都是在<bean></bean>
标签里面
字符串数组 <array></array>
<property name="myStrs">
<array>
<value>sss</value>
<value>fff</value>
<value>ddd</value>
</array>
</property>
List<list></list>
<property name="myList">
<list>
<value>123</value>
<value>234</value>
<value>345</value>
</list>
</property>
Map <map></map>
<property name="myMap">
//想使用自定义bean,用key-ref="***" value-ref="***"属性连接外部bean
<map key-type="java.lang.String" value-type="java.lang.String">
//用value属性传值
<entry key="墨菲特" value="势不可挡" ></entry>
<entry key="亚托克斯" value="大灭"></entry>
//用value标签传值
<entry key="亚索">
<value>狂风绝息斩</value>
</entry>
</map>
</property>
Set<set></set>
<property name="mySet">
<set>
<value>set1</value>
<value>set2</value>
<value>set3</value>
</set>
</property>
Properties <props></props>
<property name="myProperties">
<props>
<prop key="accountService1">唉唉唉</prop>
<prop key="accountService2">哈哈哈</prop>
<prop key="accountService3">嘻嘻嘻</prop>
</props>
</property>
用 key-ref=”“ value-ref=”“属性连接外部bean
<bean id="now" class="java.util.Date"></bean>//自定义date对象
<bean id="accountService" class="com.cbw.service.impl.AccountServiceImpl4">
<property name="myMap">
<map>
<entry key-ref="now" value-ref="now"></entry>
</map>
</property>
</bean>
基于注解的IoC
一.applicationContext.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-3.0.xsd">
<!--告知spring在创建容器的时候要扫描的包,配置所需要的标签不是在beans的约束中,
而是一个名称为context名称空间和约束中-->
<context:component-scan base-package="com.cbw">
</context:component-scan>
</beans>
二.创建对象相关
他们的作用就和在xml配置文件中编写一个<bean></bean>
标签实现的功能是一样的
1.@Component
作用: 用于把当前类对象存入spring容器
位置: 类
属性:
value:用于指定bean的id,当我们不写时,默认值是当前类名,且首字母变小写
2.@Controller ———一般用在表现层
3.@Service ———–一般用在业务层
4.@Repository ——–一般用在持久层
以上三个注解他们的作用和属性和Component是一模一样,他们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层更加清晰
三.用于注入数据
他们的作用就和在xml配置文件中的bean标签中写一个property标签的作用是一样的
1.@Autowired
作用: 自动按照类型注入,只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
位置: 方法,变量
细节: 在使用注解注入时,set方法就不是必须的
例子:
@Repository("accountDaoImpl")
public class AccountDaoImpl implements IAccountDao {
@Autowired
private IAccountDao iAccountDao;
省略众多方法
}
注意: IoC容器是一个Map类型,注解时的id是key,bean的类名是value,这里就是id=accountDaoImpl,value=AccountDaoImpl(implement IAccountDao也会记录下来)
自动注入:
1.根据数据类型寻找value是一样的bean
2.如果IoC容器中,一个匹配的bean类型都没有,注入就会失败报错(如果是接口,那么他的实现类也能匹配)
3.如果IoC容器中,有bean匹配的时候:
1.有一个匹配的时候
直接注入
2.有多个匹配的时候
根据变量类型,圈定出所有匹配的bean
根据变量名称,逐个比对圈定的bean的id值,如果有相同的,那么就匹配这个
如果几个都一样,或者一个都没有,就会报错
2.@Qalifier
作用:在按照类中注入的基础上再按照名称注入。它在给类成员注入时不能单独使用,必须和@Autowired同时使用。但是在给方法参数注入时可以单独使用
属性:
value:用于指定注入bean的id
例子:
@Autowired
@Qualifier("accountDaoImpl")
private IAccountDao accountDaoImpl;
3.@Resource
作用:直接按照bean的id注入,可以单独使用
属性:
name:用于指定bean的id
例子:
@Resource(name = "accountDaoImpl")
private IAccountDao accountDaoImpl;
**注意:** 以上三个注解,只能注入其他bean类型的数据,集合类型的注入只能通过xml来实现
4.@Value
作用:用于注入基本类型和String类型的数据
属性:
value: 用于指定数据的值。它可以使用spring中的SpEL(也就是spring中的el表达式)
SpEl写法: ${表达式}
注意: el表达式在哪里,就是谁的el表达式,如果出现在jsp里面,就是jsp的el表达式,如果写在spring的配置文件中就是spring的el表达式,如果写在mybatis的配置文件中,那么就是mybatis的配置文件中
四.作用范围相关
他们的作用就和在bean标签中使用scope属性实现的功能是一样的
1.@Scope
作用:用于指定bean的作用范围
位置:类
属性:
value:指定范围的取值。取值和xml中bean标签中的scope属性一样,常用取值为singleton prototype,默认值是singleton
五.生命周期相关
他们的作用就和在bean标签中使用init-method和destroy-method的作用是一样的
1.@PreDestroy
作用:用于指定销毁方法
2.@PostConstruct
作用:用于指定初始化方法
用法:在bean类中,定义init()方法,用PostConstruct注解,定义destory()方法,用ProDestroy注解,bean对象在创建时候会执行init()方法,在销毁时会执行destroy()方法
xml和注解相结合的IoC
就是在applicationContext.xml文件中<context:component-scan/>
和<bean/>
一起使用
自定义类可以用注解也就是扫描包的形式加入IoC,而外部的包中的类可以用<bean/>
标签来加入IoC
<?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-3.0.xsd">
<!--告知spring在创建容器的时候要扫描的包,配置所需要的标签不是在beans的约束中,
而是一个名称为context名称空间和约束中-->
<context:component-scan base-package="com.cbw">
</context:component-scan>
<bean id="" class=""></bean>
<bean id="" class=""></bean>
</beans>
纯注解的IoC
根据xml配置文件获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//这个同理,也可以传xml配置文件数组
根据配置类获取容器
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
//该构造方法可以传字节码数组,也就意味着,如果你有多个配置类,你都不想加@Configuration注解,那么就一起传进构造方法,用,隔开
1.创建一个Configuration类
作用:配置类,相当于applicationContext.xml文件
2.@Configuration
作用:指定当前类是一个配置类
细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注释可以不写,如果有多个配置类,不作为参数的配置类必须加上这个注解,且要在@ComponentScan注解中加入这个配置类,否则,扫描包的时候,spring不会去扫描该类下的其他注解
3.@ComponentScan
作用:用于通过注释指定spring在创建容器时要扫描的包,相当于applicationContext.xml文件中<context:component-scan/>标签
属性:
basePackage:用于指定创建容器时要扫描的包
value:它和basePackages的作用一样
注意:
1.这两个都是数组类型的,一个类的时候,直接写,多个类要用{}隔开
@ComponentScan(basePackages = {"com.cbw","com.cbw.domain"})
2.basePackage和value也可以不写,直接写字符串
@ComponentScan("com.cbw")
4.@Bean
作用:用于把当前方法的返回值作为bean对象存入spring的核心容器中
属性:
name:用于指定bean的id,当不写时,默认值是当前方法的名称
细节:当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象,查找方式和@AutoWired注解是一样的
5.@Import
作用:用来导入其他的配置类
位置:主配置类
属性:
value:用于指定其他配置类的字节码,是个数组
细节:
当我们使用import注解之后,有import注解的类就是父配置类,而导入的都是子配置类,而我们如果通过ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class)构造方法传入多个配置类,那他们就是并列关系,推荐使用第一种
6.@PropertySource与@PropertySources
作用:用于指定properties文件的位置
位置:主配置类
属性:
value:指定文件的名称和路径
关键字:classpath,表示类路径下,如果properties文件在包中,可以再加上包路径
格式:@PropertySource("classpath:jdbc.properties"),@PropertySource("classpath:包/包/jdbc.properties")
细节:前者只能传一个properties文件,后者可以传properties文件数组
7.例子
@Configuration
@ComponentScan("com.cbw")
public class SpringConfiguration {
/**
* 用于创建一个QueryRuanner对象,并且存入springIoC容器中
* @param dataSource
* @return
*/
@Bean("runner")
public QueryRunner createQueryRunner(DataSource dataSource){
return new QueryRunner(dataSource);
}
/**
* 用于创建一个数据源对象,并且存入springIoC容器中
* @return
*/
@Bean("dataSource")
public DataSource createDataSource(){
ComboPooledDataSource ds = new ComboPooledDataSource();
try {
ds.setDriverClass("com.mysql.jdbc.Driver");
ds.setJdbcUrl("jdbc:mysql://localhost:3306/myspringspace?useUnicode=true&characterEncoding=utf8");
ds.setUser("root");
ds.setPassword("123456");
} catch (PropertyVetoException e) {
e.printStackTrace();
}
return ds;
}
}
spring整合Junit
问题分析
实际开发中,开发和测试是分开的,测试类中只能有测试方法,就像这样
而创建容器或者初始化对象都是没有的,这就引出来一串问题
1.程序的入口
main()方法
2.Junit单元测试中,没有main方法也能执行
原因:Junit集成了一个main方法,该方法会判断当前测试类中哪些方法有@Test注解,Junit会让有注解的方法执行
3.Junit不会管我们是否采用spring框架
在执行测试方法时,Junit根本不知道我们是不是使用了spring框架,所以就不会为我们读取配置文件/配置类创建spring核心容器
4.结论
当测试方法执行时,没有IoC容器,就算写了@Autowired注解,也无法实现注入
解决
1.导入spring整合Junit的jar包(坐标)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
2.使用Junit提供的一个注解替换原来Junit集成的main()方法,替换成spring提供的@Runwith注解
意思就是,Junit提供了一个注解替换自己的main方法,将它替换成spring提供的@Runwith注解@RunWith
标签
@RunWith(SpringJUnit4ClassRunner.class)
public class accountTest2 {
private IAccountService accountService;
~
~
一堆测试方法
~
~
}
3.告知spring的运行器,spring和ioc创建是基于xml还是注解的,并且说明位置
@ContextConfiguration
标签
属性:
locations:指定xml文件的位置,加上classpath关键字,表示在类路径下
classes:指定注解类(配置类)所在地位置
注意:spring版本为5.x版本时,要求Junit的版本必须是4.12及以上
例子:
使用注解:
/**
* 使用junit单元测试测试我们的配置
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class accountTest2 {
private IAccountService accountService;
/**
* 测试查询所有方法
*/
@Test
public void testFindAll(){
//3.执行方法
List<Account> accountList = accountService.findAllAccount();
for (Account aclist:accountList
) {
System.out.println(aclist);
}
}
}
使用xml文件:
/**
* 使用junit单元测试测试我们的配置
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class accountTest2 {
@Autowired
private IAccountService accountService = null;
/**
* 测试查询所有方法
*/
@Test
public void testFindAll(){
//3.执行方法
List<Account> accountList = accountService.findAllAccount();
for (Account aclist:accountList
) {
System.out.println(aclist);
}
}
}