spring boot 原理及使用

什么是springboot?

springboot是一个用于快速开发的框架,去除了繁琐的xml配置文件,全部采用注解的方式完成配置,内置Web服务器,可以快速的整合第三方的各种框架。是便捷开发的首选利器

springcloud与springboot的关系

spring只是一个快速开发的框架。而springcloud是微服务的一站式解决方案(服务熔断、降级、负载均衡、配置中心、注册中心)等等。
springcloud 依赖于springboot。

spring-boot-statat-web 依赖与springmvc的关系

这个依赖只是集成了springmvc

springboot 的静态资源默认目录

	# 在以下目录存放的资源可以直接通过
	# 域名/资源路径名访问到,不需要添加一个static或者public
	"classpath:/META-INF/resources/","classpath:/resources/", 
	"classpath:/static/", "classpath:/public/"

springboot的组件解释

  • spring-boot-starter-parent作用
     它可以提供dependency management,也就是说依赖管理,引入以后在申明其它dependency的时候就不需要version了,后面可以看到。
  • spring-boot-starter-web作用
    springweb 核心组件,包含springmvc之类的

Springboot多环境配置

  • 定义三个环境的不同配置文件
    在这里插入图片描述
  • 在application.properties中配置spring.profiles.active属性
	spring.profiles.active=prod
	#spring.profiles.active=dev
	#spring.profiles.active=test
  • 也可以在运行的时候添加虚拟机参数
--spring.profiles.active=dev

整合框架部分

整合mybatis框架

  • 引入依赖 mybatis-spring-boot-starter、以及数据库连接驱动mysql-connector-java
  • 在springboot的配置文件中直接配置数据库连接参数,springboot默认集成了HikariCP数据源
