三天掌握Spring系列第一讲 IOC 解决耦合就是这么简单
首先要了解到一个小知识,Spring和Spring Framework的关系。没错,他们是包含的关系。我们常说Spring框架就是Spring Framework框架。其实,Spring项目是一个集合,Spring Framework是其中的一个子项目,其他的还有Spring Boot、Spring Cloud等。只不过我们把Spring Framework简称为Spring了。自己理解即可!
Spring 与其他方便集成,自身Spring 提供一些集成的组件,实际就是按照其他厂商提供的接口(使用文档)进行了封装,方便使用Spring框架的开发人员。给Spring点赞!
Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架。(详细请参考官方文档或者百度百科)
1.2、Spring体系结构
当你学习一个新东西的时候,首先要了解它里面包含什么,哪些是在项目中可能被使用到的。当然,很多公司选择它,它里面肯定会有一些被大家认可和常常使用的组件。
-
数据访问/集成
-
Web(Spring MVC)
-
核心
-
测试
-
其他组件
1.3、Spring的俩大核心
-
依赖注入(DI):
Spring 最认同的技术是控制反转的依赖注入(DI)模式。控制反转(IoC)是一个通用的概念,它可以用许多不同的方式去表达,依赖注入仅仅是控制反转的一个具体的例子。 当编写一个复杂的 Java 应用程序时,应用程序类应该尽可能的独立于其他的 Java 类来增加这些类可重用可能性,当进行单元测试时,可以使它们独立于其他类进行测试。依赖注入(或者有时被称为配线)有助于将这些类粘合在一起,并且在同一时间让它们保持独立。 到底什么是依赖注入?让我们将这两个词分开来看一看。这里将依赖关系部分转化为两个类之间的关联。例如,类 A 依赖于类 B。现在,让我们看一看第二部分,注入。所有这一切都意味着类 B 将通过 IoC 被注入到类 A 中。 依赖注入可以以向构造函数传递参数的方式发生,或者通过使用 setter 方法 post-construction。由于依赖注入是 Spring 框架的核心部分,所以我将在一个单独的章节中利用很好的例子去解释这一概念。
-
面向切面的程序设计(AOP):
Spring 框架的一个关键组件是面向方面的程序设计(AOP)框架。一个程序中跨越多个点的功能被称为横切关注点,这些横切关注点在概念上独立于应用程序的业务逻辑。有各种各样常见的很好的关于方面的例子,比如日志记录、声明性事务、安全性,和缓存等等。 在 OOP 中模块化的关键单元是类,而在 AOP 中模块化的关键单元是方面。AOP 帮助你将横切关注点从它们所影响的对象中分离出来,然而依赖注入帮助你将你的应用程序对象从彼此中分离出来。 Spring 框架的 AOP 模块提 供了面向方面的程序设计实现,可以定义诸如方法拦截器和切入点等,从而使实现功能的代码彻底的解耦出来。使用源码级的元数据,可以用类似于.Net属性的方式合并行为信息到代码中。我将在一个独立的章节中讨论更多关于 Spring AOP 的概念。
1.4、Spring的好处
-
方便解耦,简化开发
通过 Spring 提供的 IoC 容器,可以将对象间的依赖关系交由 Spring进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
-
AOP编程的支持
通过 Spring的 AOP 功能,方便进行面向切面的编程,许多不容易用传统OOP 实现的功能可以通过 AOP 轻松应付。
-
声明式事务的支持
可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。
-
方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。
-
方便集成各种优秀框架
Spring可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的直接支持。
2、IOC容器
2.1、为什么需要IOC
我们项目中的数据基本都使用到数据库,那么连接数据库是非常重要的,例如:
public class JdbcDemo {
public static void main(String[] args) throws Exception{
//1.注册驱动
//DriverManager.registerDriver(new com.mysql.jdbc.Driver());
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接
//3.获取预处理 sql 语句对象
//4.获取结果集
//5.遍历结果集
}
}
我们的类依赖了数据库的具体驱动类(MySQL),如果这时候更换了数据库品牌(比如 Oracle),需要修改源码来重新数据库驱动。这显然不是我们想要的。
当然你可以说,我写个工具类不就好了!卧槽??
同时,也产生了一个新的问题,mysql 驱动的全限定类名字符串是在 java 类中写死的,一旦要改还是要修改源码。
解决这个问题也很简单,使用配置文件配置。
这个例子是用来介绍耦合的,IOC在一定程度上解决了,代码中极大多数出现的异常为空指针,类型异常等。
2.2、基于 XML 的配置(入门案例)[掌握]
项目准备
public interface UserDao {
User saveUser(User user);
}
public class UserDaoImpl implements UserDao {
1、在Maven工程的resource下创建bean.xml
给配置文件导入约束:
<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">
<!-- more bean definitions go here -->
</beans>
2、让spring管理资源,在配置文件中配置 service 和 dao
<!-- bean 标签:用于配置让 spring 创建对象,并且存入 ioc 容器之中
id 属性:对象的唯一标识。
class 属性:指定要创建对象的全限定类名
-->
<!-- 配置 service -->
<bean id="userService" class="cn.fage.spring.impl.UserServiceImpl"></bean>
<!-- 配置 dao -->
<bean id="userDao" class="cn.fage.spring.impl.UserDaoImpl"></bean>
内部执行了一个new 对象(); 的一个操作
3、单元测试
public class TestSpring {
ApplicationContext context;
2.3、ApplicationContext继承关系
1、 Spring 中 工厂的类结构图
2、 BeanFactory和 ApplicationContext 的区别
BeanFactory 才是 Spring 容器中的顶层接口。
ApplicationContext 是它的子接口。
BeanFactory 和 ApplicationContext 的区别:
-
创建对象的时间点不一样。
-
ApplicationContext:只要一读取配置文件,默认情况下就会创建对象。
-
BeanFactory:什么使用什么时候创建对象。
-
3、 ApplicationContext 接口的实现类
-
ClassPathXmlApplicationContext: 它是从类的根路径下加载配置文件 推荐使用这种
-
FileSystemXmlApplicationContext: 它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
-
AnnotationConfigApplicationContext: 当我们使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。
2.4、Bean
1、bean标签
作用:
-
用于配置对象让 spring 来创建的。
-
默认情况下它调用的是类中的无参构造函数。如果没有无参构造函数则不能创建成功。
属性:
-
id: 给对象在容器中提供一个唯一标识。用于获取对象。
-
class:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数。
-
scope:指定对象的作用范围。
-
singleton :默认值,单例的。
-
prototype :多例的。
-
request :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中。
-
session :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中。
-
global session :WEB 项目中,应用在 Portlet 环境。如果没有 Portlet 环境那么 globalSession 相当于 session。
-
2、bean 的作用范围和生命周期
单例对象:scope="singleton"
一个应用只有一个对象的实例。它的作用范围就是整个引用。
生命周期:
-
对象出生:当应用加载,创建容器时,对象就被创建了。
-
对象活着:只要容器在,对象一直活着。
-
对象死亡:当应用卸载,销毁容器时,对象就被销毁了。
多例对象:scope="prototype"
每次访问对象时,都会重新创建对象实例。
生命周期:
-
对象出生:当使用对象时,创建新的对象实例。
-
对象活着:只要对象在使用中,就一直活着。
-
对象死亡:当对象长时间不用时,被 Java 的垃圾回收器回收了。
3、实例化 Bean 的三种方式
1、使用默认无参构造函数
<!--在默认情况下:
它会根据默认无参构造函数来创建类对象。如果 bean 中没有默认无参构造函数,将会创建失败。
-->
<bean id="userService" class="cn.fage.spring.impl.UserServiceImpl"/>
2、Spring管理静态工厂-使用静态工厂的方法创建对象
/**
* @author lin
* @version 1.0
* @date 2020-08-10 15:30
* @Description //模拟一个静态工厂,创建业务层实现类
*/
public class StaticFactory {
public static UserService createUserService() {
return new UserServiceImpl();
}
}
static-factory.xml:
<!-- 此种方式是:
使用 StaticFactory 类中的静态方法 createUserService 创建对象,并存入 spring 容器
id 属性:指定 bean 的 id,用于从容器中获取
class 属性:指定静态工厂的全限定类名
factory-method 属性:指定生产对象的 静态方法
-->
<!-- 配置 service -->
<bean id="userService" class="cn.fage.factory.StaticFactory" factory- method="createUserService"/>
3、Spring管理实例工厂-使用实例工厂的方法创建对象
public class InstanceFactory {
/**
* 模拟一个实例工厂,创建业务层实现类
* 此工厂创建对象,必须现有工厂实例对象,再调用方法
*/
public UserService createUserService() {
return new UserServiceImpl();
}
}
<!-- 此种方式是:
先把工厂的创建交给 spring 来管理。
然后在使用工厂的 bean 来调用里面的方法
factory-bean 属性:用于指定实例工厂 bean 的 id。
factory-method 属性:用于指定实例工厂中创建对象的方法。
-->
<bean id="instanceFactory" class="cn.fage.factory.InstanceFactory"/>
<bean id="userService2" factory-bean="instanceFactory" factory-method="createUserService"/>
2.5、Spring的依赖注入
1、依赖注入的概念
依赖注入:Dependency Injection。它是 spring 框架核心 ioc 的具体实现。
我们的程序在编写时,通过控制反转,把对象的创建交给了 spring,但是代码中不可能出现没有依赖的情况。 ioc 解耦只是降低他们的依赖关系,但不会消除。例如:我们的业务层仍会调用持久层的方法。
那这种业务层和持久层的依赖关系,在使用 spring 之后,就让 spring 来维护了。 简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。
2、构造函数注入
顾名思义,就是使用类中的构造函数,给成员变量赋值。注意,赋值的操作不是我们自己做的,而是通过配置的方式,让 spring 框架来为我们注入。具体代码如下:
@Data @AllArgsConstructor @NoArgsConstructor public class User { private Integer id; private String name; private int age; private Date birthday; } <!-- 使用构造函数的方式,给 service 中的属性传值 要求: 类中需要提供一个对应参数列表的构造函数。 涉及的标签: constructor-arg 属性: index:指定参数在构造函数参数列表的索引位置 type:指定参数在构造函数中的数据类型 name:指定参数在构造函数中的名称 用这个找给谁赋值 =======上面三个都是找给谁赋值,下面两个指的是赋什么值的============== value:它能赋的值是基本数据类型和 String 类型 ref:它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件中配置过的 bean --> <bean id="user" class="cn.fage.pojo.User"> <constructor-arg name="id" value="1"/> <constructor-arg name="name" value="张三"/> <constructor-arg name="age" value="18"/> <constructor-arg name="birthday" ref="now"/> </bean> <bean id="now" class="java.util.Date"/>
3、Set方法注入
顾名思义,就是在类中提供需要注入成员的 set 方法。具体代码如下:
<!-- 通过配置文件给 bean 中的属性传值:使用 set 方法的方式 涉及的标签: property 属性: name:找的是类中 set 方法后面的部分 ref:给属性赋值是其他 bean 类型的 value:给属性赋值是基本数据类型和 string 类型的 实际开发中,此种方式用的较多。 --> <bean id="user2" class="cn.fage.pojo.User"> <property name="age" value="21"/> <property name="name" value="test"/> <property name="birthday" ref="now"/> </bean>
4、使用 p 名称空间注入数据(本质还是调用 set 方法)
此种方式是通过在 xml中导入 p名称空间,使用 p:propertyName 来注入数据,它的本质仍然是调用类中的 set 方法实现注入功能。
--引入命名空间描述 xmlns:p="http://www.springframework.org/schema/p" <!-- P 标签 注入 --> <bean id="user3" class="cn.fage.pojo.User" p:name="user3" p:age="22" p:birthday-ref="now"/>
5 、注入集合属性
顾名思义,就是给类中的集合成员传值,它用的也是set方法注入的方式,只不过变量的数据类型都是集合。 我们这里介绍注入数组,List,Set,Map,Properties。具体代码如下:
@Data @AllArgsConstructor @NoArgsConstructor public class User { private Integer id; private String name; private int age; private Date birthday; private String[] phones; private Set<String> tels; private List<User> friends; private Map<String, Object> family; private Properties pros; public User(Integer id, String name, int age, Date birthday) { this.id = id; this.name = name; this.age = age; this.birthday = birthday; } }
<!-- 注入集合数据 List 结构的: array,list,set Map 结构的 map,entry,props,prop --> <!-- 集合注入 --> <bean class="cn.fage.pojo.User" id="user4"> <!-- 基础属性 --> <property name="id" value="1"/> <property name="age" value="18"/> <property name="name" value="user4"/> <property name="birthday" ref="now"/> <!-- 在注入集合数据时,只要结构相同,标签可以互换 --> <!-- 给数组注入数据 --> <property name="phones"> <array> <value>华为手机</value> <value>苹果手机</value> </array> </property> <!-- 注入Set集合数据 --> <property name="tels"> <set> <value>134xxx</value> <value>135xxx</value> <value>136xxx</value> <!-- 测试重复数据 --> <value>134xxx</value> </set> </property> <!-- 注入List集合数据 --> <property name="friends"> <list value-type="cn.fage.pojo.User"> <ref bean="user"/> <ref bean="user2"/> <ref bean="user3"/> </list> </property> <!-- 注入Map数据 --> <property name="family"> <map> <entry key="老婆" value="陈乔恩"/> <entry key="朋友" value="易烊千玺"/> </map> </property> <!-- 注入properties数据 --> <property name="pros"> <props> <prop key="testA">aaa</prop> <prop key="testB">bbb</prop> </props> </property> </bean>
结果:
User(id=1, name=user4, age=18, birthday=Mon Aug 10 16:17:35 CST 2020, phones=[华为手机, 苹果手机], tels=[134xxx, 135xxx, 136xxx], friends=[User(id=1, name=user, age=18, birthday=Mon Aug 10 16:17:35 CST 2020, phones=null, tels=null, friends=null, family=null, pros=null), User(id=null, name=user2, age=21, birthday=Mon Aug 10 16:17:35 CST 2020, phones=null, tels=null, friends=null, family=null, pros=null), User(id=null, name=user3, age=22, birthday=Mon Aug 10 16:17:35 CST 2020, phones=null, tels=null, friends=null, family=null, pros=null)], family={老婆=陈乔恩, 朋友=易烊千玺}, pros={testB=bbb, testA=aaa})
3、给予注解的IOC配置
1、明确:写在最前
企业中开发常常使用的一种方式
注解配置和 xml 配置要实现的功能都是一样的,都是要降低程序间的耦合。只是配置的形式不一样。
关于实际的开发中到底使用xml还是注解,每家公司有着不同的使用习惯。所以这两种配置方式我们都需要掌握。
在学习注解配置时,采用上一章节的案例,把 spring 的 xml 配置内容改为使用注解逐步实现。
2 、使用注解配置管理的资源
@Repository(value = "userDao") public class UserDaoImpl implements UserDao { @Override public User saveUser(User user) { System.out.println("UserDaoImpl 执行 saveUser() 方法 "); return user; } } @Service(value="userService") public class UserServiceImpl implements UserService { @Autowired @Qualifier("userDao") private UserDao userDao; @Override public User saveUser(User user) { return userDao.saveUser(user); } } @Configuration public class SpringConfiguration { @Bean(value = "user") public User createUser1() { return new User(1, "user1", 18, new Date()); } }
3、修改bean.xml开启对注解的支持
注意: 基于注解整合时,导入约束时需要多导入一个 context 名称空间下的约束。
<?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"> <!-- 告知spring在创建容器时要扫描注解的包 --> <context:component-scan base-package="cn.fage"/> </beans>
4、常用注解
4.1 、用于创建对象的
相当于:<bean id="" class="">
1、 @Component
作用: 把资源让 spring 来管理。相当于在 xml 中配置一个 bean。
属性: value:指定 bean 的 id。如果不指定 value 属性,默认 bean 的 id 是当前类的类名。首字母小写。
2、 @Controller @Service @Repository
他们三个注解都是针对@Component的衍生注解,他们的作用及属性都是一模一样的。
他们只不过是提供了更加明确的语义化。
-
@Controller:一般用于表现层的注解。
-
@Service:一般用于业务层的注解。
-
@Repository:一般用于持久层的注解。
细节:如果注解中有且只有一个属性要赋值时,且名称是 value,value在赋值是可以不写。
4.2 用于注入数据的
相当于:<property name="" ref=""> <property name="" value="">
1、 @Autowired
作用:
自动按照类型注入。当使用注解注入属性时,set方法可以省略。它只能注入其他 bean 类型。当有多个类型匹配时,使用要注入的对象变量名称作为 bean 的 id,在 spring 容器查找,找到了也可以注入成功。找不到就报错。
2、 @Qualifier
作用:
在自动按照类型注入的基础之上,再按照 Bean 的 id 注入。它在给字段注入时不能独立使用,必须和@Autowire 一起使用;但是给方法参数注入时,可以独立使用。
属性:
value:指定 bean 的 id。
3、 @Resource
作用:
直接按照 Bean 的 id 注入。它也只能注入其他 bean 类型。
属性:
name:指定 bean 的 id。
4、 @Value
作用:
注入基本数据类型和 String 类型数据的
属性:
value:用于指定值
4.3、用于改变作用范围的
相当于:<bean id="" class=""scope=""/>
1、 @Scope
作用:
指定 bean 的作用范围。
属性:
value: 指定范围的值
取值:singleton prototype request session globalsession
4.4、和生命周期相关的(了解)
相当于:<bean id="" class=""init-method="" destroy-method=""/>
1、 @PostConstruct
作用: 用于指定初始化方法。
2、 @PreDestroy
作用: 用于指定销毁方法。
4.5 、关于 Spring 注解 和 XML 的选择问题
-
注解的优势
-
配置简单,维护方便(我们找到类,就相当于找到了对应的配置)。
-
-
XML的优势
-
修改时,不用改源码。不涉及重新编译和部署。
-
-
Spring管理Bean方式的比较:
4.5 、spring管理对象细节
基于注解的 spring IoC 配置中,bean 对象的特点和基于 XML 配置是一模一样的。
4.6、 spring的纯注解配置
写到此处,基于注解的 IoC 配置已经完成,但是我们依然离不开 spring 的 xml 配置文件,那么能不能不写这个 bean.xml,所有配置都用注解来实现呢?
4.7、配置类注解
1、 @Configuration
作用:
用于指定当前类是一个 spring 配置类,当创建容器时会从该类上加载注解。获取容器时需要使用 AnnotationApplicationContext(有@Configuration 注解的类.class)。
属性:
value:用于指定配置类的字节码
//spring的配置类,相当于bean.xml文件 @Configuration public class SpringConfiguration { }
2 、@ComponentScan
如何配置创建容器时要扫描的包呢?
作用:
用于指定 spring 在初始化容器时要扫描的包。作用和在 spring 的 xml 配置文件中的:<context:component-scan base-package="cn.fage"/>
是一样的。
属性:
basePackages:用于指定要扫描的包。和该注解中的 value 属性作用一样。
//spring的配置类,相当于bean.xml文件 @Configuration @ComponentScan("cn.fage") public class SpringConfiguration { }
3、 @Bean
作用:
该注解只能写在方法上,用于把当前方法的返回值作为bean对象存入spring的ioc容器中。
属性:
name:给当前@Bean 注解方法创建的对象指定一个名称(即 bean 的 id)。当不写时,默认值是当前方法的名称。
细节:
当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象。 查找的方式和Autowired注解的作用是一样的。
示例参考第三章第三节
4、 @PropertySource
读取配置文件
作用:
用于加载.properties 文件中的配置。例如我们配置数据源时,可以把连接数据库的信息写到 properties 配置文件中,就可以使用此注解指定 properties 配置文件的位置。
属性:
value[]:用于指定 properties 文件位置。如果是在类路径下,需要写上 classpath:
示例代码:
@Configuration @PropertySource(value = {"classpath:user/user.properties"}) public class UserConfigs { @Value("${id}") int id; @Value("${name}") String name; @Value(("${age}")) int age; @Bean(value = "user2") public User createUser1() { return new User(id, name, age); } }
id=2 name=user2 age=18
5、 @Import
作用:
用于导入其他配置类,在引入其他配置类时,可以不用再写@Configuration 注解。当然,写上也没问题。如果ComponentScan扫描包里面包括其,那么则不需要引入。
属性:
value[]:用于指定其他配置类的字节码。
示例代码:
复制代码
@Configuration @Import(value = {UserConfigs.class}) @ComponentScan(value = "cn.fage") public class SpringConfiguration {}
6 、通过注解获取容器
/** * @author lin * @version 1.0 * @date 2020-08-10 11:36 * @Description TODO */ public class TestSpringAnnotation { ApplicationContext context; @Before public void before() { // 使用 xml 里面的 <context:component-scan base-package="cn.fage"/> // context = new ClassPathXmlApplicationContext("spring-xml/annotation-bean.xml"); context = new AnnotationConfigApplicationContext(SpringConfiguration.class); } @Test public void test01() { System.out.println(context); for (String name : context.getBeanDefinitionNames()) { System.out.println(name); } UserService userService = (UserService) context.getBean("userService"); userService.saveUser(context.getBean("user", User.class)); } }
运行结果如下:
org.springframework.context.annotation.AnnotationConfigApplicationContext@4d49af10, started on Mon Aug 10 17:28:07 CST 2020 org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory springConfiguration userConfigs userDao userService user2 user UserDaoImpl 执行 saveUser() 方法 进程已结束,退出代码0
5、使用spring-test的启动器
在代码中我们使用before方法
ApplicationContext context; @Before public void before() { context = new ClassPathXmlApplicationContext("spring-xml/bean.xml"); }
这两行代码的作用是获取容器,如果不写的话,直接会提示空指针异常。所以又不能轻易删掉。
1、解决思路分析
针对上述问题,我们需要的是程序能自动帮我们创建容器。一旦程序能自动为我们创建 spring 容器,我们就无须手动创建了,问题也就解决了。
我们都知道,junit 单元测试的原理,但显然,junit 是无法实现的,因为它自己都无法知晓我们是否使用了 spring 框架,更不用说帮我们创建 spring 容器了。不过好在,junit 给我们暴露了一个注解,可以让我们替换掉它的运行器。
这时,我们需要依靠 spring 框架,因为它提供了一个运行器,可以读取配置文件(或注解)来创建容器。我们只需要告诉它配置文件在哪就行了。
当然你可以想,你如果启动的是tomcat后台服务器,再把接口暴露出来,那么是不是就想到Spring Boot了!!
2、配置步骤
第一步:使用@RunWith注解替换原有运行器
第二步:使用@ContextConfiguration指定spring配置文件的位置
第三步:使用@Autowired给测试类中的变量注入数据(就是使用了!!)
/** * @author lin * @version 1.0 * @date 2020-08-10 17:31 * @Description TODO */ // SpringJUnit4ClassRunner 和 SpringRunner 都可以 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfiguration.class) public class TestRunner { @Autowired UserService userService; @Autowired User user; @Test public void test01() { User user = userService.saveUser(this.user); System.out.println(user); } }
运行结果如下:
UserDaoImpl 执行 saveUser() 方法 User(id=1, name=user1, age=18, birthday=Mon Aug 10 18:00:09 CST 2020, phones=null, tels=null, friends=null, family=null, pros=null)
@ContextConfiguration 注解:
-
locations 属性:用于指定配置文件的位置。如果是类路径下,需要用 classpath:表明 例如
locations=("classpath:bean.xml")
-
classes 属性:用于指定注解的类。当不使用 xml 配置时,需要用此属性指定注解类的位置。
3、为什么到 不把测试类配到 xml 中
配到xml中肯定是可以使用的。
那么为什么不采用配置到 xml 中的方式呢?
这个原因是这样的:
第一:当我们在 xml 中配置了一个 bean,spring 加载配置文件创建容器时,就会创建对象。
第二:测试类只是我们在测试功能时使用,而在项目中它并不参与程序逻辑,也不会解决需求上的问题,所以创建完了,并没有使用。那么存在容器中就会造成资源的浪费。
代码分享
微信公众号 点击关于我,加入QQ群,即可获取到代码
这是一个稍偏基础和偏技术的公众号,甚至其中包括一些可能阅读量很低的包含代码的技术文,不知道你是不是喜欢,期待你的关注。
如果你觉得文章还不错,就请点击右上角选择发送给朋友或者转发到朋友圈~
● 扫码关注我们
据说看到好文章不推荐的人,服务器容易宕机!