前言
SpringBoot凭借"约定大于配置"的理念,已经成为最流行的web开发框架,所以有必须对其进行深入的了解;本文通过整合Mybatis类来分析SpringBoot提供的自动配置(AutoConfigure)功能,在此之前首先看一个整合Mybatis的实例。
SpringBoot整合Mybatis
提供SpringBoot整合Mybatis的实例,通过Mybatis实现简单的增删改查功能;
1.表数据
CREATE TABLE `role` ( `note` varchar(255) CHARACTER SET utf8 DEFAULT NULL, `role_name` varchar(255) DEFAULT NULL, `id` bigint(20) DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8
提供创建role表相关的sql,对表进行增删改查操作;
2.整合Mybatis的依赖
主要是mybatis-spring-boot-starter和使用的mysql驱动:
org.mybatis.spring.boot mybatis-spring-boot-starter 2.0.1mysql mysql-connector-java 5.1.29
3.配置application.properties
提供连接mysql相关的信息:url,驱动,用户名,密码;
spring.datasource.url=jdbc:mysql://localhost/mybatisspring.datasource.username=rootspring.datasource.password=rootspring.datasource.driver-class-name=com.mysql.jdbc.Driver
4.提供bean和Dao
分别提供表对应的bean类和操作数据库的dao类;
public class Role { private long id; private String roleName; private String note; //省略get/set方法}@Mapperpublic interface RoleDao { @Select("SELECT id,role_name as roleName,note FROM role WHERE id = #{id}") Role findRoleById(@Param("id") long id);}
5.提供Service和Controller
public interface RoleService { public Role findRoleById(long roleId);}@Servicepublic class RoleServiceImpl implements RoleService { @Autowired private RoleDao roleDao; @Override public Role findRoleById(long roleId) { return roleDao.findRoleById(roleId); }}@RestControllerpublic class RoleController { @Autowired private RoleService roleService; @RequestMapping("/role") public String getRole(long id) { return roleService.findRoleById(id).toString(); }}
启动服务,进行简单的测试:http://localhost:8888/role?id=111
结果如下:
Role [id=111, roleName=zhaohui, note=hello]
6.提出问题
如上实例中,我们使用了很少的配置,就通过mybatis实现了操作数据库;正常使用mybatis需要的SqlSessionFactory和SqlSession没有看到被实例化,同时mybatis依赖的数据源也没有看到被引用,那SpringBoot是如何帮我们自动配置的,下面重点分析一下;
SpringBoot自动配置
1.自动配置注解
要想使用自动配置功能,SpringBoot提供了注解@EnableAutoConfiguration,当然不需要我们配置因为在@SpringBootApplication注解中默认以及启用了;
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })public @interface SpringBootApplication {//...省略...}
可以看到@SpringBootApplication注解本身也有注解@EnableAutoConfiguration:
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class)public @interface EnableAutoConfiguration {//...省略...}
在注解@EnableAutoConfiguration中重点看一下@Import注解中使用的AutoConfigurationImportSelector类,此类是自动注解的核心类,会有条件的加载我们默认指定的配置类;这里有两个概念一个是有条件,一个是配置类,分别简单介绍一下:配置类可以简单理解就是相关组件对接SpringBoot的对接类,此类可以做一些初始化的工作;有条件表示并不是有配置类就能被对接上,是有条件的,SpringBoot默认提供了大量配置类,但并不是所有配置类都能被加载初始化的,是有条件的,比如mybatis在没有数据源的情况下,没有mybatis基础包的情况下是不能被对接的;下面首先看一下SpringBoot提供的哪些条件类;
2.条件类
SpringBoot提供了很多条件类,可以在配置中上配置注解条件类,相关条件类可以在spring-boot-autoconfigure包下的org.springframework.boot.autoconfigure.condition下找到,主要包含如下:
- ConditionalOnBean:当前容器有指定Bean的条件下;
- ConditionalOnClass:当前类路径下有指定类的条件下;
- ConditionalOnCloudPlatform:当指定了云平台的时候;
- ConditionalOnExpression:SpEL表达式作为判断条件;
- ConditionalOnJava:JVM版本作为判断条件;
- ConditionalOnJndi:在JNDI存在的条件下查找指定的位置;
- ConditionalOnMissingBean:当容器里没有指定Bean的情况下;
- ConditionalOnMissingClass:当类路径下没有指定的类的条件下;
- ConditionalOnNotWebApplication:当前项目不是WEB项目的条件下;
- ConditionalOnProperty:当前应用是否配置了指定属性指定的值;
- ConditionalOnResource:只有当指定的资源位于类路径下;
- ConditionalOnSingleCandidate:bean工厂中只有一个或者有多个情况下是主要的候选bean;
- ConditionalOnWebApplication:当前项目是WEB项目的条件下。
以上是注解类,注解本身没有功能,只是提供标记的功能,具体功能在@Conditional中指定的,比如ConditionalOnBean注解如下所示:
@Target({ ElementType.TYPE, ElementType.METHOD })@Retention(RetentionPolicy.RUNTIME)@Documented@Conditional(OnBeanCondition.class)public @interface ConditionalOnBean {//...省略...}
相关功能的实现就在OnBeanCondition类中,同样其他注解类的实现类也在包org.springframework.boot.autoconfigure.condition下找到;
3.自动配置过程
Springboot应用启动过程中使用ConfigurationClassParser分析配置类,此类中有一个processImports方法,此方法用来处理@Import注解,在@EnableAutoConfiguration注解存在@Import注解,这时候会实例化注解中的AutoConfigurationImportSelector,在其内部有一个AutoConfigurationGroup内部类,内部类有两个核心方法分别是:process和selectImports;
@Override public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) { Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> String.format("Only %s implementations are supported, got %s