一、什么是spring
spring是一个轻量级的DI(IOC)和AOP的容器框架。
轻量级:应用大小和应用开始,包括应用方式;
DI(IOC):spring通过一种称为控制反转(IOC)的技术促进低耦合;
AOP:面向切面,将业务逻辑从应用程序中分离;
容器:包含并管理应用对象的生命周期和配置;
框架:使用组件配置组合成复杂的应用,并提供很多基础的功能。
二、hello spring
第一个hello spring程序,最少需要三个包:
apache.commons.logging-1.1.1.jar 日志依赖包(必须)
spring-beans-5.0.4.RELEASE.jar spring的bean对象包
spring-core-5.0.4.RELEASE.jar spring的核心包
写配置,将该配置文件放在classpath目录下,一般默认命名为applicationContext.xml,为什么要命名为这个名称呢?将在后面记录,如下:
然后去文档里面查找,该如何写配置文件:
写一个类,包含一个简单的方法:
public class TestBean {
public void helloSpring() {
System.out.println("hello spring !");
}
}
在配置文件applicationContext.xml中配置这个TestBean:
<?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="testBean" class="cn.laiwenbo.spring.learn.TestBean">
</bean>
</beans>
注意,在中间的bean的bean中,class里面的内容为该类的全类名。
接下来写测试代码,导入junit包:junit-4.11.jar hamcrest-core-1.3.jar
@Test
public void testHelloSpring() {
//第一步,加载配置文件
Resource resouce = new ClassPathResource("applicationContext.xml");
//第二步,拿到bean工厂
BeanFactory beanFactory = new XmlBeanFactory(resouce);
//第三步,拿到配置的bean,id为配置文件中写好的那个testBean
TestBean testBean = (TestBean) beanFactory.getBean("testBean");
//第四步,调用bean中的方法
testBean.helloSpring();
}
输出结果:
说明hello spring 整个流程成功。
三、BeanFactory的认识
①获取bean的方式
那么在hello spring 中使用的BeanFactory有什么特点呢?其实,beanFactory获取bean的方式有三种:
第一种:通过bean的id来获取bean(如果两个bean的id相同,报错)
TestBean testBean = (TestBean) beanFactory.getBean("testBean");
第一种:通过id和对象类型class来拿到bean,不需要强转(如果两个bean的id和类型相同,报错)
TestBean testBean = beanFactory.getBean("testBean",TestBean.class);
第三种:直接通过类型来拿到bean(如果两个bean的类型相同,报错)
TestBean testBean = beanFactory.getBean(TestBean.class);
②BeanFactory常用方法
public void testBeanFactory() {
Resource resource = new ClassPathResource("applicationContext.xml");
BeanFactory beanFactory = new XmlBeanFactory(resource);
//查看是不是包含某个bean
boolean containsBean = beanFactory.containsBean("testBean");
System.out.println(containsBean); //输出true
//是否是单例的(默认是单例的)
boolean singleton = beanFactory.isSingleton("testBean");
System.out.println(singleton); //输出true
//获取bean的类型
Class<?> aClass = beanFactory.getType("testBean");
System.out.println(aClass); //输出cn.laiwenbo.spring.learn.TestBean
//获取bean的别名
String[] aliases = beanFactory.getAliases("testBean");
}
四、ApplicationContext的认识
①认识ApplicationContext
ApplicationContext是spring的一个核心对象,它也是实现了BeanFactory这个接口,因此可以说ApplicationContext也是一个Bean工厂。ApplicationContext的优势在于:
①提供了文本信息的解析工具,包括I18N的支持。
②提供了载入文件资源的通用方法。
③提供了发送事件的功能。
要使用ApplicationContext还需要导入额外的两个包:spring-context spring-expression
public void applicationContextTest() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
TestBean bean = applicationContext.getBean("testBean", TestBean.class);
bean.helloSpring();
}
②ApplicationContext和BeanFactory的区别
解释:ApplicationContext是BeanFactory的子类,具有更多的方法和功能。
重点:ApplicationContext是在读取的时候就创建Bean对象,而BeanFactory是在使用的时候才创建Bean对象(懒加载)。
拓展:在使用ApplicationContext的时候,也可以配置成想BeanFactory一样的懒加载。
实例一:让所有的Bean都变成懒加载
<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-lazy-init="true">
</beans>
default-lazy-init="true">
</beans>
实例二:让一个Bean变成懒加载
<bean id="testBean" class="cn.laiwenbo.spring.learn.TestBean" lazy-init="true"></bean>
lazy-init="true"></bean>
如果在Beans上配置了懒加载,但是又想里面某些Bean不是懒加载:
<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-lazy-init="true">
<bean id="testBean" class="cn.laiwenbo.spring.learn.TestBean" lazy-init="false"></bean>
</beans>
default-lazy-init="true">
<bean id="testBean" class="cn.laiwenbo.spring.learn.TestBean" lazy-init="false"></bean>
</beans>
五、Spring的注解测试
spring提供了自己的测试模块,使用Spring的测试,需要导入包:spring-aop和spring-test
第一种:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class SpringTestModule {
//自动注入
@Autowired
private ApplicationContext applicationContext;
@Test
public void getBean() {
TestBean testBean = applicationContext.getBean("testBean", TestBean.class);
testBean.helloSpring();
}
}
这种方式需要在SpringTestModule类的目录下加上配置文件,且名称为:类名-context.xml
第二种:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringTestModule {
//自动注入
@Autowired
private ApplicationContext applicationContext;
@Test
public void getBean() {
TestBean testBean = applicationContext.getBean("testBean", TestBean.class);
testBean.helloSpring();
}
}
这种不需要在类名下有配置文件,但是要配置文件在classpath路径下,且标签发生了变化:
@ContextConfiguration("classpath:applicationContext.xml")
六、Spring中的Bean
①在配置bean的时候,可以配置bean是单例还是多例。
多例:
<bean id="testBean1" class="cn.laiwenbo.spring.learn.TestBean" scope="prototype"></bean>
单例,单例是默认的模式:
<bean id="testBean1" class="cn.laiwenbo.spring.learn.TestBean" scope="prototype"></bean>
②spring的生命周期管理
创建一个具有初始化和销毁的bean,并配置初始化方法和销毁方法:
public class SpringLive {
public SpringLive(){
System.out.println("构造方法执行...");
}
public void init() {
System.out.println("初始化方法执行...");
}
public void service(){
System.out.println("service方法执行...");
}
public void destroy(){
System.out.println("销毁方法执行...");
}
}
<bean id="testBean2" class="cn.laiwenbo.spring.learn.SpringLive" init-method="init" destroy-method="destroy"></bean>
输出:
构造方法执行...
初始化方法执行...
service方法执行...
销毁方法执行...
需要注意的是,如果将bean配置成多例,那么spring将不会监控到销毁的方法:
<bean id="testBean2" class="cn.laiwenbo.spring.learn.SpringLive" init-method="init" destroy-method="destroy" scope="prototype"></bean>
输出:
构造方法执行...
初始化方法执行...
service方法执行...
六、spring的注入
①xml加set注入
bean:
public class UserDao {
public void userDao(){
System.out.println("userDao...");
}
}
public class UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void userService() {
System.out.println("userService...");
userDao.userDao();
}
}
public class UserController {
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
public void userController(){
System.out.println("userController...");
userService.userService();
}
}
配置文件:
<bean id="userDao" class="cn.laiwenbo.spring.learn.di.UserDao"></bean>
<bean id="userService" class="cn.laiwenbo.spring.learn.di.UserService">
<property name="userDao" ref="userDao"></property>
</bean>
<bean id="userController" class="cn.laiwenbo.spring.learn.di.UserController">
<property name="userService" ref="userService"></property>
</bean>
输出:
userController...
userService...
userDao...
需要注意的是,<property name="userDao" ref="userDao"></property>参数设置,其中,name需要与setXXX中的这个名字首字母小写一致,比如在UserService中,需要注入userDao,那么需要对应的set方法:
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
那么这个方法名setUserDao去掉set再将首字母小写,就是我们property 中的name的值。
而ref的值就是已经配置bean的id的值。
②继承的实现
在spring的xml注入中,有时候需要在多个bean中使用同一个bean作为参数,那么可以spring的集成配置:
三个bean,其中UserDao和EmployeeDao都需要使用SqlSessionFactory,如下:
public class SqlSessionFactory {
public void openSession() {
System.out.println("我是假的sqlSessionFactory....");
}
}
public class UserDao {
private SqlSessionFactory sessionFactory;
public void setSessionFactory(SqlSessionFactory sessionFactory){
this.sessionFactory = sessionFactory;
}
public void save(){
sessionFactory.openSession();
}
}
public class EmployeeDao {
private SqlSessionFactory sessionFactory;
public void setSessionFactory(SqlSessionFactory sessionFactory){
this.sessionFactory = sessionFactory;
}
public void save() {
sessionFactory.openSession();
}
}
配置文件,注意id为baseDao的这个配置,由于abstract=“true”表示为抽象的,所以没有class的属性,在
需要使用baseDao的bean中,加上parent="父bean"就可以了。
<bean id="baseDao" abstract="true">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<bean id="sessionFactory" class="cn.laiwenbo.spring.learn.extendss.SqlSessionFactory"></bean>
<bean id="userDao" class="cn.laiwenbo.spring.learn.extendss.UserDao" parent="baseDao"></bean>
<bean id="employeeDao" class="cn.laiwenbo.spring.learn.extendss.EmployeeDao" parent="baseDao"></bean>
③注入普通属性
有时候,需要在一个bean中注入普通属性(String,int,double),注入普通属性案例如下:
public class UserBean {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "UserBean{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
<bean id="userBean" class="cn.laiwenbo.spring.learn.extendss.UserBean">
<property name="id" value="1"></property>
<property name="name" value="abc"></property>
</bean>
④获取dataSource
第一种,直接将配置写在xml文件里面。
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
<!--GMT%2B8代表: 东八区-->
<property name="url" value="jdbc:mysql://localhost:3306/lwb?serverTimezone=GMT%2B8"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
@Autowired
private DataSource dataSource;
@Test
public void getDataSource() throws SQLException {
Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement("select * from user");
ResultSet resultSet = statement.executeQuery();
while (resultSet.next()) {
System.out.println(resultSet.getString(2));
}
}
第二种,将配置信息写在properties文件里面。其中system-properties-mode="NEVER"表示不使用系统用户。
<context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"></context:property-placeholder>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"></property>
<!--GMT%2B8代表: 东八区-->
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
jdbc.properties文件:
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/lwb?serverTimezone=GMT%2B8
jdbc.username=root
jdbc.password=root
解释:system-properties-mode="NEVER"表示不使用系统用户。
由于在spring中,有些特定的关键字,比如username,如果将配置文件写成如下的格式:
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/lwb?serverTimezone=GMT%2B8
username=root
password=root
<context:property-placeholder location="jdbc.properties"></context:property-placeholder>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${driverClassName}"></property>
<!--GMT%2B8代表: 东八区-->
<property name="url" value="${url}"></property>
<property name="username" value="${username}"></property>
<property name="password" value="${password}"></property>
运行测试报错:
用户为Administrator,密码错误,说明spring自动使用了我操作系统的用户名,所以会报错,注意这样的情况是没有加system-properties-mode="NEVER"的情况才会出现。因此,在配置的时候,最好是给配置信息加上前缀,比如jdbc.username
⑤构造器注入
构造器注入有多中方式,但是构造器注入遇到的场景比较少,很多情况是在用别人写好的类的时候才会用到。
1、按照构造器参数的顺序注入
构造器注入,写了两个javabean:
public class ConstructorDI {
private int age;
private String name;
public ConstructorDI(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "ConstructorDI{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
public class ConstructorDI2 {
private int age;
private String name;
private Info info;
public ConstructorDI2(int age, String name, Info info) {
this.age = age;
this.name = name;
this.info = info;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "ConstructorDI{" +
"age=" + age +
", name='" + name + '\'' +
", info=" + info +
'}';
}
}
<!--按照索引注入,起始索引为0-->
<bean id="constructorDI1" class="cn.laiwenbo.spring.learn.di2.ConstructorDI">
<constructor-arg index="0" value="1" />
<constructor-arg index="1" value="lwb"/>
</bean>
<!--按照参数名称注入-->
<bean id="constructorDI2" class="cn.laiwenbo.spring.learn.di2.ConstructorDI">
<constructor-arg name="age" value="2" />
<constructor-arg name="name" value="lwb"/>
</bean>
<!--根据类型注入-->
<bean id="constructorDI3" class="cn.laiwenbo.spring.learn.di2.ConstructorDI">
<constructor-arg type="int" value="2" />
<constructor-arg type="java.lang.String" value="lwb"/>
</bean>
<!--注入自定义的对象,这种方式,外部不能引用Info这个bean-->
<bean id="constructorDI4" class="cn.laiwenbo.spring.learn.di2.ConstructorDI2">
<constructor-arg index="0" value="2" />
<constructor-arg index="1" value="lwb"/>
<constructor-arg index="2">
<bean class="cn.laiwenbo.spring.learn.di2.Info">
<constructor-arg index="0" value="波波"/>
</bean>
</constructor-arg>
</bean>
<!--注入自定义的对象,这种方式,外部可以引用info这个bean-->
<bean id="info" class="cn.laiwenbo.spring.learn.di2.Info">
<constructor-arg index="0" value="波波"/>
</bean>
<bean id="constructorDI5" class="cn.laiwenbo.spring.learn.di2.ConstructorDI2">
<constructor-arg index="0" value="2" />
<constructor-arg index="1" value="lwb"/>
<constructor-arg index="2" ref="info"/>
</bean>
2、其他类型注入
数组注入,两种方式:
<property name="arrays" value="23,df,dsf" />
<property name="arrays">
<array>
<value>xxx</value>
<value>tyy</value>
<value>zzz</value>
</array>
</property>
List注入:
<property name="list">
<list>
<value>xxx</value>
<value>aaa</value>
<value>bbbb</value>
</list>
</property>
Set注入:
<property name="set">
<set>
<value>xxx</value>
<value>aaa</value>
<value>bbbb</value>
</set>
</property>
List<Bean>注入:
<property name="otherBeanList">
<list>
<bean class="cn.lwb.OtherBean" />
<bean class="cn.lwb.OtherBean" />
<ref bean="otherBean" />
<ref bean="otherBean" />
</list>
</property>
Set<Bean>注入:
<property name="otherBeanSet">
<set>
<bean class="cn.lwb.OtherBean" />
<bean class="cn.lwb.OtherBean" />
<ref bean="otherBean" />
<ref bean="otherBean" />
</set>
</property>
Map注入:
<property name="map">
<map>
<entry key="xx" value="value1"></entry>
<entry key="yy" value="value2"></entry>
</map>
</property
配置一个Properties对象:
<!-- 方式一:简单,不支持中文 -->
<property name="props1">
<value>
hibernate.dialect=org.hibernate.dialect.HSQLDialect
hibernate.driverClassName=com.mysql.jdbc.Driver
</value>
</property>
<!-- 方式二:支持中文 -->
<property name="props2">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
<prop key="hibernate.driverClassName">com.mysql.jdbc.Driver中文 </prop>
</props>
</property>
⑥xml版自动注入
首先创建三个javabean对象:
public class UserController {
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
public void get() {
System.out.println("userController get ...");
userService.get();
}
}
public class UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void get() {
System.out.println("userService get ...");
userDao.get();
}
}
public class UserDao {
public void get(){
System.out.println("userDao get ...");
}
}
然后是配置xml的自动注入:
第一种,根据javabean的类型来注入,注意在beans后面的default-autowire="byName"
<?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="byName">
<bean id="userDao" class="cn.laiwenbo.spring.learn.autowireXML.UserDao"></bean>
<bean id="userService" class="cn.laiwenbo.spring.learn.autowireXML.UserService"></bean>
<bean id="userController" class="cn.laiwenbo.spring.learn.autowireXML.UserController"></bean>
</beans>
第二种,根据名称来注入,注意同样是setXX()这个方法的规则来注入的,也就是说bean的id需要与XX首字母小写之后的一直:default-autowire="byName"
<?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="byName">
<bean id="userDao" class="cn.laiwenbo.spring.learn.autowireXML.UserDao"></bean>
<bean id="userService" class="cn.laiwenbo.spring.learn.autowireXML.UserService"></bean>
<bean id="userController" class="cn.laiwenbo.spring.learn.autowireXML.UserController"></bean>
</beans>
当然,在总的beans配置之后,也可以单独的为某些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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd" default-autowire="byName">
<bean id="userDao" class="cn.laiwenbo.spring.learn.autowireXML.UserDao"></bean>
<bean id="userService" class="cn.laiwenbo.spring.learn.autowireXML.UserService"></bean>
<bean id="userController" class="cn.laiwenbo.spring.learn.autowireXML.UserController" autowire="byType"></bean>
</beans>
在上面,单独的为userController配置为autowire="byType"。
总结:如果用byType类型必须保证唯一,如果用byName必须保证名字唯一。因此,代码必须保证规范。
⑦全注解配置
@Repository
public class UserDao {
public void get(){
System.out.println("userDao get ...");
}
}
public class UserDao {
public void get(){
System.out.println("userDao get ...");
}
}
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void get() {
System.out.println("userService get ...");
userDao.get();
}
}
public class UserService {
@Autowired
private UserDao userDao;
public void get() {
System.out.println("userService get ...");
userDao.get();
}
}
@Controller
public class UserController {
@Autowired
private UserService userService;
public void get() {
System.out.println("userController get ...");
userService.get();
}
}
public class UserController {
@Autowired
private UserService userService;
public void get() {
System.out.println("userController get ...");
userService.get();
}
}
<?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="cn.laiwenbo.spring.learn.anno"/>
<!--开始注解支持,上面已经开启了注解支持,这句是为了兼容spring3.0以及之前的版本
spring3.0之前,这句必须要
-->
<context:annotation-config />
</beans>
扫描具有注解的包,自动开始注解支持-->
<context:component-scan base-package="cn.laiwenbo.spring.learn.anno"/>
<!--开始注解支持,上面已经开启了注解支持,这句是为了兼容spring3.0以及之前的版本
spring3.0之前,这句必须要
-->
<context:annotation-config />
</beans>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class AnnoAutowireTest {
@Autowired
private UserController userController;
@Test
public void test() {
userController.get();
}
}
注意,在上面bean中,UserDao上面加上了注解@Respository,在UserService上面加上了注解@Service,
在UserController上面加上了Controller,其实这三个注解都表示要将该bean交给Spring来管理,其功能完全是一样的。其实这样功能完全一样的注解共有四个,还有一个@Component,既然功能完全一样为什么要写四个出来呢?其实,这是给程序员看的,具体情况如下:
@Respository : 表示该bean为操作仓库的类,既操作数据库的。
@Service : 表示Service层。
@Controller :表示控制层。
@Component :表示普通javabean,既不在上面三个范围之中。
七、自动注入细节
在spring自动注入中, 有时候会遇见一个接口有多个实现类的情况,遇到这样的情况,需要注意一下一些细节。
首先一个公共的抽象接口:
public interface IBaseDao {
public void save();
}
然后一个类实现该接口:
@Repository
public class UserDao implements IBaseDao {
@Override
public void save() {
System.out.println("我是userDao....");
}
}
配置文件如下:
<!--注解扫描位置-->
<context:component-scan base-package="cn.laiwenbo.spring.learn.di3"/>
<!--兼容性设置,开启注解-->
<context:annotation-config/>
经过测试可以发现,输出为我是userDao...
@RunWith(SpringJUnit4ClassRunner.class)
@Configuration
public class Test {
@Autowired
private IBaseDao myUserDao;
@org.junit.Test
public void test01() {
myUserDao.save();
}
}
需要注意的是,在测试类中,自动注入的类型仅仅是接口的类型:
@Autowired
private IBaseDao myUserDao;
那么为什么可以找到UserDao中的save()方法呢?其实是通过类型来找到的实现类,因为UserDao实现了IBaseDao 既然这里是通过类型来找到的,如果有多个类实现了IBaseDao呢?结果会怎么样呢?
另一个实现了IBaseDao的类:
@Repository
public class OtherDao implements IBaseDao{
@Override
public void save() {
System.out.println("otherDao....");
}
}
结果会报错,找到两个类型的Dao
上面就充分的说明了,spring是按照接口的类型去找的实现子类,这里就是矛盾所在,如果一个接口被多个类实现了,那么这样的操作方式就会报错了。该如何解决呢? 将注入的名字改为实现类的类名+首字母小写
@RunWith(SpringJUnit4ClassRunner.class)
@Configuration
public class Test {
@Autowired
private IBaseDao userDao;
@Autowired
private IBaseDao otherDao;
@org.junit.Test
public void test01() {
userDao.save();
otherDao.save();
}
}
这里注入的名字需要特别注意,是按照实现类的类名来的。说明spring首先按照类型来查找,然后在按照类名的规则来查找的。
但是有没有办法不按照类名来,也可以找到我们想找的实现类呢?当然是有的,在实现类的注解上,加上名称就可以了:
@Repository("oDao")//指定一个名称
public class OtherDao implements IBaseDao{
@Override
public void save() {
System.out.println("otherDao....");
}
}
@Repository("uDao")//指定一个名称
public class UserDao implements IBaseDao {
@Override
public void save() {
System.out.println("我是userDao....");
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@Configuration
public class Test {
@Autowired
private IBaseDao uDao; //指定的名陈
@Autowired
private IBaseDao oDao; //指定的名称
@org.junit.Test
public void test01() {
uDao.save();
oDao.save();
}
}
或者:
@RunWith(SpringJUnit4ClassRunner.class)
@Configuration
public class Test {
@Autowired
@Qualifier("uDao") //限定需要注入的类是哪一个,uDao在对应的类上已经配置,这样下面的变量可以随意命名了
private IBaseDao userDao;
@Autowired
@Qualifier("oDao")//限定需要注入的类是哪一个,oDao在对应的类上已经配置,这样下面的变量可以随意命名了
private IBaseDao otherDao;
@org.junit.Test
public void test01() {
userDao.save();
otherDao.save();
}
}
这里需要额外知道的是@Resource标签:
@RunWith(SpringJUnit4ClassRunner.class)
@Configuration
public class Test {
@Autowired
@Qualifier("uDao")
private IBaseDao userDao;
/*@Autowired
@Qualifier("oDao")
private IBaseDao otherDao;*/
@Resource(name="oDao") //这里的配置等同于 @Autowired @Qualifier("oDao")这两个标签
private IBaseDao otherDao;
@org.junit.Test
public void test01() {
userDao.save();
otherDao.save();
}
}
@Autowired是spring提供的标签,而@Resource是jdk自带的,他们的作用是完全一样的,但是使用细节上还是有一定区别的。另外,在使用过程中,一般不用@Resource标签,因为使用spring的时候,最好是使用和spring一套的东西,规范代码。