今日目标
- 能够掌握注解开发定义Bean对象
- 能够掌握纯注解开发模式
- 能够配置注解开发依赖注入
- 能够配置注解开发管理第三方Bean
- 能够配置注解开发为第三方Bean注入资源
- 能够使用Spring整合Mybatis
- 能够使用Spring整合Junit
一、加载properties属性文件【重点】
目的:将数据库的连接参数抽取到一个单独的文件中,与Spring配置文件解耦。
2.1 基本用法
【第一步】编写jdbc.properties属性文件
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/db1
jdbc.username=root
jdbc.password=123456
【第二步】在applicationContext.xml中开启开启context命名空间,加载jdbc.properties属性文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
小技巧:如果同学们觉得上述复制粘贴方式不好改或者容易改错,其实idea是有提示功能的,注意不要选错就行了。有些版本的idea没有这个提示,那么就按照上面复制粘贴的方式改,改完之后可以做成live template模板,后期直接用。
<context:property-placeholder location="jdbc.properties"/>
【第三步】在配置连接池Bean的地方使用EL表达式获取jdbc.properties属性文件中的值
<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>
配置完成之后,运行之前的获取Druid连接池代码,可以获取到连接池对象就表示配置成功。
ApplicationContext app = new FileSystemXmlApplicationContext("E:\\IdeaProject\\88\\java-web\\spring-demo\\src\\main\\resources\\applicationContext2.xml");
DruidDataSource dataSource = (DruidDataSource) app.getBean("dataSource");
System.out.println(dataSource.getUsername());
System.out.println(dataSource);
2.2 配置不加载系统属性
问题
如果属性文件中配置的不是jdbc.username,而是username=root666,那么使用${username}获取到的不是root666,而是计算机的名称。
原因
系统属性的优先级比我们属性文件中的高,替换了我们的username=root666。
解决
解决1:换一个名称,例如不叫username,叫jdbc.username。
解决2:使用system-properties-mode="NEVER"属性表示不使用系统属性。
<context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>
2.3 加载properties文件写法
- 不加载系统属性
<context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>
- 加载多个properties文件
<context:property-placeholder location="jdbc.properties,msg.properties"/>
- 加载所有properties文件
<context:property-placeholder location="*.properties"/>
- 加载properties文件**标准格式**
<context:property-placeholder location="classpath:*.properties"/>
- 加载properties文件标准格式
<context:property-placeholder location="classpath*:*.properties"/>
随堂练习:
- spring管理druid连接池
- 要求:把数据源放到jdbc.properties里
- 测试连接
二、Spring容器(了解)
1 Spring核心容器介绍
问题导入
问题:按照Bean名称获取Bean有什么弊端,按照Bean类型获取Bean有什么弊端?
1.1 创建容器
- 方式一:类路径加载配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
- 方式二:文件路径加载配置文件
ApplicationContext ctx = new FileSystemXmlApplicationContext("D:\\applicationContext.xml");
- 加载多个配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean1.xml", "bean2.xml");
1.2 获取bean对象
- 方式一:使用bean名称获取
弊端:需要自己强制类型转换
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
- 方式二:使用bean名称获取并指定类型
弊端:推荐使用
BookDao bookDao = ctx.getBean("bookDao", BookDao.class);
- 方式三:使用bean类型获取
弊端:如果IOC容器中同类型的Bean对象有多个,此处获取会报错
BookDao bookDao = ctx.getBean(BookDao.class);
1.3 容器类层次结构
– ctrl+h快捷键
1.4 BeanFactory
- 类路径加载配置文件
Resource resources = new ClassPathResource("applicationContext.xml");
BeanFactory bf = new XmlBeanFactory(resources);
BookDao bookDao = bf.getBean("bookDao", BookDao.class);
bookDao.save();
- BeanFactory创建完毕后,所有的Bean均为延迟加载,也就是说我们调用getBean()方法获取Bean对象时才创建Bean对象并返回给我们
2 Spring核心容器总结
2.1 容器相关
- BeanFactory是IoC容器的顶层接口,初始化BeanFactory对象时,加载的bean延迟加载
- ApplicationContext接口是Spring容器的核心接口,初始化时bean立即加载
- ApplicationContext接口提供基础的bean操作相关方法,通过其他接口扩展其功能
- ApplicationContext接口常用初始化类
- ClassPathXmlApplicationContext(常用)
- FileSystemXmlApplicationContext
2.2 bean相关
<bean
id="bookDao" bean的id
name="dao bookDaoImpl daoImpl" bean的别名
class="com.itgaohe.dao.impl.BookDaoImpl"bean类型,静态工厂类,FactoryBean类
scope="singleton" 控制bean的实例数量
init-method="init" 生命周期初始化方法
destroy-method="destory" 生命周期的销毁方法
autowire="byType" 自动装配类型
factory-method="getInstance" bean工厂方法,应用于静态工厂或实例工厂
factory-bean="com.itgaohe.factory.BookDaoFactory" 实例工厂bean
lazy-init="true" 控制bean延迟加载
>
2.3 依赖注入相关
<bean id="bookService" class="com.itgaohe.service.impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"/>
构造器注入引用类型
<constructor-arg name="userDao" ref="userDao"/>
构造器注入引用类型
<constructor-arg name="msg" value="WRAN"/>
构造器注入简单类型
<constructor-arg type="java.lang.String" index="3" value="WARN"/>
类型和索引匹配注入
<property name="bookDao" ref="bookDao"/>
Setter方法注入引用类型
<property name="userDao" ref="userDao"/>
Setter方法注入引用类型
<property name="msg" value="WARN"/>
Setter方法注入简单类型
<property name="names">
Setter方法注入集合类型
<list>
集合
<value>itgaohe</value>
集合注入简单类型
<ref bean="dataSource"/>
集合注入引用类型
</list>
</property>
</bean>
三、Spring注解开发
1 注解开发定义Bean对象【重点】
目的:xml配置Bean对象有些繁琐,使用注解简化Bean对象的定义
1.1 基本使用
【第一步】在applicationContext.xml中开启Spring注解包扫描
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--扫描com.gaohe包及其子包下的类中注解-->
<context:component-scan base-package="com.itgaohe"/>
</beans>
【第二步】在类上使用@Component注解定义Bean。
//@Component定义bean
@Component("bookDao")
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
}
@Component
public class BookServiceImpl implements BookService {
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
补充说明:如果@Component注解没有使用参数指定Bean的名称,那么类名首字母小写就是Bean在IOC容器中的默认名称。例如:BookServiceImpl对象在IOC容器中的名称是bookServiceImpl。
【第三步】在测试类中获取Bean对象
public class AppForAnnotation {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
System.out.println(bookDao);
//按类型获取bean
BookService bookService = ctx.getBean(BookService.class);
System.out.println(bookService);
}
}
注意:在测试类中不要调用bookService的save方法,因为还没有给BookServiceImpl中的bookDao赋值,调用bookService的save方法会出现空指针异常。
1.2 @Component三个衍生注解
说明:加粗的注解为常用注解
- Spring提供**
@Component
**注解的三个衍生注解@Controller
:用于表现层bean定义@Service
:用于业务层bean定义@Repository
:用于数据层bean定义
@Repository("bookDao")
public class BookDaoImpl implements BookDao {
}
@Service
public class BookServiceImpl implements BookService {
}
随堂练习:
创建UserDao,和UserDaoImpl接口
通过注解方式加载bean
2 纯注解开发模式【重点】
2.1 纯注解开发模式介绍
- Spring3.0开启了纯注解开发模式,使用Java类替代配置文件,开启了Spring快速开发赛道
- Java类代替Spring核心配置文件
- @Configuration注解用于设定当前类为配置类
- @ComponentScan注解用于设定扫描路径,此注解只能添加一次,多个数据请用数组格式
2.2 代码演示
【第一步】定义配置类代替配置文件
//定义该类为spring配置文件类
@Configuration
//定义包扫描
@ComponentScan({"com.itgaohe.dao","com.itgaohe.service"})
//@ComponentScan(basePackages = "com.itgaohe")
public class SpringConfig {
}
【第二步】资源类声明被扫描
BookService
public interface BookService {
void showBook();
}
BookServiceImpl
@Service("bookService")
public class BookServiceImpl implements BookService {
@Override
public void showBook() {
System.out.println("book service save ...");
}
}
BookDao
public interface BookDao {
void show();
}
BookDaoImpl
//资源类声明被扫描
@Repository("bookDao1")
public class BookDaoImpl implements BookDao {
@Override
public void show() {
System.out.println("dao show!");
}
}
【第二步】在测试类中加载配置类,获取Bean对象并使用
public class AppForAnnotation {
public static void main(String[] args) {
//AnnotationConfigApplicationContext加载Spring配置类初始化Spring容器
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
BookDao bookDao = (BookDao) app.getBean("bookDao1");
bookDao.show();
BookService bookService = app.getBean("bookService", BookService.class);
bookService.showBook();
ctx.close();
}
}
3 注解开发Bean作用范围和生命周期管理(了解)
3.1 bean作用范围注解配置
- 使用@Scope定义bean作用范围
@Repository("bookDao1")
@Scope("singleton")
public class BookDaoImpl implements BookDao {
}
@Scope(“prototype”) 非单例
3.2 bean生命周期注解配置
- 使用@PostConstruct、@PreDestroy定义bean生命周期
@Repository("bookDao1")
@Scope("singleton")
public class BookDaoImpl implements BookDao {
public BookDaoImpl() {
System.out.println("book dao constructor ...");
}
@Override
public void show() {
System.out.println("dao show!");
}
@PostConstruct
public void init(){
System.out.println("book init ...");
}
@PreDestroy
public void destroy(){
System.out.println("book destory ...");
}
}
注意:@PostConstruct和@PreDestroy注解是jdk中提供的注解,从jdk9开始,jdk中的javax.annotation包被移除了,也就是说这两个注解就用不了了,可以额外导入一下依赖解决这个问题。
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
4 注解开发依赖注入【重点】
- 引用依赖注入
- 普通依赖注入
4.1 使用@Autowired注解开启自动装配模式(按类型)
@Service
public class BookServiceImpl implements BookService {
//@Autowired:注入引用类型,自动装配模式,默认按类型装配
@Autowired
private BookDao bookDao;
public void save() {
System.out.println("book service save ...");
bookDao.show();
}
}
说明:不管是使用配置文件还是配置类,都必须进行对应的Spring注解包扫描才可以使用。@Autowired默认按照类型自动装配,如果IOC容器中同类的Bean有多个,那么默认按照变量名和Bean的名称匹配,建议使用@Qualifier注解指定要装配的bean名称
注意:自动装配基于反射设计创建对象并暴力反射对应属性为私有属性初始化数据,因此无需提供setter方法。
4.2 使用@Qualifier注解指定要装配的bean名称
目的:解决IOC容器中同类型Bean有多个装配哪一个的问题
@Service
public class BookServiceImpl implements BookService {
//@Autowired:注入引用类型,自动装配模式,默认按类型装配
@Autowired
//@Qualifier:自动装配bean时按bean名称装配
@Qualifier("bookDao1")
private BookDao bookDao;
public void save() {
System.out.println("book service save ...");
bookDao.show();
}
}
注意:@Qualifier注解无法单独使用,必须配合@Autowired注解使用
4.3 使用@Value实现简单类型注入
@Repository("bookDao1")
@Scope("singleton")
public class BookDaoImpl implements BookDao {
//@Value:注入简单类型(无需提供set方法)
@Value("张三")
private String username;
@Value("${jdbc.driver}")
private String driver;
public BookDaoImpl() {
System.out.println("book dao constructor ...");
}
@Override
public void show() {
System.out.println("dao show!");
System.out.println("username = " + username);
System.out.println("driver = " + driver);
System.out.println("add/save .... is running");
}
@PostConstruct
public void before() {
System.out.println("init");
}
@PreDestroy
public void after() {
System.out.println("destroy");
}
}
测试…
如果@Value注解中使用${jdbc.driver}从属性文件中读取name值,需要在配置类或者配置文件中加载属性文件。
/*定义配置类*/
@Configuration
//定义包扫描
@ComponentScan(basePackages = "com.itgaohe")
@Import(value = {JdbcConfig.class})
public class SpringConfig {
}
/*
* 标注数据源配置
*
* */
@PropertySource("classpath:jdbc.properties")
public class JdbcConfig {
}
注意:@PropertySource()中加载多文件请使用数组格式配置,不允许使用通配符*
@PropertySource(value = {"classpath:jdbc.properties","classpath:jdbc2.properties"})
随堂练习:
- bookDao bookService
- service调用dao 完成依赖注入
5 注解开发管理第三方Bean【重点】
【第一步】单独定义配置类
public class JdbcConfig {
//@Bean:表示当前方法的返回值是一个bean对象,添加到IOC容器中
//方法名就是这个对象的唯一标识
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
ds.setUsername("root");
ds.setPassword("123456");
return ds;
}
}
【第二步】将独立的配置类加入核心配置
方式1:@Import注解导入式
@Configuration
@ComponentScan("com.itgaohe")
//@Import:导入配置信息
@Import({JdbcConfig.class})
public class SpringConfig {
}
方式2:@ComponentScan扫描式
@Configuration
@ComponentScan({"com.itgaohe.config","com.itgaohe.service","com.itgaohe.dao"}) //只要com.itgaohe.config包扫到了就行,三个包可以合并写成com.itgaohe
public class SpringConfig {
}
6 注解开发为第三方Bean注入资源【重点】
6.1 简单类型依赖注入
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/db1
jdbc.username=root
jdbc.password=123456
@PropertySource("jdbc.properties")
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
// 数据源-第三方
/*@Bean 第三方bean放入容器*/
// 方法名是这个对象的唯一标识
// @Bean("dataSource1") 别名
@Bean("dataSource1")
public DataSource dataSource(){
DruidDataSource dds = new DruidDataSource();
dds.setDriverClassName(driver);
dds.setUrl(url);
dds.setUsername(username);
dds.setPassword(password);
return dds;
}
}
说明:如果@Value()中使用了EL表达式读取properties属性文件中的内容,那么就需要加载properties属性文件。
6.2 引用类型依赖注入
//Spring会自动从IOC容器中找到BookDao对象赋值给参数bookDao变量,如果没有就会报错。
@Bean
public DataSource dataSource(BookDao bookDao1){
System.out.println("bookDao:" + bookDao1);
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(userName);
ds.setPassword(password);
return ds;
}
说明:引用类型注入只需要为bean定义方法设置形参即可,容器会根据类型自动装配对象
注解小结:参考《注解.xls》
四、Spring整合其他技术【重点】
1 Spring整合mybatis【重点】
1.1 思路分析
1.1.1 MyBatis程序核心对象分析
1.1.2 整合MyBatis
- 使用SqlSessionFactoryBean封装SqlSessionFactory需要的环境信息
- 使用MapperScannerConfigurer加载Dao接口,创建代理对象保存到IOC容器中
1.2 代码实现
1.创建模块
2.导入依赖mysql、mybatis、spring、spirng-jdbc、mybatis-spring、lombok、junit、spring-test
3. 写SringConfig
jdbc.properties
写JdbcConfig
写MybatisConfig
4.写pojo
5.写dao
6.写service
7.写测试整合junit
【前置工作】
-
在pom.xml中添加spring-context、druid、mybatis、mysql-connector-java等基础依赖。
-
准备service和dao层基础代码
【第一步】导入Spring整合Mybatis依赖
<!--数据库--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.27</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.13.RELEASE</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.1</version> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.5</version> </dependency> <!-- 阿里的连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.6</version> </dependency>
User
@Alias("user")
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String username;
private String password;
}
public interface UserService {
List<User> findAll();
}
@Service
public class UserServiceImpl implements UserService {
/*依赖注入dao*/
@Autowired
private UserDao userDao;
public List<User> findAll() {
List<User> all = userDao.findAll();
return all;
}
}
//userdao
public interface UserDao {
@Insert("insert into user(name,sex)values(#{name},#{sex})")
void save(User user);
@Delete("delete from user where id = #{id} ")
void delete(Integer id);
@Update("update user set name = #{name} , sex = #{sex} where id = #{id} ")
void update(User user);
@Select("select * from user")
List<User> findAll();
@Select("select * from user where id = #{id} ")
User findById(Integer id);
}
【第二步】创建JdbcConfig.properties配置DataSource数据源
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_db?useSSL=false
jdbc.username=root
jdbc.password=123456
@PropertySource(value = {"classpath:jdbc.properties"})
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String userName;
@Value("${jdbc.password}")
private String password;
@Bean("dataSource")
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(userName);
ds.setPassword(password);
return ds;
}
}
【第三步】创建MybatisConfig整合mybatis
public class MybatisConfig {
//定义bean,SqlSessionFactoryBean,用于产生SqlSessionFactory对象
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
ssfb.setTypeAliasesPackage("com.itgaohe.pojo");
//如果把sql语句放到在mapper映射文件中时
ssfb.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:com/itgaohe/dao/*.xml"));
ssfb.setDataSource(dataSource);
return ssfb;
}
//定义bean,返回MapperScannerConfigurer对象
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer msc = new MapperScannerConfigurer();
// 扫描接口--- 动态代理生成子类 --- 把代理类放到容器中了
msc.setBasePackage("com.itgaohe.dao");
return msc;
}
}
【第四步】创建SpringConfig主配置类进行包扫描和加载其他配置类
@Configuration
@ComponentScan("com.gaohe")
//@PropertySource:加载类路径jdbc.properties文件
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
public class SpringConfig {
}
【第五步】定义测试类进行测试
public class UserApp {
public static void main(String[] args) {
// 1. 获取ioc
ApplicationContext ioc = new
AnnotationConfigApplicationContext(SpringConfig.class);
// 2. 获取业务bean
UserService userService = ioc.getBean(UserService.class);
// 3. 放法执行
List<User> all = userService.findAll();
System.out.println(list);
}
}
2 Spring整合Junit单元测试【重点】
【第一步】导入整合的依赖坐标spring-test
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--spring整合junit-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
【第二步】使用Spring整合Junit专用的类加载器
【第三步】加载配置文件或者配置类
//【第二步】使用Spring整合Junit专用的类加载器
@RunWith(SpringJUnit4ClassRunner.class)
//【第三步】加载配置文件或者配置类
@ContextConfiguration(classes = {SpringConfig.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());
}
}
注意:junit的依赖至少要是4.12版本,可以是4.13等版本,否则出现如下异常:
今日小结&今日目标
-
spring注解开发(主流)
-
常见注解总结:
@Component 其他层 被扫描 @Service 业务层 @Repository 持久层 @Controller 控制层 @Configuration 定义配置文件 @ComponentScan 扫描 @Scope 类作用范围 @Value() 普通注入 注入 @Autowired 引用注入 @Qualifier @Import() 导入配置类 @PropertySource() 扫描配置文件 @Bean() 第三方bean配置 //设置类运行器 @RunWith(SpringJUnit4ClassRunner.class) //设置Spring环境对应的配置类 @ContextConfiguration(classes = {SpringConfig.class})
-
spring整合jdbc 、mybatis 、junit