注解开发
1 开启注解扫描
<context:component-scan base-package="cn.oldlu"></context:component-scan>
2 注解分类
2.1 bean相关注解1
以下注解的功能用于标记指定的类的对象交给spring管理
注解 | 描述 |
@Component | 使用在类上用于实例化Bean |
@Controller | 使用在web层类上用于实例化Bean |
@Service | 使用在service层类上用于实例化Bean |
@Repository | 使用在dao类上用于实例化Bean |
使用方式1:bean的名字显示指定
@Repository("userDaoImpl") public class UserDaoImpl implements UserDao{ }
使用方式2:bean的名字由框架默认设置,省略bean的名字,bean的名字默认是当前类的名字首字母小写,代码中这个bean的名字其实就是userDaoImpl
@Repository public class UserDaoImpl implements UserDao{ }
2.3 Bean相关注解2(了解)
注解 | 描述 |
@Primary | 设置类对应的bean按类型装配时优先装配,,比如如果有多个dao实现类,service要根据类型注入,为了提高dao的优先级,可以在类上加该注解 |
@DependsOn | 控制bean的加载顺序,使其在指定bean加载完毕后再加载 |
@Order | 控制配置类的加载顺序 |
@Lazy | 控制bean的加载时机,使其延迟加载 |
@Primary
位置:类定义上方
作用:设置类对应的bean按类型装配时优先装配
范例:
@Primary public class ClassName{}
说明:
- @Autowired默认按类型装配,当出现相同类型的bean,使用@Primary提高按类型自动装配的优先级,多个@Primary会导致优先级设置无效
依赖加载
(1)@DependsOn
类型:类注解、方法注解
位置:bean定义的位置(类上或方法上)
作用:控制bean的加载顺序,使其在指定bean加载完毕后再加载
范例:
@DependsOn("beanId") public class ClassName { }
说明:
配置在方法上,使@DependsOn指定的bean优先于@Bean配置的bean进行加载
配置在类上,使@DependsOn指定的bean优先于当前类中所有@Bean配置的bean进行加载
配置在类上,使@DependsOn指定的bean优先于@Component等配置的bean进行加载
相关属性
value(默认):设置当前bean所依赖的bean的id
@Order
类型:配置类注解
位置:配置类定义的位置(类上)
作用:控制配置类的加载顺序
范例:
@Order(1) public class SpringConfigClassName { }
@Lazy
类型:类注解、方法注解
位置:bean定义的位置(类上或方法上)
作用:控制bean的加载时机,使其延迟加载
范例:
@Lazy public class ClassName { }
依赖加载应用场景
@DependsOn
微信订阅号,发布消息和订阅消息的bean的加载顺序控制
双11活动期间,零点前是结算策略A,零点后是结算策略B,策略B操作的数据为促销数据。策略B加载顺序与促销数据的加载顺序
@Lazy
程序灾难出现后对应的应急预案处理是启动容器时加载时机
@Order
多个种类的配置出现后,优先加载系统级的,然后加载业务级的,避免细粒度的加载控制
2.4 依赖注入相关注解
以下注解作用在属性上,完成对属性值的注入
注解 | 描述 |
@Autowired | 使用在成员变量上,会根据类型依赖注入。 |
@Qualifier | 结合@Autowired一起使用,根据名称依赖注入 |
@Resource | 相当于@Autowired+@Qualifier,按照名称进行注入,JDK1.9之后不能使用 |
@Value | 注入普通属性 |
注意1
@Resource(name=”bean名”)在JDK1.9中使用后无法注入,不建议使用
注意2
Autowired根据类型匹配,换句话说就是一个接口如果有多个实现类就必须配合Qualifier一起使用了
注意3
Qualifier必须配合@Autowired一起使用,当某个接口的实现类有多个的时候配合该注解使用
2.5 配置单例和多例注解
注解 | 描述 |
@Scope | 标注Bean的作用范围,该注解配合@Component,@Controller,@Service,@Repository使用 |
2.6 生命周期相关注解
注意:以下注解JDK1.9不能用
注解 | 描述 |
@PostConstruct | 该注解标注在方法上,该方法会在Bean创建的时候执行 |
@PreDestroy | 该注解标注在方法上,该方法会在Bean销毁的时候执行 |
2.7 替换applicationContext相关注解
注解 | 描述 |
@Configuration | 替换applicationContext.xml文件 |
@ComponentScan | 替换包扫描context:component-scan |
@Bean | 替换bean标签 |
@PropertySource | 替换<context:property-placeholder location=”classpath:jdbc.properties”> |
@Import | 替换<import resource="xxx.xml"></import> 标签导入其他的configuration |
2.8 全注解开发步骤
1.编写专门操作数据库的相关配置
package com.itheima.cofig; import com.mchange.v2.c3p0.ComboPooledDataSource; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.PropertySource; import javax.sql.DataSource; import java.beans.PropertyVetoException; @PropertySource("classpath:jdbc.properties") public class DataSourceConfiguration { @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") //替代<bean id="dataSource"> public DataSource getDataSource() throws PropertyVetoException { ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setDriverClass(driver); dataSource.setJdbcUrl(url); dataSource.setUser(username); dataSource.setPassword(password); return dataSource; } }
2.编写替换applicationContext.xml的类
package com.itheima.cofig; import com.mchange.v2.c3p0.ComboPooledDataSource; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.*; import javax.sql.DataSource; import java.beans.PropertyVetoException; //标志该类是Spring的核心配置类 @Configuration //<context:component-scan base-package="com.itheima"/> @ComponentScan("com.itheima") //<import resource=""/> @Import({DataSourceConfiguration.class}) public class SpringCofiguration { }
3.编写测试方法
public static void main(String[] args) { //ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); ApplicationContext app = new AnnotationConfigApplicationContext(SpringCofiguration.class); UserService userService = app.getBean(UserService.class); userService.save(); }
注意事项
1.@Bean注解必须给一个bean的名字
2.SpringCofiguration是主配置文件,引入了DataSourceConfiguration配置,所以在DataSourceConfiguration类上可以省略@Configuration注解,该注解在主配置文件配置一次就够了,当然每个配置文件都配置也没有任何问题
2.9 注解方式和XML方式配置对比
3 集成junit
3.1 为什么要集成junit
1.每次需要手动加载配置文件创建ApplicationContext对象
2.调用getBean方法获取bean
3.getBean方法返回的是Object对象,需要强转
3.2 集成junit的好处
可以直接将我们要使用的bean注入到当前测试类
3.3 使用方式
3.3.1 引入依赖
注意junit版本要大于等于4.12,否则会报如下错误
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.5.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency>
3.3.2 junit测试类上加注解
注解功能
@RunWith是个运行器,SpringJUnit4ClassRunner表示运行在Spring的开发环境中,里面自带applicationContext,所以加上该注解后不需要手动创建applicationContext对象
@ContextConfiguration指定要读取的配置文件
package com.itheima.test; import com.itheima.cofig.SpringCofiguration; import com.itheima.service.UserService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import javax.sql.DataSource; import java.sql.SQLException; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class SpringJunitTest { }
3.3.3 在测试类中注入要使用的bean
package com.itheima.test; import com.itheima.cofig.SpringCofiguration; import com.itheima.service.UserService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import javax.sql.DataSource; import java.sql.SQLException; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml")//导入XML版配置文件 //@ContextConfiguration(classes = {SpringConfig.class})//导入注解版配置文件 public class SpringJunitTest { @Autowired private UserService userService;//测试使用 @Autowired private DataSource dataSource;//测试使用 @Test public void test1() throws SQLException { userService.save(); System.out.println(dataSource.getConnection()); } }
4 注解方式整合
4.1 第一步 引入依赖
需要引入mysql,mybatis,spring,druid连接池,以及整合mybatis和spring的依赖
<dependencies> <!--Lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.4</version> </dependency> <!--Spring核心--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.5.RELEASE</version> </dependency> <!--Spring集成Junit测试--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.5.RELEASE</version> </dependency> <!--Spring集成Aspect切面--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.13</version> </dependency> <!--Spring 事物--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.0.5.RELEASE</version> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.3</version> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.46</version> </dependency> <!--spring连接数据库--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.1.6.RELEASE</version> </dependency> <!--数据库连接池--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.21</version> </dependency> <!--spring集成mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.0</version> </dependency> </dependencies>
4.2 第二步 创建数据库
/* SQLyog Ultimate v12.09 (64 bit) MySQL - 5.5.40 : Database - spring01 ********************************************************************* */ /*!40101 SET NAMES utf8 */; /*!40101 SET SQL_MODE=''*/; /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; CREATE DATABASE /*!32312 IF NOT EXISTS*/`spring01` /*!40100 DEFAULT CHARACTER SET utf8 */; USE `spring01`; /*Table structure for table `account` */ DROP TABLE IF EXISTS `account`; CREATE TABLE `account` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(24) DEFAULT NULL, `money` double(10,2) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; /*Data for the table `account` */ insert into `account`(`id`,`name`,`money`) values (1,'jack',1000.00),(2,'tom',1000.00),(3,'rose',1000.00); /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
4.3 第三步 创建dao\service\domain
创建实体类 Account
package cn.oldlu.domain; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class Account { private Integer id; private String name; private Double money; }
创建 Dao
package cn.oldlu.dao; import cn.oldlu.domain.Account; import org.apache.ibatis.annotations.Select; import java.util.List; public interface AccountDao { @Select("select * from account") List<Account> findAll(); }
创建Service
package cn.oldlu.service; import cn.oldlu.domain.Account; import java.util.List; public interface AccountService { List<Account> findAll() ; }
创建Service实现类
package cn.oldlu.service.impl; import cn.oldlu.dao.AccountDao; import cn.oldlu.domain.Account; import cn.oldlu.service.AccountService; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.util.List; public class AccountServiceImpl implements AccountService { private AccountDao accountDao; public List<Account> findAll() { return accountDao.findAll(); } public AccountDao getAccountDao() { return accountDao; } public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } }
4.4 第四步 创建jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/spring01 jdbc.username=root jdbc.password=root
4.5 第五步 创建数据库连接池的配置文件
package cn.oldlu.config; import com.alibaba.druid.pool.DruidDataSource; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import javax.sql.DataSource; //声明当前java类是配置文件 @Configuration //引入jdbc.properties配置文件 @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对象,并交给srping容器管理 @Bean public DataSource getDataSource(){ DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setUsername(this.username); druidDataSource.setPassword(this.password); druidDataSource.setUrl(this.url); druidDataSource.setDriverClassName(this.driver); return druidDataSource; } }
4.6 第六步 创建整合MyBatis的配置文件
package cn.oldlu.config; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.mapper.MapperScannerConfigurer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; @Configuration public class MyBatisConfig { @Bean public SqlSessionFactoryBean getSqlSessionFactoryBean(@Autowired DataSource dataSource){ SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setTypeAliasesPackage("cn.oldlu.domain"); sqlSessionFactoryBean.setDataSource(dataSource); return sqlSessionFactoryBean; } @Bean public MapperScannerConfigurer getMapperScannerConfigurer(){ MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer(); mapperScannerConfigurer.setBasePackage("cn.oldlu.dao"); return mapperScannerConfigurer; } }
4.7 第七步 创建主配置文件
package cn.oldlu.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @Configuration @ComponentScan("cn.oldlu") @Import({JdbcConfig.class,MyBatisConfig.class}) public class AppConfig { }
4.8 第八步 配置Service层
自定义类,如果需要交由Spring管理,添加 @Component,@Controller,@Service,@Repository 等注解
本案例在Service的实现类上添加注解
package cn.oldlu.service.impl; import cn.oldlu.dao.AccountDao; import cn.oldlu.domain.Account; import cn.oldlu.service.AccountService; import lombok.Data; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service("accountService") public class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao ; public List<Account> findAll() { return accountDao.findAll(); } public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } }
4.9 第九步 编写测试类
import cn.oldlu.config.AppConfig; import cn.oldlu.domain.Account; import cn.oldlu.service.AccountService; import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import java.util.List; public class AppTest { @Test public void findAll(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class); AccountService accountService = applicationContext.getBean(AccountService.class); List<Account> all = accountService.findAll(); System.out.println(all); } }
5 面试题
BeanFactory和FactoryBean的区别
- FactoryBean:封装单个bean的创建过程,提供了了一些方法比如获取当前bean,判断是否是单例,获取当前bean的类型。目的是为了创建某些复杂的bean
- BeanFactory:Spring容器顶层接口,定义了bean相关的获取操作