Springboot自动配置原理:
主类 @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 { ... }
@Import(EnableAutoConfigurationImportSelector.class) 通过注解的方式实现把实例EnableAutoConfigurationImportSelector加入springIOC容器中,
EnableAutoConfigurationImportSelector实现了接口ImportSelector,并在其父类AutoConfigurationImportSelector中实现了ImportSelector定义的
org.springframework.context.annotation.ImportSelector#selectImports 【String[] selectImports(AnnotationMetadata importingClassMetadata)】),
selectImports返回了一系列的***AutoConfiguration配置类,例如org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration,具体返回内容请查看动图一
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(EnableAutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { ... }
org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration 中通过注解 @ConditionalXXX 定义了创建bean的条件 ,
例如下面代码片段的 @ConditionalOnMissingBean(CharacterEncodingFilter.class)定义了当CharacterEncodingFilter类型的bean不存在才创建bean,当我们通过maven引入某个starter后(例如 spring-boot-starter-web),会匹配@Conditional定义的条件从而创建bean
@Configuration @EnableConfigurationProperties(HttpEncodingProperties.class) @ConditionalOnWebApplication @ConditionalOnClass(CharacterEncodingFilter.class) @ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true) public class HttpEncodingAutoConfiguration { private final HttpEncodingProperties properties; public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) { this.properties = properties; } @Bean @ConditionalOnMissingBean(CharacterEncodingFilter.class) public CharacterEncodingFilter characterEncodingFilter() { CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter(); filter.setEncoding(this.properties.getCharset().name()); filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST)); filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE)); return filter; } ... }
自动化配置类都有一个这样的注解@EnableConfigurationProperties ,例如 @EnableConfigurationProperties(HttpEncodingProperties.class) 来关联配置文件(application.yml或application.properties)的内容,可以打开这些类来看支持哪些配置项
一个简单的demo项目
下面贴出部分代码
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>lddxfs.springboot</groupId> <artifactId>springboot-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <name>springboot-demo</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.18.BUILD-SNAPSHOT</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!--<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> </dependency> <!-- Provided --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.7</version> </dependency> <!-- <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <scope>provided</scope> </dependency>--> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <repositories> <repository> <id>spring-snapshots</id> <name>Spring Snapshots</name> <url>https://repo.spring.io/snapshot</url> <snapshots> <enabled>true</enabled> </snapshots> </repository> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>spring-snapshots</id> <name>Spring Snapshots</name> <url>https://repo.spring.io/snapshot</url> <snapshots> <enabled>true</enabled> </snapshots> </pluginRepository> <pluginRepository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </pluginRepository> </pluginRepositories> </project>
入口类
package lddxfs.springboot.springbootdemo; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.transaction.annotation.EnableTransactionManagement; @SpringBootApplication @EnableTransactionManagement @MapperScan("lddxfs.springboot.springbootdemo.mapper") public class SpringbootDemoApplication { public static void main(String[] args) { SpringApplication.run(SpringbootDemoApplication.class, args); } }
yml
application: message: bootJSP spring: datasource: url: jdbc:mysql://localhost:3306/dubbostudy?useUnicode=true&characterEncoding=utf8 username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver mvc: view: prefix: /WEB-INF/jsp/ suffix: .jsp formcontent: putfilter: enabled: true http: encoding: charset: utf-8 force: true mybatis: mapper-locations: classpath*:mapper*.xml
使用外部tomcat运行(支持jsp)
package lddxfs.springboot.springbootdemo; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.support.SpringBootServletInitializer; /** * Author:lddxfs(lddxfs@qq.com;https://www.cnblogs.com/LDDXFS/) * Date:2018/10/24 * 使用外置tomcat */ public class DemoSpringBootServletInitializer extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(SpringbootDemoApplication.class); } }
监听器
package lddxfs.springboot.springbootdemo.web; /** * Author:lddxfs(lddxfs@qq.com;https://www.cnblogs.com/LDDXFS/) * Date:2018/10/24 */ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.http.HttpSessionAttributeListener; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; /** * Author:lddxfs(lddxfs@qq.com;https://www.cnblogs.com/LDDXFS/) * Date:2018/10/23 */ public class DemoListener implements ServletContextListener, HttpSessionListener, HttpSessionAttributeListener { private static Logger log = LoggerFactory.getLogger(DemoListener.class); public DemoListener() { } // ------------------------------------------------------- // ServletContextListener implementation // ------------------------------------------------------- public void contextInitialized(ServletContextEvent sce) { /* This method is called when the servlet context is initialized(when the Web application is deployed). You can initialize servlet context related data here. */ log.info("容器启动了"); } public void contextDestroyed(ServletContextEvent sce) { /* This method is invoked when the Servlet Context (the Web application) is undeployed or Application Server shuts down. */ log.info("容器销毁了"); } // ------------------------------------------------------- // HttpSessionListener implementation // ------------------------------------------------------- public void sessionCreated(HttpSessionEvent se) { /* Session is created. */ log.info("Session创建了 sessionId:{}",se.getSession().getId()); } public void sessionDestroyed(HttpSessionEvent se) { /* Session is destroyed. */ log.info("Session销毁了 sessionId:{}",se.getSession().getId()); } // ------------------------------------------------------- // HttpSessionAttributeListener implementation // ------------------------------------------------------- public void attributeAdded(HttpSessionBindingEvent sbe) { /* This method is called when an attribute is added to a session. */ log.info("放入值到Session了 name:{}",sbe.getName()); } public void attributeRemoved(HttpSessionBindingEvent sbe) { /* This method is called when an attribute is removed from a session. */ log.info("Session移除值 name:{}",sbe.getName()); } public void attributeReplaced(HttpSessionBindingEvent sbe) { /* This method is invoked when an attibute is replaced in a session. */ log.info("Session修改值 name:{}",sbe.getName()); } }
过滤器
package lddxfs.springboot.springbootdemo.web; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; /** * Author:lddxfs(lddxfs@qq.com;https://www.cnblogs.com/LDDXFS/) * Date:2018/10/24 */ public class DemoFilter implements Filter { private static Logger log = LoggerFactory.getLogger(DemoFilter.class); @Override public void init(FilterConfig filterConfig) { log.info("DemoFilter.init()"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (request instanceof HttpServletRequest) { HttpServletRequest req = (HttpServletRequest) request; log.info("请求uri:{}", req.getRequestURI()); } chain.doFilter(request, response); } @Override public void destroy() { log.info("DemoFilter.destroy()"); } }
三大组件注册配置类
package lddxfs.springboot.springbootdemo.config; import lddxfs.springboot.springbootdemo.web.DemoFilter; import lddxfs.springboot.springbootdemo.web.DemoListener; import lddxfs.springboot.springbootdemo.web.DemoServlet; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletListenerRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.HashSet; import java.util.Set; /** * Author:lddxfs(lddxfs@qq.com;https://www.cnblogs.com/LDDXFS/) * Date:2018/10/23 * web三大组件注册配置类 */ @Configuration public class WebBeanConfig { /** * 注册serlet * @return */ @Bean public ServletRegistrationBean servletRegistrationBean() { ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(); servletRegistrationBean.setServlet(new DemoServlet()); servletRegistrationBean.addUrlMappings("/testServletA"); return servletRegistrationBean; } /** * 注册filter * @return */ @Bean public FilterRegistrationBean filterRegistrationBean() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); filterRegistrationBean.setFilter(new DemoFilter()); filterRegistrationBean.setOrder(0); Set<String> urlPatterns = new HashSet<>(); urlPatterns.add("/*"); filterRegistrationBean.setUrlPatterns(urlPatterns); return filterRegistrationBean; } /** * 注册listener * @return */ @Bean public ServletListenerRegistrationBean servletListenerRegistrationBean() { ServletListenerRegistrationBean servletListenerRegistrationBean = new ServletListenerRegistrationBean(); servletListenerRegistrationBean.setListener(new DemoListener()); servletListenerRegistrationBean.setOrder(0); return servletListenerRegistrationBean; } }
异常处理
package lddxfs.springboot.springbootdemo.web; import lddxfs.springboot.springbootdemo.common.resutcode.Result; import org.springframework.boot.autoconfigure.web.DefaultErrorAttributes; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestAttributes; import java.util.LinkedHashMap; import java.util.Map; /** * Author:lddxfs(lddxfs@qq.com;https://www.cnblogs.com/LDDXFS/) * Date:2018/10/24 */ @Component public class DemoErrorAttribute extends DefaultErrorAttributes { @Override public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) { Result<Void> result =( Result<Void>)requestAttributes.getAttribute("ext",RequestAttributes.SCOPE_REQUEST); Map<String, Object> errorMap=new LinkedHashMap<>(); errorMap.put("message",result.getMessage()); errorMap.put("data",result.getData()); errorMap.put("code",result.getCode()); return errorMap; } }
package lddxfs.springboot.springbootdemo.web; import lddxfs.springboot.springbootdemo.common.resutcode.Result; import lddxfs.springboot.springbootdemo.common.resutcode.ResultCode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import javax.servlet.http.HttpServletRequest; /** * Author:lddxfs(lddxfs@qq.com;https://www.cnblogs.com/LDDXFS/) * Date:2018/10/24 * 异常处理 */ @ControllerAdvice public class DemoExeceptionHandler { private static final Logger LOGGER = LoggerFactory.getLogger(DemoExeceptionHandler.class); /** * 所有客户端返回json数据 * * @param e * @return */ /* @ResponseBody @ExceptionHandler(Exception.class) public Result<Void> HandleException(Exception e) { Result<Void> result = ResultCode.ERROR.clone(); result.setMessage(e.getMessage()); return result; }*/ /** * 不同客户端返回JSON或Html * @param e * @param request * @return */ @ExceptionHandler(Exception.class) public String handleException(Exception e, HttpServletRequest request) { Result<Void> result = ResultCode.ERROR.clone(); result.setMessage(e.getMessage()); request.setAttribute("ext", result); request.setAttribute("javax.servlet.error.status_code", HttpStatus.INTERNAL_SERVER_ERROR.value()); LOGGER.error("系统异常:", e); return "forward:/error"; } }
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>springboot-demo</display-name> </web-app>
Controller
package lddxfs.springboot.springbootdemo.web.controller; import lddxfs.springboot.springbootdemo.common.resutcode.Result; import lddxfs.springboot.springbootdemo.common.resutcode.ResultCode; import lddxfs.springboot.springbootdemo.javabean.Category; import lddxfs.springboot.springbootdemo.service.CategoryService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.util.List; /** * Author:lddxfs(lddxfs@qq.com;https://www.cnblogs.com/LDDXFS/) * Date:2018/10/6 * REST风格 管理分类 */ @RequestMapping("api") @RestController public class CategoryController { private static Logger logger = LoggerFactory.getLogger(CategoryController.class); @Resource private CategoryService categoryService; @GetMapping("categories") public Result<List<Category>> categories() { Result<List<Category>> result = ResultCode.SUCCESS.clone(); result.setData(categoryService.categories()); return result; } public CategoryController() { System.out.println("CategoryController"); } @GetMapping("/categories/{id}") public Result<Category> categories(@PathVariable("id") Integer id) { Result<Category> result = ResultCode.SUCCESS.clone(); result.setData(categoryService.categoriesById(id)); return result; } @DeleteMapping("/categories/{id}") public Result<Void> deleteCategories(@PathVariable("id") Integer id) { Result<Void> result = ResultCode.SUCCESS.clone(); categoryService.deleteCategories(id); return result; } @PutMapping("/categories") public Result<Category> updateCategories(Category category) { Result<Category> result = ResultCode.SUCCESS.clone(); result.setData(categoryService.updateCategories(category)); return result; } @PostMapping("/categories") public Result<Category> createCategories(Category category) { Result<Category> result = ResultCode.SUCCESS.clone(); result.setData(categoryService.saveCategories(category)); return result; } @PostMapping(value = "/categories/transaction_test") public Result<List<Category>> transactionTest(@RequestBody List<Category> categories) { Result<List<Category>> result = ResultCode.SUCCESS.clone(); result.setData(categoryService.transactionTest(categories)); return result; } }
mybatis代码生成配置
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> <generatorConfiguration> <!--数据库驱动--> <classPathEntry location="mysql-connector-java-5.1.45.jar"/> <context id="mysql"> <commentGenerator> <property name="suppressDate" value="true"/> <property name="suppressAllComments" value="true"/> </commentGenerator> <!--数据库链接地址账号密码--> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://127.0.0.1:3306/dubbostudy?useSSL=true" userId="root" password="123456"> </jdbcConnection> <javaTypeResolver> <property name="forceBigDecimals" value="false"/> </javaTypeResolver> <!--生成Model类存放位置--> <javaModelGenerator targetPackage="lddxfs.springboot.springbootdemo.javabean" targetProject="springboot-demosrc"> <property name="enableSubPackages" value="true"/> <property name="trimStrings" value="true"/> </javaModelGenerator> <!--生成映射文件存放位置--> <sqlMapGenerator targetPackage="mapper" targetProject="springboot-demosrc"> <property name="enableSubPackages" value="true"/> </sqlMapGenerator> <!--生成mapper类存放位置--> <javaClientGenerator type="XMLMAPPER" targetPackage="lddxfs.springboot.springbootdemo.mapper" targetProject="springboot-demosrc"> <property name="enableSubPackages" value="true"/> </javaClientGenerator> <!--生成对应表及类名--> <table tableName="tb_good_category" domainObjectName="Category" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"> <!--<generatedKey column="id" sqlStatement="mySql"></generatedKey>--> <generatedKey column="id" sqlStatement="JDBC" identity="true" type="post"/> </table> </context> </generatorConfiguration>
其他代码就不贴了
idea配置外部tomcat
补充spring常用注解的使用
上传代码到码云了,代码位置: https://gitee.com/lddxfs/spring-annotation-use