mybatis:
  mapper-locations: classpath:mapping/*Mapper.xml # 扫描dao层对应的映射
  type-aliases-package: com.it.entity # 为这个包的类定义别名,不用写全限定类名,并且类名首字母可以小写
  configuration:
   # log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 想要打印sql日志必须加上这一句
  # map-underscore-to-camel-case: true

spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:192.168.254.128/test?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
    driver-class-name: com.mysql.jdbc.Driver

#打印mybatis查询日志  相当于在日志的配置文件里加了一个 logging 如果使用log4j2,则不管用,就需要配置mybatis 的log-impl: org.apache.ibatis.logging.stdout.StdOutImpl 属性
logging:
  level:
    com:
      it:
        mapper : debug
  • 扫描Dao层,可以在springboot的启动类上加上@MapperScan注解配置需要扫描的Dao层(用于告诉mybatis这是一个Dao层),会将dao中加上了 @Insert @Select 等接口的类利用动态代理的技术创建动态代理对象来拦截执行方法。
  • 或者在Dao的接口上直接加上@Mapper注解,一样能实现一样的功能,但是比较麻烦(不推荐)

常用功能

springboot的actuator监控中心

作用

针对微服务服务器进行监控,监控服务器内存变化(堆内存、线程、日志管理等)、检测服务器链接地址是否可用(模拟访问)、通知容器中有多少个baen(spring容器中的bean)、统计@RequestMapping中有多少个映射。
Actuator是没有图形化界面的(返回Json格式)
AdminUI 底层使用Actuator监控应用,实现可视化界面
应用场景:生产环境

为什么要使用监控中心

可以帮助应用程序在生产环境下的监视和管理、统计应用的运行情况,特别堆微服务的管理有意义。

使用actuator监控中心

  • 在pom文件中添加依赖
  • 修改yml文件,配置允许访问所有监控端点,默认只开放了3个
management:
  endpoints:
    web:
      exposure:
        include: "*"
  • 这个时候项目启动后浏览器访问时,项目路径+/actuator/mappings,然后在里面可以搜索到所有的当前项目的requestmapping的映射
  • 常用的监控信息
/actuator/beans 显示应用程序中所有spring中的bean
/actuator/configprops 显示所有的配置信息
/actuator/env 显示所有的环境变量
/actuator/mappings 显示所有@RequestMapping的url列表
/actuator/health 显示程序的运行状况,会模拟访问配置文件中配置的mysql、redis等连接是否可用,不可用(控制台抛出异常,页面返回down)。可用页面返回up,控制台无输出
/actuator/info 查看在配置文件中自定义的信息,例如
info:
  name: 张三
  age: 15

使用AdminUi监控actuator信息

  • 定义一个服务端
        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-server</artifactId>
            <version>2.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <!-- Spring Boot Actuator对外暴露应用的监控信息,Jolokia提供使用HTTP接口获取JSON格式 的数据 -->
        <dependency>
            <groupId>org.jolokia</groupId>
            <artifactId>jolokia-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>com.googlecode.json-simple</groupId>
            <artifactId>json-simple</artifactId>
            <version>1.1</version>
        </dependency>
  • 修改配置文件
spring:
  application:
    name: spring-boot-admin-server
  • 创建客户端,将其注册到AdminUi服务端中去
 <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-client</artifactId>
            <version>2.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.jolokia</groupId>
            <artifactId>jolokia-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.googlecode.json-simple</groupId>
            <artifactId>json-simple</artifactId>
            <version>1.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
  • 修改配置文件
spring:
  boot:
    admin:
      client:
        url: http://localhost:8080
server:
  port: 8081
   
management:
  endpoints:
    web:
      exposure:
        include: "*"
  endpoint:
    health:
      show-details: ALWAYS
  • 项目启动,服务端使用
@Configuration
@EnableAutoConfiguration
@EnableAdminServer
  • 客户端使用
@SpingBootApplication

配置多数据源

  • 在application配置文件中配置2个dataSource,注意这里必须要用大驼峰命名
spring:
#  datasource:
#    username: root
#    password: root
#    url: jdbc:mysql://192.168.254.128:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
#    driver-class-name: com.mysql.jdbc.Driver
  datasource1:
    username: root
    password: root
    jdbcUrl: jdbc:mysql://192.168.254.128:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
    driverClassName: com.mysql.jdbc.Driver

  datasource2:
    username: root
    password: root
    jdbcUrl: jdbc:mysql://192.168.254.128:3306/test2?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
    driverClassName: com.mysql.jdbc.Driver
  • 配置2个配置类,用于给不用的包使用不同的DataSource,哪个@Primary是指定这个为主数据源
@Configuration
@MapperScan(basePackages = "com.it.test1.dao", sqlSessionFactoryRef = "test1SqlSessionFactory")
public class DataSourceConfig1 {

    @Bean("test1DataSource")
    @ConfigurationProperties("spring.datasource1")
    @Primary
    public DataSource test1DataSource(){
        return DataSourceBuilder.create().build();
    }


    @Bean("test1SqlSessionFactory")
    @Primary
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
        sqlSessionFactory.setDataSource(test1DataSource());
        //        sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().
//                getResources("classpath:mapper/test1/*Mapper.xml"));
        return sqlSessionFactory.getObject();
    }

    @Bean("test1DataSourceTransactionManager")
    @Primary
    public DataSourceTransactionManager transactionManager() {
        return new DataSourceTransactionManager(test1DataSource());
    }

    @Bean(name = "test1SqlSessionTemplate")
    @Primary
    public SqlSessionTemplate testSqlSessionTemplate() throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory());
    }

}
  • 开启事物、由于springboot本来就自带了事物,所以直接添加注解即可
@Transactional(transactionManager = "test2DataSourceTransactionManager")

传统分布式事物解决方案(比如在控制器中调用了2个不同数据源的Dao层,如果只是在控制器上配置了事物,则会出现事物无法一同回滚的情况)

  • 使用springboot + jta-atomikos 的方式来配置多数据源,由jta-atomikos来管理数据源。它会将本地事物转换成全局事物,大家都使用同一个事物
  • 引入依赖 spring-boot-starter-jta-atomikos
  • 还是跟之前一样在application配置文件中配置多个数据源
mysql.datasource.test2.url =jdbc:mysql://localhost:3306/test02?useUnicode=true&characterEncoding=utf-8
mysql.datasource.test2.username =root
mysql.datasource.test2.password =root

mysql.datasource.test2.minPoolSize = 3
mysql.datasource.test2.maxPoolSize = 25
mysql.datasource.test2.maxLifetime = 20000
mysql.datasource.test2.borrowConnectionTimeout = 30
mysql.datasource.test2.loginTimeout = 30
mysql.datasource.test2.maintenanceInterval = 60
mysql.datasource.test2.maxIdleTime = 60

  • 可以配置2个Entity类来注入配置文件的数据源。
@ConfigurationProperties(prefix = "mysql.datasource1")
  • 配置多数据源
@MapperScan(basePackages = "com.itmayiedu.test01", sqlSessionTemplateRef = "testSqlSessionTemplate")
public class MyBatisConfig1 {

	// 配置数据源
	@Primary
	@Bean(name = "testDataSource")
	// 这里的DBConfig1 就是上一步所说的装载的Bean,这里面有着数据源信息
	public DataSource testDataSource(DBConfig1 testConfig) throws SQLException {
		MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
		mysqlXaDataSource.setUrl(testConfig.getUrl());
		mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
		mysqlXaDataSource.setPassword(testConfig.getPassword());
		mysqlXaDataSource.setUser(testConfig.getUsername());
		mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);

		AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
		xaDataSource.setXaDataSource(mysqlXaDataSource);
		xaDataSource.setUniqueResourceName("testDataSource");

		xaDataSource.setMinPoolSize(testConfig.getMinPoolSize());
		xaDataSource.setMaxPoolSize(testConfig.getMaxPoolSize());
		xaDataSource.setMaxLifetime(testConfig.getMaxLifetime());
		xaDataSource.setBorrowConnectionTimeout(testConfig.getBorrowConnectionTimeout());
		xaDataSource.setLoginTimeout(testConfig.getLoginTimeout());
		xaDataSource.setMaintenanceInterval(testConfig.getMaintenanceInterval());
		xaDataSource.setMaxIdleTime(testConfig.getMaxIdleTime());
		xaDataSource.setTestQuery(testConfig.getTestQuery());
		return xaDataSource;
	}

	@Primary
	@Bean(name = "testSqlSessionFactory")
	public SqlSessionFactory testSqlSessionFactory(@Qualifier("testDataSource") DataSource dataSource)
			throws Exception {
		SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
		bean.setDataSource(dataSource);
		return bean.getObject();
	}

	@Primary
	@Bean(name = "testSqlSessionTemplate")
	public SqlSessionTemplate testSqlSessionTemplate(
			@Qualifier("testSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
		return new SqlSessionTemplate(sqlSessionFactory);
	}
}

项目打包运行

  • 在pom文件中加入如下配置
<build>
        <plugins>
            <!-- 指定jdk编译版本-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <!-- 这样就可以将项目打包成可运行的jar包 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
  • 在当前项目的根目录执行 mvn package
  • 然后会在target文件夹目录下看到一个jar包
  • 命令运行 java -jar jar包的全名.jar 即可运行

热部署实现原理

1、监听class文件是否发生改变。通过保存时间或者版本号
2、通过类加载器classloader 将修改后的class文件重新加载进jvm内存中。

热部署是否可以放在生产环境?

不可以,对性能不好。不安全。
应用场景:本地开发,提高开发效率,不需要重启服务器
缺点:如果项目比较大,就会很大,因为读取哪些字节码文件发生了改变,需要扫包。占用内存大。

springboot的devtools原理

原理就是监听保存,在你每次保存的时候帮你重启服务器

监听文件或文件夹的增删改查

@Component
public class MonitorInitializer implements ApplicationRunner {
    @Autowired
    private FileAlteration fileAlteration;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        FileAlterationObserver observer =
                new FileAlterationObserver("D:\\IdeaProjects\\spring-boot-demo\\target\\classes\\com\\it\\controller");
        observer.addListener(fileAlteration);
        FileAlterationMonitor monitor = new FileAlterationMonitor(1000); // 监听频率
        monitor.addObserver(observer);
        monitor.start();
    }
}

@Component
public class FileAlteration extends FileAlterationListenerAdaptor {

    @Override
    public void onFileCreate(File file) {
        System.out.println("创建了新的文件");
    }

    @Override
    public void onFileChange(File file) {
        System.out.println("文件发生了改变");
    }
}

常见错误

  • 如果将jar包或war包放到外部tomcat运行,出现版本不兼容问题。需要切换到更高版本的tomcat,springboot2.0以上使用的都是tomcat8.5及以上了
  • 使用springboot1.5版本打包后访问不到页面的,需要指定pom文件中的resources标签

常用注解

@ResponseBody

该注解是使用一个convert转换器来将返回的数据转换为Json格式,适用于方法上

@AfterReturning

在AOP切面类中使用,在方法执行后可以获取到拦截方法的返回值结果。

@Aspect
@Component
public class LogAop {
    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @Pointcut("execution(* com.it.controller.*.*(..))")
    public void logPointcut(){}

    @Before("logPointcut()")
    public void before(JoinPoint joinPoint) {
        log.info("日志系统开始记录日志");
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        log.info("当前访问的路径是:" + request.getRequestURL().toString());
        log.info("当前执行的方法是:" + request.getMethod());
        log.info("当前执行的方法的IP是:" + request.getRemoteAddr());
    }

    /**
     * ret这个名字随便取的
     * @param ret
     */
    @AfterReturning(returning = "ret",pointcut = "logPointcut()")
    public void doAfterReturning(Object ret) {
        log.info("返回的结果为 " + ret);
    }
}

