SSM框架学习笔记二
依赖注入
依赖注入方式
向一个类中传递数据的方式有几种?
-
普通方法(set方法)
-
构造方法
依赖注入描述了在容器中建立bean与bean之间依赖关系的过程,如果bean运行需要的是数组或字符串呢?
-
引用类型
-
简单类型(基本数据类型与String)
依赖注入方式
-
setter注入
-
简单类型
-
引用类型
-
-
构造器注入
- 简单类型
- 引用类型
Setter注入–引用类型
- 在bean中定义引用类型属性并提供可访问的set方法
public class BookServiceImpl implements BookService {
//1.删除Service的通过new 创建的代码,留下接口
//这个是需要用的接口
//想给它注入依赖就要创建一个setter方法,让bean通过setter方法来配置
private BookDao bookDao;
//2.提供依赖对象对应的Setter方法
//之后就可以通过容器给里面传对象
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}
- 配置中使用property标签ref属性注入引用类型对象
<bean id="BookDao" class="com.yjm.dao.BookDaoImpl"/>
<bean id="BookService" class="com.yjm.service.BookServiceImpl">
<!--配置dao和Service关系-->
<!--name对应的是在BookService业务层中需要配置的属性(接口)-->
<!--ref引用对应的bean,也就是上面的id为BookDao的bean-->
<property name="bookDao" ref="BookDao"/>
</bean>
setter注入–简单类型
- 在bean中定义简单类型并提供可访问的set方法
public class BookDaoImpl implements BookDao {
//测试配置简单类型
private int connection;
private String databaseName;
//提供容器配置属性入口的setter方法
public void setConnection(int connection) {
this.connection = connection;
}
public void setDatabaseName(String databaseName) {
this.databaseName = databaseName;
}
@Override
public void save() {
System.out.println("测试配置简单属性:" + connection + ",," + databaseName);
System.out.println("Dao save...");
}
}
- 配置中使用property标签value属性注入简单数据类型
<bean id="BookDao" class="com.yjm.dao.BookDaoImpl">
<!--使用围堵标签注入依赖-->
<!--每个配置先后顺序灵活不固定-->
<property name="connection" value="10"/>
<property name="databaseName" value="mysql"/>
</bean>
构造器注入–引用类型(了解)
在bean中定义引用类型并提供可访问的构造器
public class BookServiceImpl implements BookService {
private BookDao bookDao;
private UserDao userDao;
//通过构造方法注入依赖
public BookServiceImpl(BookDao bookDao, UserDao userDao) {
this.bookDao = bookDao;
this.userDao = userDao;
}
@Override
public void save() {
System.out.println("service save...");
bookDao.save();
userDao.save();
}
}
配置中使用constructor0arg标签ref属性注入引用类型对象
<bean id="BookService" class="com.yjm.service.BookServiceImpl">
<!--做构造器注入-->
<!--name是构造器的形参的名称!-->
<constructor-arg name="bookDao" ref="BookDao"/>
<constructor-arg name="userDao" ref="userDao"/>
</bean>
构造器注入–参数适配(了解)
配置中使用constructor0arg标签type属性设置形参类型注入
<bean id="BookDao" class="com.yjm.dao.BookDaoImpl">
<constructor-arg type="int" value="10"/>
<constructor-arg type="java.lang.String" value="mysql"/>
</bean>
配置中使用constructor-arg标签index属性按形参位置注入
可解决多个形参类型一样的问题
<bean id="BookService" class="com.yjm.service.BookServiceImpl">
<!--做构造器注入-->
<!--name是构造器的形参的名称!-->
<constructor-arg index="0" ref="BookDao"/>
<constructor-arg index="1" ref="userDao"/>
</bean>
依赖注入方式选择
- 强制依赖(bean运行必须要的东西)使用构造器进行,使用setter注入有概率不进行注入导致null对象出现
- 可选依赖使用setter注入进行,灵活性强
- Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化, 相对严谨
- 有必要可两者同时使用
- 实际开发根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
- 自己开发的模块推荐使用setter注入
依赖自动装配
配置中使用bean标签autowire属性设置自动装配的类型类型
<!--
根据引用类型自动装配
前提要在实现类中设置setter入口
-->
<bean id="BookService" class="com.yjm.service.BookServiceImpl" autowire="byType"/>
依赖自动装配特征
- 自动装配用于引用类型依赖注入,不能对简单类型进行操作
- 使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用
- 使用按名称装配时(byName)必须保障容器中具有指定名称的bean, 因变量名与配置耦合,不推荐
- 自动装配优先级低于setter注入和构造器注入, 同时出现时自动装配失效
注入集合对象
注入数组对象
<!--数组注入-->
<property name="array">
<array>
<value>100</value>
<value>200</value>
<value>300</value>
</array>
</property>
注入List对象(重点)
<!--list集合注入-->
<property name="list">
<list>
<value>元素1</value>
<value>元素2</value>
<value>元素3</value>
</list>
</property>
注入set对象
<!--set集合注入-->
<property name="set">
<set>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>boxuegu</value>
</set>
</property>
注入Map对象(对象)
<!--map集合注入-->
<property name="map">
<map>
<entry key="country" value="china"/>
<entry key="province" value="henan"/>
<entry key="city" value="kaifeng"/>
</map>
</property>
注入Properties对象
<!--Properties注入-->
<property name="properties">
<props>
<prop key="country">china</prop>
<prop key="province">henan</prop>
<prop key="city">kaifeng</prop>
</props>
</property>
数据源对象管理
-
导入druid坐标
在pom.xml文件中导入
<!--导入需要管理的德鲁伊坐标--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency>
-
配置数据源对象作为spring管理的bean
<!--管理DruidDataSource对象--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <!-- 不能使用构造器注入是因为构造器是空参没有对应的参数 --> <!--驱动类名--> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <!--统一资源定位符--> <property name="url" value="jdbc:mysql://localhost:3306/spring_db"/> <!--数据库名--> <property name="username" value="root"/> <!--数据库密码--> <property name="password" value="123456"/> </bean>
加载properties文件
表示开启新的namespace叫做context,由spirng提供
xmlns:context="http://www.springframework.org/schema/context"
然后复制粘贴下面
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
完成的如下
<?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
">
<!--2. 使用context空间加载properties文件-->
<context:property-placeholder location="jdbc.properties"/>
<!--3. 使用属性占位符${}读取properties文件中的属性-->
<!-- 管理DruidDataSource对象-->
<bean class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
- 测试
前提写好满足配置的接口和实现类
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" >
<!--给name为name(这里的name是实现类中的属性)读取配置文件中的jdbc.driver的信息-->
<property name="name" value="${jdbc.driver}"/>
</bean>
加载properties文件方法
-
不加载系统属性
<context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>
-
加载多个properties文件
<context:property-placeholder location="jdbc.properties,jdbc2.properties" system-properties-mode="NEVER"/>
-
加载所有properties文件
<context:property-placeholder location="*.properties" system-properties-mode="NEVER"/>
-
加载properties文件标准格式
<!--只能读取当前工程的配置文件-->
<context:property-placeholder location="classpath:*.properties" />
- 从类路径或jar包中搜索并加载properties文件
<context:property-placeholder location="classpath*:*.properties" />
容器
1. 创建容器
//方式一: 类路径加载配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//方式二: 文件路径加载配置文件(参数位置填写绝对路径)
ApplicationContext ctx = new FileSystemXmlApplicationContext("D...\\applicationContext.xml");
//加载个配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean1.xml,bean2.xml");
//方式一: 使用bean名称获取
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
//方式二:使用bean名称并指定类型
BookDao bookDao = ctx.getBean("bookDao", BookDao.class);
//方式三:使用bean类型获取(要求:此类型的bean只能有一个)
BookDao bookDao = ctx.getBean( BookDao.class);
4.BeanFactory初始化
-
类路径加载配置文件
Resource resources = new ClassPathResource("applicationContext.xml"); BeanFactory bf = new XmlBeanFactory(resources); // BookDao bookDao = bf.getBean(BookDao.class); // bookDao.save();
-
Beanfactory创建完毕后,所有bean均为延迟加载
-
现在通过常用实现类加载配置文件也想实现延迟加载可以在配置文件中使用lazy-init=“true”
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" lazy-init="true"/>
核心容器总结
1.容器相关
- BeanFactory是IoC容器的顶层接口,初始化BeanFactory对象时,加载的bean延迟加载
- ApplicationContext接口是Spring容器的核心接口,初始化时bean立即加载
- ApplicationContext接口提供基础的bean操作相关方法,通过其他接口扩展其功能
- ApplicationContext接口常用初始化类
ClassPathXmlApplicationContext
FileSystemXmlApplicationContext
2.bean相关
3.依赖注入相关
注解开发
注解开发定义bean
-
使用@Component定义bean
在实现类上直接写注解,括号里面写想定义的id
@Component("bookDao") public class BookDaoImpl implements BookDao { public void save() { System.out.println("book dao save ..." ); } }
如果不写id要通过类型获取bean才可以扫描到!
BookDao bookDao = ctx.getBean(BookDao.class);
-
核心配置文件中通过组件扫描加载bean
包名写到组织名字就可以了
<context:component-scan base-package="com.itheima"/>
Spring提供@Component注解的三个衍生注解
@Controller:用于表现层bean定义
@Service:用于业务层bean定义
@Repository:用于数据bean定义
作用: 和@Component一样,但可以方便区分三个层。
@Component以后并不是没用了,还有可能用于工具类。
纯注解开发模式
1.使用Java类代替配置文件
//这个类是自己创建的,这样就可以不使用配置文件applicationContext.xml了,直接删除!
//使用Java类代替配置文件
@Configuration //设定当前类为配置类
@ComponentScan("com.itheima") //用与设定扫描路径,此注解只能添加一次, 多个数据使用数组格式
public class SpringConfig {
}
2.读取方式变成通过加载配置类容器
//加载类路径下的配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//加载配置类初始化容器
ApplicationContext ctx =new AnnotationConfigApplicationContext(SpringConfig.class);
bean作用范围与生命周期管理
- 使用@Scope定义bean作用范围
@Repository
//@Scope设置bean的作用范围
//就是设置单例和非单例,决定调用的bean的时候是同一个还是重新创建一个
@Scope("singleton")
//@Scope("prototype") //非单例
public class BookDaoImpl implements BookDao {
}
知识点补充
Bean的作用域如果是非单例-prototype,IOC容器并不会执行相关的清理工作,只有单例模式下才会进行Bean的销毁,因此示例代码中destroy方法根本不会被调用,多例Bean的生命周期不归IOC容器来管理,单例Bean的生命周期归IOC容器管理。
- 使用@PostConstruct, @PreDestory定义bean生命周期
@Repository
//@Scope设置bean的作用范围
//就是设置单例和非单例,决定调用的bean的时候是同一个还是重新创建一个
@Scope("singleton")
//@Scope("prototype") //非单例
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
//@PostConstruct设置bean的初始化方法
@PostConstruct
public void init() {
System.out.println("init ...");
}
//@PreDestroy设置bean的销毁方法
@PreDestroy
public void destroy() {
System.out.println("destroy ...");
}
}
- 关闭容器
//ApplicationContext 接口没有关闭容器的操作
//需要使用 AnnotationConfigApplicationContext
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
BookDao bookDao1 = ctx.getBean(BookDao.class);
//调用close()方法关闭容器
ctx.close();
依赖注入
注入引用类型
使用@Autowired注解开启自动装配模式(按类型)
@Service //用于定义bean,可以理解为服务层专用的bean
public class BookServiceImpl implements BookService {
//@Autowired:注入引用类型,自动装配模式,默认按类型装配
//不用setter方法注入,直接使用暴力反射给它加值
@Autowired
private BookDao bookDao;
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
- 注意: 自动装配基于发射设计创建对象并暴力反射对应属性为私有属性初始化数据, 因此无需提供setter方法
- 自动装配建议使用无参构造方法创建对象(默认), 如果不提供对应构造方法, 请提供唯一的构造方法
使用@Qualifier注解开启指定名称装配bean
@Service //用于定义bean,可以理解为服务层专用的bean
public class BookServiceImpl implements BookService {
//@Autowired:注入引用类型,自动装配模式,默认按类型装配
//不用setter方法注入,直接使用暴力反射给它加值
@Autowired
//@Qualifier:自动装配bean时按bean名称装配
@Qualifier("bookDao2") //括号写的是bean名称
private BookDao bookDao;
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
- 注意 @Qualifier注解无法单独使用, 必须配合@Autowired注解使用
注入简单类型
使用@Value实现简单类型注入
@Repository("bookDao")
public class BookDaoImpl implements BookDao {
//@Value:注入简单类型(无需提供set方法)
@Value("给name注入简单类型")
//定义普通类型
private String name;
}
通过外部配置文件实现简单类型注入
- 使用@PropertySource加载properties配置文件
@Configuration //设置当前类为配置类
@ComponentScan("com.itheima") //扫描路径
//@PropertySource加载properties配置文件
//配置文件名不支持使用通配符*
@PropertySource({"jdbc.properties"})
public class SpringConfig {
}
注意: 路径仅支持单一文件配置, 多文件要使用数组配置, 不允许使用通配符**
-
同样通过@Value实现
@Repository("bookDao") public class BookDaoImpl implements BookDao { //@Value:注入简单类型(无需提供set方法) //${} 大括号里面的是配置文件中用于注入属性名 @Value("${name}") //定义普通类型 private String name; public void save() { System.out.println("book dao save ..." + name); } }
注解开发管理第三方bean
使用@Bean配置第三方bean
-
使用独立的配置类管理第三方bean(建议使用)
//@Configuration public class JdbcConfig { //1.定义一个方法获得要管理的对象 @Value("com.mysql.jdbc.Driver") private String driver; @Value("jdbc:mysql://localhost:3306/spring_db") private String url; @Value("root") private String userName; @Value("root") private String password; //2.添加@Bean,表示当前方法的返回值是一个bean //@Bean修饰的方法,形参根据类型自动装配 @Bean public DataSource dataSource(){ DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName(driver); ds.setUrl(url); ds.setUsername(userName); ds.setPassword(password); return ds; } }
-
将独立的配置类加入核心配置
方式一 :导入式
//@Configuration public class JdbcConfig { //1.定义一个方法获得要管理的对象 @Value("com.mysql.jdbc.Driver") private String driver; @Value("jdbc:mysql://localhost:3306/spring_db") private String url; @Value("root") private String userName; @Value("root") private String password; //2.添加@Bean,表示当前方法的返回值是一个bean //@Bean修饰的方法,形参根据类型自动装配 @Bean public DataSource dataSource(){ DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName(driver); ds.setUrl(url); ds.setUsername(userName); ds.setPassword(password); return ds; } }
-
使用@Import注解手动加入配置类到核心配置文件, 此注解只能添加一次, 多个数据使用数组格式
@Configuration //@Import:导入配置信息 @Import({JdbcConfig.class}) public class SpringConfig { }
方式二 :扫描式
@Configuration //装配成bean可扫描
public class JdbcConfig {
//1.定义一个方法获得要管理的对象
@Value("com.mysql.jdbc.Driver")
private String driver;
@Value("jdbc:mysql://localhost:3306/spring_db")
private String url;
@Value("root")
private String userName;
@Value("root")
private String password;
//2.添加@Bean,表示当前方法的返回值是一个bean
//@Bean修饰的方法,形参根据类型自动装配
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(userName);
ds.setPassword(password);
return ds;
}
}
-
使用@ComponentScan 注解扫描配置类所在的包, 加载对应的配置类信息
@Configuration @ComponentScan("com.itheima") //这个扫描是为了扫描bookDao这个实现类的bean public class SpringConfig { }
第三方bean依赖注入
简单类型依赖注入
通过Value值给就是了
public class JdbcConfig {
//1.定义一个方法获得要管理的对象
//定义成员变量通过value值给就是了
@Value("com.mysql.jdbc.Driver")
private String driver;
@Value("jdbc:mysql://localhost:3306/spring_db")
private String url;
@Value("root")
private String userName;
@Value("root")
private String password;
//2.添加@Bean,表示当前方法的返回值是一个bean
//@Bean修饰的方法,形参根据类型自动装配
@Bean
public DataSource dataSource(BookDao bookDao){
System.out.println(bookDao);
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(userName);
ds.setPassword(password);
return ds;
}
}
引用类型依赖注入
当然前提要在Spring配置类中引用注解@ComponentScan去扫描到需要的bean
@ComponentScan("com.itheima") //这个扫描是为了扫描bookDao这个实现类的bean
//@Bean修饰的方法,形参根据类型自动装配
@Bean
public DataSource dataSource(BookDao bookDao){
System.out.println(bookDao);
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(userName);
ds.setPassword(password);
return ds;
}
引用类型注入只需要为bean定义方法设置形参即可, 容器会根据类型自动装配对象
XML配置对比注解配置
Spring整合MyBatis思路分析
MyBatis程序核心对象分析
public class App {
public static void main(String[] args) throws IOException {
/*
* 初始化SqlSessionFactory
* SqlSessionFactory 是核心对象
* 怎么来的?
* 通过核心配置
* */
// 1. 创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 2. 加载SqlMapConfig.xml配置文件
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml.bak");
// 3. 创建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
/*
*获取连接,获取实现
* */
// 4. 获取SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 5. 执行SqlSession对象执行查询,获取结果User
AccountDao accountDao = sqlSession.getMapper(AccountDao.class);
/*
* 获取数据层接口
* */
Account ac = accountDao.findById(2);
System.out.println(ac);
/*
* 关闭连接
* */
// 6. 释放资源
sqlSession.close();
}
}
整合MyBatis
<configuration>
//加载数据库对应外部的配置信息, 初始化属性数据
<properties resource="jdbc.properties"></properties>
//初始化类型别名
<typeAliases>
<package name="com.itheima.domain"/>
</typeAliases>
//初始化dataSource
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
//为连接数据库配置的dataSource
//为SqlSessionFactory服务的
<property name="driver" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</dataSource>
</environment>
</environments>
//初始化映射配置
<mappers>
<package name="com.itheima.dao"></package>
</mappers>
</configuration>
//总结 mybatis应该管理SqlSessionFactory对象
补充:MyBatis作用
主要用于处理java程序与数据库之间的交互。
mybatis是一款用于持久层的、轻量级的半自动化ORM框架,封装了所有jdbc操作以及设置查询参数和获取结果集的操作,支持自定义sql、存储过程和高级映射。
支持Spring与Mybatis的整合就已经完成了,其中主要用到的两个类分别是:
- SqlSessionFactoryBean
- MapperScannerConfigurer
Spring整合Junit
整合Junit与整合Druid和MyBatis差异比较大,为什么呢?Junit是一个搞单元测试用的工具,它不是我们程序的主体,也不会参加最终程序的运行,从作用上来说就和之前的东西不一样,它不是做功能的,看做是一个辅助工具就可以了。
环境准备
这块环境,大家可以直接使用Spring与Mybatis整合的环境即可。当然也可以重新创建一个,因为内容是一模一样,所以我们直接来看下项目结构即可:
整合Junit步骤
在上述环境的基础上,我们来对Junit进行整合。
步骤1:引入依赖
pom.xml
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
步骤2:编写测试类
在test\java下创建一个AccountServiceTest,这个名字任意
//设置类运行器
@RunWith(SpringJUnit4ClassRunner.class)
//设置Spring环境对应的配置类
@ContextConfiguration(classes = {SpringConfiguration.class}) //加载配置类
//@ContextConfiguration(locations={"classpath:applicationContext.xml"})//加载配置文件
public class AccountServiceTest {
//支持自动装配注入bean
@Autowired
private AccountService accountService;
@Test
public void testFindById(){
System.out.println(accountService.findById(1));
}
@Test
public void testFindAll(){
System.out.println(accountService.findAll());
}
}
注意:
- 单元测试,如果测试的是注解配置类,则使用
@ContextConfiguration(classes = 配置类.class)
- 单元测试,如果测试的是配置文件,则使用
@ContextConfiguration(locations={配置文件名,...})
- Junit运行后是基于Spring环境运行的,所以Spring提供了一个专用的类运行器,这个务必要设置,这个类运行器就在Spring的测试专用包中提供的,导入的坐标就是这个东西
SpringJUnit4ClassRunner
- 上面两个配置都是固定格式,当需要测试哪个bean时,使用自动装配加载对应的对象,下面的工作就和以前做Junit单元测试完全一样了
知识点1:@RunWith
名称 | @RunWith |
---|---|
类型 | 测试类注解 |
位置 | 测试类定义上方 |
作用 | 设置JUnit运行器 |
属性 | value(默认):运行所使用的运行期 |
知识点2:@ContextConfiguration
名称 | @ContextConfiguration |
---|---|
类型 | 测试类注解 |
位置 | 测试类定义上方 |
作用 | 设置JUnit加载的Spring核心配置 |
属性 | classes:核心配置类,可以使用数组的格式设定加载多个配置类 locations:配置文件,可以使用数组的格式设定加载多个配置文件名称 |