Spring02
spring第二天:Spring基于注解的IOC以及IOC的案例
1、spring中ioc的常用注解
2、案例使用xml方式和注解方式实现单表的CRUD操作
持久层技术选择:dbutils
3、改造基于注解的ioc案例,使用纯注解的方式实现
4、spring和Junit整合。
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"
scope="" init-method="" destroy-method="" >
<property name="" value=""|ref=""></property>
</bean>
IOC注解按照作用分类:
用于创建对象的,
用于注入数据的,
用于改变作用范围的,
和生命周期相关
-------------------------------------------------------------------------------------------------------------
第一个注解,用于创建对象的@Component
曾经XML的配置:
* <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"
* scope="" init-method="" destroy-method="">
* <property name="" value="" | ref=""></property>
* </bean>
*
* 用于创建对象的
* 他们的作用就和在XML配置文件中编写一个<bean>标签实现的功能是一样的
* Component:
* 作用:用于把当前类对象存入spring容器中
* 属性:
* value:用于指定bean的id。当我们不写时,它的默认值是当前类名,且首字母改小写。
* Controller:一般用在表现层
* Service:一般用在业务层
* Repository:一般用在持久层
* 以上三个注解他们的作用和属性与Component是一模一样。
* 他们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰
* <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"
* scope="" init-method="" destroy-method="">
* <property name="" value="" | ref=""></property>
* </bean>
*
* 用于创建对象的
* 他们的作用就和在XML配置文件中编写一个<bean>标签实现的功能是一样的
* Component:
* 作用:用于把当前类对象存入spring容器中
* 属性:
* value:用于指定bean的id。当我们不写时,它的默认值是当前类名,且首字母改小写。
* Controller:一般用在表现层
* Service:一般用在业务层
* Repository:一般用在持久层
* 以上三个注解他们的作用和属性与Component是一模一样。
* 他们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰
在spring framework document中ctrl+F,搜xmlns:context
导入到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="com.itheima"></context:component-scan> </beans>AccountServiceImpl.java
/** * 账户的业务层实现类 * 曾经xml的配置 * <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean> */ @Component public class AccountServiceImpl implements IAccountService { private IAccountDao accountDao ; public AccountServiceImpl(){ System.out.println("对象创建了"); } public void saveAccount(){ accountDao.saveAccount(); } }Client.java
/** * 模拟一个表现层,用于调用业务层 */ public class Client { public static void main(String[] args) { //1.获取核心容器对象 ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); //2.根据id获取Bean对象 IAccountService as = (IAccountService)ac.getBean("accountServiceImpl"); System.out.println(as); } }-------------------------------------------------------------------------------------------------------------
用于注入数据的
* 他们的作用就和在xml配置文件中的bean标签中写一个<property>标签的作用是一样的
* Autowired:
* 作用:自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
* 如果ioc容器中没有任何bean的类型和要注入的变量类型匹配,则报错。
* 如果Ioc容器中有多个类型匹配时:
* 出现位置:
* 可以是变量上,也可以是方法上
* 细节:
* 在使用注解注入时,set方法就不是必须的了。
当有多个类型相同时,则根据变量名
* 他们的作用就和在xml配置文件中的bean标签中写一个<property>标签的作用是一样的
* Autowired:
* 作用:自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
* 如果ioc容器中没有任何bean的类型和要注入的变量类型匹配,则报错。
* 如果Ioc容器中有多个类型匹配时:
* 出现位置:
* 可以是变量上,也可以是方法上
* 细节:
* 在使用注解注入时,set方法就不是必须的了。
当有多个类型相同时,则根据变量名
比如在AccountDaoImpl1中,
@Repository("accountDao1")
public class AccountDaoImpl implements IAccountDao
在AccountDaoImpl2中,
@Repository("accountDao2")
public class AccountDaoImpl implements IAccountDao
/** * 账户的业务层实现类 * 曾经xml的配置 * <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean> */ @Component public class AccountServiceImpl implements IAccountService { @Autowired private IAccountDao accountDao ; public AccountServiceImpl(){ System.out.println("对象创建了"); } public void saveAccount(){ accountDao.saveAccount(); } }会报错
@Autowired private IAccountDao accountDao1 ;这样就不会报错了。
所有如果有唯一的匹配时,直接注入,
如果有多个匹配时,首先按照类型圈定出来匹配的对象,
接下来使用变量名称在圈定的对象继续查找,
-------------------------------------------------------------------------------------------------------------
用于注入数据的
* 他们的作用就和在xml配置文件中的bean标签中写一个<property>标签的作用是一样的
* Autowired:
* 作用:自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
* 如果ioc容器中没有任何bean的类型和要注入的变量类型匹配,则报错。
* 如果Ioc容器中有多个类型匹配时:
* 出现位置:
* 可以是变量上,也可以是方法上
* 细节:
* 在使用注解注入时,set方法就不是必须的了。
* Qualifier:
* 作用:在按照类中注入的基础之上再按照名称注入。它在给类成员注入时不能单独使用。但是在给方法参数注入时可以(稍后我们讲)
* 属性:
* value:用于指定注入bean的id。
* 他们的作用就和在xml配置文件中的bean标签中写一个<property>标签的作用是一样的
* Autowired:
* 作用:自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
* 如果ioc容器中没有任何bean的类型和要注入的变量类型匹配,则报错。
* 如果Ioc容器中有多个类型匹配时:
* 出现位置:
* 可以是变量上,也可以是方法上
* 细节:
* 在使用注解注入时,set方法就不是必须的了。
* Qualifier:
* 作用:在按照类中注入的基础之上再按照名称注入。它在给类成员注入时不能单独使用。但是在给方法参数注入时可以(稍后我们讲)
* 属性:
* value:用于指定注入bean的id。
例如:
@Autowired
@Qualifier("accountDao1")
private IAccountDao accountDao=null;
* Resource
* 作用:直接按照bean的id注入。它可以独立使用
* 属性:
* name:用于指定bean的id。
@Resource(name="accountDao2")
* 以上三个注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现。
* 另外,集合类型的注入只能通过XML来实现。
*
* Value
* 作用:用于注入基本类型和String类型的数据
* 属性:
* value:用于指定数据的值。它可以使用spring中SpEL(也就是spring的el表达式)
* SpEL的写法:${表达式}
-------------------------------------------------------------------------------------------------------------
* 另外,集合类型的注入只能通过XML来实现。
*
* Value
* 作用:用于注入基本类型和String类型的数据
* 属性:
* value:用于指定数据的值。它可以使用spring中SpEL(也就是spring的el表达式)
* SpEL的写法:${表达式}
-------------------------------------------------------------------------------------------------------------
用于改变作用范围的
* 他们的作用就和在bean标签中使用scope属性实现的功能是一样的
* Scope
* 作用:用于指定bean的作用范围
* 属性:
* value:指定范围的取值。常用取值:singleton prototype
-------------------------------------------------------------------------------------------------------------
* 他们的作用就和在bean标签中使用scope属性实现的功能是一样的
* Scope
* 作用:用于指定bean的作用范围
* 属性:
* value:指定范围的取值。常用取值:singleton prototype
-------------------------------------------------------------------------------------------------------------
和生命周期相关 了解
* 他们的作用就和在bean标签中使用init-method和destroy-methode的作用是一样的
* PreDestroy
* 作用:用于指定销毁方法
* PostConstruct
* 作用:用于指定初始化方法
-------------------------------------------------------------------------------------------------------------
* 他们的作用就和在bean标签中使用init-method和destroy-methode的作用是一样的
* PreDestroy
* 作用:用于指定销毁方法
* PostConstruct
* 作用:用于指定初始化方法
-------------------------------------------------------------------------------------------------------------
基于XML的IOC案例
xmlns:content??????
在写bean.xml时,
发现accountDao标红,思考了一段时间才想到在AccountServiceImpl.java1中的
private IAccountDao accountDao;没有set。
public void setAccountDao(IAccountDao accountDao) { this.accountDao = accountDao; }
晕菜
queryRunner.update("delete from account where id=?",id);delete语句写成了
queryRunner.update("delete account where id=?",id);少加了个from我还不知道,看来最基础的sqlCRUD语句还得练一下。
下面是整个案例的代码以及步骤解析:
我们先创建一个查询账户信息的service接口(注释等已删除)
service.IAccountService
public interface IAccountService { List<Account> findAllAccount(); Account findOneAccountById(Integer id); void saveOneAccount(Account account); void updateOneAccount(Account account); void deleteOneAccount(Integer id); }impl.AccountServiceImpl
public class AccountServiceImpl implements IAccountService { private IAccountDao accountDao; public void setAccountDao(IAccountDao accountDao) { this.accountDao = accountDao; } public List<Account> findAllAccount() { return accountDao.findAllAccount(); } public Account findOneAccountById(Integer id) { return accountDao.findOneAccountById(id); } public void saveOneAccount(Account account) { accountDao.saveOneAccount(account); } public void updateOneAccount(Account account) { accountDao.updateOneAccount(account); } public void deleteOneAccount(Integer id) { accountDao.deleteOneAccount(id); } }持久层接口IAccountDao.java
public interface IAccountDao { List<Account> findAllAccount(); Account findOneAccountById(Integer id); void saveOneAccount(Account account); void updateOneAccount(Account account); void deleteOneAccount(Integer id); }持久层实现类impl.AccountDaoImpl
public class AccountDaoImpl implements IAccountDao { QueryRunner queryRunner; public void setQueryRunner(QueryRunner queryRunner) { this.queryRunner = queryRunner; } public List<Account> findAllAccount() { try { return queryRunner.query("select * from account", new BeanListHandler<Account>(Account.class)); }catch (Exception e) { throw new RuntimeException(e); } } public Account findOneAccountById(Integer id) { try { return queryRunner.query("select * from account where id=?",new BeanHandler<Account>(Account.class),id); }catch (Exception e) { throw new RuntimeException(e); } } public void saveOneAccount(Account account) { try { queryRunner.update("insert into account(name,money) values(?,?)",account.getName(),account.getMoney()); }catch (Exception e) { throw new RuntimeException(e); } } public void updateOneAccount(Account account) { try { queryRunner.update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId()); }catch (Exception e) { throw new RuntimeException(e); } } public void deleteOneAccount(Integer id) { try { queryRunner.update("delete from account where id=?",id); }catch (Exception e) { throw new RuntimeException(e); } } }domain.Account
public class Account implements Serializable { private Integer id; private String name; private Float money; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Float getMoney() { return money; } public void setMoney(Float money) { this.money = money; } @Override public String toString() { return "Account{" + "id=" + id + ", name='" + name + '\'' + ", money=" + money + '}'; } }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" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"> <property name="accountDao" ref="accountDao"></property> </bean> <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl"> <property name="queryRunner" ref="runner"></property> </bean> <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"> <constructor-arg name="ds" ref="dataSource"></constructor-arg> </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://localhost:3306/eesy"></property> <property name="user" value="root"></property> <property name="password" value="123"></property> </bean> </beans>AccountServiceTest.java
public class AccountServiceTest { @Test public void testFindAll() { ApplicationContext ac=new ClassPathXmlApplicationContext("bean.xml"); IAccountService accountService=(IAccountService) ac.getBean("accountService"); List<Account> accounts=accountService.findAllAccount(); for (Account account:accounts) { System.out.println(account); } } @Test public void testFindOne() { ApplicationContext ac=new ClassPathXmlApplicationContext("bean.xml"); IAccountService accountService=(IAccountService) ac.getBean("accountService"); Account account=accountService.findOneAccountById(1); System.out.println(account); } @Test public void testSave() { ApplicationContext ac=new ClassPathXmlApplicationContext("bean.xml"); IAccountService accountService=(IAccountService) ac.getBean("accountService"); Account account=new Account(); account.setName("test11"); account.setMoney(200f); accountService.saveOneAccount(account); } @Test public void testUpdate() { ApplicationContext ac=new ClassPathXmlApplicationContext("bean.xml"); IAccountService accountService=(IAccountService) ac.getBean("accountService"); Account account=new Account(); account.setName("test111"); account.setMoney(300f); account.setId(4); accountService.updateOneAccount(account); } @Test public void testDelete() { ApplicationContext ac=new ClassPathXmlApplicationContext("bean.xml"); IAccountService accountService=(IAccountService) ac.getBean("accountService"); accountService.deleteOneAccount(4); } }pom.xml
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.3.RELEASE</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.32</version> </dependency> <dependency> <groupId>commons-dbutils</groupId> <artifactId>commons-dbutils</artifactId> <version>1.6</version> </dependency> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies> </project>注解
把xmlns:context拷到bean.xml中去,
删去下面这些,
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"> <property name="accountDao" ref="accountDao"></property> </bean> <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl"> <property name="queryRunner" ref="runner"></property> </bean>
@Service("accountService") public class AccountServiceImpl implements IAccountService { @Autowired private IAccountDao accountDao;
@Repository("accountDao") public class AccountDaoImpl implements IAccountDao { QueryRunner queryRunner;
当我们用注解注入时,set方法就不再需要了
@Service("accountService") public class AccountServiceImpl implements IAccountService { @Autowired private IAccountDao accountDao; public void setAccountDao(IAccountDao accountDao) { this.accountDao = accountDao; }在bean.xml中
<!--告知Spring在创建容器时要扫描的包--> <context:component-scan base-package="com.itheima"></context:component-scan>-------------------------------------------------------------------------------------------------------------
Spring的新注解Configuration和ComponentScan
config.SpringConfiguration
@Configuration @ComponentScan("com.itheima") public class SpringConfiguration { @Bean(name = "runner") @Scope("prototype") public QueryRunner createQueryRunner(DataSource dataSource) { return new QueryRunner(dataSource); } @Bean(name = "dataSource") public DataSource createDataSource() { try { ComboPooledDataSource ds=new ComboPooledDataSource(); ds.setDriverClass("com.mysql.jdbc.Driver"); ds.setJdbcUrl("jdbc:mysql://localhost:3306/eesy"); ds.setUser("root"); ds.setPassword("123"); return ds; }catch (Exception e) { throw new RuntimeException(e); } } }
ApplicationContext ac=new AnnotationConfigApplicationContext(SpringConfiguration.class);当我们把config.SpringConfiguration中的注解
@Configuration给去掉之后,发现我们的程序仍然能够运行,
Configuration
* 作用:指定当前类是一个配置类
* 细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。
* 作用:指定当前类是一个配置类
* 细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。
/**
* 该类是一个配置类,它的作用和bean.xml是一样的
* spring中的新注解
* Configuration
* 作用:指定当前类是一个配置类
* 细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。
* ComponentScan
* 作用:用于通过注解指定spring在创建容器时要扫描的包
* 属性:
* value:它和basePackages的作用是一样的,都是用于指定创建容器时要扫描的包。
* 我们使用此注解就等同于在xml中配置了:
* <context:component-scan base-package="com.itheima"></context:component-scan>
* Bean
* 作用:用于把当前方法的返回值作为bean对象存入spring的ioc容器中
* 属性:
* name:用于指定bean的id。当不写时,默认值是当前方法的名称
* 细节:
* 当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象。
* 查找的方式和Autowired注解的作用是一样的
* Import
* 作用:用于导入其他的配置类
* 属性:
* value:用于指定其他配置类的字节码。
* 当我们使用Import的注解之后,有Import注解的类就父配置类,而导入的都是子配置类
* PropertySource
* 作用:用于指定properties文件的位置
* 属性:
* value:指定文件的名称和路径。
* 关键字:classpath,表示类路径下
*/
-------------------------------------------------------------------------------------------------------------
* 该类是一个配置类,它的作用和bean.xml是一样的
* spring中的新注解
* Configuration
* 作用:指定当前类是一个配置类
* 细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。
* ComponentScan
* 作用:用于通过注解指定spring在创建容器时要扫描的包
* 属性:
* value:它和basePackages的作用是一样的,都是用于指定创建容器时要扫描的包。
* 我们使用此注解就等同于在xml中配置了:
* <context:component-scan base-package="com.itheima"></context:component-scan>
* Bean
* 作用:用于把当前方法的返回值作为bean对象存入spring的ioc容器中
* 属性:
* name:用于指定bean的id。当不写时,默认值是当前方法的名称
* 细节:
* 当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象。
* 查找的方式和Autowired注解的作用是一样的
* Import
* 作用:用于导入其他的配置类
* 属性:
* value:用于指定其他配置类的字节码。
* 当我们使用Import的注解之后,有Import注解的类就父配置类,而导入的都是子配置类
* PropertySource
* 作用:用于指定properties文件的位置
* 属性:
* value:指定文件的名称和路径。
* 关键字:classpath,表示类路径下
*/
-------------------------------------------------------------------------------------------------------------
我们可能选择有注解有XML的方式更合适一些。
如果这个类是别人写的存在于jar包中的类,我们还是用XML来配更直接,也更省事。
如果这个类是我么自己写的,我们用注解更方便。
所以我们在实际开发中,哪个方便就使用哪种方式。
-------------------------------------------------------------------------------------------------------------
spring整合junit问题分析
pom.xml
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.2.RELEASE</version> </dependency>AccountServiceTest.java
/** * 使用Junit单元测试:测试我们的配置 * Spring整合junit的配置 * 1、导入spring整合junit的jar(坐标) * 2、使用Junit提供的一个注解把原有的main方法替换了,替换成spring提供的 * @Runwith * 3、告知spring的运行器,spring和ioc创建是基于xml还是注解的,并且说明位置 * @ContextConfiguration * locations:指定xml文件的位置,加上classpath关键字,表示在类路径下 * classes:指定注解类所在地位置 * * 当我们使用spring 5.x版本的时候,要求junit的jar必须是4.12及以上 */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfiguration.class) public class AccountServiceTest { @Autowired private IAccountService as = null; @Test public void testFindAll() { //3.执行方法 List<Account> accounts = as.findAllAccount(); for(Account account : accounts){ System.out.println(account); } }