@EnableAsync 开启异步,使@Async生效

@EnableAsync实现原理,开启了之后在springboot启动扫包的时候会扫描添加了@Controller,@Service 等等注入到了容器中的bean下的方法上的@Async注解。
@Async(添加到方法上):如果扫描到了这个注解,那么会利用aop技术为该类创建代理对象,其中会创建一条新的线程来执行这个方法。这样的话主线程就不会进入阻塞状态。

@Value

底层在创建bean对象的时候访问配置文件,从配置文件中取出对应的value然后通过反射给添加了@Value注解的属性赋值

@ControllerAdvice 增强控制器

底层也是使用aop技术实现的

@ControllerAdvice
@Slf4j
public class MyGlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public ModelAndView customException(Exception e) {
        //通常在这里记录日志,将日志存到nosql中
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        log.warn(request.getRequestURL().toString());

        ModelAndView mv = new ModelAndView();
        mv.addObject("message", e.getMessage());
        mv.setViewName("error");
        return mv;
    }

    @InitBinder("b")
    public void b(WebDataBinder binder) {
        //在控制器接收参数加上@ModelAttribute("b") 即可使用,这样传递的参数就需要加上前缀 b.
        binder.setFieldDefaultPrefix("b.");
    }

    // 定义全局属性
    @ModelAttribute(name = "md")
    public Map<String,Object> myData(ModelAndView mv) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("age", 20);
        map.put("gender", "男");
        return map;
    }

