Spring框架(2
一、Spring的依赖注入
依赖注入:Dependency Injection
IOC的作用:降低程序间耦合(依赖关系)。
依赖关系的管理,以后都交给spring来维护,在当前类需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明。
依赖关系的维护就称之为依赖注入。
1.依赖注入:能注入的数据有三种
基本类型和String
其他bean类型(在配置文件中或者注解配置过的bean)
复杂类型/集合类型
2.依赖注入方式:有三种
1)第一种:使用构造函数提供
构造函数注入:
使用的标签:consteuctor-arg
标签出现的位置:bean标签的内部
(1)标签中的属性:
type:指定参数在构造函数中的数据类型
index:指定参数在构造函数参数列表的索引位置
name:指定参数在构造函数中的名称
=以上三个用于指定给构造函数中哪个参数赋值========
value:它能赋的值是基本数据类型和 String 类型
ref:它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件(spring的IOC核心配置)中配置过的 bean。
(2)优势:在获取bean对象时,注入数据是必须操作,否则对象无法创建成功。
(3)缺点:改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供。
<bean id="accountService" class="com.anyi.service.impl.AccountServiceImpl" scope="singleton">
<constructor-arg name="name" value="test"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="brithday" ref="now"></constructor-arg>
</bean>
<!--配置一个日期对象-->
<bean id="now" class="java.util.Date"></bean>
2)第二种:使用set方法提供
set方法注入(更常用)
涉及的标签:propety
出现的位置:bean标签内部、
标签的属性:
name:指定注入时所调用的set方法名称
=以上三个用于指定给构造函数中哪个参数赋值========
value:它能赋的值是基本数据类型和 String 类型
ref:它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件(spring的IOC核心配置)中配置过的 bean对象
优点:创建对象时没有明确的限制,可以直接使用默认构造函数。
缺点:如果有某个成员必须有值,则获取对象时有可能set方法没有执行。
<bean id="accountService2" class="com.anyi.service.impl.AccountServiceImpl2">
<property name="name" value="TEST"></property>
<property name="age" value="21"></property>
<property name="brithday" ref="now"></property>
</bean>
复杂类型的注入/集合类型的注入
用于给List结构集合注入的标签:list、array、set
用于给Map集合注入的标签:map、props
结构相同,标签可以互换。
<bean id="accountService3" class="com.anyi.service.impl.AccountServiceImpl3">
<!-- 给数组注入数据 -->
<property name="myStrs">
<set>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</set>
</property>
<!-- 注入 list 集合数据 -->
<property name="myList">
<array>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</array>
</property>
<!-- 注入 Map 数据 -->
<property name="myMap">
<props>
<prop key="testA">aaa</prop>
<prop key="testB">bbb</prop>
</props>
</property>
<!-- 注入 properties 数据 -->
<property name="myProps">
<map>
<entry key="testA" value="aaa"></entry>
<entry key="testB"> <value>bbb</value></entry>
</map>
</property>
</bean>
3)第三种:使用注解提供
二、常用的IOC注解(按照作用分类)、
曾经的xml配置:
<bean id="accountService" class="com.anyi.service.impl.AccountServiceImpl"
scope ="" inint-method="" destory-method="">
<property name="" value=""/ref=""></property>
</bean>
1.用于创建对象的:
他们的作用就和在xml配置文件中编写一个标签实现的功能是一样的
1)Component:
作用:用于把当前类对象存入spring容器中
属性:
value:用于指定bean的id。当我们不写时,它的默认值是当前类名,且首字母改小写。
2)Controller:一般用于表现层
3)Service:一般用在业务层
4)Repository:一般用在持久层
以上三个注解他们的作用和属性于Component是一模一样的。他们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰。
@Component(value = "accountService")//只有一个value可以不写
public class AccountServiceImpl implements IAccountService {
private IAccountDao accountDao = new AccountDaoImpl();
public AccountServiceImpl(){
System.out.println("对象创建了");
}
@Override
public void saveAccount() {
accountDao.saveAccount();
}
}
2.用于注入数据的:
他们的作用就和在xml配置文件中的bean标签中写一个标签的作用是一样的。
1)Autowried:
- 作用:自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功。
- 如果ioc容器中没有任何bean的类型和要注入的变量类型匹配,则报错。
- 如果ioc容器中有多个类型匹配时:
- 出现位置:可以是变量上,也可以是方法上。
- 细节:使用注解注入,set方法就不是必须的。
2) Qualifier
- 作用:在按照类中注入的基础之上再按照名称注入,它在给类成员注入时不能单独使用。但是在给方法参数注入时可以单独使用
- 属性: value:用于指定bean的id。
3)Resource
- 作用:直接按照bean的id注入。它可以独立使用。
- 属性: value:用于指定bean的id。
以上三个注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现。另外,集合类型的注入只能通过xml来实现。
3.用于改变作用范围的:
他们的作用就和在bean标签中使用scope属性实现的功能是一样的。
4.和生命周期相关的:
他们的作用就和在bean标签中使用init-method和destory-method的作用是一样的。
三、用spring框架创建一个案例实现CRUD
1.创建一个maven项目,在pom配置文件中配置需要用到的依赖jar包。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.anyi</groupId>
<artifactId>0910_spring</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</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>
</dependencies>
</project>
2.创建数据库表。
3.创建三层架构的类、接口以及实现类。
4.创建并配置spring框架的核心文件
<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.anyi.service.impl.AccountServiceImpl">
<!--注入dao对象(set方法注入)-->
<property name="accountDao" ref="accountDao"></property>
</bean>
<!--配置dao对象-->
<bean id="accountDao" class="com.anyi.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/user?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC"></property>
<property name="user" value="root"></property>
<property name="password" value=""></property>
</bean>
</beans>
5.测试类:
/**
* @description:使用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);
}
}
#1075 - Incorrect table definition; there can be only one auto column and it must be defined as a key。
这个错误1653的意思是:#1075-表定义不正确;只能有一个自动列,必须将其定义为主键。
如果出现此错误需要检查建表语句,如果设置了自增,需要把自增字段设置成主键。
四、基于注解的IOC配置(依赖注入)
业务层实现类和持久层实现类的改动:
/**
* @description:业务层实现类
**/
@Service("accountService")
public class AccountServiceImpl implements IAccountService {
@Autowired
private IAccountDao accountDao;
}
/**
* @description:持久层实现类
**/
@Repository("accountDao")
public class AccountDaoImpl implements IAccountDao {
@Autowired
private QueryRunner runner;
}
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.anyi"></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/user?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC"></property>
<property name="user" value="root"></property>
<property name="password" value=""></property>
</bean>
</beans>
1.创建一个配置类,它的作用和bean.xml是一样的
spring中的新注解
(1) Configuration
作用:指定当前类是一个配置类
(2) ComponentScan
- 作用:用于通过注解指定spring在创建容器时要扫描的包
- 属性:
- value:它和backPackages的作用是一样的,都是用于指定创建容器时要扫描的包
我们使用此注解就等同于在xml文件中配置了:<context:component-scan base-package=“com.anyi”></context:component-scan>
(3)Bean
- 作用:用于把当前方法返回值作为bean对象存入spring的ioc容器中
- 属性:
- name:用于指定bean的id(有默认值——当前方法的名称)
细节:当我们用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象。 查找的方式和Autowired注解的作用是一样的
(4)import
- 作用:用于导入其他的配置类
- 属性:
- value:用于指定其他配置类的字节码。
- 当我们使用import的注解之后,有import注解的类就是父配置类,而导入的都是子配置类。
(5)PropertySource
作用:用于指定properties文件的位置
属性:
value:指定文件的名称和路径。
关键字:classpath,表示类路径下
@Configuration
@ComponentScan(basePackages = "com.anyi")//<!--告知spring在创建容器时要扫描的包-->
public class SpringConfiguration {
/***
* @Description: 用于创建一个QueryRunner对象
*/
@Bean(name = "runner")
public QueryRunner createQueryRunner(DataSource dataSource){
return new QueryRunner(dataSource);
}
/***
* @Description: 创建数据源对象
*/
@Bean(name = "dataSource")
public DataSource createDataSource(){
try {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setDriverClass("com.mysql.jdbc.Driver");
ds.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/user?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT");
ds.setUser("root");
ds.setPassword("");
return ds;
}catch (Exception e){
throw new RuntimeException(e);
}
}
}
有了上面的配置类就可以取代bean.xml。
总结:在实际开发中可以选择纯注解配置核心容器,也可以使用xml文件配置,或者两种方法相结合。
选择的原则:首先考虑公司所使用的技术,如果能够自己选择,类存在于jar包中,选择xml文件配置更加方便;如果类是自己写的,用注解更方便(直接在类的前面写注解即可 )。
五、spring整合junit的配置
1.junit单元测试
1.应用程序的入口:main方法
2.junit单元测试中,没有main方法也能执行。
junit集成了一个main方法。
该方法就会判断当前测试类中哪些方法有@Test注解
junit就让有Test注解的方法执行。
3.junit不会管我们是否采用spring框架,在执行测试方法时,junit根本不知道我们是不是使用了spring,所有也就不会为我们读取配置文件和配置类创建spring核心容器。
4.由以上三点可知,当测试方法执行时,没有Ioc容器,就算写了Autowired注解,也无法实现。
2.导入整合需要的jar包坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
3.使用junit提供的一个注解把原有的main方法替换了,替换成spring提供的
@RunWith(SpringJUnit4ClassRunner.class)
4.告知spring的运行器,spring和ioc创建是基于xml还是注解,并且说明位置。
@ContextConfiguration(classes = SpringConfiguration.class)
locations:指定xml文件的位置,加上classpath关键字,表示在类路径下。
classes:指定注解类所在位置。
最后记录一个坑了我一下午的错误:
jar包明明在项目里面,编译也不报错,但是运行的时候就是找不到。
(例如:Error:(4, 52) java: 程序包org.springframework.beans.factory.annotation不存在),ctrl+鼠标左键却能定位到jar包(说明jar成功导入)
报错原因:
IDEA启动程序按钮和maven的build使用的jar包环境不一样
解决方案:
设置idea构建/运行操作委托给maven就行了。具体设置:
Settings搜索Runner,勾选delegate ide build/run actions to maven
进行以上修改后可能会出现中文乱码:
原因:maven自带的字符集要与JDK提供的字符集保持一致。
我们可以在 File -->setting --> maven --> runner 里面的 VM Options 添加 -Dfile.encoding=GB2312 或者-Dfile.encoding=UTF-8