Spring-Day2
spring第二天:spring基于注解的IOC以及IoC的案例
1、spring中ioc的常用注解
新建模块day02_ssey_01anno_ioc (day2的内容pdf讲义和word教案顺序都不对。。
对AccountServiceImpl.java使用注解 就不用配置它的bean.xml中的对象了,但是还需要bean.xml告知spring创建容器时要扫描的包,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复制头信息!--> <context:component-scan base-package="com.xxw"></context:component-scan> </beans>
昨天的那个伏笔:由于service层里变量dao=null,所以在表现层调用service层的方法就会报空指针异常。所以外面要用的话,这儿dao必须是一个对象,不赋值但要创建对象。使用注解就不用=new或者解析bean.xml,直接@Autowired在定义变量上!!这个注解的作用是自动按照类型注入,但只能找唯一对应的数据类型,若有多个daoImpl就找不着了,所以辅助@Qualifier加上daoimpl的名称。但是独立用@Resource更好,都不需要@Autowired了。
弹幕说关于mybatis的el:$是拼写sql语句,#是预编译
以下代码是学了几个注解取代昨天的xml的4种配置
/** * 帐户的业务层实现类 * 曾经XML的配置: * <bean id="accountService" class="com.xxw.service.impl.AccountServiceImpl"> * scope="" init-method="" destroy-method=""> * <property name="" value="" | ref=""></property> * </bean> * 昨天围绕bean的xml配置讲了上面4个功能标签类 所以今天的注解要分为这4类!! * 1.用于创建对象的 * 他们的作用就和在XML配置文件中编写一个<bean>标签实现的功能是一样的 * @Component: * 作用:用于把当前类对象存入spring容器中 * 属性: * value:用于指定bean的id。当我们不写时,它的默认值是当前类名,且首字母改小写。 * Controller:一般用在表现层 * Service:一般用在业务层 * Repository:一般用在持久层 * 以上三个注解他们的作用和属性与Component是一模一样!!!! * 他们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰 * * 2.用于注入数据的!! * 他们的作用就和在xml配置文件中的bean标签中写一个<property>标签的作用是一样的 * @Autowired: * 作用:自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功 * 如果ioc容器中没有任何bean的类型和要注入的变量类型匹配,则报错。 * 如果Ioc容器中有多个类型匹配时: * 出现位置: * 可以是变量上,也可以是方法上!!!!!!!(变量上!!!=new了 * 细节: * 在使用注解注入时,set方法就不是必须的了。 * @Qualifier: * 作用:在按照类中注入的基础之上再按照名称注入。它在给类成员注入时不能单独使用。但是在给方法参数注入时可以(稍后我们讲) * 属性: * value:用于指定注入bean的id。 * @Resource * 作用:直接按照bean的id注入。它可以独立使用 * 属性: * name:用于指定bean的id。 * 以上三个注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现!!!!!???用@value!!el传数据!! * 另外,集合类型的注入只能通过XML来实现!!!!!! * * @Value!!!! * 作用:用于注入基本类型和String类型的数据!!! * 属性: * value:用于指定数据的值。它可以使用spring中SpEL(也就是spring的el表达式) * SpEL的写法:${表达式} * * 3.用于改变作用范围的 * 他们的作用就和在bean标签中使用scope属性实现的功能是一样的 * @Scope * 作用:用于指定bean的作用范围 * 属性: * value:指定范围的取值。常用取值:singleton prototype * * 4.和生命周期相关 了解 * 他们的作用就和在bean标签中使用init-method和destroy-methode的作用是一样的 * PreDestroy * 作用:用于指定销毁方法 * PostConstruct * 作用:用于指定初始化方法 */ //@Component//或者指定标识 @Component(value="accountService") @Service public class AccountServiceImpl implements IAccountService { // @Autowired // @Qualifier("accountDao1") @Resource(name = "accountDao1") private IAccountDao accountDao;// = new AccountDaoImpl();//??? public void saveAccount() { accountDao.saveAccount(); } }
2、案例使用xml方式和注解方式实现单表的CRUD操作
持久层技术选择:dbutils
新建模块day02_ssey_02account_xmlioc 新依赖dbutils,c3p0好像本地仓库没有。。。哎呀install成功啦
新建数据库eesy2,table account
怎么又回到bean.xml配置啦?dbutils感觉还比mybatis的sqlmap复杂,啊它只是一个类时jdbctemplate的执行。。。还得写dao接口实现类,还得加上c3p0连接池包。。
还不如mybatis的xml写sql更别说mybatis的注解写sql了。跳了。。。
<?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"> <!-- 配置Service --> <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"> <!-- 注入dao --> <property name="accountDao" ref="accountDao"></property> </bean> <!--配置Dao对象--> <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl"> <!-- 注入QueryRunner --> <property name="runner" ref="runner"></property> </bean> <!--配置QueryRunner--> <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/eesy2"></property> <property name="user" value="root"></property> <property name="password" value="root"></property> </bean> </beans>
3、改造基于注解的ioc案例,使用纯注解的方式实现
新建模块 day02_ssey_03account_annoioc
注解也就是把上面bean用注解改了。。。没有用mybais...
dbutils还需要在bean.xml里配置bean...然后注入使用@autowired!!!!!!!!!!!!!!
sql也没有用注解写。。。这个只是改了bean.xml中的<bean>标签用注解替代。。。
/** * 账户的持久层接口 */ public interface IAccountDao { /** * 查询所有 * @return */ List<Account> findAllAccount(); /** * 查询一个 * @return */ Account findAccountById(Integer accountId); /** * 保存 * @param account */ void saveAccount(Account account); /** * 更新 * @param account */ void updateAccount(Account account); /** * 删除 * @param acccountId */ void deleteAccount(Integer acccountId); }
/** * 账户的持久层实现类 */ @Repository("accountDao") public class AccountDaoImpl implements IAccountDao { @Autowired private QueryRunner runner; public void setRunner(QueryRunner runner) { this.runner = runner; } @Override public List<Account> findAllAccount() { try{ return runner.query("select * from account",new BeanListHandler<Account>(Account.class)); }catch (Exception e) { throw new RuntimeException(e); } } @Override public Account findAccountById(Integer accountId) { try{ return runner.query("select * from account where id = ? ",new BeanHandler<Account>(Account.class),accountId); }catch (Exception e) { throw new RuntimeException(e); } } @Override public void saveAccount(Account account) { try{ runner.update("insert into account(name,money)values(?,?)",account.getName(),account.getMoney()); }catch (Exception e) { throw new RuntimeException(e); } } @Override public void updateAccount(Account account) { try{ runner.update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId()); }catch (Exception e) { throw new RuntimeException(e); } } @Override public void deleteAccount(Integer accountId) { try{ runner.update("delete from account where id=?",accountId); }catch (Exception e) { throw new RuntimeException(e); } } }
明明mybatis就都不用实现类里写sql了。。。
/** * 账户的业务层接口 */ public interface IAccountService { /** * 查询所有 * @return */ List<Account> findAllAccount(); /** * 查询一个 * @return */ Account findAccountById(Integer accountId); /** * 保存 * @param account */ void saveAccount(Account account); /** * 更新 * @param account */ void updateAccount(Account account); /** * 删除 * @param acccountId */ void deleteAccount(Integer acccountId); }
/** * 账户的业务层实现类 */ @Service("accountService") public class AccountServiceImpl implements IAccountService{ @Autowired private IAccountDao accountDao; public void setAccountDao(IAccountDao accountDao) { this.accountDao = accountDao; } @Override public List<Account> findAllAccount() { return accountDao.findAllAccount(); } @Override public Account findAccountById(Integer accountId) { return accountDao.findAccountById(accountId); } @Override public void saveAccount(Account account) { accountDao.saveAccount(account); } @Override public void updateAccount(Account account) { accountDao.updateAccount(account); } @Override public void deleteAccount(Integer acccountId) { accountDao.deleteAccount(acccountId); } }
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在创建容器时要扫描的包 --> <context:component-scan base-package="com.itheima"></context:component-scan> <!--配置QueryRunner--> <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/eesy2"></property> <property name="user" value="root"></property> <property name="password" value="root"></property> </bean> </beans>
测试类:
/** * 使用Junit单元测试:测试我们的配置 */ public class AccountServiceTest { @Test public void testFindAll() { //1.获取容器 ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); //2.得到业务层对象 IAccountService as = ac.getBean("accountService",IAccountService.class); //3.执行方法 List<Account> accounts = as.findAllAccount(); for(Account account : accounts){ System.out.println(account); } } @Test public void testFindOne() { //1.获取容器 ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); //2.得到业务层对象 IAccountService as = ac.getBean("accountService",IAccountService.class); //3.执行方法 Account account = as.findAccountById(1); System.out.println(account); } @Test public void testSave() { Account account = new Account(); account.setName("test"); account.setMoney(12345f); //1.获取容器 ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); //2.得到业务层对象 IAccountService as = ac.getBean("accountService",IAccountService.class); //3.执行方法 as.saveAccount(account); } @Test public void testUpdate() { //1.获取容器 ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); //2.得到业务层对象 IAccountService as = ac.getBean("accountService",IAccountService.class); //3.执行方法 Account account = as.findAccountById(4); account.setMoney(23456f); as.updateAccount(account); } }
4、spring的一些新注解使用
关于我们如何脱离bean.xml?
新建day02_ssey_04account_annoioc_withoutxml模块
java包下新建config包SpringConfiguration类和JdbcConfig类!!(在里面写新注解代替bean.xml的)
jdbc的数据库信息单独建立jdbcConfig.properties文件,获取信息用@propertySource注解,获取数据用@value注解和el表达式
SpringConfiguration.java
/** * 该类是一个配置类,它的作用和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 * 作用:用于导入其他的配置类 (则jdbcconfig就不用再写注解configration注解了,或者也不用测试类中加jdbc.class了 * 属性: * value:用于指定其他配置类的字节码。 * 当我们使用Import的注解之后,有Import注解的类就父配置类,而导入的都是子配置类 * @PropertySource * 作用:用于指定properties文件的位置 * 属性: * value:指定文件的名称和路径。 * 关键字:classpath,表示类路径下 */ //@Configuration 反正下面都会scan就可以不用了 @ComponentScan("com.itheima") @Import(JdbcConfig.class) @PropertySource("classpath:jdbcConfig.properties") public class SpringConfiguration { }
JdbcConfig.java
/** * 和spring连接数据库相关的配置类 */ public class JdbcConfig { @Value("${jdbc.driver}")//pro中的key 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") public QueryRunner createQueryRunner(@Qualifier("ds2") DataSource dataSource){//多个数据源时根据名字选一个 return new QueryRunner(dataSource); } /** * 创建数据源对象 * @return */ @Bean(name="ds2") public DataSource createDataSource(){ try { 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); } } @Bean(name="ds1") public DataSource createDataSource1(){ try { ComboPooledDataSource ds = new ComboPooledDataSource(); ds.setDriverClass(driver); ds.setJdbcUrl("jdbc:mysql://localhost:3306/eesy");//换个数据库 不从pro中那个 ds.setUser(username); ds.setPassword(password); return ds; }catch (Exception e){ throw new RuntimeException(e); } } }
测试类也改了
/** * 使用Junit单元测试:测试我们的配置 */ public class AccountServiceTest { @Test public void testFindAll() { //1.获取容器 // ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");不能再用了 bena.xml都没了 ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class); //2.得到业务层对象 IAccountService as = ac.getBean("accountService",IAccountService.class); //3.执行方法 List<Account> accounts = as.findAllAccount(); for(Account account : accounts){ System.out.println(account); } } @Test public void testFindOne() { //1.获取容器 ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class); //2.得到业务层对象 IAccountService as = ac.getBean("accountService",IAccountService.class); //3.执行方法 Account account = as.findAccountById(1); System.out.println(account); } @Test public void testSave() { Account account = new Account(); account.setName("test"); account.setMoney(123f); //1.获取容器 ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class); //2.得到业务层对象 IAccountService as = ac.getBean("accountService",IAccountService.class); //3.执行方法 as.saveAccount(account); }
4比3加了新注解去掉bean.xml好像并没有省什么事儿。。。3有注解有xml,好像对于dbutil这种jar包里的类创建QueryRunner对象更好
5、spring和Junit整合
解决测试类的重复代码
依赖spring_test包
导入后的测试类(还是withoutxml项目的测试类,注意看与上面测试类的区别)
/** * 使用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.执行方法 (1.获取容器 2.得到对象 都由spring整合的junit注解完成了 List<Account> accounts = as.findAllAccount(); for(Account account : accounts){ System.out.println(account); } } @Test public void testFindOne() { //3.执行方法 Account account = as.findAccountById(1); System.out.println(account); } @Test public void testSave() { Account account = new Account(); account.setName("test"); account.setMoney(123f); //3.执行方法 as.saveAccount(account); } }
再去改上一个模块annoioc的测试类,它是基于xml
/** * 使用Junit单元测试:测试我们的配置 */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:bean.xml")//基于xml的 public class AccountServiceTest { @Autowired private IAccountService as; @Test public void testFindAll() { //3.执行方法 List<Account> accounts = as.findAllAccount(); for(Account account : accounts){ System.out.println(account); } } @Test public void testFindOne() { //3.执行方法 Account account = as.findAccountById(1); System.out.println(account); } @Test public void testSave() { Account account = new Account(); account.setName("test"); account.setMoney(12345f); //3.执行方法 as.saveAccount(account); } @Test public void testUpdate() { //3.执行方法 Account account = as.findAccountById(4); account.setMoney(23456f); as.updateAccount(account); } @Test public void testDelete() { //3.执行方法 as.deleteAccount(5); } }
今天学了把对象用bean标签在xml中配置,或者用注解注入。还是要从头到尾复习一下博客写的很详细了。