SpringBoot优化

扫包优化(提高启动速度)

	//项目中不使用@SpringBootApplication注解
	/*而是使用
	@SpringBootConfiguration 
	@EnableAutoConfiguration
	@ComponentScan(自行指定需要扫描的包)
	*/

JVM参数调优(影响到运行效果,例如吞吐量)

调优策略

  • 初始化堆大小与最大堆大小一致(可以减少gc回收次数)
  • 设置新生代的ende区与form/to的比例为2:1:1
  • 设置新生代与老年代的比例为 1:2 或者 1:3
  • 尽量使用并行回收器
  • 如果是并发量大的话,可以将tomcat服务器替换成Ontertow服务器
    替换的方式就是在spring-boot的web依赖包中去掉tomcat的依赖
    然后引入Ontertow的依赖
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
   <exclusions>
      <exclusion>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-tomcat</artifactId>
      </exclusion>
   </exclusions>
</dependency>

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

常见的jvm参数

//
-XX:+PrintGC      每次触发GC的时候打印相关日志
-XX:+UseSerialGC      串行回收
-XX:+PrintGCDetails  更详细的GC日志
-Xms               堆初始值
-Xmx               堆最大可用值
-Xmn               新生代堆最大可用值
-XX:SurvivorRatio  用来设置新生代中eden空间和from/to空间的比例.
-XX:NewRatio       配置新生代与老年代占比 1:2
含以-XX:SurvivorRatio=eden/from=den/to
总结:在实际工作中,我们可以直接将初始的堆大小与最大堆大小相等,
这样的好处是可以减少程序运行时垃圾回收次数,从而提高效率。
-XX:SurvivorRatio     用来设置新生代中eden空间和from/to空间的比例.

