Spring学习笔记-day2
第三章 基于注解的IOC配置
3.1概述
3.2使用注解实现IOC
曾经的xml配置:
<bean id="helloService" class="com.jzt.service.impl.HelloServiceImpl"
scope="" init-method="" destroy-method="">
<property name="" value=""|ref=""></property>
</bean>
(1)用于创建对象的
它们的作用就是和xml配置文件中编写一个标签实现功能时一样的;
@Component
作用:用于把当前类对象存入spring容器中。
属性:
value:用于指定bean的id。当我们不指定具体指时,默认是当前类的类型,首字母小写。
package com.jzt.service.impl;
import com.jzt.service.IHelloService;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class HelloServiceImpl implements IHelloService {
public void sayHello() {
System.out.println("HelloServiceImpl");
}
}
注意:使用注解时需要在配置文件中配置自动扫描的包:
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 告知spring在创建容器时需要扫描的包,配置所需要的标签不是在beans的约束中,而是一个名称为context名称名称空间和约束中 -->
<context:component-scan base-package="com.jzt"></context:component-scan>
</beans>
@Controller
一般用于表示层
@Service
一般用于业务层
@Repository
一般用于持久层
以上三个注解的作用于和属性与@component注解完全一样;
他们三个是spring框架为我们提供明确的MVC三层使用的注解,使我们对三层有更加清晰的认识。
(2)用于注入数据的
它们的作用就是和xml配置文件中标签中写一个标签的作用是一样的;
@Autowired
作用:自动按照类型注入。只要spring容器中有唯一一个bean对象类型和要注入的变量类型一致,就可以注入成功。
如果ioc容器中没有任何bean类型和需要注入的变量类型匹配,则报错;
如果Ioc容器中有多个类型匹配时:首先匹配类型,然后根据变量名进行匹配;
首先在service层注入:
@Autowired
IHelloDao helloDao1;
然后持久层定义:
package com.jzt.dao.impl;
import com.jzt.dao.IHelloDao;
import org.springframework.stereotype.Repository;
@Repository("helloDao1")
public class HelloDaoImpl implements IHelloDao {
public void sayHello() {
System.out.println("hello spring init1!");
}
}
package com.jzt.dao.impl;
import com.jzt.dao.IHelloDao;
import org.springframework.stereotype.Repository;
@Repository("helloDao2")
public class HelloDaoImpl2 implements IHelloDao {
public void sayHello() {
System.out.println("hello spring init2!");
}
}
位置:
可以在变量上,也可以在方法上;
细节:
在使用注解注入时,set方法就不是必须的了。
@Qulafier
作用:在按照类中注入基础上再按照名称注入。他在给类成员注入时不能单独使用。但是在给方法注入时可以(后面再说)。
属性:
value:指定注入bean的id。
@Qualifier("helloDao2")
@Autowired
IHelloDao helloDao;
在给方法参数注入时,用于按名称匹配要注入的参数。如下:
@Bean
public QueryRunner createQueryRunner(@Qualifier("ds1") DataSource dataSource){
return new QueryRunner(dataSource);
}
@Bean("ds1")
public DataSource createDataSource1(){
ComboPooledDataSource ds = new ComboPooledDataSource();
try {
ds.setDriverClass(driver);
ds.setJdbcUrl(url);
ds.setUser(user);
ds.setPassword(password);
return ds;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Bean("ds2")
public DataSource createDataSource2(){
ComboPooledDataSource ds = new ComboPooledDataSource();
try {
ds.setDriverClass(driver);
ds.setJdbcUrl(url);
ds.setUser(user);
ds.setPassword(password);
return ds;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Resource
作用:直接按照bean的id注入,可以独立使用。
属性:
name:用于指定bean的id。
@Resource(name = "helloDao1")
IHelloDao helloDao;
@Value
作用:用于注入基本类型和String类型的数据。
属性:
value:用于指定数据的值。他可以使用spring中的spEL(也就是spring中的EL表达式)
spring中EL表达式:${表达式}
(3)用于改变作用范围的
它们的作用就和在标签中使用scope属性是一样的;
@scope
作用:用于指定bean的作用范围。
属性:
value:指定范围取值。常用的取值:singleton、prototype
@Scope("prototype")
public class HelloServiceImpl implements IHelloService
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
IHelloService helloService = (IHelloService) ac.getBean("helloServiceImpl");
IHelloService helloService2 = (IHelloService) ac.getBean("helloServiceImpl");
System.out.println(helloService == helloService2);
执行结果:
(4)和生命周期相关(了解)
它们的作用就是和在标签中使用init-method和destroy-method的作用是一样的。
@PostConstruct
作用:指定初始化方法;
@PreDestroy
作用:指定销毁方法。
@Service
public class HelloServiceImpl implements IHelloService {
@PreDestroy
public void destroy() {
System.out.println("do destroy");
}
@PostConstruct
public void construct() {
System.out.println("do construct");
}
}
测试:
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
IHelloService helloService = (IHelloService) ac.getBean("helloServiceImpl");
ac.close();
结果:
第四章 spring IOC案例
4.1基于XML的IOC案例
准备环境:
(1)数据库
create table account (
id int primary key auto_increment,
name varchar(10),
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);
(2)业务层
package com.jzt.service;
import com.jzt.entity.Account;
import java.util.List;
public interface IAccountService {
public List<Account> getAll();
public Account getOne(Integer id);
public void add(Account account);
public void delete(Integer id);
}
package com.jzt.service.impl;
import com.jzt.dao.IAccountDao;
import com.jzt.entity.Account;
import com.jzt.service.IAccountService;
import java.util.List;
public class AccountServiceImpl implements IAccountService {
private IAccountDao iAccountDao;
public void setiAccountDao(IAccountDao iAccountDao) {
this.iAccountDao = iAccountDao;
}
public List<Account> getAll() {
return iAccountDao.getAll();
}
public Account getOne(Integer id) {
return iAccountDao.getOne(id);
}
public void add(Account account) {
iAccountDao.add(account);
}
public void delete(Integer id) {
iAccountDao.delete(id);
}
}
(3)持久层
package com.jzt.dao;
import com.jzt.entity.Account;
import java.util.List;
public interface IAccountDao {
List<Account> getAll();
Account getOne(Integer id);
void add(Account account);
void delete(Integer id);
}
package com.jzt.dao.impl;
import com.jzt.dao.IAccountDao;
import com.jzt.entity.Account;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import java.util.List;
public class AccountDaoImpl implements IAccountDao {
private QueryRunner queryRunner;
public void setQueryRunner(QueryRunner queryRunner) {
this.queryRunner = queryRunner;
}
public List<Account> getAll() {
try {
return queryRunner.query("select * from account", new BeanListHandler<Account>(Account.class));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public Account getOne(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 add(Account account) {
try {
queryRunner.update("insert into account(name, money) values(?, ?)", account.getName(), account.getMoney());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void delete(Integer id) {
try {
queryRunner.update("delete from account where id = ?", id);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
(4)配置xml
<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-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置业务层对象 -->
<bean id="accountService" class="com.jzt.service.impl.AccountServiceImpl">
<!-- 注入dao层依赖 -->
<property name="iAccountDao" ref="accountDao"></property>
</bean>
<!-- 配置持久层对象 -->
<bean id="accountDao" class="com.jzt.dao.impl.AccountDaoImpl">
<!-- 注入queryRunner -->
<property name="queryRunner" 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://192.168.31.121:3306/springtest"></property>
<property name="user" value="root"></property>
<property name="password" value="123456"></property>
</bean>
</beans>
(5)测试代码
package com.jzt.test;
import com.jzt.entity.Account;
import com.jzt.service.IAccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
/**8
* spring IOC
*/
public class HelloTest {
public static <HelloDao> void main(String[] args) {
//获取spring容器
ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
IAccountService accountService = (IAccountService) ac.getBean("accountService");
List<Account> accoutList = accountService.getAll();
System.out.println(accoutList);
}
}
结果:
4.2基于注解的IOC案例
(1)业务层
在类上加上@Service注解,依赖的bean使用@Autowired注解,并删除set方法:
package com.jzt.service.impl;
import com.jzt.dao.IAccountDao;
import com.jzt.entity.Account;
import com.jzt.service.IAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service("accountService")
public class AccountServiceImpl implements IAccountService {
@Autowired
private IAccountDao iAccountDao;
public List<Account> getAll() {
return iAccountDao.getAll();
}
public Account getOne(Integer id) {
return iAccountDao.getOne(id);
}
public void add(Account account) {
iAccountDao.add(account);
}
public void delete(Integer id) {
iAccountDao.delete(id);
}
}
(2)持久层
在类上加上@Repository注解,依赖的bean使用@Autowired注解,并删除set方法:
package com.jzt.dao.impl;
import com.jzt.dao.IAccountDao;
import com.jzt.entity.Account;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository("accountDao")
public class AccountDaoImpl implements IAccountDao {
@Autowired
private QueryRunner queryRunner;
public List<Account> getAll() {
try {
return queryRunner.query("select * from account", new BeanListHandler<Account>(Account.class));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public Account getOne(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 add(Account account) {
try {
queryRunner.update("insert into account(name, money) values(?, ?)", account.getName(), account.getMoney());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void delete(Integer id) {
try {
queryRunner.update("delete from account where id = ?", id);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
(3)配置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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 告知spring在创建容器时需要扫描的包 -->
<context:component-scan base-package="com.jzt"></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://192.168.31.121:3306/springtest"></property>
<property name="user" value="root"></property>
<property name="password" value="123456"></property>
</bean>
</beans>
(4)测试
代码不变;
结果:
(4)分析优化
以上基于注解的IOC案例我们发现,我们依然还是要配置xml,如扫描包的配置、配置queryRunner、配置数据源等操作,我们如何才能完全脱离配置文件呢?也就是纯注解开发?
<!-- 告知spring在创建容器时需要扫描的包 -->
<context:component-scan base-package="com.jzt"></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://192.168.31.121:3306/springtest"></property>
<property name="user" value="root"></property>
<property name="password" value="123456"></property>
</bean>
我们需要有相同的注解来时先以上功能。下面我们介绍相关注解:
@Configuration
作用:指定当前类是一个配置类,也就是等效于一个xml配置文件。
细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,如下,该注解可以不写。
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
@ComponentScan
作用:指定spring在创建容器是需要扫描的包。作用与<context:component-scan>标签一致。
属性:
value:用于指定要扫描的包路径。与base-package作用一致。
使用了该注解作用等效于:<context:component-scan base-package=“com.jzt”></context:component-scan >
@Bean
作用:用于将当前方法的返回值作为bean对象传入spring的ioc容器中。
属性:
name:用于指定bean的id。裆部写时,默认是当前方法名。
细节:
当我们使用注解配置方法时,如果该方法含有参数,spring框架会去容器中查找有没有对应的bean对象。
查找的方式与@Autowired注解作用一致。
首先创建配置类,使用这个配置类来代替xml配置文件:
package com.jzt.config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
@Configuration //使得当前类作为一个配置类,效果类似于一个xml文件
@ComponentScan("com.jzt") //用于在spring容器创建时扫描包路径
public class SpringConfiguration {
@Bean
public QueryRunner createQueryRunner(DataSource dataSource){
return new QueryRunner(dataSource);
}
/**
* //使用@Bean注解,将方法的返回值对象放入spring的ioc容器中
* @return
*/
@Bean
public DataSource createDataSource(){
ComboPooledDataSource ds = new ComboPooledDataSource();
try {
ds.setDriverClass("com.mysql.jdbc.Driver");
ds.setJdbcUrl("jdbc:mysql://192.168.31.121:3306/springtest");
ds.setUser("root");
ds.setPassword("123456");
return ds;
} catch (PropertyVetoException e) {
throw new RuntimeException(e);
}
}
}
然后测试:
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);//使用注解配置的时候applicationcontext的实现类使用AnnotationConfigApplicationContext
IAccountService accountService = (IAccountService) ac.getBean("accountService");
List<Account> accoutList = accountService.getAll();
System.out.println(accoutList);
@Import
作用:用于导入其它的配置类。
属性:
value:用于指定其他配置类的字节码。
当我们使用import的注解后,有Import注解的配置类是父配置类,而导入的是子配置类。
@Configuration //使得当前类作为一个配置类,效果类似于一个xml文件
@ComponentScan("com.jzt") //用于在spring容器创建时扫描包路径
@Import(JdbcConfiguration.class) //通过该注解指定JdbcConfiguration配置类的字节码。
public class SpringConfiguration {
}
package com.jzt.config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.context.annotation.Bean;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
public class JdbcConfiguration {
@Bean
public QueryRunner createQueryRunner(DataSource dataSource){
return new QueryRunner(dataSource);
}
/**
* //使用@Bean注解,将方法的返回值对象放入spring的ioc容器中
* @return
*/
@Bean
public DataSource createDataSource(){
ComboPooledDataSource ds = new ComboPooledDataSource();
try {
ds.setDriverClass("com.mysql.jdbc.Driver");
ds.setJdbcUrl("jdbc:mysql://192.168.31.121:3306/springtest");
ds.setUser("root");
ds.setPassword("123456");
return ds;
} catch (PropertyVetoException e) {
throw new RuntimeException(e);
}
}
}
@PropertySource
作用:用于指定properties文件的位置。
属性:
value:指定文件的名称和路径。
关键字:classpath,表示类路径下。
package com.jzt.config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import javax.sql.DataSource;
@PropertySource("classpath:jdbcConfig.properties")
public class JdbcConfiguration {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String user;
@Value("${jdbc.password}")
private String password;
@Bean
public QueryRunner createQueryRunner(DataSource dataSource){
return new QueryRunner(dataSource);
}
/**
* //使用@Bean注解,将方法的返回值对象放入spring的ioc容器中
* @return
*/
@Bean
public DataSource createDataSource(){
ComboPooledDataSource ds = new ComboPooledDataSource();
try {
ds.setDriverClass(driver);
ds.setJdbcUrl(url);
ds.setUser(user);
ds.setPassword(password);
return ds;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
4.3spring整合junit
问题分析:
-
应用程序入口:
- main()方法;
-
junit单元测试中,没有main()方法也能执行;
- junit内部集成了一个main方法,该方法会判断当前测试类中有哪些方法标注了@Test注解;
- junit会让标注有@Test的方法执行;
-
junit不会在没有特别设置的情况下,并不知道spring框架
- 在执行方法时,由于不知道spring框架,所以也就无法读取我们自己定义的配置文件和配置类中的内容发哦IOC容器中;
综合以上,当执行测试方法时,我们的配置和注入的数据将无法正常执行。
spring整合junit步骤:
(1)导入jar依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
(2)使用junit提供的一个注解把原有的main()方法替换,使用spring提供的。
@RunWith
(3)告知spring的运行期,spring的ioc创建是基于xml还是注解的,并说明位置;
@ContextConfiguration
locations:指定xml配置文件的位置,加上classpath关键字,表示在类路径下;
classes:指定注解类所在的位置。
注意:当我们使用spring5.x版本时,需要junit的版本在4.12及以上。
下面使用基于注解类的方式实现整合:
package com.jzt.test;
import com.jzt.config.SpringConfiguration;
import com.jzt.entity.Account;
import com.jzt.service.IAccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class AccountServiceTest {
@Test
public void test1(){
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
IAccountService accountService = (IAccountService) ac.getBean("accountService");
List<Account> accoutList = accountService.getAll();
System.out.println(accoutList);
}
}