一、Spirng中IoC注解
在bean.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.xsd">
<!-- 告知spring在创建容器时要扫描的包,配置所需要的标签不是在beans的约束中,而是一个名称为
context名称空间和约束中 -->
<context:component-scan base-package="cn.hyz"></context:component-scan>
<!-- <context:annotation-config/> -->
</beans>
1.用于创建对象的注解
相当于:<bean id="" class="">
它们的作用和在xml配置文件中编写一个<bean>
标签实现的功能是一样的
1.1. @Component
- 作用:
把当前类对象存入 Spring 容器中。相当于在 xml 中配置一个 bean。 - 属性:
value:指定 bean 的 id。如果不指定 value 属性,默认 bean 的 id 是当前类的类名。首字母小写。
// 没有写value 默认值accountServiceImpl
@Component
public class AccountServiceImpl implements IAccountService {
}
public class Client {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
IAccountService as = (IAccountService) ac.getBean("accountServiceImpl");
System.out.println(as);
}
}
1.2.@Controller @Service @Repository
以上三个注释它们的作用和属性与Component一模一样
它们三个是spring框架为我们提供明确的三层使用的注解,使用我们的三层对象更加清晰
@Controller
:一般在用于表现层@Service
:一般用于业务层@Repository
:一般用在持久层
2.用于注入数据的注解
相当于:
<property name="" ref="">
<property name="" value="">
它们的作用和在 XML 配置文件的 bean 标签中编写一个 property 标签实现的功能是一样的(set方法注入)
2.1.@Autowired
- 作用:自动根据类型注入,只要容器中有唯一一个bean对象类型和要注入的变量类型匹配,就可以注入成功。
- 出现位置:可以是变量上,也可以是方法上
- 细节:在使用注解注入时,set方法不是必须是用的
- 特点:只能注入其他 bean 类型
/**
* 账户的业务层实现类
*/
@Repository("accountDao1")
public class AccountDaoImpl implements IAccountDao {
public void saveAccount() {
System.out.println("保存了账户");
}
}
/**
* 账户的业务层实现类
*/
@Repository("accountDao2")
public class AccountDaoImpl2 implements IAccountDao {
public void saveAccount() {
System.out.println("保存了账户22");
}
}
public class AccountServiceImpl implements AccountService {
// @Autowired
// @Qualifier("accountDao1")
@Resource(name = "accountDao1")
private IAccountDao accountDao1;
public void saveAccount() {
accountDao1.saveAccount();
}
- 只有一个相符合的bean时,直接匹配数据类型
- 有多个相匹配的bean时,先匹配数据类型,再根据 变量名称 与 bean的id 进行精确匹配
- 如果IOC容器中有多个类型匹配时,此时注解不知道该用哪一个所以此时我们再加一个
@Qualifier
注解或者使用@Resource
注解。
2.2.@Qualifier
- 作用:
在自动按照类型注入的基础之上,再按照 Bean 的 id 注入。
它在给字段注入时不能独立使用,必须和@Autowire
一起使用;但是给方法参数注入时,可以独立使用。 - 属性:
value:指定 bean 的 id。
@Component
public class AccountServiceImpl implements IAccountService {
@Autowired
@Qualifier("accountDao1")
private IAccountDao accountDao1;
@Override
public void saveAccount() {
accountDao1.saveAccount();
}
}
2.3.@Resource
- 作用:
直接按照 Bean 的 id 注入。它也只能注入其他 bean 类型。可以单独使用 - 属性:
name:指定 bean 的 id。
@Component
public class AccountServiceImpl implements IAccountService {
@Resource(name = "accountDao2")
private IAccountDao accountDao1;
@Override
public void saveAccount() {
accountDao1.saveAccount();
}
}
以上三个注入都只能注入其他bean类型数据,而基本类型和String类型无法使用上述注解实现。
另外,集合类型的注入只能通过xml配置文件来实现。
2.4.@Value
- 作用:
注入基本数据类型和 String 类型数据的 - 属性:
value:用于指定数据的值,可以使用Spring的SpEL(Spring的EL表达式) - SpEL表达式格式:
${表达式}
3.用于改变作用范围的
相当于:<bean id="" class="" scope="">
它们的作用何在bean标签中使用scope属性实现的功能是一样的
3.1. @Scope
- 作用:用于指定bean的作用范围
- 属性:指定范围的取值。常用取值:
singleton
和prototype
4.和生命周期相关的(扩展了解)
相当于:<bean id="" class="" init-method="" destroy-method="" />
他们的作用和在bean
标签中使用init-method
和destroy-method
的作用是一样的
4.1.@PostConstruct
- 作用:指定初始化方法,放在方法上
4.2@PreDestory
- 作用:指定销毁方法,放在方法上
@Service("accountService")
//@Scope("prototype")
public class AccountServiceImpl implements AccountService {
// @Autowired
// @Qualifier("accountDao1")
@Resource(name = "accountDao1")
private IAccountDao accountDao1;
@PostConstruct
public void init() {
System.out.println("对象初始化了");;
}
@PreDestroy
public void destroy() {
System.out.println("对象销毁了");;
}
public void saveAccount() {
accountDao1.saveAccount();
}
}
二、Spring的纯注解配置
写到此处,基于注解的 IoC 配置已经完成,但是:我们依然离不开 spring 的 xml 配置文件,那么可否不写这个 bean.xml
,完全使用注解来完成我们的配置?
需要特别注意的是我们选择哪种配置的原则是简化开发和配置方便,而非追求某种技术
。
1.待改造的问题
我们发现,之所以我们现在离不开 xml 配置文件,是因为我们有一句很关键的配置:
<!-- 告知spring框架在,读取配置文件,创建容器时,扫描注解,依据注解创建对象,并存入容器中 -->
<context:component-scan base-package="com.itheima"></context:component-scan>
如果他要也能用注解配置,那么我们就离脱离 xml 文件又进了一步。
另外,数据源和 JdbcTemplate 的配置也需要靠注解来实现。
<!-- 配置 dbAssit -->
<bean id="dbAssit" class="com.itheima.dbassit.DBAssit">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///spring_day02"></property>
<property name="user" value="root"></property>
<property name="password" value="1234"></property>
</bean>
2.新注解说明
2.1.@Configuration
- 作用:
用于指定当前类是一个 spring 配置类,当创建容器时会从该类上加载注解。获取容器时需要使用AnnotationApplicationContext(
有@Configuration
注解的类.class
)。 - 属性:
value:用于指定配置类的字节码 - 细节:当配置类作为
AnnotationConfigApplicationContext
对象创建的参数时,该注解可以不写
@Configuration
public class SpringConfiguration {
}
2.2.@ComponentScan
- 作用:用于通过注解指定spring在创建容器时要扫描的包
- 属性:
value:它和basePackages的作用是一样的,都是用于指定创建容器时要扫描的包 - 我们使用此注解就等同于在xml中配置了:
<context:component-scan base-package="cn.hyz"></context:component-scan>
@Configuration
@ComponentScan("cn.hyz")
public class SpringConfiguration {
}
2.3.@Bean
- 作用:用于把当前方法的返回值作为
bean
对象存入spring的IOC容器中 - 属性:
name
:用于指定bean
的id,默认值是当前方法的名称 - 细节:当我们使用注解配置方法是,如果方法有参数,spring框架会去容器中查找有没有可用的
bean
对象,查找的方式和@Autowired
注解的作用是一样的
/**
* 连接数据库的配置类
*/
@Configuration
@ComponentScan("com.smallbeef")
public class JDBCConfiguration {
/**
* 创建一个数据源,并存入 spring 容器中
* * @return
* */
@Bean(name="dataSource")
public DataSource createDataSource() {
try {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setUser("root");
ds.setPassword("1234");
ds.setDriverClass("com.mysql.jdbc.Driver");
ds.setJdbcUrl("jdbc:mysql:///spring_day02");
return ds;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 创建一个 QuerryRunner对象,并且也存入 spring 容器中
* * @param dataSource
* * @return
* */
@Bean(name="dbAssit")
public DBAssit createDBAssit(DataSource dataSource) {
return new DBAssit(dataSource);
}
}
我们已经把数据源和 DBAssit 从配置文件中移除了,此时可以删除 bean.xml
了。
但是由于没有了配置文件,创建数据源的配置又都写死在类中了。
2.4.@PropertySource
- 作用:用于加载
.properties
文件中的配置。例如我们配置数据源时,可以把连接数据库的信息写到properties
配置文件中,就可以使用此注解指定properties
配置文件的位置。 - 属性:
value[]:用于指定properties
文件位置。如果是在类路径下,需要写上classpath:
在下面的代码中可以看到数据库的配置是写死的
@Bean(name="dataSource")
public DataSource createDataSource() {
try {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setUser("root");
ds.setPassword("1234");
ds.setDriverClass("com.mysql.jdbc.Driver");
ds.setJdbcUrl("jdbc:mysql://localhost:3306/day44_ee247_spring");
return ds;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
我们将数据库配置放在 .properties
文件中,利用 @PropertySource
注解读取该文件,并用 @Value
注解传值
jdbcConfig.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/day44_ee247_spring
jdbc.username=root
jdbc.password=1234
利用@Value
传值
/**
* 连接数据库的配置类
*/
@Configuration
@ComponentScan("com.smallbeef")
public class JDBCConfiguration {
@Value("${jdbc.driver}") //与properties中属性一致
private Stirng driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
/**
* 创建一个数据源,并存入 spring 容器中
* * @return
* */
@Bean(name="dataSource")
public DataSource createDataSource() {
try {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setUser("username");
ds.setPassword("password");
ds.setDriverClass("driver");
ds.setJdbcUrl("url");
return ds;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 创建一个 QuerryRunner对象,并且也存入 spring 容器中
* * @param dataSource
* * @return
* */
@Bean(name="dbAssit")
public DBAssit createDBAssit(DataSource dataSource) {
return new DBAssit(dataSource);
}
}
2.5.@Import
- 作用:
用于导入其他配置类,在引入其他配置类时,可以不用再写@Configuration
注解。 - 属性:
value[]:用于指定其他配置类的字节码。
当我们使用Import注解之后,有Import注解的类就是父配置类,导入的都是子配置类 - 示例代码:
@Configuration
@ComponentScan(basePackages = "com.itheima.spring")
@Import({ JdbcConfig.class})
public class SpringConfiguration {
}
@Configuration
@PropertySource("classpath:jdbc.properties")
public class JdbcConfig{
}
3.通过注解获取容器
ApplicationContext ac =
new AnnotationConfigApplicationContext(SpringConfiguration.class);
4.关于 Spring 注解和 XML 的选择问题
- 注解的优势:
配置简单,维护方便(我们找到类,就相当于找到了对应的配置)。 - XML 的优势:
修改时,不用改源码。不涉及重新编译和部署。 - Spring 管理 Bean 方式的比较:
三、Spring整合Junit(必须掌握)
问题:
在测试类中,每个测试方法都有以下两行代码:
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
IAccountService as = ac.getBean("accountService",IAccountService.class);
这两行代码的作用是获取容器,如果不写的话,直接会提示空指针异常。所以又不能轻易删掉。
1.Spring整合Junit配置
- 第一步:拷贝整合 junit 的必备 jar 包到 lib 目录
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
//当我们使用spring5.x版本的时候,要求junit的jar包必须是4.12及以上
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
- 第二步:使用
@RunWith
注解替换原有运行器
/**
* 测试类
*/
@RunWith(SpringJUnit4ClassRunner.class)
public class AccountServiceTest {
}
- 第三步:使用
@ContextConfiguration
指定 spring 配置文件的位置
@ContextConfiguration
注解:- locations 属性:用于指定配置文件的位置。如果是类路径下,需要用 classpath:表明
- classes 属性:用于指定注解的类。当不使用 xml 配置时,需要用此属性指定注解类的位置。
/**
* 测试类
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations= {"classpath:bean.xml"})
public class AccountServiceTest {
}
- 第四步:使用@Autowired 给测试类中的变量注入数据
/**
* 测试类
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations= {"classpath:bean.xml"})
public class AccountServiceTest {
@Autowired
private IAccountService as ;
}
2.为什么不把测试类配到 xml 中
原因是因为:
第一:当我们在 xml 中配置了一个 bean,spring 加载配置文件创建容器时,就会创建对象。
第二:测试类只是我们在测试功能时使用,而在项目中它并不参与程序逻辑,也不会解决需求上的问题,所以创建完了,并没有使用。那么存在容器中就会造成资源的浪费。
所以,基于以上两点,我们不应该把测试配置到 xml 文件中。