示例:

-Xms20m -Xmx20m  # 堆初始化大小20m,最大值20M
-XX:SurvivorRatio=2 # Ened区和from/to的比例为  2:1:1
-XX:+PrintGCDetails # 打印更详细的GC日志
-XX:+UseSerialGC # 使用串行GC回收
-XX:+UseParNewGC # 使用并行回收,其实就是串行回收的多线程版,新生代使用并行回收、老年代使用串行回收
XX:+USeParNewGC # 使用parnew收集器,这个收集器更关注系统的吞吐量。新生代使用复制算法、老年代使用标记-压缩
-XX:+UseConcMarkSweepGC # CMS收集器,
-XX:NewRatio=2 # 设置新生代与老年代的比例为2:1
-XX:+HeapDumpOnOutOfMemoryError 当堆空间溢出时生成dump文件(堆的内存快照),也可以指定路径 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/export/home/tomcat/logs/...


  • 并发与并行的区别
    并发:具备同时处理多件事的能力,但不能同时
    并行:同时处理多件事的能力
    区别就在于是否能够同时处理。
  • 内存溢出与内存泄漏的区别
    内存溢出:你申请的内存已经超过了堆的最大内存值,所以会出现内存溢出oom
    内存泄漏:指的是你程序申请到了内存空间之后,无法释放已经申请的内存空间,最后这种无法释放的空间越来越多,就造成了内存泄漏(例如你不关流)

springboot 底层实现原理

实现快速化整合第三方框架

  • 使用maven的子父依赖,直接引入第三方框架的所需jar包,意思就是将需要整合的环境的jar包封装好成一个依赖,导入一个依赖就导入了全部

完全无配置文件(注解方式)如何实现

  • spring 3.0 以上开始提供注解
  • spring内置注解加载整个SpringMvc容器 @EnableWebMvc
  • 使用java语言创建tomcat 来加载class文件运行
  • 手写类似springboot的效果
需要引入的依赖有
tomcat-embed-core java创建tomcat容器
tomcat-jasper tomcat支持JSP
tomcat-embed-core、spring-web、spring-webmvc、tomcat-jasper
@EnableWebMvc
@Configuration
@ComponentScan("com.it.controller")
public class MvcConfig extends WebMvcConfigurerAdapter {

    @Bean
    public ViewResolver viewResolver(){
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/pages/");
        resolver.setSuffix(".jsp");
        //可以在JSP页面中通过${}访问beans
        resolver.setExposeContextBeansAsAttributes(true);
        return resolver;
    }

}

@Configuration
@ComponentScan("com.it")
public class RootConfig {
}

public class SpringDispathServlet extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{RootConfig.class};
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{MvcConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{ "/" };
    }
}

@Controller
public class UserController {

    @GetMapping("/index")
    public String index() {
        return "index";
    }
}

public class MyTomcat {
    public static void main(String[] args) throws Exception {
        Tomcat tomcat = new Tomcat();
        tomcat.setPort(8082);

        File file = new File("src/main");
        //这里是直接通过扫描src/main生成的classes文件执行
        tomcat.addWebapp("springboot", file.getAbsolutePath());
        // 以下的代码是可以将classes创建到内存中,然后在内存中执行
        /*// 读取项目的路径
        StandardContext standardContext = (StandardContext) tomcat.addWebapp("springboot", file.getAbsolutePath());
        //禁止重新载入
        standardContext.setReloadable(false);

        //CLass文件读取地址
        File classesFile = new File("target/classes");
        //WebRoot
        WebResourceRoot resource = new StandardRoot(standardContext);

        resource.addPreResources(
                new DirResourceSet(resource,"/WEB-INF/classes",classesFile.getAbsolutePath(),"/")
        );*/
        tomcat.start();
        //异步等待执行的请求
        tomcat.getServer().await();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值