第2章基于注解的IOC配置
1.1 环境搭建
1.1.1 第一步:拷贝必备jar包到工程的lib目录。
注意:在基于注解的配置中,我们还要多拷贝一个aop的jar包。如下图:
1.1.2 第二步:在类的根路径下创建一个任意名称的xml文件(不能是中文)
基于注解整合时,导入约束时需要多导入一个context名称空间下的约束。
给配置文件导入约束:
<?xml version="1.0"encoding="UTF-8"?>
<!--导入schema
约束的位置在:
..\spring-framework-4.2.4.RELEASE\docs\spring-framework-reference\html\xsd-configuration.html
文件中。
注意:要导入schema约束
-->
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd ">
</beans>
1.1.3 第三步:使用@Component注解配置管理的资源
/**
* 客户的业务层实现类
*/
@Component(value="customerService")
public class CustomerServiceImplimplements ICustomerService {
@Override
public void saveCustomer() {
System.out.println("执行了保存客户");
}
}
1.1.4 第四步在spring的配置文件中开启spring对注解ioc的支持
<!-- 告知spring框架在,读取配置文件,创建容器时,扫描注解,依据注解创建对象,并存入容器中 -->
<context:component-scanbase-package="com.itheima"></context:component-scan>
1.2 常用注解
1.2.1 用于创建对象的
相当于:<bean id="" class="">
1.2.1.1 @Component
作用:
把资源让spring来管理。相当于在xml中配置一个bean。
属性:
value:指定bean的id。如果不指定value属性,默认bean的id是当前类的类名。首字母小写。
1.2.1.2 @Controller @Service @Repository
他们三个注解都是针对一个的衍生注解,他们的作用及属性都是一模一样的。
他们只不过是提供了更加明确的语义化。
@Controller:一般用于表现层的注解。
@Service:一般用于业务层的注解。
@Repository:一般用于持久层的注解。
细节:如果注解中有且只有一个属性要赋值时,且名称是value,value在赋值时可以不写。
1.2.2 用于注入数据的
相当于:<property name="" ref=""> <propertyname="" value="">
1.2.2.1 @Autowired
作用:
自动按照类型注入。当使用注解注入属性时,set方法可以省略。它只能注入其他bean类型。当有多个类型匹配时,使用要注入的对象变量名称作为bean的id,在spring容器查找,找到了也可以注入成功。找不到就报错。
1.2.2.2 @Qualifier
作用:
在自动按照类型注入的基础之上,再按照Bean的id注入。它不能独立使用,必须和@Autowire一起使用。
属性:
value:指定bean的id。
1.2.2.3 @Resource
作用:
直接按照Bean的id注入。它也只能注入其他bean类型。
属性:
name:指定bean的id。
1.2.2.4 @Value
作用:
注入基本数据类型和String类型数据的
属性:
value:用于指定值
1.2.3 用于改变作用范围的:
相当于:<bean id="" class=""scope="">
1.2.3.1 @Scope
作用:
指定bean的作用范围。
属性:
value:指定范围的值。
取值:singleton prototype requestsession globalsession
1.2.4 和生命周期相关的:(了解)
相当于:<bean id="" class=""init-method=""destroy-method="" />
1.2.4.1 @PostConstruct
作用:
用于指定初始化方法。
1.2.4.2 @PreDestroy
作用:
用于指定销毁方法。
1.2.5 代码示例
业务层代码:
/**
* 客户的业务层接口
* @authorzhy
*/
public interface ICustomerService {
/**
* 保存客户
* @param customer
*/
void saveCustomer();
}
/**
* 客户的业务层实现类
* @authorzhy
*/
//作用就相当于在xml中配置了一个bean标签,该注解有value属性,含义是bean的id。
//不写的时候,默认的id是:当前类名,且首字母小写。即:customerServiceImpl
@Component(value="customerService")
@Scope(value="singleton")
public class CustomerServiceImplimplements ICustomerService {
// 自动按照数据类型注入,拿着当前变量的数据类型在spring的容器中找,找到后,给变量赋值。
// 当有多个类型匹配时,会使用当前变量名称customerDao作为bean的id,继续在容器中找。
// 找到了,也能注入成功。找不到就报错。
// @Autowired
// @Qualifier(value="customerDao2")//在自动按照类型注入的基础之上,再按照id注入
@Resource(name="customerDao2")//直接按照bean的id注入
private ICustomerDaocustomerDao =null;
@Value("com.mysql.jdbc.Driver")//注入基本类型和String类型数据
private Stringdriver;
@Override
public void saveCustomer() {
System.out.println(driver);
customerDao.saveCustomer();
}
}
持久层代码:
/**
* 客户的持久层接口
* @authorzhy
*/
public interface ICustomerDao {
/**
* 保存客户
*/
void saveCustomer();
}
/**
* 客户的持久层实现类11111111111111111111
* @authorzhy
*/
@Repository("customerDao1")
public class CustomerDaoImplimplements ICustomerDao {
@Override
public void saveCustomer() {
System.out.println("保存了客户111111111111111111");
}
}
/**
* 客户的持久层实现类222222222222222222222222
* @authorzhy
*/
@Repository("customerDao2")
public class CustomerDaoImpl2implements ICustomerDao {
@Override
public void saveCustomer() {
System.out.println("保存了客户2222222222222222222");
}
}
测试类代码:
publicclass Client {
public static void main(String[] args) {
//1.获取容器
ApplicationContext ac =newClassPathXmlApplicationContext("bean.xml");
//2.根据id获取对象
ICustomerService cs = (ICustomerService)ac.getBean("customerService"); cs.saveCustomer();
}
}
配置文件:
<?xml version="1.0"encoding="UTF-8"?>
<!--我们导入约束时,除了昨天的那部分之外,还要单独导入一个context名称空间 -->
<beansxmlns="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-scanbase-package="com.itheima"></context:component-scan>
</beans>
1.2.6 关于注解和XML的选择问题
注解的优势:
配置简单,维护方便(我们找到类,就相当于找到了对应的配置)。
XML的优势:
修改时,不用改源码。不涉及重新编译和部署。
1.3 spring管理对象细节
基于注解的spring IoC配置中,bean对象的特点和基于XML配置是一模一样的。
写到此处,基于注解的IoC配置已经完成,但是大家都发现了一个问题:我们依然离不开spring的xml配置文件,那么能不能不写这个bean.xml,所有配置都用注解来实现呢?
答案是肯定的,请看下一章节。
1.4 spring的纯注解配置
1.4.1 待改造的问题
我们发现,之所以我们现在离不开xml配置文件,是因为我们有一句很关键的配置:
<!-- 告知spring框架在读取配置文件,创建容器时,扫描注解,依据注解创建对象,并存入容器中 -->
<context:component-scanbase-package="com.itheima"></context:component-scan>
如果它要也能用注解配置,那么我们就可以脱离xml文件了。
1.4.2 使用注解配置要扫描的包
如果不使用xml对bean进行管理。那么,那句关键的配置跑哪去了呢?
在service的实现类上:
/**
* 客户的业务层实现类
* @authorzhy
*/
@Configuration//表明当前类是一个配置类
@ComponentScan(basePackages ="com.itheima")//配置要扫描的包
//以上两个注解可以写在业务层上,可以单独创建一个类,然后写在类上。
@Component//作用就相当于在xml中配置了一个bean标签,该注解有value属性,含义是bean的id。不写的时候,默认的id是:customerServiceImpl
@Scope(value="singleton")
public class CustomerServiceImplimplements ICustomerService {
// @Autowired//自动按照数据类型注入,拿着当前变量的数据类型在spring的容器中找,找到后,给变量赋值。
// @Qualifier(value="customerDao2")//在自动按照类型注入的基础之上,再按照id注入
@Resource(name="customerDao2")//直接按照bean的id注入
private ICustomerDaocustomerDao =null;
@Value("com.mysql.jdbc.Driver")
private Stringdriver;
@Override
public void saveCustomer() {
System.out.println(driver);
customerDao.saveCustomer();
}
}
那么新的问题又来了,我们如何获取容器呢?
public class Client {
public static void main(String[] args) {
//1.获取容器:由于我们已经没有了xml文件,所以再用读取xml方式就不能用了。
//这时需要指定加载哪个类上的注解
ApplicationContext ac =
new AnnotationConfigApplicationContext(CustomerServiceImpl.class);
//2.根据id获取对象
ICustomerService cs = (ICustomerService)ac.getBean("customerService");
cs.saveCustomer();
}
}
1.4.3 新注解说明
1.4.3.1 @Configuration
作用:
用于指定当前类是一个配置类,会从该类上加载注解。读取该类上@ ComponentScan注解初始化spring容器。
1.4.3.2 @ComponentScan
作用:
用于指定spring在初始化容器时要扫描的包。
属性:
basePackages:用于指定要扫描的包。和该注解中的value属性作用一样。
1.4.3.3 @PrppertySource
作用:用于加载.properties文件中的配置
属性:
value[]:用于指定properties文件位置。如果是在类路径下,需要写上classpath:
示例代码:
配置:
@Configuration
@ComponentScan(basePackages ="cn.itcast.spring")
@PropertySource("classpath:info.properties")
public class Configuration_A {
}
实体类:
@Component("car")
public class Car {
private Stringid ;
@Value("${car.name}")//此时是读取properties文件,根据key取value了。
private Stringname ;
@Value("${car.price}")//此时是读取properties文件,根据key取value了。
private Doubleprice ;
public String getId() {
return id;
}
public void setId(Stringid) {
this.id =id;
}
public String getName() {
return name;
}
public void setName(Stringname) {
this.name =name;
}
public Double getPrice() {
returnprice;
}
public void setPrice(Doubleprice) {
this.price =price;
}
@Override
public String toString() {
return"Car [id=" +id + ", name=" +name +", price=" +price + "]";
}
}
info.properties文件:
car.name= \u4FDD\u65F6\u6377//中文是保时捷
car.price= 1000000
1.4.3.4 @Import
作用:
用于导入其他配置类
属性:
value[]:用于指定其他配置类的字节码。
示例代码:
@Configuration
@ComponentScan(basePackages ="cn.itcast.spring")
@Import({ Configuration_B.class})
public class Configuration_A {
}
@Configuration
@PropertySource("classpath:info.properties")
public class Configuration_B {
}
1.4.3.5 @Bean
作用:
该注解只能写在方法上,表明使用此方法创建一个对象,并且交给spring管理。
属性:
name:给当前@Bean注解方法创建的对象指定一个名称(即bean的id)。
示例代码:
@Bean(name ="datasource2")
public DataSource createDS()throwsException {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
comboPooledDataSource.setUser("root");
comboPooledDataSource.setPassword("1234");
comboPooledDataSource.setDriverClass("com.mysql.jdbc.Driver");
comboPooledDataSource.setJdbcUrl("jdbc:mysql:///spring_ioc");
return comboPooledDataSource;
}
第2章 Spring整合Junit
2.1 准备测试环境
2.1.1 创建业务层接口实现类
/**
* 客户的业务层接口
* @authorzhy
*/
public interface ICustomerService {
/**
* 查询所有客户
* @return
*/
List<Customer> findAllCustomer();
/**
* 保存客户
* @param customer
*/
void saveCustomer(Customercustomer);
}
/**
* 客户的业务层实现类
* @authorzhy
*/
publicclass CustomerServiceImplimplementsICustomerService {
private ICustomerDaocustomerDao;
public void setCustomerDao(ICustomerDaocustomerDao) {
this.customerDao =customerDao;
}
@Override
public List<Customer> findAllCustomer() {
returncustomerDao.findAllCustomer();
}
@Override
public void saveCustomer(Customercustomer) {
customerDao.save(customer);
}
}
2.1.2 创建持久层接口实现类
/**
* 客户的持久层接口
* @authorzhy
*/
public interface ICustomerDao {
/**
* 查询所有客户
* @return
*/
List<Customer> findAllCustomer();
/**
* 保存客户
* @param customer
*/
void save(Customercustomer);
}
/**
* 客户的持久层实现类
* @authorzhy
*/
public class CustomerDaoImplimplements ICustomerDao {
@Override
public List<Customer>findAllCustomer() {
System.out.println("查询了所有客户");
return null;
}
@Override
public void save(Customercustomer) {
System.out.println("保存了客户");
}
}
2.1.3 导入junit的jar包
2.1.4 编写测试类
/**
* 测试客户的业务层和持久层
* @authorzhy
*/
public class CustomerServiceTest {
private ICustomerServicecustomerService;
@Test
public void testFindAll(){
customerService.findAllCustomer();
}
@Test
public void testSave(){
Customerc =new Customer();
c.setCustName("传智学院 ");
customerService.saveCustomer(c);
}
}
2.2 使用xml配置步骤
2.2.1 xml文件中的配置
<?xml version="1.0"encoding="UTF-8"?>
<beansxmlns="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">
<!--把资源交给spring来管理 -->
<beanid="customerDao"class="com.itheima.dao.impl.CustomerDaoImpl"></bean>
<beanid="customerService"class="com.itheima.service.impl.CustomerServiceImpl">
<propertyname="customerDao"ref="customerDao"></property>
</bean>
</beans>
2.2.2 第一步:拷贝整合junit的必备jar包到lib目录
此处需要注意的是,导入jar包时,需要导入一个spring中aop的jar包。
2.2.3 第二步:使用@RunWith注解替换原有运行器
@RunWith(SpringJUnit4ClassRunner.class)
public class CustomerServiceTest {
private ICustomerServicecustomerService;
@Test
public void testFindAll(){
customerService.findAllCustomer();
}
@Test
public void testSave(){
Customerc =new Customer();
c.setCustName("传智学院 ");
customerService.saveCustomer(c);
}
}
2.2.4 第三步:使用@ContextConfiguration指定spring配置文件的位置
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:bean.xml"})
publicclassCustomerServiceTest {
privateICustomerServicecustomerService;
@Test
public voidtestFindAll(){
customerService.findAllCustomer();
}
@Test
public void testSave(){
Customer c = newCustomer();
c.setCustName("传智学院 ");
customerService.saveCustomer(c);
}
}
2.2.5 第四步:使用@Autowired给测试类中的变量注入数据
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:bean.xml"})
publicclass CustomerServiceTest {
@Autowired
private ICustomerServicecustomerService;
@Test
public voidtestFindAll(){
customerService.findAllCustomer();
}
@Test
public void testSave(){
Customer c = newCustomer();
c.setCustName("传智学院 ");
customerService.saveCustomer(c);
}
}
2.3 使用纯注解配置步骤
2.3.1 第一步:拷贝整合junit的必备jar包到lib目录
此处需要注意的是,导入jar包时,需要导入一个spring中aop的jar包。
2.3.2 第二步:把资源都用注解管理
@Service("customerService")
public class CustomerServiceImplimplements ICustomerService {
@Autowired
private ICustomerDaocustomerDao;
@Override
public List<Customer>findAllCustomer() {
returncustomerDao.findAllCustomer();
}
@Override
public void saveCustomer(Customer customer) {
customerDao.save(customer);
}
}
/**
* 客户的持久层实现类
* @authorzhy
*/
@Repository("customerDao")
public class CustomerDaoImplimplements ICustomerDao {
@Override
public List<Customer>findAllCustomer() {
System.out.println("查询了所有客户");
return null;
}
@Override
public void save(Customer customer) {
System.out.println("保存了客户");
}
}
2.3.3 第三步:使用注解配置方式创建spring容器
@Configuration
@ComponentScan(basePackages={"com.itheima"})
publicclass CustomerServiceTest {
@Autowired
private ICustomerServicecustomerService;
@Test
public void testFindAll(){
customerService.findAllCustomer();
}
@Test
public void testSave(){
Customer c =new Customer();
c.setCustName("传智学院 ");
customerService.saveCustomer(c);
}
}
2.3.4 第四步:使用RunWith注解和ContextConfiguration注解配置
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={CustomerServiceTest.class})
@Configuration
@ComponentScan(basePackages={"com.itheima"})
publicclass CustomerServiceTest {
@Autowired
private ICustomerServicecustomerService;
@Test
public void testFindAll(){
customerService.findAllCustomer();
}
@Test
public void testSave(){
Customer c = newCustomer();
c.setCustName("传智学院 ");
customerService.saveCustomer(c);
}
}
2.4 为什么不把测试类配到xml中
在解释这个问题之前,先解除大家的疑虑,配到XML中能不能用呢?
答案是肯定的,没问题,可以使用。
那么为什么不采用配置到xml中的方式呢?
这个原因是这样的:
第一:当我们在xml中配置了一个bean,spring加载配置文件创建容器时,就会创建对象。
第二:测试类只是我们在测试功能时使用,而在项目中它并不参与程序逻辑,也不会解决需求上的问题,所以创建完了,并没有使用。那么存在容器中就会造成资源的浪费。
所以,基于以上两点,我们不应该把测试配置到xml文件中。