示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。
Spring Boot自动配置原理
在传统的SSM框架集成中,需要编写大量的XML配置文件,比如集成Mybatis时,需要编写mybatis_config.xml文件,在集成springmvc时,需要编写springmvc.xml文件,这些配置文件十分繁琐,还很容易出现错误,导致开发效率低。而Spring Boot采用约定大于配置的思想,将大量的spring配置文件集成到Spring Boot的内部,帮助开发人员自动配置各类XML文件,极大的简化了开发过程。
步骤一:自动配置原理
Spring Boot自动配置的核心注解是@SpringBootApplication,该注解是spring boot的启动类注解,它是一个复合注解,里面包含:
在这里插入图片描述
(1)@SpringBootConfiguration:该注解上有一个 @Configuration注解,表示这个spring boot启动类是一个配置类,最终要被注入到spring容器中。
在这里插入图片描述
(2)@EnableAutoConfiguration:表示开启自动配置,它也是一个复合注解,里面包含:
在这里插入图片描述
① @AutoConfigurationPackage,该注解上有一个@Import(AutoConfigurationPackages.Registrar.class)注解,其中 Registrar 类的作用是将启动类所在包下的所有子包的组件扫描注入到spring容器中。
这也是为什么在spring boot中,我们通常将controller、pojo、service、dao等包放在启动类同级目录下的原因。
在这里插入图片描述
② @Import(AutoConfigurationImportSelector.class):其中AutoConfigurationImportSelector类中有一个getCandidateConfigurations()方法,该方法通过SpringFactoriesLoader.loadFactoryNames()方法查找位于META-INF/spring.factories文件中的所有自动配置类,并加载这些类。
在这里插入图片描述
spring.factories文件是以key-value键值对的形式存储文件里,其中有一个key=EnableAutoConfiguration,它对应的value值就是一个个以AutoConfiguration结尾来命名的 xxxAutoConfiguration 自动配置类。
在这里插入图片描述
所以spring boot在整个的启动过程中,其实就是在类路径的META-INF/spring.factories 文件中找到EnableAutoConfiguration对应的所有的自动配置类,然后将所有自动配置类加载到spring容器中。
所有自动配置类都是以AutoConfiguration结尾来命名的,而诸多的xxxAutoConfiguration 其实就是Spring容器的JavaConfig形式,它的作用就是为Spring容器导入bean。
在这里插入图片描述
步骤二:自动配置生效
前面加载的所有自动配置类并不是都生效的,每一个xxxAutoConfiguration自动配置类都是在某些特定的条件之下才会生效。这些条件限制是通过@ConditionOnxxx注解实现的。
常见的@@ConditionOnxxx注解有以下几种:
@ConditionalOnBean:当容器里存在指定bean的条件下。
@ConditionalOnMissingBean:当容器里不存在指定bean的条件下。
@ConditionalOnClass:当类路径下有指定类的条件下。
@ConditionalOnMissingClass:当类路径下不存在指定类的条件下。
@ConditionalOnProperty:指定的属性是否有指定的值,比如
@ConditionalOnProperties(prefix=”xxx.xxx”, value=”enable”, matchIfMissing=true),代表当xxx.xxx为enable时条件的布尔值为true,如果没有设置的情况下也为true。
@ConditionalOnBean:当容器里存在指定bean的条件下。
@ConditionalOnMissingBean:当容器里不存在指定bean的条件下。
@ConditionalOnClass:当类路径下有指定类的条件下。
@ConditionalOnMissingClass:当类路径下不存在指定类的条件下。
@ConditionalOnProperty:指定的属性是否有指定的值,比如
@ConditionalOnProperties(prefix=”xxx.xxx”, value=”enable”, matchIfMissing=true),代表当xxx.xxx为enable时条件的布尔值为true,如果没有设置的情况下也为true。
@ConditionOnxxx注解用来指定自动配置类在哪些条件下会生效。我们要使用哪些类,就直接在spring boot项目中的pom.xml文件中导入相应的启动器即可,这样spring boot就会利用@ConditionOnxxx注解使我们需要的自动配置类生效,将该类的bean注入到spring容器中,这样就完成了整个自动配置的过程。
总结
spring boot中,每一个xxxAutoConfiguration 自动配置类,其实就是一个spring容器的JavaConfig形式,它的作用就是为spring容器注入相应的bean。而在注入bean的过程中,所有需要的属性值则是通过xxxProperties的bean来获得的。
拓展:xxxProperties类
或者问:在spring boot中,全局配置文件application.yaml或application.properties中自定义的属性如何产生作用?
在每一个xxxAutoConfiguration 类上都有一个@EnableConfigurationProperties(xxxProperties.class)注解,该注解表示开启配置属性,它的参数是一个xxxProperties类。
而xxxProperties类上又有一个@ConfigurationProperties(prefix = “xxx”)注解,该注解的作用就是从配置文件中绑定属性到对应的bean上,该注解的参数prefix 关键字定义的属性就是我们可以在全局配置文件application.yaml中自定义的属性。
举个栗子:假设在application.properties文件中,定义server.port=8081。
首先会找到根据prefix="server"找到对应的ServerProperties类,可以看到该类中定义了一个port属性,所以我们才可以在application.properties文件中自定义server.port属性。
在这里插入图片描述
接着@ConfigurationProperties起作用,将我们自定义的属性绑定到对应的bean上
然后@EnableConfigurationProperties注解将已经绑定新属性的bean注入到spring 容器中,这样开发人员自定义的属性就能起作用了。
在这里插入图片描述
总结:在全局配置的属性,如server.port=8081等,通过@ConfigurationProperties注解的prefix关键字将自定义的属性绑定到对应的xxxProperties实体类的bean上,然后通过@EnableConfigurationProperties注解将这个绑定了自定义属性的bean注入到spring容器中。
一、自定义错误页面
1.1 SpringBoot 默认的处理异常的机制:SpringBoot 默认的已经提供了一套处理异常的机制。
一旦程序中出现了异常 SpringBoot 会像/error 的 url 发送请求。在 springBoot 中提供了一个
叫 BasicExceptionController 来处理/error 请求,然后跳转到默认显示异常的页面来展示异常
信息。
1.2 如 果 我 们 需 要 将 所 有 的 异 常 同 一 跳 转 到 自 定 义 的 错 误 页 面 , 需 要 再
src/main/resources/templates 目录下创建 error.html 页面。注意:名称必须叫 error
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>错误提示页面</title>
</head>
<body>
出错了,请与管理员联系。。。
<span th:text="${exception}"></span>
</body>
</html>
二、@ExceptionHandle 注解处理异常
2.1 上一种方法不管发生什么异常,都只能跳转到一个页面,颗粒度太大,这一种方式可以实现对不同的异常做不同的处理。
2.2 Controller
@Controller
public class DemoController {
@RequestMapping("/show")
public String showInfo(){
String str = null;
str.length();
return "index";
}
@RequestMapping("/show2")
public String showInfo2(){
int a = 10/0;
return "index";
}
/**
* java.lang.ArithmeticException
* 该方法需要返回一个 ModelAndView:目的是可以让我们封装异常信息以及视图的指定
* 参数 Exception e:会将产生异常对象注入到方法中
*/
@ExceptionHandler(value={java.lang.ArithmeticException.class})
public ModelAndView arithmeticExceptionHandler(Exception e){
ModelAndView mv = new ModelAndView();
mv.addObject("error", e.toString());
mv.setViewName("error1");
return mv;
}
/**
* java.lang.NullPointerException
* 该方法需要返回一个 ModelAndView:目的是可以让我们封装异常信息以及视图的指定
* 参数 Exception e:会将产生异常对象注入到方法中
*/
@ExceptionHandler(value={java.lang.NullPointerException.class})
public ModelAndView nullPointerExceptionHandler(Exception e){
ModelAndView mv = new ModelAndView();
mv.addObject("error", e.toString());
mv.setViewName("error2");
return mv;
}
}
2.3 页面1
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>错误提示页面-ArithmeticException</title>
</head>
<body>
出错了。。。
<span th:text="${error}"></span>
</body>
</html>
2.4 页面2
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>错误提示页面-NullPointerException</title>
</head>
<body>
出错了。。。
<span th:text="${error}"></span>
</body>
</html>
三、@ControllerAdvice+@ExceptionHandler 注解处理异常
3.1 上一种方式必须要在每一个Controler里面重复写异常处理代码,代码复用性太差,这一种方法可以实现异常的全局处理。需要创建一个能够处理异常的全局异常类。在该类上需要添加@ControllerAdvice 注解
/**
* 全局异常处理类
*/
@ControllerAdvice
public class GlobalException {
/**
* java.lang.ArithmeticException
* 该方法需要返回一个 ModelAndView:目的是可以让我们封装异常信息以及视图的指定
* 参数 Exception e:会将产生异常对象注入到方法中
*/
@ExceptionHandler(value={java.lang.ArithmeticException.class})
public ModelAndView arithmeticExceptionHandler(Exception e){
ModelAndView mv = new ModelAndView();
mv.addObject("error", e.toString());
mv.setViewName("error1");
return mv;
}
/**
* java.lang.NullPointerException
* 该方法需要返回一个 ModelAndView:目的是可以让我们封装异常信息以及视图的指定
* 参数 Exception e:会将产生异常对象注入到方法中
*/
@ExceptionHandler(value={java.lang.NullPointerException.class})
public ModelAndView nullPointerExceptionHandler(Exception e){
ModelAndView mv = new ModelAndView();
mv.addObject("error", e.toString());
mv.setViewName("error2");
return mv;
}
}
四、配置 SimpleMappingExceptionResolver 处理异常
4.1 上一种方式,每处理一种异常就要写一个处理方法,如果有很多异常需要处理,写起来会很麻烦,这一种方式可以很好的解决这种问题,需要在全局异常类中添加一个方法完成异常的统一处理
/**
* 通过 SimpleMappingExceptionResolver 做全局异常处理
*/
@Configuration
public class GlobalException {
/**
* 该方法必须要有返回值。返回值类型必须是:SimpleMappingExceptionResolver
*/
@Bean
public SimpleMappingExceptionResolver
getSimpleMappingExceptionResolver(){
SimpleMappingExceptionResolver resolver = new
SimpleMappingExceptionResolver();
Properties mappings = new Properties();
/**
* 参数一:异常的类型,注意必须是异常类型的全名
* 参数二:视图名称
*/
mappings.put("java.lang.ArithmeticException", "error1");
mappings.put("java.lang.NullPointerException","error2");
//设置异常与视图映射信息的
resolver.setExceptionMappings(mappings);
return resolver;
}
}
五、自定义 HandlerExceptionResolver 类处理异常
5.1 上一种方式不能在跳转页面的同时携带异常信息,这样不利于排错,当前这种方式可以解决上述问题,我们需 要 再 全 局 异 常 处 理 类 中 实 现HandlerExceptionResolver 接口
/**
* 通过实现 HandlerExceptionResolver 接口做全局异常处理
*/
@Configuration
public class GlobalException implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler,Exception ex) {
ModelAndView mv = new ModelAndView();
//判断不同异常类型,做不同视图跳转
if(ex instanceof ArithmeticException){
mv.setViewName("error1");
}
if(ex instanceof NullPointerException){
mv.setViewName("error2");
}
mv.addObject("error", ex.toString());
return mv;
}
}
1.首先我们自定义一个config,然后实现一个WebMvcConfigurer,我们自定义访问登录页面
@Configuration
public class WebMVCConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
registry.addViewController("/dashboard.html").setViewName("dashboard");
}
2.所以这个时候我们在controller中需要将登录信息存入session域中,
@Controller
@RequestMapping("user")
public class LoginController {
@RequestMapping("login")
public String login(@RequestParam("username") String username,
@RequestParam("password") String password, Map map, HttpServletRequest request){
if (username.equals("admin")&&password.equals("123456")){
request.getSession().setAttribute("loginUser","username");
return "redirect:/dashboard.html";
}else {
map.put("msg","用户名或者密码错误");
return "index";
}
}
}
3.然后定义一个拦截器,实现HandlerInterceptor,实现preHandle方法。
在preHandle,获取session中的信息,判断是否为空,为空则进行拦截到登录页面并提示信息,不为空则直接跳转主页面
public class WebInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object user = request.getSession().getAttribute("loginUser");
if (user==null){
request.setAttribute("msg","请先登录");
request.getRequestDispatcher("/index.html").forward(request,response);
return false;
}else {
return true;
}
}
4.最后需要注册拦截,主要是对于自定义访问登录页面进行放行,以及访问路径,js,css,img等进行放行。
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new WebInterceptor()).addPathPatterns("/**").excludePathPatterns("/","/index.html",
"/user/login", "/css/**", "/js/**", "/img/**");
}
1 .SpringMVC自动配置
WebMvcAutoConfiguration
1)包括ContentNegotiatingViewResolver and BeanNameViewResolver beans.
a)自动配置了ViewResolver(视图解析器:根据方法的返回值得到视图对象(View),视图对象决定如何渲染(转发?重定向?))
b)ContentNegotiatingViewResolver:组合所有的视图解析器的;
c)如何定制:我们可以自己给容器中添加一个视图解析器;自动的将其组合进来;
2. 扩展SpringMVC(自动配置and自定义配置)
以前SpringMVC写xml配置文件配置如下访问/hello跳转success定义拦截器
3.SpringBoot自动配置不能满足我们的需求,所以需要扩展
==编写一个配置类(@Configuration),是WebMvcConfigurerAdapter类型;不能标注@EnableWebMvc;这样既保留了所有的自动配置,也能用我们扩展的配置;==用配置类代替以前的springmvc.xml配置
4.4 如何修改SpringBoot的默认配置
模式:
1)、SpringBoot在自动配置很多组件的时候,==先看容器中有没有用户自己配置的(@Bean、@Component)如果有就用用户配置的,如果没有,才自动配置;==如果有些组件可以有多个(ViewResolver)将用户配置的和自己默认的组合起来;
2)、在SpringBoot中会有非常多的xxxConfigurer帮助我们进行扩展配置
3)、在SpringBoot中会有很多的xxxCustomizer帮助我们进行定制配置