一、基于注解的IOC和DI
1) 准备工作
创建Maven项目,导入依赖坐标
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
2) 编写代码,并注解配置
UserDao
接口
package com.execise.dao;
public interface UserDao {
void add();
}
UserDaoImpl
实现类
package com.execise.dao.impl;
import com.execise.dao.UserDao;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
@Component
public class UserDaoImpl implements UserDao {
public void add() {
System.out.println("调用了UserDaoImpl的add方法~!~");
}
}
UserService
接口
package com.execise.service;
public interface UserService {
void add();
}
UserServiceImpl
实现类
package com.execise.service.impl;
import com.execise.dao.UserDao;
import com.v.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
/*
需求:
在UserServiceImpl里面调用UserDaoImpl的add方法
分析:
1. 把这两个类交给spring管理,让spring创建这两个类的对象
2. 在UserServiceImpl里面注入UserDaoImpl 的对象
3. 使用对象来调用方法
步骤:
1. 在UserServiceImpl和UserDaoImpl身上打注解 : @Component
2. 在UserServiceImpl里面定义属性,private UserDao userDao;
3. 在属性身上打注解: @AutoWired
4. 在xml里面打开扫描的开关,这样spring才能知道哪些类要创建对象,里面的什么属性要注入!
*/
@Component
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDaoImpl;
public void add() {
System.out.println("调用了UserServiceImpl的add方法~!");
userDaoImpl.add();
}
}
3) 开启组件扫描
创建applicationContext.xml
,注意引入的context
名称空间
<?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">
<!--
1. 告诉spring要扫描指定的包,因为这个包下有些类要创建对象
context:component-scan : 用来指定扫描组件的标签
base-package : 表示要扫描哪个包
a. 可以写一个总的包名
b. 可以写具体的包名,可以写多个!使用 空格,逗号,分号,tab 分隔!
-->
<!--<context:component-scan base-package="com.execise.dao.impl com.execise.service.impl"/>-->
<context:component-scan base-package="com.execise"/>
</beans>
4) 功能测试
创建一个测试类,调用Service
package com.execise.test;
import com.execise.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestUserServiceImpl {
@Test
public void testAdd(){
//1. 创建工厂
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2. 问工厂要对象
UserService us = context.getBean(UserService.class);
//3. 调用方法
us.add();
}
}
步骤小结
-
导入依赖
-
定义接口和实现类(dao 和 service)
-
在实现类上面打上注解 @Component
-
在属性上面打上注解@AutoWired
-
在applicationContext.xml里面打开扫描的开关
<context:component-scan base-package=“com.execise”/>
注解使用详解
开启组件扫描
在Spring中,如果要使用注解开发,就需要在applicationContext.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">
<!--
1. 告诉spring要扫描指定的包,因为这个包下有些类要创建对象
context:component-scan : 用来指定扫描组件的标签
base-package : 表示要扫描哪个包
a. 可以写一个总的包名
b. 可以写具体的包名,可以写多!使用 空格,逗号,分号,tab 分隔!
-->
<!--<context:component-scan base-package="com.execise.dao.impl com.execise.service.impl"/>-->
<context:component-scan base-package="com.execise"/>
</beans>
声明bean的注解【IOC】
注解 | 说明 |
---|---|
@Component | 用在类上,相当于bean标签 |
@Controller | 用在web层类上,配置一个bean(是@Component 的衍生注解) |
@Service | 用在service层类上,配置一个bean(是@Component 的衍生注解) |
@Repository | 用在dao层类上,配置一个bean(是@Component 的衍生注解) |
@Component
:类级别的一个注解,用于声明一个bean,使用不多
value
属性:bean的唯一标识 (id值)。如果不配置,默认以首字母小写的类名为id
@Controller, @Service, @Repository
,作用和@Component
完全一样,但更加的语义化,使用更多
-
@Controller
:用于web层的bean -
@Service
:用于Service层的bean -
@Repository
:用于dao层的bean
示例
UserDaoImpl
类上使用注解@Repository
@Repository("userDao")
public class UserDaoImpl implements UserDao{
}
UserServiceImpl
类上使用注解@Service
@Service("userService")
public class UserServiceImpl implements UserService{
}
UserController
类上使用注解@Controller
@Controller
public class UserController{}
配置bean的注解 【IOC】
注解 | 说明 |
---|---|
@Scope | 相当于bean标签的scope 属性 |
@PostConstruct | 相当于bean标签的init-method 属性 |
@PreDestroy | 相当于bean标签的destory-method 属性 |
配置bean的作用范围:
-
@Scope
:配置bean的作用范围,相当于bean标签的scope属性。加在bean对象上 -
@Scope
的常用值有: -
singleton
:单例的,容器中只有一个该bean对象- 何时创建:容器初始化时
- 何时销毁:容器关闭时
-
prototype
:多例的,每次获取该bean时,都会创建一个bean对象- 何时创建:获取bean对象时
- 何时销毁:长时间不使用,垃圾回收
@Scope("prototype")
@Service("userService")
public class UserServiceImpl implements UserService{
//...
}
配置bean生命周期的方法
-
@PostConstruct
是方法级别的注解,用于指定bean的初始化方法 -
@PreDestroy
是方法级别的注解,用于指定bean的销毁方法
package com.execise.service.impl;
import com.execise.dao.UserDao;
import com.execise.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Service("us")
@Scope("prototype")
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDaoImpl;
public void add() {
System.out.println("调用了UserServiceImpl的add方法~!");
userDaoImpl.add();
}
//=========================================
//对象创建完毕,就执行这个方法
@PostConstruct
public void init(){
System.out.println("调用了UserServiceImpl的init方法~!");
}
//对象销毁的时候,就执行这个方法! 只有单例才会走这个方法
@PreDestroy
public void destroy(){
System.out.println("调用了UserServiceImpl的destroy方法~!");
}
}
依赖注入的注解【DI】
注解 | 说明 |
---|---|
@Autowired | 相当于property标签的ref 注入对象 |
@Qualifier | 结合@Autowired 使用,用于根据名称(标识符)注入依赖 |
@Resource | 相当于@Autowired + @Qualifier |
@Value | 相当于property标签的value ,注入普通 的属性 |
注入bean对象
@Autowired
:用于byType(按照类型来找对象)注入bean对象,按照依赖(属性)的类型,从Spring容器中查找要注入的bean对象
-
如果找到一个,直接注入
-
如果找到多个,则以变量名为id,查找bean对象并注入
-
如果找不到,抛异常
@Qualifier
:是按id注入,但需要和@Autowired
配合使用。
@Resource
:(是jdk提供的)用于注入bean对象(byName注入),相当于@Autowired + @Qualifier
绝大多数情况下,只要使用
@Autowired
注解注入即可使用注解注入时,不需要set方法了
UserDao
package com.itehima.dao;
public interface UserDao {
void add();
}
UserDao实现
package com.execise.dao.impl;
import com.execise.dao.UserDao;
import org.springframework.stereotype.Component;
@Repository
public class UserDaoImpl implements UserDao {
public void add() {
System.out.println("调用了UserdaoImpl的add方法~!~");
}
}
UserService
package com.itehima.service;
public interface UserService {
void add();
}
UserService实现
package com.execise.service.impl;
import com.execise.dao.UserDao;
import com.execise.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
@Service("us02")
public class UserServiceImpl02 implements UserService {
@Autowired
private UserDao userDaoImpl02;
@Autowired
@Qualifier("userDaoImpl02")
private UserDao abc;
@Resource(name = "userDaoImpl02")
private UserDao cba;
@Value("深圳")
private String address;
public void add() {
System.out.println("调用了UserServiceImpl02的add方法~!" + address);
//userDaoImpl02.add();
//abc.add();
cba.add();
}
}
测试
package com.execise.test;
import com.execise.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestUserServiceImpl02 {
@Test
public void testAdd(){
//1. 创建工厂
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2. 问工厂要对象
UserService us = (UserService) context.getBean("us02");
//3. 调用方法
us.add();
}
}
注入普通值
@Value
:注入简单类型值,例如:基本数据类型和String
@Service
public class UserServiceImpl02 implements UserService{
@Value("深圳")
private String address;
//...
}
小结
在xml里要开启组件扫描
<context:component-scan base-package="com.execise"/>
声明bean的注解(注册bean的注解) | IOC的注解
-
@Component("bean的名称")
, 括号里面bean的名称就是id 值, 可以用在任何类上,注册bean对象 -
@Controller("bean名称"), @Service("bean名称"), @Repository("bean名称")
,分别用于web层、service层、dao层的类上
配置bean的注解
-
如果要给一个bean设置作用范围:在bean上加注解
@Scope("singleton/prototype")
-
如果要给一个bean设置一个初始化方法:就在方法上加注解
@PostConstruct
-
如果要给一个bean设置一个销毁方法:就在方法上加注解
@PreDestroy
依赖注入的注解
-
@Autowired
:byType注入,直接加在依赖项那个成员变量上-
Spring会根据类型,从容器里查找bean并注入进来
-
如果只找到一
-
合的就会报错
-
-
@Autowired + @Qualifier("要注入的bean的名称")
: 这种组合一般不怎么用,因为比较麻烦。 -
@Resource(name="要注入的bean的名称")
:byName注入 -
@Value("要注入的简单值")
:用于注入简单值@Value("${properties里的key}")
:把properties里指定key的值注入进来。前提条件是必须已经引入了properties文件
二、整合Mybatis(XML)
spring作为service层框架,对dao层和web层提供了很好的支持,整合Mybatis其实就是把mybatis的核心配置文件(SQLMapConfig.xml) 给去掉,并且把调用mybatis的代码简化,简化成注入对象,然后直接调用方法。
创建数据库
create database day32;
use day32;
create table account(
id int primary key auto_increment,
name varchar(40),
money int
);
insert into account(name,money) values('zs',1000);
insert into account(name,money) values('ls',1000);
insert into account(name,money) values('ww',1000);
创建Maven工程,添加依赖
<!--依赖管理-->
<dependencies>
<!--junit单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--Spring核心依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
<!--整合Mybatis-->
<!--1. 数据库依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--2. 接池依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.23</version>
</dependency>
<!--3. mybatis本身的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!--4. 整合mybatis和spring的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
<!--5. 日志依赖-->
<!-- 添加slf4j日志api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.20</version>
</dependency>
<!-- 添加logback-classic依赖 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- 添加logback-core依赖 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
创建bean
package com.execise.bean;
import java.io.Serializable;
public class Account implements Serializable {
private Integer id;
private String name;
private Integer money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getMoney() {
return money;
}
public void setMoney(Integer money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
定义dao接口和映射文件
接口
package com.execise.dao;
import com.execise.bean.Account;
import java.util.List;
public interface AccountDao {
/**
* 查询所有账户信息列表
* @return 账户列表
*/
List<Account> findAll();
}
映射文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.execise.dao.AccountDao">
<select id="findAll" resultType="Account">
select * from account
</select>
</mapper>
定义service接口和实现
接口
package com.execise.service;
import com.execise.bean.Account;
import java.util.List;
public interface AccountService {
/**
* 查询所有账户信息列表
* @return 账户列表
*/
List<Account> findAll();
}
实现
package com.execise.service.impl;
import com.execise.bean.Account;
import com.execise.dao.AccountDao;
import com.execise.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
/*
需求:
在service里面调用dao的findAll方法。
步骤:
1. 不写mybatis的核心配置文件了,但是核心配置文件的内容还是需要有的。由spring来接管这个工作。
2. mybatis的核心配置文件里面包含两个部分的内容: 环境配置和映射文件配置
2.1 环境配置及映射文件配置由 SqlSessionFactoryBean类来接管
2.2 接口代理对象生成由MapperScannerConfigure类来接管
3. 以上的两个类对象需要在applicationContext.xml中定义出来!
4. 在service类上打注解: @Service
5. 在service里面定义出来dao的属性,然后在属性上打注解 : @Autowired
*/
@Override
public List<Account> findAll() {
System.out.println("调用了AccountService的findAll方法...");
return accountDao.findAll();
}
}
定义配置文件
db.properties
db.driverClass=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/day32
db.username=root
db.password=root
applicationContext.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">
<!--1.打开注解扫描开关-->
<context:component-scan base-package="com.execise"/>
<!--2.加载外部properties配置文件-->
<context:property-placeholder location="classpath:db.properties"/>
<!--3.配置第三方数据源【连接池】 交由Spring管理-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${db.driverClass}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</bean>
<!--4.配置SqlSessionFactory 通过MyBatis-Spring整合包提供的SqlSessionFactoryBean创建对象 交由Spring管理 从而可以取代MyBatis配置文件-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--1.注入数据源-->
<property name="dataSource" ref="dataSource"/>
<!--2.设置实体类别名-->
<property name="typeAliasesPackage" value="com.execise.bean"/>
<!--3.加载Mybatis核心配置文件-->
<!--<property name="configLocation" value="classpath:mybatis-config.xml"/>-->
<!--4.配置映射文件位置【当映射文件和dao接口不在同一目录时配置】-->
<!--<property name="mapperLocations" value="classpath:com/aa/*Dao.xml" />-->
</bean>
<!--5.配置MapperScannerConfigurer 扫描dao接口创建代理对象-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.execise.dao"/>
</bean>
</beans>
单元测试
package com.v.test;
import com.execise.service.AccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestAccountServiceImpl {
@Test
public void testFindAll(){
//1. 创建工厂
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2. 问工厂要对象
AccountService accountService = context.getBean(AccountService.class);
//3. 调用方法
System.out.println(accountService.findAll());
}
}
三、纯注解开发IOC和DI
在上边的CURD练习中,仍然有部分配置需要使用applicationContext.xml
,那么能不能使用注解替换掉所有的xml呢?Spring提供了一些新注解,可以达到这个目标。
请注意:Spring提供的这部分新注解,并非为了完全代替掉XML,只是提供了另外一种配置方案
注解 | 说明 |
---|---|
@Configuration | 被此注解标记的类,是配置类 等同于applicationContext.xml |
@ComponentScan | 用在配置类上,开启注解扫描。使用basePackage属性指定扫描的包 |
@PropertySource | 用在配置类上,加载properties文件。使用value属性指定properties文件路径 |
@Import | 用在配置类上,引入子配置类。用value属性指定子配置类的Class |
@Bean | 用在配置类的方法上,把返回值声明为一个bean。用name/value属性指定bean的id |
注解详解
导入依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--连接池依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.23</version>
</dependency>
<!--mybatis和spring整合的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
</dependencies>
@Configuration配置类
@Configuration
把一个Java类声明为核心配置类
-
加上Java类上,这个Java类就成为了Spring的核心配置类,用于代替
applicationContext.xml
-
是
@Component
的衍生注解,所以:核心配置类也是bean,也会被spring管理起来,当然里边也可以注入依赖
/*
@Configuration:
1. 表示这个类是一个核心配置类
2. 它的作用等价于以前的applicationContext.xml
3. @Configuration 是从@Component注解衍生出来的,所以这个类也会被spring管理起来(创建对象)
*/
@Configuration
public class AppConfig {
}
配置类上的注解
@ComponentScan
配置组件注解扫描
-
basePackages
或者value
属性:指定扫描的基本包 -
等同于替代了applicationContext.xml里面的这句话
<!--1. 打开包的扫描开关-->
<context:component-scan base-package="com.execise"/>
@PropertySource
用于加载properties文件
-
value
属性:指定propertis文件的路径,从类加载路径里加载 -
等同于替代了applicationContext.xml里面的这句话
<!--导入properties文件-->
<context:property-placeholder location="classpath:db.properties"/>
@Import
用于导入其它配置类
-
Spring允许提供多个配置类(模块化配置),在核心配置类里加载其它配置类
-
相当于
xml
中的<import resource="模块化xml文件路径"/>
标签
核心配置类
/*
@Configuration:
1. 表示这个类是一个核心配置类
2. 它的作用等价于以前的applicationContext.xml
3. @Configuration 是从@Component注解衍生出来的,所以这个类也会被spring管理起来(创建对象)
@ComponentScan :
1. 用来指定扫描包,等价于 <context:component-scan base-package=""/>
@PropertyScource:
1. 用来导入外部的properties文件,等价于<context:property-placeholder location=""/>
2. 导入这个properties文件的时候,如果有错(1. 找不到这个文件,2.这个文件无法打开,)可以在
名字的前面加上 classpath:
3. 要想把properties里面的内容注入进来,就需要定义好对应的属性,然后使用@Value来注入值
@Value("${KEY}")
@Import:
1. 可以用来导入子配置类
*/
@Configuration
@Import(AppConfig01.class) //导入子配置类
public class AppConfig {
@Value("${db.driverClass}")
private String driverClass;
@Value("${db.url}")
private String url;
@Value("${db.username}")
private String username;
@Value("${db.password}")
private String password;
public void show(){
System.out.println("driverClass = " + driverClass);
System.out.println("jdbcUrl = " + url);
System.out.println("user = " + username);
System.out.println("password = " + password);
}
}
子配置类
package com.execise.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.PropertySource;
@ComponentScan("com.execise")
@PropertySource("db.properties")
public class AppConfig01 {
}
@Bean声明bean
1) @Bean
定义bean
@Bean
注解:方法级别的注解
-
作用: 方法会由spring调用,并且把方法返回值声明成为一个bean,作用相当于
<bean>
标签 , 这个方法的返回值将会被Spring管理起来。 -
@Bean注解可以写在方法上,这些方法可以写在核心配置类里面,也可以写在其他的组件类里面,但是一般会写在核心配置类里面。
@Bean
注解的属性:
value
属性:bean的id。如果不设置,那么方法名就是bean的id
@Configuration
@ComponentScan("com.execise")
public class AppConfig {
/*
@Bean
1. 打在方法身上,spring会来调用这个方法
2. spring会把这个方法的返回值管理起来,丢到spring的工厂里面去
3. 可以在@Bean的value属性里面设置对象的id,如果不设置,那么将会使用方法的名字作为id值
*/
//1. 把DataSource对象交给spring管理,使用方法名字作为id值
@Bean
public DataSource dataSource() throws PropertyVetoException {
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driverClass);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
return ds;
}
// 2. 把DataSource对象交给spring管理,使用属性来指定id值
@Bean("ds2")
public DataSource dataSource2() throws PropertyVetoException {
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driverClass);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
return ds;
}
/*
3.使用@Bean注解标记的方法,方法内部需要用到spring工厂(容器)里面的某一个对象,怎么办?
3.1 此时可以在方法的参数上,写上想要的对象参数即可
a. 其实它就是在方法参数的前面,隐藏着一个注解: @Autowired
3.2 但是也要考虑出现极端的情况:如果在spring的容器里面,有多个对象满足这个参数的类型,咋办?
a. 搭配使用@Qualifier ,指定id值。
b. 投机取巧,把方法的参数名字,写成id的名字即可
*/
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean01(@Qualifier("dataSource") DataSource ds){
System.out.println("ds===" + ds);
SqlSessionFactoryBean sfb = new SqlSessionFactoryBean();
sfb.setDataSource(ds);
return sfb;
}
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean02(DataSource dataSource){
System.out.println("dataSource===" + dataSource);
SqlSessionFactoryBean sfb = new SqlSessionFactoryBean();
sfb.setDataSource(dataSource);
return sfb;
}
}
2) @Bean
的依赖注入
-
@Bean
注解的方法可以有任意参数,这些参数即是bean所需要的依赖,默认采用byType方式注入 -
可以在方法参数上增加注解
@Qualifier
,用于byName注入
3) 完整代码
/*
@Configuration:
1. 表示这个类是一个核心配置类
2. 它的作用等价于以前的applicationContext.xml
3. @Configuration 是从@Component注解衍生出来的,所以这个类也会被spring管理起来(创建对象)
@ComponentScan :
1. 用来指定扫描包,等价于 <context:component-scan base-package=""/>
@PropertyScource:
1. 用来导入外部的properties文件,等价于<context:property-placeholder location=""/>
2. 导入这个properties文件的时候,如果有错(1. 找不到这个文件,2.这个文件无法打开,)可以在
名字的前面加上 classpath:
3. 要想把properties里面的内容注入进来,就需要定义好对应的属性,然后使用@Value来注入值
@Value("${KEY}")
@Import:
1. 可以用来导入子配置类
*/
@Configuration
//@ComponentScan("com.execise")
//@PropertySource("db.properties")
@Import(AppConfig01.class) //导入子配置类
public class AppConfig {
@Value("${db.driverClass}")
private String driverClass;
@Value("${db.url}")
private String url;
@Value("${db.username}")
private String username;
@Value("${db.password}")
private String password;
public void show(){
System.out.println("driverClass = " + driverClass);
System.out.println("jdbcUrl = " + url);
System.out.println("user = " + username);
System.out.println("password = " + password);
}
/*
@Bean
1. 打在方法身上,spring会来调用这个方法
2. spring会把这个方法的返回值管理起来,丢到spring的工厂里面去
3. 可以在@Bean的value属性里面设置对象的id,如果不设置,那么将会使用方法的名字作为id值
*/
//1. 把DataSource对象交给spring管理,使用方法名字作为id值
@Bean
public DataSource dataSource() throws PropertyVetoException {
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driverClass);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
return ds;
}
// 2. 把DataSource对象交给spring管理,使用属性来指定id值
@Bean("ds2")
public DataSource dataSource2() throws PropertyVetoException {
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driverClass);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
return ds;
}
/*
3.使用@Bean注解标记的方法,方法内部需要用到spring工厂(容器)里面的某一个对象,怎么办?
3.1 此时可以在方法的参数上,写上想要的对象参数即可
a. 其实它就是在方法参数的前面,隐藏着一个注解: @Autowired
3.2 但是也要考虑出现极端的情况:如果在spring的容器里面,有多个对象满足这个参数的类型,咋办?
a. 搭配使用@Qualifier ,指定id值。
b. 投机取巧,把方法的参数名字,写成id的名字即可
*/
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean01(@Qualifier("dataSource") DataSource ds){
System.out.println("ds===" + ds);
SqlSessionFactoryBean sfb = new SqlSessionFactoryBean();
sfb.setDataSource(ds);
return sfb;
}
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean02(DataSource dataSource){
System.out.println("dataSource===" + dataSource);
SqlSessionFactoryBean sfb = new SqlSessionFactoryBean();
sfb.setDataSource(dataSource);
return sfb;
}
}
小结
-
配置类上要加注解
@Configuration
变成核心配置类,主要是用来替代applicationContext.xml -
要开启组件扫描,在配置类上
@ComponentScan("com.execise")
-
如果要把jar包里的类注册成bean:
- 在配置类里加方法,方法上加@Bean,会把方法返回值注册bean对象
-
如果要引入外部的properties文件,在配置类上加
@PropertySource("classpath:xxx.properties")
-
引入模块配置类,在配置类上使用
@Import(子配置类.class)
-
@Bean ,如果期望让spring来管理某个方法的返回值(注意: 这个返回值大多数都是对象,很少是一个普通的数据,比如:数字、字符串…)
四、整合Mybatis(注解)
导入依赖
<!-- 设置JDK编译版本1.8 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<!--依赖管理-->
<dependencies>
<!--junit单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--Spring核心依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
<!--整合Mybatis-->
<!--1. 数据库依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--2. 接池依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.23</version>
</dependency>
<!--3. mybatis本身的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!--4. 整合mybatis和spring的依赖-->
<!--MyBatis提供的和Spring进行整合的jar包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<!--spring对jdbc封装的jar包也要导入进来,否则mybatis无法整合-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
<!--5. 日志依赖-->
<!-- 添加slf4j日志api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.20</version>
</dependency>
<!-- 添加logback-classic依赖 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- 添加logback-core依赖 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
定义dao接口和映射文件
接口
package com.execise.dao;
import com.execise.bean.Account;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface AccountDao {
/**
* 查询所有账户信息列表
* @return 账户列表
*/
@Select("select * from account")
List<Account> findAll();
}
定义service接口和实现
接口
package com.execise.service;
import com.execise.bean.Account;
import java.util.List;
public interface AccountService {
/**
* 查询所有账户信息列表
* @return 账户列表
*/
List<Account> findAll();
}
实现
package com.execise.service.impl;
import com.execise.bean.Account;
import com.execise.dao.AccountDao;
import com.execise.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
/*
需求:
在service里面调用dao的findAll方法。
步骤:
1. 不写mybatis的核心配置文件了,但是核心配置文件的内容还是需要有的。由spring来接管这个工作。
2. mybatis的核心配置文件里面包含两个部分的内容: 环境配置和映射文件配置
2.1 环境配置由 SqlSessionFactoryBean类来接管
2.2 映射文件配置由MapperScannerConfigure类来接管
3. 以上的两个类需要在applicationContext.xml中定义出来!
4. 在service类上打注解: @Service
5. 在service里面定义出来dao的属性,然后在属性上打注解 : @Autowired
*/
@Override
public List<Account> findAll() {
System.out.println("调用了AccountService的findAll方法...");
return accountDao.findAll();
}
}
定义配置文件
db.properties
db.driverClass=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/day32
db.username=root
db.password=root
AppConfig.java
package com.execise.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import javax.sql.DataSource;
@Configuration
@ComponentScan("com.execise")
@PropertySource("classpath:db.properties")
//@MapperScan("com.execise.dao")
public class AppConfig {
@Value("${db.driverClass}")
private String driver;
@Value("${db.url}")
private String url;
@Value("${db.username}")
private String username;
@Value("${db.password}")
private String password;
//1.创建数据源bean 交由Spring管理
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
//2.创建SqlSessionFactory bean 交由Spring管理
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setTypeAliasesPackage("com.execise.bean");
return sqlSessionFactoryBean;
}
/*
出现报错:
ERROR com.alibaba.druid.pool.DruidDataSource - {dataSource-1} init error
java.sql.SQLException: url not set
错误排查【查看控制台日志】:
INFO org.springframework.context.annotation.ConfigurationClassPostProcessor -
Cannot enhance @Configuration bean definition 'appConfig' since its singleton instance has been created too early.
The typical cause is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor return type: Consider declaring such methods as 'static'.
原因:
由于MapperScannerConfigurer类实现了BeanDefinitionRegistryPostProcessor接口,这会让AppConfig类初始化,创建的时机太早,
从而导致@Value注解失效,无法注入properties里面的数据
解决:
1.将这个方法变成静态方法,加上static
2.可以将这个方法提取到另一个配置类里面去写,然后在这个核心配置类上面使用@Import注解导入进来
3.也可以不在使用@Bean定义MapperScannerConfigurer的对象了,直接在配置类上添加一个注解@MapperScanner("com.execise.dao")
*/
//3.创建MapperScannerConfigurer bean 扫描dao接口创建代理对象 交由Spring管理
@Bean
public static MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setBasePackage("com.execise.dao");
return mapperScannerConfigurer;
}
}
单元测试
package com.execise.test;
import com.execise.config.AppConfig;
import com.execise.service.AccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestAccountServiceImpl {
@Test
public void testFindAll(){
//1. 创建工厂
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
//2. 问工厂要对象
AccountService accountService = context.getBean(AccountService.class);
//3. 调用方法
System.out.println(accountService.findAll());
}
}
五、Spring整合Junit
在上边的CURD中,单元测试类里还需要我们自己去创建ApplicationContext
,并自己去获取bean对象。Spring提供了整合Junit的方法,让单元测试更简洁方便。
注解 | 说明 |
---|---|
@RunWith | 用在测试类上,用于声明不再使用Junit,而是使用Spring提供的运行环境 |
@ContextConfiguration | 用在测试类上,用于指定Spring配置类、或者Spring的配置文件 |
Spring提供了单元测试的运行环境:SpringJunit4ClassRunner,配置到
@RunWith
注解上:
@RunWith(SpringJunit4ClassRunner.class)
要使用以上注解,需要导入jar包依赖:spring-test
和 junit
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
使用示例
步骤
-
在pom.xml文件中增加依赖:
spring-test
和junit
-
修改单元测试类
-
在单元测试类上增加注解:
@RunWith(SpringJunit4ClassRunner.class)
目的:使用Spring的单元测试运行器,替换Junit原生的运行器
-
在单元测试类上增加注解:
@ContextConfiguration()
目的:指定配置文件或配置类
-
在测试类里的依赖项上,直接使用
@Autowired
注入依赖
-
实现
在pom.xml文件中增加依赖:spring-test
和 junit
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
<!--spring单元测试的依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
<!--Junit的依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
- UserService接口
package com.execise.service;
public interface UserService {
void add();
}
- UserServiceImpl实现类
package com.execise.service.impl;
import com.execise.service.UserService;
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("调用了UserServiceImpl的add方法!~");
}
}
- applicationContext.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="us" class="com.execise.service.impl.UserServiceImpl"/>
</beans>
- 修改单元测试类
package com.execise.test;
import com.execise.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/*
@RunWith
1. 表示使用的单元测试的环境,不再是以前的Junit的测试环境,而是spring针对Junit提供的测试环境
2. 这套spring提供的测试环境在背后会帮助我们创建工厂。
@ContextConfiguration
1. 告诉spring的测试环境,配置文件在哪里?
2. classpath: 这是固定写法,表示要在类路径底下找配置文件。
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestUserServiceImpl02 {
//让spring把想要用到的对象给注入进来!
// 这个注解是自动注入的意思,让spring把对象注入进来。明天讲!
@Autowired
private UserService us;
@Test
public void testAdd(){
/*ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService us = (UserService) context.getBean("us");
*/
us.add();
}
}