【Spring】06 - Spring 注解
2020-09-14 本文已影响0人
itlu
1. 使用纯注解配置
- 初始化一个项目 , 在该项目的基础上实现纯注解配置。
1.1 @Configuration
-
作用:指定当前类是一个配置类。
-
案例: 创建一个配置类
@Configuration
@ComponentScan("com.lyp")
public class BeanConfig {
...
}
- 细节 : 当前配置类作为AnnotationConfigurationApplicationContext对象创建的参数时,该注解可以不写。
1.2 @ComponentScan
-
作用:用于通过注解指定spring创建容器是需要扫描的包。
-
属性:
value :
它和basePackages
的作用都是一样的,都是用于配置创建容器时需要扫描的包。basePackages:
它和value
的作用都是一样的,都是用于配置创建容器时需要扫描的包。
- 使用此注解就等同于在xml中配置了:
<!--配置注解需要扫描的包 , 高告知Spring 在创建容器时需要扫描的包-->
<context:component-scan base-package="com.lyp"/>
1.3 @Bean
-
作用 :用于将当前方法的返回值作为Bean对象存入SpringIOC容器中。
-
属性
:
- name:用于指定bean的id,当不写时,默认值就是当前方法的名称。
-
细节:当我们使用注解配置方法时,如果方法有参数Spring框架会去容器中查找有没有可用的bean对象,查找方式和
@Autowired
注解的作用是一样的。 -
@Bean
注解配置案例 :
@Configuration
@ComponentScan("com.lyp")
public class SpringConfiguration {
/**
* 创建!QueryRunner对象 ,使用@Bean对象将其交给Spring容器管理
*
* @param dataSource
* @return
*/
@Bean
public QueryRunner createQueryRunner(DataSource dataSource) {
return new QueryRunner(dataSource);
}
/**
* 创建数据源
*
* @return
*/
@Bean
public DataSource createDataSource() {
try {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/spring");
dataSource.setUser("root");
dataSource.setPassword("root");
return dataSource;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
1.4 AnnotationConfigurationApplicationContext
实现类
-
AnnotationConfigurationApplicationContext
该实现类主要用于纯注解配置,加载配置类。 -
修改测试类代码 :
public class SpringIOCAccountTest {
private ApplicationContext ac;
private AccountService as;
@Before
public void init() {
// ac = new ClassPathXmlApplicationContext("bean.xml");
ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
as = ac.getBean("accountService", AccountService.class);
}
...
}
![](https://img.haomeiwen.com/i17394504/8102213be6af50c5.png)
- 此时便可以删除bean.xml配置文件。
1.5 @Import
-
作用:用于导入其他的配置类;
-
属性
:
value:
用于指定其他配置类的字节码。当我们使用@Import
注解之后有@Import
注解的类就是主配置(父配置)类。而导入的都是子配置类。
- 需求:创建一个配置类不加任何注解,但是可以使用。 此时就需要将该配置类的字节码作为参数传递给
ApplicationContext
的实现类AnnotationConfigurationApplicationContext(配置类.class)
public class JdbcConfiguration { ... }
- 需求:创建一个配置类不加任何注解,但是可以使用。简单的方式:在主配置文件中使用
@Import(配置类.class)
注解将该配置类引入作为其子配置类。
public class JdbcConfiguration { ... }
@Configuration
@ComponentScan("com.lyp")
@Import(JdbcConfiguration.class)
public class SpringConfiguration {
}
1.6 @PropertySource
-
作用:用于指定Properties文件的位置。
-
属性
:
value:
指定文件的名称和路径。关键字:classpath
表示类路径。
1.6.1 @PropertySource
和 @Value
搭配使用 :
- 在
resources
目录下创建一个jdbcConfiguration.properties
文件,编写数据源配置信息:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring
jdbc.username=root
jdbc.password=root
- 使用
@PropertySource
注解在主配置文件中引入配置文件:
@Configuration
@ComponentScan("com.lyp")
@Import(JdbcConfiguration.class)
@PropertySource("classpath:JdbcConfiguration.properties")
public class SpringConfiguration {
}
- 在子配置文件中定义相应的属性,并使用
@Value("${}")
: 引入属性值:
public class JdbcConfiguration {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
}
1.7 @Qualifier
在方法的形参中可以单独使用
-
可以解决一个接口有多个实现类的方式,多个实现类在一个配置类中进行使用。此时根据类型自动注入可能就会显得无力。
-
创建一个新的数据库 ,并导入新的数据到数据库中 :
CREATE DATABASE spring2 CHARACTER SET utf8;
USE spring2;
CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(40),
money FLOAT
)CHARACTER SET utf8 COLLATE utf8_general_ci;
INSERT INTO account(NAME,money) VALUES('aaa',1000);
INSERT INTO account(NAME,money) VALUES('bbb',1000);
INSERT INTO account(NAME,money) VALUES('ccc',1000);
- 再编写一个数据源配置方法,并使用
@Bean
注解指定name :
/**
* 创建数据源
*
* @return
*/
@Bean(name = "dataSource")
public DataSource createDataSource() {
try {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass(driver);
dataSource.setJdbcUrl(url);
dataSource.setUser(username);
dataSource.setPassword(password);
return dataSource;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 在创建一个数据源 用于测试 在一个接口有多个实现类时 ,而且实现类同时出现在一个配置类中时。不能自动按照类型注入。此时该如何解决
* @return
*/
@Bean(name = "db2")
public DataSource createDataSource2() {
try {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass(driver);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/spring2");
dataSource.setUser(username);
dataSource.setPassword(password);
return dataSource;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
- 以上代码执行是不会出现冲突的:因为在创建
QueryRunner
对象是使用的形参叫dataSource
和 第一个数据源对象@Bean
的name
属性是一致的。在不能根据类型进行选择之后,会以形参的名称作为key在容器中查找对应的对象。但是如果将两个数据源对象的@Bean
的name
都改了和创建QueryRunner
对象传递的形参名称不一致时就会出现冲突:
![](https://img.haomeiwen.com/i17394504/3f15c8643831e861.png)
![](https://img.haomeiwen.com/i17394504/2e865ffc4523de6a.png)
- 解决:使用
@Qualifier
注解明确指定使用哪个对象 :
解决冲突:指定明确
2. 测试 QueryRunner 是否单例
-
我们使用完纯注解方式配置完成之后,是否会有疑惑,我们创建的
QueryRunner
对象是单例对象还是多例对象呢? -
进行测试 :
/**
* 创建!QueryRunner对象 ,使用@Bean对象将其交给Spring容器管理
*
* @param dataSource
* @return
*/
@Bean("runner")
public QueryRunner createQueryRunner(DataSource dataSource) {
return new QueryRunner(dataSource);
}
@Test
public void testQueryRunner() {
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
QueryRunner runner1 = (QueryRunner) ac.getBean("runner");
QueryRunner runner2 = (QueryRunner) ac.getBean("runner");
System.out.println(runner1 == runner2);
}
![](https://img.haomeiwen.com/i17394504/2ec432aedd98a005.png)
2.1 @Scope
注解
- 使用
@Scope
注解标注创建QueryRunner对象的方法,设置作用范围为 prototype (多例的)。
@Bean("runner")
@Scope("prototype")
public QueryRunner createQueryRunner(DataSource dataSource) {
return new QueryRunner(dataSource);
}
![](https://img.haomeiwen.com/i17394504/f9c4bcb99082250a.png)
3. Spring整合Junit
-
应用程序的入口:main方法。
-
Juni单元测试中,没有main方法也能执行? 其实junit集成了一个main方法。该方法就会判断当前测试类中哪些方法有
@Test
注解。junit就让有@Test注解的方法执行。 -
junit不会管我们是否采用spring框架:在执行测试方法时,
junit
根据不知道我们是不是使用了spring
框架。所以也不会为我们读取配置文件/配置类创建Spring容器。 -
由以上三点可知:当测试方法执行时,没有
IOC
容器,就算写了@Autowired
注解,也无法实现注入。此时使用该对象将出现空指针异常
。
3.1 Spring整合Junit的配置 (修改纯注解的)
- 导入Spring整合Junit配置的jar包依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
- 使用Junit提供的一个注解把原来的main方法替换了,替换成spring提供的。
@Runwith(SpringJUnit4ClassRunner.class)
;
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringIOCAccountTest {...}
- 告知Spring的运行器,spring和ioc创建是基于xml还是注解的。并且说明位置。
@ContextConfiguration()
locations:
指定xml文件位置,加上classpath关键字表示在类路径下。classes:
指定注解类所在位置。配置类的字节码。- 当我们使用
spring5.x
版本的时候,要求Junit的jar包必须是4.12
及以上。
![](https://img.haomeiwen.com/i17394504/1b78c76cbe973e7a.png)
![](https://img.haomeiwen.com/i17394504/f365523fba741e95.png)
- 经过以上配置之后便可以不在使用如下比较繁琐的配置 :
public class SpringIOCAccountTest {
private ApplicationContext ac;
private AccountService as;
@Before
public void init() {
ac = new ClassPathXmlApplicationContext("bean.xml");
as = ac.getBean("accountService", AccountService.class);
}
/**
* 查询所有账户信息
*/
@Test
public void testyFindAll() {
List<Account> accounts = as.findAll();
for (Account account : accounts) {
System.out.println(account);
}
}
...
}
- 而是使用如下简单的配置 :
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class SpringIOCAccountTest {
@Autowired
private AccountService as;
/**
* 查询所有账户信息
*/
@Test
public void testyFindAll() {
List<Account> accounts = as.findAll();
for (Account account : accounts) {
System.out.println(account);
}
}
3.2 Spring整合Junit的配置 (修改xml配置的)
- 步骤基本和上面相同 :不同的是这里配置文件的指定方式
@ContextConfiguration(locations = "classpath:bean.xml")
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class SpringIOCAccountTest {
@Autowired
private AccountService as;
/**
* 查询所有账户信息
*/
@Test
public void testyFindAll() {
List<Account> accounts = as.findAll();
for (Account account : accounts) {
System.out.println(account);
}
}
...
}
Demo码云地址
- Demo码云地址:Spring基于纯注解的account表+dbutils的增删查改