文章目录
2.基于注解的IOC配置及Spring整合Junit 总览
参考狂神说MyBatis课程笔记
参考黑马程序员Spring课程笔记
Spring笔记思维导图:
问题扩展与汇总:
1. Bean的自动装配
1.1 自动装配的理解
Spring的自动装配需要从两个角度来实现,或者说是两个操作:
- 组件扫描(component scanning):spring会自动发现应用上下文中所创建的bean;
- 自动装配(autowiring):spring自动满足bean之间的依赖,减少配置文件中的注入,也就是我们说的IoC/DI;
组件扫描和自动装配组合发挥巨大威力,使的显示的配置降低到最少,工厂模式效果更加明显。
1.2 基于xml的自动装配
1)环境搭建(基于xml手动装配)
导入依赖包
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<spring.version>5.2.9.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
- spring 需要导入commons-logging进行日志记录 .我们利用maven , 他会自动下载对应的依赖项
- 导入spring-webmvc ,会自动导入Spring几个需要用的包
新建三个entity实体类
public class Cat {
public void shout() {
System.out.println("miao~");
}
}
public class Dog {
public void shout() {
System.out.println("wang~");
}
}
public class User {
private Cat cat;
private Dog dog;
private String name;
}
配置文件导入支持自动注解的约束以及注入三个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">
<bean id="dog" class="com.sheng.entity.Dog"/>
<bean id="cat" class="com.sheng.entity.Cat"/>
<bean id="user" class="com.sheng.entity.User">
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
<property name="name" value="user1"/>
</bean>
</beans>
测试
public class MyTest {
@Test
public void testMethodAutowire() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = (User) context.getBean("user");
user.getCat().shout();
user.getDog().shout();
}
}
miao~
wang~
2)byName
autowire byName (按名称自动装配)
-
由于在手动配置xml过程中,常常发生字母缺漏和大小写等错误,而无法对其进行检查,使得开发效率降低。
-
采用自动装配将避免这些错误,并且使配置简单化。
修改bean配置,增加一个属性 autowire=”byName”
- 将查找其类中所有的set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat。
- 去spring容器中寻找是否有此字符串名称id的对象,即查看对象是否已经装配到容器中。
- 如果有,就取出注入;如果没有,就报空指针异常。
<?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="dog" class="com.sheng.entity.Dog"/>
<bean id="cat" class="com.sheng.entity.Cat"/>
<!-- 容器自动通过set方法名查找对应字符串名称的id,并自动注入该对象中-->
<bean id="user" class="com.sheng.entity.User" autowire="byName">
<property name="name" value="user1"/>
</bean>
</beans>
3)byType
autowire byType (按类型自动装配)
- 使用autowire byType首先需要保证:同一类型的对象,在spring容器中唯一。
- 如果不唯一,会报不唯一的异常。
<!-- 容器自动通过set方法名查找对应字符串名称的id,并自动注入该对象中-->
<bean id="user" class="com.sheng.entity.User" autowire="byType">
<property name="name" value="user1"/>
</bean>
</beans>
再注册一个cat 的bean对象!
<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat" class="com.kuang.pojo.Cat"/>
<bean id="cat2" class="com.kuang.pojo.Cat"/>
<bean id="user" class="com.kuang.pojo.User" autowire="byType">
<property name="name" value="user1"/>
</bean>
- 测试,报错:NoUniqueBeanDefinitionException
- 删掉cat2,将cat的bean名称改掉!测试!因为是按类型装配,所以并不会报异常,也不影响最后的结果。
- 甚至将id属性去掉,也不影响结果。
<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat2" class="com.kuang.pojo.Cat"/>
1.3 基于注解的自动装配
1)环境搭建:导入约束和注解支持
- 在spring配置文件中引入context文件头
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
- 开启属性注解支持!
<context:annotation-config/>
2)@Autowired
- @Autowired是按类型自动转配的,不支持id匹配。
- 即,与byType功能类似,不是byName
- 默认name是类的名字
- 需要导入 spring-aop的包!
测试:
- 将User类中的set方法去掉,使用@Autowired注解,
- 自动将bean装入目标对象User的bean中,无需set注入
public class User {
@Autowired
private Cat cat;
@Autowired
private Dog dog;
private String name;
public Cat getCat() {
return cat;
}
public Dog getDog() {
return dog;
}
public String getName() {
return name;
}
}
- 配置文件,只需将类注入到IOC容器中即可,bean之间的依赖,IOC容器会根据注解信息自动装配管理
<bean id="dog" class="com.sheng.entity.Dog"/>
<bean id="cat2" class="com.sheng.entity.Cat"/>
<bean id="user" class="com.sheng.entity.User">
<property name="name" value="user1"/>
</bean>
required属性
-
表明要传入的参数或注入的对象是否一定要有,默认是true,必须得有,不能为null,否则报错
-
@Autowired(required=false) 说明: false,对象可以为null;true,对象必须存对象,不能为null。
//如果允许对象为null,设置required = false,默认为true
@Autowired(required = false)
private Cat cat;
3)@Qualifier
- @Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配
- @Qualifier不能单独使用
测试:
- 配置文件修改内容,保证类型存在对象。且id设置的name属性存在多个
<bean id="dog1" class="com.sheng.entity.Dog"/>
<bean id="dog2" class="com.sheng.entity.Dog"/>
<bean id="cat1" class="com.sheng.entity.Cat"/>
<bean id="cat2" class="com.sheng.entity.Cat"/>
- 没有加Qualifier测试,直接报错
- 在属性上添加Qualifier注解
@Autowired
@Qualifier(value = "cat2")
private Cat cat;
@Autowired
@Qualifier(value = "dog2")
private Dog dog;
4)@Resource
- @Resource如有指定的name属性,先按该属性进行byName方式查找装配;
- 其次再进行默认的byName方式进行装配;
- 如果以上都不成功,则按byType的方式自动装配。
- 都不成功,则报异常。
public class User {
//如果允许对象为null,设置required = false,默认为true
@Resource(name = "cat2")
private Cat cat;
@Resource
private Dog dog;
private String str;
}
<bean id="dog" class="com.sheng.entity.Dog"/>
<bean id="dog2" class="com.sheng.entity.Dog"/>
<bean id="cat1" class="com.sheng.entity.Cat"/>
<bean id="cat2" class="com.sheng.entity.Cat"/>
- 测试ok,即先按照默认byName,类名的dog查找id=“dog”
配置文件2:beans.xml , 删掉cat2,修改dog的id名
<bean id="dog2" class="com.sheng.entity.Dog"/>
<bean id="cat1" class="com.sheng.entity.Cat"/>
@Resource
private Cat cat;
@Resource
private Dog dog;
结论:先进行byName查找,失败;再进行byType查找,成功。
@Autowired与@Resource异同:
- @Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。
- @Autowired默认按类型装配(属于spring规范),
- 默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,
- 如果我们想使用名称装配可以结合@Qualifier注解进行使用
- @Resource(属于J2EE复返),默认按照名称进行装配,
- 名称可以通过name属性进行指定。
- 如果没有指定name属性,**当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。 **
- 当找不到与名称匹配的bean时才按照类型进行装配。
- 但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。
2. 使用注解开发
1)使用注解需要的环境搭建
- 在spring4之后,想要使用注解形式,必须得要引入aop的包
- 在配置文件进行初始配置
- 还得要引入一个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">
<!--开启注解支持-->
<context:annotation-config/>
<!--指定注解扫描包-->
<context:component-scan base-package="com.sheng"/>
</beans>
2)使用注解生成Bean
@Component
- 配置扫描哪些包下的注解
<!--指定注解扫描包-->
<context:component-scan base-package="com.sheng"/>
- 在指定包下编写类,增加注解,自动将类生成bean,装配到IOC容器中
@Component
// 相当于配置文件中 <bean id="user" class="当前注解的类"/>
public class User {
@Autowired
private Cat cat;
@Autowired
private Dog dog;
...
}
@Component
public class Cat {
public void shout() {
System.out.println("miao~");
}
}
@Component
public class Dog {
public void shout() {
System.out.println("wang~");
}
}
测试
public class MyTest {
@Test
public void testMethodAutowire() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = (User) context.getBean("user");
user.getCat().shout();
user.getDog().shout();
}
miao~
wang~
@Component三个衍生注解
为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。
- @Controller:web视图层
- @Service:service层
- @Repository:dao层
写上这些注解,就相当于将这个类交给Spring管理装配了!
3)属性数据注入
@Autowired
- @Autowired是按类型自动转配的,不支持id匹配。
- 即,与byType功能类似,不是byName
- 默认name是类的名字
@Qualifier
- @Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配
- @Qualifier不能单独使用
@Resource
- @Resource如有指定的name属性,先按该属性进行byName方式查找装配;
- 其次再进行默认的byName方式进行装配;
- 如果以上都不成功,则按byType的方式自动装配。
- 都不成功,则报异常
@value(“值”)
- 可以不用提供set方法,直接在直接名上添加@value(“值”)
@Component
public class User {
@Autowired
private Cat cat;
@Autowired
private Dog dog;
@Value("user1")
// 相当于配置文件中 <property name="name" value="user1"/>
private String name;
- 如果提供了set方法,在set方法上添加@value(“值”);
@Value("user1")
public void setName(String name) {
this.name = name;
}
4)用于改变作用范围
@Scope
-
相当于:
-
singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
-
prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收
@Component
@Scope("prototype")
public class User {
@Autowired
// @Resource
private Cat cat;
@Autowired
// @Resource
private Dog dog;
}
5)和生命周期相关的: (了解)
- 相当于:
@PostConstruct
- 作用:
用于指定初始化方法。
@PreDestroy
-
作用:
用于指定销毁方法。-
在初始化方法上注解**@PostConstruct**,表明在构造对象后进行初始化
-
在销毁方法前加注解**@PreDestroy**, 表明在销毁对象前,进行销毁方面逻辑的处理
-
@Service public class AlphaService { public AlphaService() { System.out.println("实例化构造AlphaService"); } @PostConstruct public void init() { System.out.println("初始化AlphaService"); } @PreDestroy public void destroy() { System.out.println("销毁AlphaService"); } }
-
3. 关于 Spring 注解和 XML 的选择问题
-
注解的优势:
- 配置简单,维护方便(我们找到类,就相当于找到了对应的配置)。
-
XML 的优势:
- 修改时,不用改源码。不涉及重新编译和部署。
-
Spring 管理 Bean 方式的比较:
- xml与注解整合开发 :推荐最佳实践
- xml管理Bean的定义
- 注解完成属性注入
- 使用过程中, 可以不用扫描,扫描是为了类上的注解
4. spring 的纯注解配置
@Configuration
- JavaConfig 原来是 Spring 的一个子项目,它通过 Java 类的方式提供 Bean 的定义信息,
- 在 Spring4 的版本, JavaConfig 已正式成为 Spring4 的核心功能 。
- spring 的配置类,相当于 bean.xml 文件
作用:
- 用于指定当前类是一个 spring 配置类, 当创建容器时会从该类上加载注解。 获取容器时需要使用
- AnnotationApplicationContext(有@Configuration 注解的类.class)。
- 当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。
属性:
- value:用于指定配置类的字节码class
示例:
@Configuration
public class MyConfig {
}
@ComponentScan
作用:
- 用于指定 spring 在初始化容器时要扫描的包。
- 作用和在 spring 的 xml 配置文件中的:
<context:component-scan base-package=“com.sheng”/>是一样的。
属性:
- basePackages:用于指定要扫描的包。和该注解中的 value 属性作用一样。
示例:
@Configuration
@ComponentScan("com.sheng")
public class MyConfig {
}
@Bean
作用:
- 该注解只能写在方法上,表明使用此方法创建一个对象,并且放入 spring 容器。
- 用此方法==可以将其他jar包中的类注入到IOC容器中==
- 相当于注册的bean,
属性:
- name:给当前@Bean 注解方法创建的对象指定一个名称(即 bean 的 id)。
- 如果不指定,这里的返回值就Bean的类型,方法名就是默认的bean的id!
细节:
- 当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象。
-
查找的方式和Autowired注解的作用是一样的
-
@Configuration public class JdbcConfig { @Bean(name="runner") @Scope("prototype") public QueryRunner createQueryRunner(@Qualifier("ds2") DataSource dataSource){ return new QueryRunner(dataSource); } /** * 创建数据源对象 * @return */ @Bean(name="ds2") public DataSource createDataSource(){ } }
-
综合测试示例:
@Component
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
@Configuration
@ComponentScan("com.sheng")
public class MyConfig {
@Bean("user")
public User getUser() {
return new User();
}
}
public class MyTest {
@Test
public void testMethodAutowire() {
// 1.获取容器
ApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig.class);
// 2.获取对象,并处理对象
User user = ac.getBean("user", User.class);
user.setName("user2");
System.out.println(user.getName());
}
user2
@Import
作用:
- 用于导入其他配置类,
- 在引入其他配置类时,可以不用再写@Configuration 注解。 当然,写上也没问
题。
- 在引入其他配置类时,可以不用再写@Configuration 注解。 当然,写上也没问
属性:
- value[]:用于指定其他配置类的字节码。
综合示例:
@Configuration
@ComponentScan("com.sheng")
@Import({AlphaConfig.class, JdbcConfig.class})
public class MyConfig {
// @Bean("user")
// public User getUser() {
// return new User();
// }
}
@Configuration
public class JdbcConfig {
@Bean("jdbcUser")
public User getUser() {
return new User();
}
}
@Configuration
public class AlphaConfig {
@Bean("data")
public SimpleDateFormat simpleDateFormat() {
return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
}
}
public class MyTest {
@Test
public void testMethodAutowire() {
// 1.获取容器
ApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig.class);
// 2.获取对象,并处理对象
User user = ac.getBean("jdbcUser", User.class);
user.setName("user2");
System.out.println(user.getName());
SimpleDateFormat simpleDateFormat = ac.getBean("data", SimpleDateFormat.class);
System.out.println(simpleDateFormat.format(new Date()));
}
}
user2
2022-02-13 10:59:57
@PropertySource
作用:
- 用于加载.properties 文件中的配置。
- 例如我们配置数据源时,可以把连接数据库的信息写到
properties 配置文件中,就可以使用此注解指定 properties 配置文件的位置。
- 例如我们配置数据源时,可以把连接数据库的信息写到
属性:
- value[]:用于指定 properties 文件位置。如果是在类路径下,需要写上 classpath:
示例:
jdbc.properties 文件:
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis2?characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
jdbc.username=@
jdbc.password=@
连接数据库的配置类:
import com.mchange.v2.c3p0.ComboPooledDataSource; // 导入的c3p0连接池坐标
import org.apache.commons.dbutils.QueryRunner; // 导入的dbutils数据库简单封装管理工具
/**
* 和spring连接数据库相关的配置类
*/
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
/**
* 用于创建一个QueryRunner对象
* @param dataSource
* @return
*/
@Bean(name="runner")
@Scope("prototype")
//import org.apache.commons.dbutils.QueryRunner; // 导入的dbutils数据库简单封装管理工具
public QueryRunner createQueryRunner(@Qualifier("ds2") DataSource dataSource){
return new QueryRunner(dataSource);
}
/**
* 创建数据源对象
* @return
*/
@Bean(name="ds2")
public DataSource createDataSource(){
try {
//import com.mchange.v2.c3p0.ComboPooledDataSource; // 导入的c3p0连接池坐标
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setDriverClass(driver);
ds.setJdbcUrl(url);
ds.setUser(username);
ds.setPassword(password);
return ds;
}catch (Exception e){
throw new RuntimeException(e);
}
}
}
在配置类中读取配置文件jdbcConfig.properties
@Configuration
@ComponentScan("com.sheng")
@Import({AlphaConfig.class, JdbcConfig.class})
// 读取配置文件
@PropertySource("classpath:jdbcConfig.properties")
public class MyConfig {
// @Bean("user")
// public User getUser() {
// return new User();
// }
}
扩展:SpringBoot自动注解方案
- @SpringBootApplication注解的类就是配置类
- @SpringBootApplication源码查看:
- @SpringBootConfiguration结构
- @EnableAutoConfiguration结构
5. Spring 整合 Junit
解决的问题:
-
在测试类中,每个测试方法都有以下两行代码:
// 1.获取容器 ApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig.class); // 2.获取对象,并处理对象 User user = ac.getBean("jdbcUser", User.class);
-
这两行代码的作用是获取容器,如果不写的话,直接会提示空指针异常。所以又不能轻易删掉
-
程序能自动帮我们创建容器。一旦程序能自动为我们创建 spring 容器,我们就
- 无须手动创建了,问题也就解决了。
-
junit 给我们暴露了一个注解,可以让我们替换掉它的运行器
- 这时,我们需要依靠 spring 框架,因为它提供了一个运行器,可以读取配置文件(或注解)来创建容器。我们只需要告诉它配置文件在哪就行了。
配置的步骤:
-
1、导入spring整合junit的jar(坐标)
-
当我们使用spring 5.x版本的时候,要求junit的jar必须是4.12及以上
-
<!--导入junit测试坐标--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <!-- 导入spring整合junit的jar(坐标) --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.1.7.RELEASE</version> </dependency> </dependencies>
-
-
2、使用Junit提供的一个注解的运行器把原有的main方法替换了,替换成spring提供的
-
==@Runwith()==
-
-
3、告知spring的运行器,spring和ioc创建是基于xml还是注解的,并且说明位置
-
@ContextConfiguration
-
locations:指定xml文件的位置,加上classpath关键字,表示在类路径下
-
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations= {"classpath:bean.xml"}) public class AccountServiceTest { }
-
-
classes:指定注解类所在地位置
-
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = MyConfig.class) public class MyTest { }
-
-
测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = MyConfig.class)
public class MyTest {
@Autowired
private User user;
@Autowired
private SimpleDateFormat simpleDateFormat;
@Test
public void testMethodAutowire() {
user.setName("user2");
System.out.println(user.getName());
System.out.println(simpleDateFormat.format(new Date()));
}
}
user2
2022-02-14 12:23:00
扩展:SpringBoot整合测试类的源码
@RunWith(SpringRunner.class) // 运行器
@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class) // 主配置类
public class CommunityApplicationTests{
}
- SpringRunner源码: