Vllos笔记-SpringBoot

#springboot :

1:是什么
2:配置如何编写
3:自动装配原理:重要!!!
4:集成web开发:业务的核心
5:集成数据库Druid
6:分布式开发:Dubbo(RPC)+zookeeper
7:swagger:接口文档
8:任务调度
9:SpringSecurity ; shiro  (做安全的,如登录验证。横向插入) 


简化Spring的

springboot就是一个Javaweb的开发框架,和SpringMVC类似,
对比其他Javaweb框架的好处,简化开发
,**约定大于配置**,能迅速开发web应用,几行代码开发一个http接口

springboot默认配置了很多框架的使用方法,整合了所有的框架。

优点:
	为所有Spring开发者更快的入门
	开箱即用,提供各种默认配置来简化项目配置
	内嵌式容器简化web项目
	没有冗余代码生成和xml配置的要求

#微服务:

是一种架构模式或架构风格 (如MVC也是架构风格,分成三种架构),

1:模块化:它提倡将单一的应用程序划分成一组小的服务;每个服务运行在其独立的自己的进程内,服务之间互相协调、互相配置,为用户提供最终价值。
2:通过http、RPC框架等方式进行互通:服务之间采用轻量级的通信机制互相沟通,每个服务都围绕具体业务进行构建,并且能被独立部署到生产环境中。
3:分布式:另外,应尽量避免统一的、集中式的服务管理机制,
对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具对其进行构建,
可以有一个非常轻量级的集中式管理来协调这些服务,
可以使用不同的语言来编写服务,也可以使用不同的数据存储。



原始架构all in one方式:把所有的功能单元放在一个应用里,然后把整个应用部署到服务器上,
如果负载能力不行,将整个应用进行水平复制,进行拓展,然后再负载均衡。
缺点:修改一点就需要停掉整个服务,大型应用不可能把所有内容都放在一个应用里,
	  维护,分工合作问题

微服务架构:把每个功能元素独立出来,自由动态组合,需要的功能元素才拿来组合。所以微服务架构是对功能元素进行复制,而没有对整个应用进行复制。

	好处:
		节省了调用资源
		每个功能元素的服务都是一个可替换的、可独立升级的软件代码

	缺点:

		开发人员要处理分布式系统的复杂性
		多服务运维难度,随着服务的增加,运维的压力也在增大
		系统部署依赖
		服务间通信成本
		数据一致性
		系统集成测试
		性能监控

微服务技术栈:

		服务开发 --------------->     SpringBoot、Spring、SpringMVC
		服务配置与管理 --------------->     Netfix公司的Archaius、阿里的Diamond等
		服务注册与发现 --------------->     Eureka、Consul、Zookeeper等
		服务调用 --------------->     Rest、PRC、gRPC
		服务熔断器 --------------->     Hystrix、Envoy等
		负载均衡 --------------->     Ribbon、Nginx等
		服务接口调用(客户端调用服务的简化工具) --------------->     Feign等
		消息队列 --------------->     Kafka、RabbitMQ、ActiveMQ等
		服务配置中心管理 --------------->     SpringCloudConfig、Chef等
		服务路由(API网关) --------------->     Zuul等
		服务监控 --------------->     Zabbix、Nagios、Metrics、Specatator等
		全链路追踪 --------------->     Zipkin、Brave、Dapper等
		服务部署 --------------->     Docker、OpenStack、Kubernetes等
		数据流操作开发包 --------------->     SpringCloud Stream(封装与Redis、Rabbit、kafka等发送接收消息)
		事件消息总线 --------------->     SpringCloud Bus










论文:
原文:http://martinfowler.com/articles/microservices.html
译文:http://www.cnblogs.com/liuning8023/p/4493156.html



**Spring为我们带来了构建大型分布式微服务的全套、全程产品**:
		构建一个个功能独立的微服务应用单元,可以使用**springboot**,可以帮我们快速构建一个应用
		大型分布式网络服务的调用,这部分由**springcloud**完成,实现分布式
		在分布式中间,进行流式数据计算、批处理,我们有**spring cloud data flow**
		spring为我们想清楚了整个从开始构建应用到大型分布式应用全流程方案

创建第一个springboot程序

第一种:在官网下载jar包导入IDEA
第二种:直接在IDEA创建:
	IDEA中新建项目Spring Initializr
	选择依赖dependencies:web-->spring web

自动装配原理

pom.xml
	*spring-boot-dependencies : 核心依赖在父工程中
	*我们在写或者引入一些springboot依赖的时候,不需要指定版本,就因为有这些版本仓库

启动器:
		就是springboot的启动场景。比如spring-boot-starter-web,他就会自动导入web环境所有的依赖
		springboot会将所有的功能场景,都变成一个个启动器

主程序:包括注解和run

		注解:
			@SpringbootApplication  : 标注这个类是一个springboot的应用:启动类下的所有资源被导入
				@SpringBootConfiguration : springboot的配置
							@Configuration:spring配置类
							@Component:说明这也是一个spring的组件
				@EnableAutoConfiguration : 自动配置
							@AutoConfigurationPackage :自动配置包
								@Import(AutoConfigurationPackages.Registrar.class) :自动配置 包注册
							@Import
							(AutoConfigurationImportSelector.class) :自动导入包的核心--
				
								AutoConfigurationImportSelector:自动导入选择器,选择了什么东西--

									getAutoConfigurationEntry():获得自动配置的实体,调用了下面获取候选配置

									getCandidateConfigurations() :获取候选的配置----

											protected Class getSpringFactoriesLoaderFactoryClass(){
     
													return EnableAutoConfiguration.class;
													//标注了EnableAutoConfiguration注解的类
											}

									public static List loadFactoryNames() :获取所有的加载配置
									loadSpringFactories() --

										项目资源:classLoader.getResources(FACTORIES_RESOURCE_LOCATION)--
													**“META-INF/spring.factories ” :从这里获取配置**
												--在spring-boot-autoconfigure-2.2.0.RELEASE.jar包里
												---**META-INF/spring.factories:所有的自动配置类都在这里了**!!!---
															思考:为什么这么多自动配置为什么有的没有生效?需要导入对应的start才能有作用---
																核心注解:@ConditionalOnXXX:如果这里面的条件都满足,才会生效
										系统资源:ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)
										从这些资源中遍历了所有的nexrElement(自动配置),遍历完成后封装为properties供我们使用
				@ComponentScan :扫描当前主启动类同级的包
		结论:springboot所有的自动配置都是在启动的时候扫描并加载:spring.factories所有的自动配置类都在这里面,
				但是不一定生效,要判断条件是否成立,只要导入了对应的start,
				就有对应的启动器了,有了启动器,我们自动装配就会生效,然后就配置成功了。

				1:springboot在启动的时候,从类路径下/META-INF/spring.factories获取指定的值
				2:将这些自动配置的类导入容器,自动配置就会生效
				3:整个JavaEE,解决方案和自动配置的东西都在spring-boot-autoconfigure-2.2.0.RELEASE.jar这个包下
				4:它会把所有需要导入的组件,以类名的方式返回,这些组件就会被添加到容器
				5:容器中也会存在非常多的xxxAutoConfiguration的文件(@Bean),
					就是这些类给容器中导入了这个场景需要的所有组件,并自动配置,@Configuration,javaConfig
				6:有了自动配置类,免去了我们手动编写配置文件的工作

		run:
			SpringApplication类和run方法
				*SpringApplication.run(Springboot01HelloworldApplication.class,args);
				SpringApplication这个类做了四个事情:
						1:推断应用的类型是普通项目还是web项目
						2:查找并加载所有可用初始化器,设置到initializers属性中
						3:找出所有的应用程序监听器,设置到listeners属性中
						4:推断并设置main方法的定义类,找到运行的主类
				run方法

springboot的主配置文件:

			application.properties     	语法:key=value
			官方推荐改成application.yaml	语法:key 空格 value
						配置文件的作用:修改springboot自动配置的默认值,
										因为springboot在底层都给我们自动配置好了。

				yaml语法:          
						对空格要求高!注入到我们的配置类中

						*普通的key-value:
							name: vllos
						*对象:
							student:
								name: vllos
								age:3
						*行内写法:
							student: {name:vllos,age:3}
						*数组:
							pets:
							 -cat
							 -dog
							 -pig
							
							pets: [cat,dog,pig]

					相较于properties语法:只能存键值对
						student.name = vllos
						student.age = 3

				yaml:给属性赋值的方式:
									建实体类Person在里面加注解:
									@Component:注册bean
									@ConfigurationProperties(prefix = "person")			
									//通过这个注解把实体类Person和.yaml里面的参数person绑定起来   
										爆红的解决:添加依赖到pom.xml
									.......
												*@ConfigurationProperties作用:将配置文件中配置的每一个属性的值,映射到这个组件中;
													告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定   
													参数:prefix = "person" :
															将配置文件中的person下面的所有属性一一对应
													只有这个组件是容器中的组件,才能使用容器提供的
													@ConfigurationProperties功能

							yaml里:
									
							建测试类:
									@Atutowired
									privatre Person person;
		
									@Test
									.....
						结论:配置yaml和配置properties都可以获取到值,在某个业务中,
								只需要获取配置文件中的某个值,可以使用@value,
								如果专门编写了一个JavaBean来和配置文件进行映射,
								就直接使用@congifurationProperties


						yaml支持松散绑定:比如我的yaml中写的last-name,这个和lastName是一样的,-后面跟着的字母默认是大写的,这就是松散绑定。

						yaml支持JSR303数据效验:这个就是我们可以在字段是增加一层过滤器验证,可以保证数据的合法性
								@validated  :spring-boot中可以使用@validated来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理。
										如:加上@Email(),下面的值都得是邮箱格式。或者@Email("邮箱格式错误")
											JSR303格式可以百度查找

	
						yaml支持复杂类型封装:yaml中可以封装对象,使用properties对应的@value就不支持。
			

					主配置文件.yaml可以存在的位置(4种):
							file:./config    		//项目路径下的config文件夹配置文件
							file:./			 		//项目路径下配置文件
							classpath:/config/		//资源路径下的config文件夹配置文件
							classpasth:/			//资源路径下配置文件
								优先级:由高到低
					多环境切换:
						profile是spring对不同环境提供不同配置功能的支持,可以通过激活不同的环境版本,实现快速切换环境
						方式一:多配置文件,用.proeprties
							我们在主配置文件编写的时候,文件名可以是application-{profile}.properties/yml,用来指定多个环境版本。
								例如:application-test.properties代表测试环境配置  application-dev.properties代表开发环境配置
								但是springboot并不会直接启动这些配置文件,它默认使用application.properties主配置文件;
								我们需要通过一个配置来选择需要激活的环境

									比如在配置文件中指定使用dev环境,我们可以通过设置不同的端口号进行测试;
									我们启动springooot,就可以看到已经切换到dev下的配置了;
									spring.profiles.active = dev	


								server.port:8081
						方式二:yml的多文档块
							和properties配置文件一样,但是不用去创建多个配置文件,更加方便

							直接在一个配置文件里写多个环境:
								server:
								 port: 8081        //主配置文件
								spring:
								 profiles:
									active:dev     //主配值文件用dev环境
								---                //分割线
								server:
								 port: 8082
								spring:
								 profiles: dev     //dev配置环境
								---
								server:
								 port: 8083
								spring:
								 profiles: test	   //test配置环境

自动装配原理再理解
配置文件—联系—spring.factories

	在我们这个配置文件中能配置的东西,都存在一个固有的规律:
	xxxAutoConfiguration:默认值   xxxProperties 和配置文件绑定,我们就可以使用自定义的配置了。

总结:
	1:springboot启动会加载大量的自动配置类
	2:我们看我们需要的功能有没有再springboot默认写好的自动配置类中
	3:我们再来看这个自动配置类中到底配置了哪些组件(只要我们要用的组件存在其中,我们就不需要再手动配置了)
	4:给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们只需要在配置文件中指定这些属性的值即可;
		xxxxAutoConfiguration :自动配置类;给容器中添加组件
		xxxxProperties:封装配置文件中相关属性

可以通过debug:true来查看,哪些自动配置生效了,哪些没有生效!

	springboot到底帮我们配置了什么?
			xxxxAutoConfiguration..向容器中自动配置组件
	我们能不能进行修改?扩展?
			xxxxProperties:自动配置类,装配配置文件中自定义的一些内容

SpringBoot Web开发:
要解决的问题:
导入静态资源
首页
jsp,模板引擎Thymeleaf
装配扩展SpringMVC
增删改查
拦截器
国际化


新建IDEA项目,建vllos\controller\HelloController.java
					@RestController
					public class HelloController{
     
						@GetMapping("/hello")
						public String hello(){
     
							return "hello,world";
						}
					}

浏览器打开:
		localhost:8080\hello
扒源码:在***WebMvcAutoConfiguration.java***下找

1:导入静态资源:
		静态资源目录有四个:public resources static templates
			在templates目录下的所有页面,只能通过controller来跳转和需要模板引擎的支持thymeleaf

		在springboot,我们可以使用以下方式处理静态资源
			webjars(不推荐)				localhost:8080/webjars
			public、static、resources  可以把静态资源放在这三个目录下,都在resources目录下  localhost:8080访问 

		优先级:resources>static(默认)>public    
			
2:首页定制:
		在static\index.html

	图标定制:copy 图片.ico 到public或者static下试试   访问localhost:8080
			在application.properties中写 spring.mvc.favicon.enable=false


3:thymeleaf模板引擎:
	前端交给我们的页面,是html页面。如果是我们以前开发,需要把它们转成jsp页面,
	jsp页面的好处就是当我们查出一些数据转发到jsp页面以后,我们可以用jsp轻松实现数据的显示,及交互等。
	jsp支持非常强大的功能,包括能写java代码,但是,我们现在的情况:springboot这个项目首先是以jar的方式,不是war。
	第二我们用的还是嵌入式的tomcat,所以默认是不支持jsp的。
	不支持jsp,如果我们直接用纯静态页面的方式,那给我们开发会带来非常大的麻烦,
	所以springboot推荐使用模板引擎。模板引擎很多,jsp就是一个模板引擎,还有用的多的freemarker,包括thymeleaf

	引入Thymeleaf依赖:
			https://docs.spring.io/spring-boot/docs/2.3.2.RELEASE/reference/htmlsingle/

			<!--thymeleaf--->
			<dependency>
				<groupId>org.thymeleaf</groupId>
				<artifactId>thymeleaf-spring5</artifactId>
			</dependency>
			<dependency>
				<groupId>org.thymeleaf.extras</groupId>
				<artifactId>thymeleaf-extras-java9time</artifactId>
			</dependency>
	写在templates下面!
		格式.html
.html中:
					<html lang="en" xmlns:th="http://www.thymeleaf.org">    //导入约束
				语法:所有的html元素都可以被thymeleaf替换接管: th:元素名
						如:
						<div th:text="${msg}"></div>	
						<h3 th:each="user:${users}" th:text="${user}"></h3>	
							建vllos\controller\IndexController.java
								@Controller
								public class IndexController{
     
									@RequestMapping("/test")
									public String test(Model model){
     
										model.addAttribute("msg","

hello,springboot

"
); model.addAttribute("users",Arrays.asList("vllos","qintianzhu")); return "test"; } }
	结论:只要需要使用thymeleaf,只需要导入对应的依赖就可以了。我们将html放在我们的templates目录下即可。



4:扩展装配springMVC    javaconfig
	在测试前,我们需要知道springboot对我们的springMVC还做了哪些配置,包括如何扩展、如何定制。途径1:源码分析  途径2:官方文档  
		地址:https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/boot-features-
			developing-web-applications.html

	如果要**扩展springMVC**:
		官方建议我们这样做:
			**@Configuration**  //只要标注这个注解就可以

			如:
					@Configuration
					public class MyMvcConfig implements WebMvcConfigurer {
     
						
						//视图跳转
						@Override
						public void addViewControllers(ViewControllerRegistry registry) {
     
							registry.addViewController("/vllos").setViewName("test");
						}
					}	

员工管理系统

	 百度:bootstarap模板

	1:首页配置:
				1:注意点:所有页面的静态资源都需要使用thymeleaf接管
				2:url:·\@{}

	2:页面国际化:
				1:我们需要配置i18n文件
				2:我们如果需要在项目中进行按钮自动切换,我们需要自定义一个组件LocaleResovler
				3:记得将自己写的组件配置到spring容器  @Bean
				4:#{}

	3:登录功能+拦截器

	4:员工列表展示
		1:提取公共页面
			1:th:fragment="sidebar"
			2:
3:如果要传递参数,可以直接使用()传参,接收判断即可 2:列表循环展示 5:添加员工 1:按钮提交 2:跳转到添加页面 3:添加员工成功 4:返回首页 6:CRUD 7:404 前端: 模板:别人写好的拿来改成自己需要的 百度:bootstrap后端模板 框架:组件:自己组合拼接 Bootstrap,Layui,semantic-ui 后台模板:x-admin百度 1:有一套自己熟悉的前端后台模板:工作必要!x-admin 2:前端界面:至少自己能够通过前端框架,组合出来一个网站页面 -index -about -blog -post -user 3:让这个网站能够独立运行

#如何快速搭建一个web应用:

1:前端搞定:

2:设计数据库 (数据库设计难点!)

3:前端能够自动运行,独立化工程

4:数据接口对接:json

5:前后端联调测试


JDBC

Mybatis :(重点)

Druid :(重点)

Shiro :安全 (重点)

Spring Security :安全 (重点)

异步任务,邮件发送,定时任务

Swagger

Dubbo+Zookeeper

对于数据访问层,无论是SQL还是NOSQL,SpringBoot底层都是采用Spring Data的方式进行统一处理。SpringData也是Spring中与Springboot、SpringCloud齐名的知名软件


###整合JDBC

1:导依赖

		<!--web-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
			<version>2.3.2.RELEASE</version>
		</dependency>

		<!--JDBC-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>

		<!--MySQL-->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		

2.连接MySQL

3.application.yaml

	spring:
	  datasource:
	    username: root
	    password: root
	    driver-class-name: com.mysql.cj.jdbc.Driver
	    #假如时区报错,就增加一个时区的配置就OK了:severTimezone=Asia/Shanghai
	    url: jdbc:mysql://localhost:3306/mybatis?userUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai

4:test/Springboot04DataApplicationTests.java


	@SpringBootTest
	class Springboot04DataApplicationTests {
     
	
		@Qualifier("dataSource")
		@Autowired
		DataSource dataSource;
	
		@Test
		void contextLoads() throws SQLException {
     
			//查看一下默认的数据源:class com.zaxxer.hikari.HikariDataSource
			System.out.println(dataSource.getClass());
	
			//获得数据库连接
			Connection connection = dataSource.getConnection();
			System.out.println(connection);
	
			//xxxxTemplate:SpringBoot已经配置好模板bean,拿来即用  CRUD
	
	
	
			//关闭
			connection.close();
		}
	
	}

5:vllos/controller/JDBCController.java

	@RestController
	public class JDBCController {
     
	
		@Autowired
		JdbcTemplate jdbcTemplate;
	
		//查询数据库的所有信息
		//没有实体类,数据库中的东西,怎么获取?  Map
		@GetMapping("/userList")
		public List<Map<String ,Object>> userList(){
     
			String sql = "select * from user";
			List<Map<String, Object>> list_maps = jdbcTemplate.queryForList(sql);
			return  list_maps;
		}
	
		@GetMapping("/addUser")
		public String addUser(){
     
			String sql = "insert into mybatis.user(id,username,birthday,sex,address) 
							values(51,'薛凯琪',2018-03-08 11:44:00,'女'','香港')";
			jdbcTemplate.update(sql);
			return  "update-ok";
		}
	
		@GetMapping("/updateUser/{id}")
		public String updateUser(@PathVariable("id") int id){
     
			String sql = "UPDATE mybatis.user SET username=?,birthday=?,sex=?,address=? where id="+id;
	
			//封装
			Object[] objects = new Object[4];
			objects[0] = "邓紫棋";
			objects[1] = "2019-03-08 11:44:00";
			objects[2] = "女";
			objects[3] = "香港";
	
			jdbcTemplate.update(sql);
			return  "updateUser-ok";
		}
	
		@GetMapping("/deleteUser/{id}")
		public String deleteUser(@PathVariable("id") int id){
     
			String sql = "delete from mybatis.user where id =?";
			jdbcTemplate.update(sql,id);
			return  "deleteUser-ok";
		}
	}

6:浏览器访问:

	浏览器访问:localhost:8080
	浏览器访问:localhost:8080/updateUser
....

###整合Druid数据源

druid数据库连接池结合了C3P0、DBCP等DB池的优点,同时加入了日志监控。
可以很好的监控DB池连接和SQL的执行情况。
Springboot2.0以上默认使用Hikari数据源,可以说Hikari与Driud都是当前JavaWeb上最优秀的数据源。
(HikariDataSource号称JavaWeb当前速度最快的数据源,相比于传统的C3P0、DBCP、Tomcatjdbc等连接池更加优秀)

1:引入数据源,添加依赖,可以从Maven仓库官网Maven Repository中获取

	<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
	<dependency>
		<groupId>com.alibaba</groupId>
		<artifactId>druid</artifactId>
		<version>1.1.21</version>
	</dependency>

2:appolication.yaml添加

	type:com.alibaba.druid.pool.DruidDataSource
	
	#SpringBoot默认是不注入这些属性值的,需要自己绑定
	#Druid数据源专有配置
	initialSize:5
	minIdle:5
	maxActive:20
	maxWait:60000
	timeBetweenEvictionRunsMillis:60000
	minEvictableIdleTimeMillis:300000
	validationQuery:SELECT 1 FROM DUAL
	testWhileIdle:true
	testOnBorrow:false
	testOnReturn:false
	poolPreparedStatements:true
	
	#配置监控统计拦截的filters:  stat:监控统计、log4j:日志记录、wall:防御sql注入
	#如果允许时报错  java.lang.ClassNotFoundException:org.apache.log4j.Priority
	#则导入log4j 依赖即可,Maven地址:https://mvnrepository.com/artifact/log4j/log4j
	filters:stat,wall,log4j
	maxPoolPreparedStatementPerConnectionSize:20
	useGlobalDataSourceStat:true
	connectionProperties:druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

3:导入log4j依赖

4:vllos/config/DruidConfig.java

@Configuration
public class DruidConfig{
     
	
	@ConfigurationProperties(prefix = "spring.datasource")
	@Bean
	public DataSource druidDataSource(){
     
		return new DruidDataSource();
	}
	
	//后台监控:相当于web.xml, ServletRegistrationBean
	//因为spring boot内置了servlet容器,所以没有web.xml,替代方法:ServletRegistrationBean
	@Bean
	public ServletRegistrationBean statViewServlet(){
     
		ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(),"/druid/*");
	
		//后台需要有人登陆,账号密码配置
		HashMap<String,String> initParameters =new HashMap<>();
		
		//增加配置
		initParameters.put("loginUsername","admin");//登录key 是固定的
		initParameters.put("loginPassword","123456");
		
		//允许谁可以访问
		initParameters.put("allow","");
		
		//禁止谁能访问
		//initParameters.put("","");
		
		bean.setInitParameters(initParameters);//设置初始化参数
		return bean;
		
	}
	
	
	//filter
	@Bean
	public FilterRegistrationBean webStatFilter(){
     
		FilterRegistrationBean bean = new FilterRegistrationBean();
		bean.setFilter(new WebStatFilter());
		
		//可以过滤哪些请求?
		Map<String,String> initParameters = new HashMap<>();
		//这些东西不进行统计
		initParameters.put("exclusions","*.ji*.css,/druid/*");
		bean.setInitParameters(initParameters);
		return bean;
	}
}

###整合Mybatis

整合包:mybatisi-spring-boot-starter

1:导依赖

	<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
	<dependency>
		<groupId>org.mybatis.spring.boot</groupId>
		<artifactId>mybatis-spring-boot-starter</artifactId>
		<version>2.1.1</version>
	</dependency>

2: application.properties

	spring.datasource.username=root
	spring.datasource.password=root
	spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=Asia/Shanghai&userUnicode=true&characterEncoding=utf-8
	spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

3:连接mysql

4:导Lombok依赖

5:vllos/pojo/User.java

	//实体类
	@Data
	@NoArgsConstructor
	@AllArgsConstructor
	public class User{
     
		private int id;
		private String username;
		private Date birthday;
		private boolean sex;
		private String address;
		
	}
	//id,username,birthday,sex,address
	//(51,'薛凯琪',2018-03-08 11:44:00,'女'','香港')";

6:vllos/mapper/UserMapper.java --interface

	@Mapper  //这个注解表示这是一个mybatis的mapper类
	@Repository
	public interface UserMapper{
     
		
		List<User> queryUserList();
		
		User queryUserById(int id);
		
		int addUser(User user);
		
		int updateUser(User user);
		
		int deleteUser(int id);
	}     

7:在application.properties中整合mybatis

	//  #整合mybatis
	mybatis.type-aliases-package=com.vllos.pojo
	mybatis.mapper-localtions=classpath:mybatis/mapper/*.xml

8:在resources/mybatis/mapper/UserMapper.xml

	//去mybatis中文文档找
	<?xml version="1.0" encoding="UTF-8" ?>
	<!DOCTYPE mapper
	  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
	  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
	<mapper namespace="com.vllos.mapper.UserMMapper">       //这里要绑上自己的UserMapper
	  
	  <select id="queryUserList" resultType="User" useCache="true"> //useCache="true":开启缓存
		select * from user
	  </select>
	  
	  <select id="queryUserById" resultType="User">
		select * from user where id = #{
     id}
	  </select>
	  
	  <insert id="addUser" parameterType="User">
		insert into user (id,username,birthday,sex,address) values(#{
     id},#{
     username},#{
     birthday},#{
     sex},#{
     address})
	  </insert>
	  
	  <update id="updateUser" parameterType="User">
		update user set username=#{
     username},birthday=#{
     birthday},sex=#{
     sex},address=#{
     address} where id = #{
     id}
	  </update>
	  
	  <delete id="deleteUser" parameterType="User">
		delete from user where id = #{
     id}
	  </delete>
	
	</mapper>

9:业务层

10:vllos/controller/UserController.java //controller层就是整合数据,前端来这里拿

	@RestController
	public class UserController {
     
		
		@Autowired
		private UserMapper userMapper;
		
		@GetMapping("queryUserList")
		public List<User> queryUserList(){
     
			List<User> userList = userMapper.queryUserList();
			return userList;
		}
		
		@GetMapping("/addUser")
		public String addUser(){
     
			userMapper.addUser(new User(51,'薛凯琪',2018-03-08 11:44:00,'女'','香港'));
			return  "ok";
		}
		
		@GetMapping("/updateUser")
		public String updateUser(){
     
			userMapper.updateUser(new User(51,'薛凯琪',2018-03-08 11:44:00,'女'','香港'));
			return  "ok";
		}
		
		@GetMapping("/deleteUser")
		public String deleteUser(){
     
			userMapper.deleteUser(51);
			return  "ok";
		}
	}

总结:

步骤:

1:导包
2:配置文件
3;mybatis配置
4:编写sql
5:业务层调用dao层
6:controller调用service层

M:数据和业务
V:HTML
C:交接


###SpringSecurity(安全)
框架
在web开发中,安全第一位 过滤器 拦截器

做网站:安全应该在设计之初就得考虑

authentication 、 access-contrl

功能权限
访问权限
菜单权限
以前用拦截器、过滤器来实现,有大量的原生代码~ 冗余

SpringSecurity是针对spring项目的安全框架,也是spring boot底层安全模块默认的技术选型,
他可以实现强大的web安全控制,对于安全控制,我们仅需要引入spring-boot-starter-security模块,进行少量的配置
即可实现强大的安全管理。

记住这几个类:
WebSecurityConfigurerAdapter:自定义Security策略
AuthenticationManagerBuilder:自定义认证策略
@EnableWebSecurity:开启WebSecurity模式

SpringSecurity的两个主要目标是“认证”和“授权”(访问控制)
“认证”(Authentication)
“授权”(Authorization)
这个概念是通用的,而不是只在SpringSecurity中存在

前期准备:

1:引入Thymeleaf依赖:

2:导入静态资源

3:application.properties

spring.thymeleaf.cache=false

4:vllos/controller/RouterController.java


	@Controller
	public class RouterController {
     

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

		@RequestMapping("/toLogin")
		public String toLogin(){
     
			return "views/login";
		}

		@RequestMapping("/level1/{id}")
		public String level1(@PathVariable("id") int id){
     
			return "views/level1"+id;
		}

		@RequestMapping("/level2/{id}")
		public String level2(@PathVariable("id") int id){
     
			return "views/level2"+id;
		}

		@RequestMapping("/level3/{id}")
		public String level3(@PathVariable("id") int id){
     
			return "views/level1"+id;
		}

	}

1:导入Security依赖

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

2:vllos/config/SecurityConfig.java

	//这里用的是AOP横切  相当于拦截器,但是比拦截器简单多了

	@EnableWebSecurity
	public class SecurityConfig  extends WebSecurityConfigurerAdapter {
     


		//链式编程
		//授权:HttpSecurity
		@Override
		protected void configure(HttpSecurity http ) throws Exception {
     
			//首页所有人可以访问,功能页只有对应有权限的人才能访问
			http.authorizeRequests()
					.antMatchers("/").permitAll()
					.antMatchers("/level1/**").hasRole("vip1")
					.antMatchers("/level2/**").hasRole("vip2")
					.antMatchers("/level3/**").hasRole("vip3");

			//没有权限默认会导登录页面,需要开启登录的页面
			//定制登录页面:loginPage("/toLogin")
			http.formLogin().loginPage("/toLogin").usernameParameter("user").passwordParameter("pwd").loginProcessingUrl("/login");

			//防止网站攻击:csrf 默认是开启的
			http.csrf().disable();//关闭csrf功能,登录失败可能存在的原因


			//注销,开启了注销功能
			http.logout().logoutSuccessUrl("/");

			//开启记住我功能:cookie  默认保存两周
			http.rememberMe();

		}

		//认证:springboot 2.1.x 可以直接使用
		//密码编码:PasswordEncoder
		//在Spring Secutiry5.0+ 新增了很多的加密方法
		@Override
		protected void configure(AuthenticationManagerBuilder auth) throws Exception{
     

			//这些数据正常应该从数据库中读
			auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
					.withUser("vllos").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3")
					.and()
					.withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3")
					.and()
					.withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1");



		}
	}

3:导入security-thymeleaf整合包

	<!--security-thymeleaf整合包-->
	<dependency>
		<groupId>org.thymeleaf.extras</groupId>
		<artifactId>thymeleaf-extras-springsecurity4</artifactId>
		<version>3.0.4.RELEASE</version>
	</dependency>

4:pom.xml里项目中

添加:
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
spring-boot-starter-parent降版本:
2.0.9

5:改index.html

	<!DOCTYPE html>
	<html lang="en" xmlns:th="http://www.thymeleaf.org"
		  xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
		<title>首页</title>
		<!--semantic-ui-->
		<link href="https://cdn.bootcss.com/semantic-ui/2.4.1/semantic.min.css" rel="stylesheet">
		<link th:href="@{/qinjiang/css/qinstyle.css}" rel="stylesheet">
	</head>
	<body>

	<!--主容器-->
	<div class="ui container">

		<div class="ui segment" id="index-header-nav" th:fragment="nav-menu">
			<div class="ui secondary menu">
				<a class="item"  th:href="@{/index}">首页</a>

				<!--登录注销-->
				<div class="right menu">

					<!--如果未登录-->
					<div sec:authorize="!isAuthenticated()">
						<a class="item" th:href="@{/toLogin}">
							<i class="address card icon"></i> 登录
						</a>
					</div>

					<!--如果登录了:显示用户名、注销-->
					<div sec:authorize="!isAuthenticated()">
						<a class="item">
							用户名:<span sec:authentication="name"></span>
							<!--角色:<span sec:authentication=""></span>-->
						</a>
					</div>
					<div sec:authorize="!isAuthenticated()">
						<a class="item" th:href="@{/logout}">
							<i class="sign-out icon"></i> 注销
						</a>
					</div>

					<!--已登录
					<a th:href="@{/usr/toUserCenter}">
						<i class="address card icon"></i> admin
					</a>
					-->
				</div>
			</div>
		</div>

		<div class="ui segment" style="text-align: center">
			<h3>Spring Security Study by 秦疆</h3>
		</div>

		<div>
			<br>
			<div class="ui three column stackable grid">
				<!--菜单根据用户的角色动态的实现-->
				<div class="column" sec:authorize="hasRole('vip1')">
					<div class="ui raised segment">
						<div class="ui">
							<div class="content">
								<h5 class="content">Level 1</h5>
								<hr>
								<div><a th:href="@{/level1/1}"><i class="bullhorn icon"></i> Level-1-1</a></div>
								<div><a th:href="@{/level1/2}"><i class="bullhorn icon"></i> Level-1-2</a></div>
								<div><a th:href="@{/level1/3}"><i class="bullhorn icon"></i> Level-1-3</a></div>
							</div>
						</div>
					</div>
				</div>

				<div class="column" sec:authorize="hasRole('vip2')">
					<div class="ui raised segment">
						<div class="ui">
							<div class="content">
								<h5 class="content">Level 2</h5>
								<hr>
								<div><a th:href="@{/level2/1}"><i class="bullhorn icon"></i> Level-2-1</a></div>
								<div><a th:href="@{/level2/2}"><i class="bullhorn icon"></i> Level-2-2</a></div>
								<div><a th:href="@{/level2/3}"><i class="bullhorn icon"></i> Level-2-3</a></div>
							</div>
						</div>
					</div>
				</div>

				<div class="column" sec:authorize="hasRole('vip3')">
					<div class="ui raised segment">
						<div class="ui">
							<div class="content">
								<h5 class="content">Level 3</h5>
								<hr>
								<div><a th:href="@{/level3/1}"><i class="bullhorn icon"></i> Level-3-1</a></div>
								<div><a th:href="@{/level3/2}"><i class="bullhorn icon"></i> Level-3-2</a></div>
								<div><a th:href="@{/level3/3}"><i class="bullhorn icon"></i> Level-3-3</a></div>
							</div>
						</div>
					</div>
				</div>

			</div>
		</div>
		
	</div>


	<script th:src="@{/qinjiang/js/jquery-3.1.1.min.js}"></script>
	<script th:src="@{/qinjiang/js/semantic.min.js}"></script>

	</body>
	</html>

6:改login.xml

	<!DOCTYPE html>
	<html lang="en" xmlns:th="http://www.thymeleaf.org">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
		<title>登录</title>
		<!--semantic-ui-->
		<link href="https://cdn.bootcss.com/semantic-ui/2.4.1/semantic.min.css" rel="stylesheet">
	</head>
	<body>

	<!--主容器-->
	<div class="ui container">

		<div class="ui segment">

			<div style="text-align: center">
				<h1 class="header">登录</h1>
			</div>

			<div class="ui placeholder segment">
				<div class="ui column very relaxed stackable grid">
					<div class="column">
						<div class="ui form">
							<form th:action="@{/login}" method="post">
								<div class="field">
									<label>Username</label>
									<div class="ui left icon input">
										<input type="text" placeholder="Username" name="user">
										<i class="user icon"></i>
									</div>
								</div>
								<div class="field">
									<label>Password</label>
									<div class="ui left icon input">
										<input type="password" name="pwd">
										<i class="lock icon"></i>
									</div>
								</div>
								<div class="field">
									<input type="checkbox" name="remember">记住我
								</div>
								<input type="submit" class="ui blue submit button"/>
							</form>
						</div>
					</div>
				</div>
			</div>

			<div style="text-align: center">
				<div class="ui label">
					</i>注册
				</div>
				<br><br>
				<small>blog.kuangstudy.com</small>
			</div>
			<div class="ui segment" style="text-align: center">
				<h3>Spring Security Study by 秦疆</h3>
			</div>
		</div>


	</div>

	<script th:src="@{/qinjiang/js/jquery-3.1.1.min.js}"></script>
	<script th:src="@{/qinjiang/js/semantic.min.js}"></script>

	</body>
	</html>

7:浏览器访问

	localhost:8080/    
	localhost:8080/toLogin

8:未完全解决登录注销按钮转换
和主页权限控制


Shiro:

和SpringSecurity一样

Apache Shiro是一个Java的安全(权限)框架
可以在JavaSE环境和JavaEE环境下用
可以完成:认证、授权、加密、会话管理、web集成、缓存等
下载地址:http://shiro.apache.org


Subject;应用代码直接交互的对象是Subject,也就是说Shiro的对外API核心就是Subject,
Subject代表了当前的用户,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,
如网络爬虫、机器人等,与Subject的所有交互都会委托给SecurityManager;Subject其实是一个门面,
SecurityManager才是实际的执行者


SecurityManager:安全管理器,即所有与安全有关的操作都会与securitymanager交互,并且它管理着所有的Subject,
可以看出它是Shiro的核心,负责与Shiro的其他组件进行交互,它相当于SpringMVC的DispatcherServlet的角色


Realm:Shiro从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager需要验证用户身份,
那么它需要从Realm获取相应的用户进行比较,来确定用户的身份是否合法;也需要从Realm得到用户相应的角色、
权限,进行验证用户的操作是否能够进行,可以把Realm看成DataSource

1:导入依赖

	<dependencies>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.4.1</version>
        </dependency>

        <!--configure Logging-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>1.7.21</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.21</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.12</version>
        </dependency>
    </dependencies>

2:配置文件

	//https://github.com/apache/shiro
	//导入log4j:resources/log4j.properties

	log4j.rootLogger=INFO, stdout

	log4j.appender.stdout=org.apache.log4j.ConsoleAppender
	log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
	log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n

	# General Apache libraries
	log4j.logger.org.apache=WARN

	# Spring
	log4j.logger.org.springframework=WARN

	# Default Shiro logging
	log4j.logger.org.apache.shiro=INFO

	# Disable verbose logging
	log4j.logger.org.apache.shiro.util.ThreadContext=WARN
	log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN


	//导入shiro:resources/shiro.ini

	[users]
	# user 'root' with password 'secret' and the 'admin' role
	root = secret, admin
	# user 'guest' with the password 'guest' and the 'guest' role
	guest = guest, guest
	# user 'presidentskroob' with password '12345' ("That's the same combination on
	# my luggage!!!" ;)), and role 'president'
	presidentskroob = 12345, president
	# user 'darkhelmet' with password 'ludicrousspeed' and roles 'darklord' and 'schwartz'
	darkhelmet = ludicrousspeed, darklord, schwartz
	# user 'lonestarr' with password 'vespa' and roles 'goodguy' and 'schwartz'
	lonestarr = vespa, goodguy, schwartz

	# -----------------------------------------------------------------------------
	# Roles with assigned permissions
	#
	# Each line conforms to the format defined in the
	# org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc
	# -----------------------------------------------------------------------------
	[roles]
	# 'admin' role has all permissions, indicated by the wildcard '*'
	admin = *
	# The 'schwartz' role can do anything (*) with any lightsaber:
	schwartz = lightsaber:*
	# The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with
	# license plate 'eagle5' (instance specific id)
	goodguy = winnebago:drive:eagle5

3:快速开始:java/Quickstart.java

	/*
	 * Licensed to the Apache Software Foundation (ASF) under one
	 * or more contributor license agreements.  See the NOTICE file
	 * distributed with this work for additional information
	 * regarding copyright ownership.  The ASF licenses this file
	 * to you under the Apache License, Version 2.0 (the
	 * "License"); you may not use this file except in compliance
	 * with the License.  You may obtain a copy of the License at
	 *
	 *     http://www.apache.org/licenses/LICENSE-2.0
	 *
	 * Unless required by applicable law or agreed to in writing,
	 * software distributed under the License is distributed on an
	 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
	 * KIND, either express or implied.  See the License for the
	 * specific language governing permissions and limitations
	 * under the License.
	 */

	import org.apache.shiro.SecurityUtils;
	import org.apache.shiro.authc.*;
	import org.apache.shiro.ini.IniSecurityManagerFactory;
	import org.apache.shiro.mgt.SecurityManager;
	import org.apache.shiro.session.Session;
	import org.apache.shiro.subject.Subject;
	import org.apache.shiro.lang.util.Factory;
	import org.slf4j.Logger;
	import org.slf4j.LoggerFactory;


	/**
	 * Simple Quickstart application showing how to use Shiro's API.
	 *
	 * @since 0.9 RC2
	 */
	public class Quickstart {
     

		private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);


		public static void main(String[] args) {
     

			// The easiest way to create a Shiro SecurityManager with configured
			// realms, users, roles and permissions is to use the simple INI config.
			// We'll do that by using a factory that can ingest a .ini file and
			// return a SecurityManager instance:

			// Use the shiro.ini file at the root of the classpath
			// (file: and url: prefixes load from files and urls respectively):
			Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
			SecurityManager securityManager = factory.getInstance();

			// for this simple example quickstart, make the SecurityManager
			// accessible as a JVM singleton.  Most applications wouldn't do this
			// and instead rely on their container configuration or web.xml for
			// webapps.  That is outside the scope of this simple quickstart, so
			// we'll just do the bare minimum so you can continue to get a feel
			// for things.
			SecurityUtils.setSecurityManager(securityManager);

			// Now that a simple Shiro environment is set up, let's see what you can do:

			// get the currently executing user:
			Subject currentUser = SecurityUtils.getSubject();

			// Do some stuff with a Session (no need for a web or EJB container!!!)
			Session session = currentUser.getSession();
			session.setAttribute("someKey", "aValue");
			String value = (String) session.getAttribute("someKey");
			if (value.equals("aValue")) {
     
				log.info("Retrieved the correct value! [" + value + "]");
			}

			// let's login the current user so we can check against roles and permissions:
			if (!currentUser.isAuthenticated()) {
     
				UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
				token.setRememberMe(true);
				try {
     
					currentUser.login(token);
				} catch (UnknownAccountException uae) {
     
					log.info("There is no user with username of " + token.getPrincipal());
				} catch (IncorrectCredentialsException ice) {
     
					log.info("Password for account " + token.getPrincipal() + " was incorrect!");
				} catch (LockedAccountException lae) {
     
					log.info("The account for username " + token.getPrincipal() + " is locked.  " +
							"Please contact your administrator to unlock it.");
				}
				// ... catch more exceptions here (maybe custom ones specific to your application?
				catch (AuthenticationException ae) {
     
					//unexpected condition?  error?
				}
			}

			//say who they are:
			//print their identifying principal (in this case, a username):
			log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");

			//test a role:
			if (currentUser.hasRole("schwartz")) {
     
				log.info("May the Schwartz be with you!");
			} else {
     
				log.info("Hello, mere mortal.");
			}

			//test a typed permission (not instance-level)
			if (currentUser.isPermitted("lightsaber:wield")) {
     
				log.info("You may use a lightsaber ring.  Use it wisely.");
			} else {
     
				log.info("Sorry, lightsaber rings are for schwartz masters only.");
			}

			//a (very powerful) Instance Level permission:
			if (currentUser.isPermitted("winnebago:drive:eagle5")) {
     
				log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'.  " +
						"Here are the keys - have fun!");
			} else {
     
				log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
			}

			//all done - log out!
			currentUser.logout();

			System.exit(0);
		}
	}

subject源码分析:

Subject currentUser = SecurityUtils.getSubject();		// 获取当前的用户对象Subject
Session session = currentUser.getSession();				//通过当前用户拿到Session
currentUser.isAuthenticated()		//判断当前用户是否被认证
currentUser.getPrincipal()  		//获得当前用户的认证
currentUser.hasRole         		//是否拥有这个角色,拥有这个角色就会....
currentUser.isPermitted()  			//获得当前用户的权限
currentUser.logout()       			//注销

###SpringBoot整合Shiro

1:环境搭建

1:导thymeleaf依赖
		<!--thymeleaf模板-->
		<dependency>
			<groupId>org.thymeleaf</groupId>
			<artifactId>thymeleaf-spring5</artifactId>
		</dependency>
		<dependency>
			<groupId>org.thymeleaf.extras</groupId>
			<artifactId>thymeleaf-extras-java8time</artifactId>
		</dependency>
		//我们要使用thymeleaf代码提示:可以在html文件中导入命名空间的约束:
		xmlns:th="http://www.thymeleaf.org"
2:resources/templates/index.html
		<!DOCTYPE html>
		<html lang="en">
		<head>
			<meta charset="UTF-8">
			<title>Title</title>
		</head>
		<body>
		<h1>首页</h1>
		<p th:text="${msg}"></p>
		</body>
		</html>
3:vllos/controller/MyController.java
		@Controller
		public class MyController {
     
			@RequestMapping({
     "/","/index"})
			public String toIndex(Model model){
     
				model.addAttribute("msg","hello,Shiro");
				return "index";
			}
		}

4:导包:spring-boot-starter-web和shiro-spring
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<!--shiro整合spring的包-->
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-spring</artifactId>
			<version>1.4.1</version>
		</dependency>

5:vllos/config/ShiroConfig.java和UserRealm.java

	1:UserRealm.java
		//自定义的UserRealm
		public class UserRealm  extends AuthorizingRealm{
     
			//授权
			@Override
			protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
     
				System.out.println("执行了=>授权");
				return null;
			}

			//认证
			@Override
			protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
     
				System.out.println("执行了=>认证");
				return null;
			}
		}
	2:ShiroConfig.java
		@Configuration
		public class ShiroConfig {
     

			//ShiroFilterFactoryBean
			@Bean
			public ShiroFilterFactoryBean 
					getShiroFilterFactoryBean(@Qualifier("securityManager") 
					DefaultWebSecurityManager defaultWebSecurityManager){
     

				ShiroFilterFactoryBean ShiroFilterFactoryBean = new ShiroFilterFactoryBean();
				//设置安全管理器
				bean.serSecurityManager(defaultWebSecurityManager);
				return bean;
			}


			//DafaultWebSecurityManager
			@Bean(name="securityManager")
			public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
     
				DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
				//关联UserRealm
				securityManager.setRealm(userRealm);
				return securityManager;
			}

			//创建realm 对象,需要自定义
			@Bean
			public UserRealm userRealm{
     
				return new UserRealm();
			}

		}
6:resources/templates/add.html和update.html
	1:add.html
		<!DOCTYPE html>
		<html lang="en">
		<head>
			<meta charset="UTF-8">
			<title>Title</title>
		</head>
		<body>
		<h1>add</h1>
		</body>
		</html>
	2:update.html

		<!DOCTYPE html>
		<html lang="en">
		<head>
			<meta charset="UTF-8">
			<title>Title</title>
		</head>
		<body>
		<h1>update</h1>
		</body>
		</html>
7:在MyController.java中添加:
		@RequestMapping("/user/add")
		public String add(){
     
			return "user/add";
		}

		@RequestMapping("/user/update")
		public String update(){
     
			return "user/update";
		}

2:Shiro实现登录拦截

1:写一个登录页面:templates/login.html
		<!DOCTYPE html>
		<html lang="en">
		<head>
			<meta charset="UTF-8">
			<title>Title</title>
		</head>
		<body>

		<h1>登录</h1>
		<hr>

		<form action="">
			<p>用户名:<input type="text" name="username"></p>
			<p>密码:<input type="text" name="password"></p>
			<p><input type="submit"></p>
		</form>
		</body>
		</html>

2:在MyController.java中加:
		@RequestMapping("/toLogin")
		public String toLogin(){
     
			return "login";
		}

3:在ShiroConfig.java中添加:
		//设置安全管理器
		bean.serSecurityManager(defaultWebSecurityManager);
		
		//以下为添加的代码:
		
		//添加shiro的内置过滤器
		/*
		*	anon:无需认证就可以访问
		* 	authc:必须认证才能访问
		* 	user:必须拥有记住我功能才能用
		* 	perms:拥有对某个资源的权限才能访问
		* 	role:拥有某个角色权限才能访问
		* */
		Map<String,String> filterMap = new LinkedHashMap<>();
		filterMap.put("/user/*","authc");

		bean.setFilterChainDefinitionMap(filterMap);

		//设置登录的请求
		bean.setLoginUrl("/toLogin");

3:实现用户认证

1:在MyController.java中添加:
		@RequestMapping("/login")
		public String login(String username,String password){
     
			//获取当前的用户
			Subject subject = SecurityUtils.getSubject();
			//封装用户的登录数据
			UsernamePasswordToken token = new UsernamePasswordToken(username,password);
			
			try{
     
				subject.login(token);//执行登录方法,如果没有异常就说明OK了
				return "index";
			}catch(UnknownAccountException e){
     //用户名不存在
				model.addAttribute("msg","用户名错误");
				return "login";
			}catch (IncorrectCredentialsException e){
     //密码不存在
				model.addAttribute("msg","密码错误");
				return "login";
			}			
		}
2:login里面添加标签:
		<p th:text="${msg}" style="color: red;"></p>
3:在UserRealm.java中添加:
		System.out.println("执行了=>认证");
		//以下为添加代码:

		//用户名,密码  数据中取
		String name = "root";
		String password = "123456";

		UsernamePasswordToken userToken = (UsernamePasswordToken) token;

		if(!userToken.getUsername().equals(name)){
     
			return  null;//抛出异常:UnknownAccountException
		}

		//密码认证:Shiro做
		return new SimpleAuthenticationInfo("",password,"");

4:Shiro整合Mybatis
1:导依赖:

			<!--subject:用户
			securityManager :管理所有用户
			Realm:连接数据-->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connerctor-java</artifactId>
		</dependency>

		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>1.1.12</version>
		</dependency>

		<!--引入Mybatis-->
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.1.1</version>
		</dependency>

		<!--log4j-->
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.12</version>
		</dependency>
2:resources/application.yaml
 	spring:
	   datasource:
		username: root
		password: root
		driver-class-name: com.mysql.cj.jdbc.Driver
		#假如时区报错,就增加一个时区的配置就OK了:severTimezone=UTC
		url: jdbc:mysql://localhost:3306/mybatis?userUnicode=true&
			characterEncoding=utf-8&serverTimezone=Asia/Shanghaicom
			.alibaba.druid.pool.DruidDataSource
		type: com.alibaba.druid.pool.DruidDataSource

		#SpringBoot默认是不注入这些属性值的,需要自己绑定
		#Druid数据源专有配置
		initialSize:5
		minIdle:5
		maxActive:20
		maxWait:60000
		timeBetweenEvictionRunsMillis:60000
		minEvictableIdleTimeMillis:300000
		validationQuery:SELECT 1 FROM DUAL
		testWhileIdle:true
		testOnBorrow:false
		testOnReturn:false
		poolPreparedStatements:true

		#配置监控统计拦截的filters:  stat:监控统计、log4j:日志记录、wall:防御sql注入
		#如果允许时报错  java.lang.ClassNotFoundException:org.apache.log4j.Priority
		#则导入log4j 依赖即可,Maven地址:https://mvnrepository.com/artifact/log4j/log4j
		filters:stat,wall,log4j
		maxPoolPreparedStatementPerConnectionSize:20
		useGlobalDataSourceStat:true
		connectionProperties:druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
3:连接MySQL

4:application.properties:
		mybatis.type-aliases-package=com.vloos.pojo
		mybatis.mapper-locations=classpath:mapper/*.xml

5:vllos/pojo  

	1:马上去添加lombok依赖:
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.12</version>
		</dependency>
	2:pojo/User.java
		@Data
		@AllArgsConstructor
		@NoArgsConstructor
		public class User {
     
			private int id;

			....
		}	
6:vllos/mapper/UserMapper.java
		@Repository
		@AllArgsConstructor
		@NoArgsConstructor
		public interface UserMapper {
     
			
			public User queryUserByName(String name);
		}
7:resources/mapper/UserMapper.xml
		<?xml version="1.0" encoding="UTF-8" ?>
		<!DOCTYPE mapper 
			PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
			"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

		<mapper namespace="com.vllos.mapper.UserMapper">

			<select id="queryUserByName" parameterType="String" resultType="User">
			select * from mybatis.user where name = #{
     name}
			</select>
		</mapper>        
8:vllos/service/UserService.java
		public interface UserService {
     
			public User queryUserByName(String name);
		}

9:vllos/service/UserServiceImpl.java
		@Service
		public class UserServiceImpl implements UserService {
     
			@Autowired
			UserMapper userMapper;

			@Override
			public User queryUserByName(String name) {
     
			return userMapper.queryUserByName(name);
			}
		}
10:test/../ShiroSpringbootApplicationTests.java
		@SpringBootTest
		class ShiroSpringbootApplicationTests {
     
		
			@Autowired
			UserServiceImpl userService;

			@Test
			void contextLoads() {
     
				System.out.println(userService.queryUserByName("vllos"));
			}

		}
11:UserRealm.java 添加:
	
	1:
		@Autowired
		UserService userService;
	2:
		if(!userToken.getUsername().equals(name)){
     
			return  null;//抛出异常:UnknownAccountException
		}

		//密码认证:Shiro做
		return new SimpleAuthenticationInfo("",password,"");
		
		改成:
		
		//连接真实数据库
		User user = userService.queryUserByName(userToken.getUsername());
		
		if (user==null){
     //没有这个人
			return null;
		}
		//密码认证:Shiro做
		return new SimpleAuthenticationInfo("",user.getPwd(),"");

5:Shiro请求授权实现

1:ShiroConfig.java中:

		//拦截
		Map<String,String> filterMap = new LinkedHashMap<>();

		//添加下面代码
		//授权
		filterMap.put("/user/add","perms[user:add]");
		filterMap.put("/user/update","perms[user:update]");

2:ShiroConfig.java中:
		//未授权页面
		bean.setUnauthorizedUrl("/noauth");

3:MyController.java中:
		@RequestMapping("/noauth")
		@ResponseBody
		public String unauthorized(){
     
			return "未授权无法访问此页面";
		}
4:UserRealm.java中:
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		info.addStringPermission("user:add");
		return info;

5:在MySQL中增加一列:用来实现用户拥有的权限
		如:perms
			user:add
			user:update
		
		User.java中也要相应改
6:UserRealm.java
		//拿到当前登录的这个对象
		Subject subject = SecurityUtils.getSubject();
		User currentUser = (User) subject.getPrincipal(); //拿到User对象
	
		//设置当前用户的权限
		info.addStringPermission(currentUser.getPerms());
		
		2:
		认证下面改:
		return new SimpleAuthenticationInfo("",user.getPwd(),"");
		
		改成:
		return new SimpleAuthenticationInfo("user",user.getPwd(),"");

6:Shiro整合thymeleaf

1:导shiro-thymeleaf整合包
		!--shiro-thymeleaf整合-->
		<dependency>
			<groupId>org.github.theborakompanioni</groupId>
			<artifactId>thymeleaf-extras-shiro</artifactId>
		</dependency>
2:ShiroConfig.java中添加:
		//整合shiroDialect:用来整合shiro  thymeleaf
		@Bean
		public ShiroDialect getShiroDialect(){
     
			return new ShiroDialect();
		}
3:index.xml
		<!DOCTYPE html>
		<html lang="en" xmlns:th="http://www.thymeleaf.org"
				xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
		<head>
			<meta charset="UTF-8">
			<title>Title</title>
		</head>
		<body>
		<h1>首页</h1>
		<div th:if="${session.loginUser==null}">
			<a th:href="@{/toLogin}">登录</a>
		</div>

		<p th:text="${msg}"></p>
		<hr>

		<div shiro:hasPermission="user:add">
		<a th:href="@{/user/add}">add</a>
		</div>

		<div shiro:hasPermission="user:update">
			<a th:href="@{/user/update}">update</a>
		</div>

		</body>
		</html>
4:在UserRealm.java中添加:
		if (user==null){
     //没有这个人
			return null;
		}
		
		//以下为添加代码
		Subject currentSubject = SecurityUtils.getSubject();
		Session session = currentSubject.getSession();
		session.setAttribute("loginUser",user);


shiro-springboot码:

	1:pom.xml
	
```java
		
		
			4.0.0
			
				org.springframework.boot
				spring-boot-starter-parent
				2.3.2.RELEASE
				 
			
			com.vllos
			shiro-springboot
			0.0.1-SNAPSHOT
			shiro-springboot
			Demo project for Spring Boot
		
			
				9
			
		
			
				
					org.springframework.boot
					spring-boot-starter-web
				
		
				
				
					mysql
					mysql-connerctor-java
				
		
				
					org.projectlombok
					lombok
					1.18.12
				
		
				
					com.alibaba
					druid
					1.1.12
				
		
				
				
					org.mybatis.spring.boot
					mybatis-spring-boot-starter
					2.1.1
				
		
				
				
					log4j
					log4j
					1.2.12
				
		
				
				
					org.apache.shiro
					shiro-spring
					1.4.1
				
		
				
				
					org.github.theborakompanioni
					thymeleaf-extras-shiro
				
		
				
				
					org.thymeleaf
					thymeleaf-spring5
				
				
					org.thymeleaf.extras
					thymeleaf-extras-java8time
				
		
				
					org.springframework.boot
					spring-boot-starter-test
					test
					
						
							org.junit.vintage
							junit-vintage-engine
						
					
				
			
		
			
				
					
						org.springframework.boot
						spring-boot-maven-plugin
					
				
			
		
		
```
	
	2:vllos/config:

		1:ShiroConfig.java
		
		
```java
		@Configuration
		public class ShiroConfig {
	
			//ShiroFilterFactoryBean
			@Bean
			public ShiroFilterFactoryBean getShiroFilterFactoryBean(
					@Qualifier("securityManager") 
					DefaultWebSecurityManager defaultWebSecurityManager){

				ShiroFilterFactoryBean ShiroFilterFactoryBean = new ShiroFilterFactoryBean();
				//设置安全管理器
				bean.serSecurityManager(defaultWebSecurityManager);
	
				//添加shiro的内置过滤器
				/*
				*	anon:无需认证就可以访问
				* 	authc:必须认证才能访问
				* 	user:必须拥有记住我功能才能用
				* 	perms:拥有对某个资源的权限才能访问
				* 	role:拥有某个角色权限才能访问
				* */
				//拦截
				Map filterMap = new LinkedHashMap<>();
	
				//授权,正常情况下,没有授权会跳到未授权的页面
				filterMap.put("/user/add","perms[user:add]");
				filterMap.put("/user/update","perms[user:update]");
	
				//设置登录的请求
				bean.setLoginUrl("/toLogin");
	
				//未授权页面
				bean.setUnauthorizedUrl("/noauth");
	
	
				return bean;
			}
	
	
			//DafaultWebSecurityManager
			@Bean(name="securityManager")
			public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
				DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
				//关联UserRealm
				securityManager.setRealm(userRealm);
				return securityManager;
			}
	
			//创建realm 对象,需要自定义
			@Bean
			public UserRealm userRealm{
				return new UserRealm();
			}
	
			//整合shiroDialect:用来整合shiro  thymeleaf
			@Bean
			public ShiroDialect getShiroDialect(){
				return new ShiroDialect();
			}
		}
```
		
		
		2:UserRealm.java
		
		
```java
		//自定义的UserRealm
		public class UserRealm  extends AuthorizingRealm{
	
			@Autowired
			UserService userService;
	
			//授权
			@Override
			protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
				System.out.println("执行了=>授权");
	
				SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
				info.addStringPermission("user:add");
				return info;
	
				//拿到当前登录的这个对象
				Subject subject = SecurityUtils.getSubject();
				User currentUser = (User) subject.getPrincipal();//拿到User对象
	
				//设置当前用户的权限
				info.addStringPermission(currentUser.getPerms());
	
			}
	
			//认证
			@Override
			protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
				System.out.println("执行了=>认证");
	
				//用户名,密码  数据中取
				String name = "root";
				String password = "123456";
	
				UsernamePasswordToken userToken = (UsernamePasswordToken) token;
	
				//连接真实数据库
				User user = userService.queryUserByName(userToken.getUsername());
	
				if (user==null){//没有这个人
					return null;
				}
	
				Subject currentSubject = SecurityUtils.getSubject();
				Session session = currentSubject.getSession();
				session.setAttribute("loginUser",user);
	
				//可以做加密:MD5   MD5盐值加密
				//密码认证:Shiro做
				return new SimpleAuthenticationInfo("user",user.getPwd(),"");
			}
		}
```
	
	
	3:vllos/controller
		
		MyController.java
	
```java
		@Controller
		public class MyController {
			@RequestMapping({"/","/index"})
			public String toIndex(Model model){
				model.addAttribute("msg","hello,Shiro");
				return "index";
			}
	
			@RequestMapping("/user/add")
			public String add(){
				return "user/add";
			}
	
			@RequestMapping("/user/update")
			public String update(){
				return "user/update";
			}
	
			@RequestMapping("/toLogin")
			public String toLogin(){
				return "login";
			}
	
			@RequestMapping("/login")
			public String login(String username,String password){
				//获取当前的用户
				Subject subject = SecurityUtils.getSubject();
				//封装用户的登录数据
				UsernamePasswordToken token = new UsernamePasswordToken(username,password);
	
				try{
					subject.login(token);//执行登录方法,如果没有异常就说明OK了
	
					return "index";
				}catch(UnknownAccountException e){//用户名不存在
					model.addAttribute("msg","用户名错误");
					return "login";
				}catch (IncorrectCredentialsException e){//密码不存在
					model.addAttribute("msg","密码错误");
					return "login";
				}
			}
	
			@RequestMapping("/noauth")
			@ResponseBody
			public String unauthorized(){
				return "未授权无法访问此页面";
			}
	
	
	
	
		}
	
```
	
	
	4:vllos/mapper
		UserMapper.java
		
		
	
	
```java
		@Repository
		@AllArgsConstructor
		@NoArgsConstructor
		public interface UserMapper {
	
			public User queryUserByName(String name);
		}
	
```
	
	5:vllos/pojo
		User.java
		
		
```java
		@Data
		@AllArgsConstructor
		@NoArgsConstructor
		public class User {
			private int id;

		}
```
	
	
	6:vllos/service
	
		1:UserService.java
		
		
	
```java
		import com.vllos.pojo.User;
	
		public interface UserService {
			public User queryUserByName(String name);
		}
```
	
		
		
		
		2:UserServiceImpl.java
	
		
	
		

```java
		import com.vllos.mapper.UserMapper;
		import com.vllos.pojo.User;
		import 	org.springframework.beans.factory.annotation.Autowired;
		import org.springframework.stereotype.Service;
	
		@Service
		public class UserServiceImpl implements UserService {
			@Autowired
			UserMapper userMapper;
	
			@Override
			public User queryUserByName(String name) {
				return userMapper.queryUserByName(name);
			}
	
	
		}
```
	
	7:resources/

		1:application.yaml
		
		spring:
		  datasource:
			username: root
			password: root
			driver-class-name: com.mysql.cj.jdbc.Driver
			#假如时区报错,就增加一个时区的配置就OK了:severTimezone=UTC
			url: jdbc:mysql://localhost:3306/mybatis?userUnicode=true&characterEncoding=utf-8&
				serverTimezone=Asia/Shanghaicom.alibaba.druid.pool.DruidDataSource
			type: com.alibaba.druid.pool.DruidDataSource
	
			#SpringBoot默认是不注入这些属性值的,需要自己绑定
			#Druid数据源专有配置
			initialSize:5
			minIdle:5
			maxActive:20
			maxWait:60000
			timeBetweenEvictionRunsMillis:60000
			minEvictableIdleTimeMillis:300000
			validationQuery:SELECT 1 FROM DUAL
			testWhileIdle:true
			testOnBorrow:false
			testOnReturn:false
			poolPreparedStatements:true
	
			#配置监控统计拦截的filters:  stat:监控统计、log4j:日志记录、wall:防御sql注入
			#如果允许时报错  java.lang.ClassNotFoundException:org.apache.log4j.Priority
			#则导入log4j 依赖即可,Maven地址:https://mvnrepository.com/artifact/log4j/log4j
			filters:stat,wall,log4j
			maxPoolPreparedStatementPerConnectionSize:20
			useGlobalDataSourceStat:true
			connectionProperties:druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
	
		2:application.properties
		
		mybatis.type-aliases-package=com.vloos.pojo
		mybatis.mapper-locations=classpath:mapper/*.xml
	
	
	
	8:resources/mapper
		UserMapper.xml
		
		
		
	
		
	
				
		
	
	
	9:resources/templates
	
		1:index.xml
		
		
		
		
			
			Title
		
		
		

首页


add
2:login.xml Title

登录


用户名:

密码:

10:resources/templates/user 1:add.html Title

add

2:update.html Title

update

---------- ###Swagger 学习目标: 了解Sqagger的作用和概念 了解前后端分离 在SpringBoot中集成Swagger 1:Swagger 简介 前后端分离 前后端相对独立,解耦 前后端甚至可以部署在不同的服务器上 后端:后端控制层controller 、服务层server 、数据访问层Dao 前端:前端控制层、视图层 *伪造后端数据,json。已经存在了, 前后端如何交互 -->API 问题:前后端集成联调,前端人员和后端人员无法做到即时协商,及早解决。最终导致问题集中爆发 解决方案: 首先指定schema[计划的提纲],实时更新最新API,降低集成的f风险 所以:前后端分离: 前端测试后端接口的工具:postman第三方工具 后端提供接口,需要实时更新最新的消息及改动 所以Swagger应运而生,号称世界上最流行的API框架, RestFul Api文档在线自动生成工具 --->Api文档与API定义同步更新 直接运行,可以在线测试API接口 支持多种语言 在项目使用Swagger需要springbox :jar包 swagger2 ui 2:SpringBoot集成Swagger 1:新建web项目 2:导入相关依赖:swagger2、ui io.springfox springfox-swagger2 2.9.2 io.springfox sprngfox-swagger-ui 2.9.2 3:编写一个HelloWorld工程 vllos/controller/HelloController.java package com.vllos.springboot09swagger.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @RequestMapping(value = "/hello") public String Hello(){ return "hello"; } } 4:配置Swagger:Config @Configuration //相当于Componet, @EnableSwaggers2 //开启swagger2 public class SwaggerConfig { } 5:测试运行:http://localhost:8080/swagger-ui.html 3:配置Swagger信息: vllos/config.SwaggerConfig.java package com.vllos.springboot09swagger.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; import java.util.ArrayList; import static springfox.documentation.service.ApiInfo.DEFAULT_CONTACT; @Configuration //相当于Componet, @EnableSwaggers2 //开启swagger2 class SwaggerConfig { //配置了Swagger的Docket的bean实例 @Bean public Docket docket(){ return new Docket(DocumentationType.SPRING_WEB.SWAGGER_2) .apiInfo(apiInfo()); } //配置Swagger信息=apiInfo private ApiInfo apiInfo(){ //作者信息 Contact contact = new Contact("vllos","https://blog.vllos.com","[email protected]"); return new ApiInfo("Api Documentation", "Api Documentation", "1.0", "urn:tos", DEFAULT_CONTACT, "Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0", new ArrayList<>()); } } 4:配置扫描接口及开关: vllos/config.SwaggerConfig.java package com.vllos.springboot09swagger.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; import java.util.ArrayList; import static springfox.documentation.service.ApiInfo.DEFAULT_CONTACT; @Configuration //相当于Componet, @EnableSwaggers2 //开启swagger2 class SwaggerConfig { //配置了Swagger的Docket的bean实例 @Bean public Docket docket(){ return new Docket(DocumentationType.SPRING_WEB.SWAGGER_2) .apiInfo(apiInfo()) //开关,是否启用Swagger,如果为false则不能访问 .enable(true) .select() //RequestHandlerSelectors:配置要扫描接口的方式 //basePackage:指定要扫描的包 //any():扫描全部 //none():不扫描 //withClassAnnotation:扫描类上的注解,参数是一个注解的反射对象 //withMethodAnnotation:扫描方法上的注解, .apis(RequestHandlerSelectors.basePackage("com.vllos.springboot09swagger.controller")) //paths():过滤什么路径 //.paths(PathSelectors.ant("/vllos/**")) .build(); } //配置Swagger信息=apiInfo private ApiInfo apiInfo(){ //作者信息 Contact contact = new Contact("vllos","https://blog.vllos.com","[email protected]"); return new ApiInfo("Api Documentation", "Api Documentation", "1.0", "urn:tos", DEFAULT_CONTACT, "Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0", new ArrayList<>()); } } 浏览器访问:localhost:8080/swagger-ui.html 5:如果我只希望我的Swagger在生产环境中使用,在发布的时候不使用: 解决: *判断是不是生产环境 flag=false *注入enable(flag) 1:application.proeprties spring.profiles.active=dev //环境设置成调用dev环境 2:application-dev.properties //生产环境 server.port=8081 3:application-pro.properties //发布环境 server.port=8082 4:SwaggerConfig.java添加: //配置了Swagger的Docket的bean实例 @Bean public Docket docket(Environment environment){ //设置要显示的Swagger环境 Profiles profiles = Profiles.of("dev","test"); //获取项目的环境: //通过environment.acceptsProfilrs判断是否处在自己设定的环境当中 boolean b = environment.acceptsProfiles(profiles); 5: 浏览器访问:localhost:8081/swagger-ui.html 6:配置API文档的分组: .apiInfo(apiInfo()) //下面为添加的代码 .groupName("vllos") 7:如果配置多个分组? 解决: 多个Docket实例即可: 1:swaggerConfig.java class SwaggerConfig { @Bean public Docket docket1(){ return new Docket(DocumentationType.SWAGGER_2).groupName("A"); } @Bean public Docket docket2(){ return new Docket(DocumentationType.SWAGGER_2).groupName("A"); } @Bean public Docket docket3(){ return new Docket(DocumentationType.SWAGGER_2).groupName("A"); } } 实体类配置 1:springboot09swagger/pojo/User public class User { public String username; public String password; } 2:HelloController.java改成: @RestController public class HelloController { @GetMapping(value = "/hello") public String Hello(){ return "hello"; } //只要我们的接口中,返回值中存在实体类,它就会被扫描到Swagger中 @PostMapping(value = "/hello") public User user(){ return new User(); } } 3:给实体类加文档注释 springboot09swagger/pojo/User改成: @ApiModel("用户实体类") public class User { @ApiModelProperty("用户名") public String username; @ApiModelProperty("密码") public String password; } 4:给接口加注释 HelloController.java @ApiOperation("Hello控制类") public class HelloController { @ApiOperation("Hello控制类") //Operation接口,不是放在类上的,是方法 @GetMapping(value = "/hello") public String hello2(@ApiParam("用户名") String username){ return "hello"+username; } } 8:在UI上测试 1:HelloController.java添加: @ApiOperation("Post测试类") @PostMapping(value = "/postt") public User postt(@ApiParam("用户名") User user){ return user; } 2:测试 localhost:8081:swagger-ui.html try it out 9:总结: 我们可以通过Swagger给一些比较难理解的属性或接口,增加注释信息 接口文档实时更新 可以在线测试 注意点: 在正式发布的时候,关闭Swagger。出于安全考虑,而且节省运行的内存 ~~~~~~~~~~~~~~~~~~~ Swagger代码: 1:pom.xml 4.0.0 org.springframework.boot spring-boot-starter-parent 2.3.2.RELEASE com.vllos springboot-09-swagger 0.0.1-SNAPSHOT springboot-09-swagger Demo project for Spring Boot 9 org.springframework.boot spring-boot-starter-web io.springfox springfox-swagger2 2.9.2 io.springfox sprngfox-swagger-ui 2.9.2 org.springframework.boot spring-boot-starter-test test org.junit.vintage junit-vintage-engine org.springframework.boot spring-boot-maven-plugin 2:com/vllos/springboot09swagger/controller/HellocOntroller.java @ApiOperation("Hello控制类") //给接口加注释 @RestController public class HelloController { @GetMapping(value = "/hello") public String Hello(){ return "hello"; } //只要我们的接口中,返回值中存在实体类,它就会被扫描到Swagger中 @PostMapping(value = "/hello") public User user(){ return new User(); } @ApiOperation("Hello控制类") //Operation接口,不是放在类上的,是方法 @GetMapping(value = "/hello") public String hello2(@ApiParam("用户名") String username){ return "hello"+username; } } 3:com/vllos/springboot09swagger/Springboot09SwaggerApplication.java @SpringBootApplication @EnableSwagger2 public class Springboot09SwaggerApplication { public static void main(String[] args) { SpringApplication.run(Springboot09SwaggerApplication.class, args); } } 4:com/vllos/springboot09swagger/config/SwaggerConfig.java @Configuration //相当于Componet, @EnableSwaggers2 //开启swagger2 class SwaggerConfig { //配置了Swagger的Docket的bean实例 @Bean public Docket docket(Environment environment){ //设置要显示的Swagger环境 Profiles profiles = Profiles.of("dev","test"); //获取项目的环境: //通过environment.acceptsProfilrs判断是否处在自己设定的环境当中 boolean b = environment.acceptsProfiles(profiles); return new Docket(DocumentationType.SPRING_WEB.SWAGGER_2) .apiInfo(apiInfo()) // .groupName("vllos") //开关,是否启用Swagger,如果为false则不能访问 .enable(true) .select() //RequestHandlerSelectors:配置要扫描接口的方式 //basePackage:指定要扫描的包 //any():扫描全部 //none():不扫描 //withClassAnnotation:扫描类上的注解,参数是一个注解的反射对象 //withMethodAnnotation:扫描方法上的注解, .apis(RequestHandlerSelectors.basePackage("com.vllos.springboot09swagger.controller")) //paths():过滤什么路径 //.paths(PathSelectors.ant("/vllos/**")) .build(); } @Bean public Docket docket1(){ return new Docket(DocumentationType.SWAGGER_2).groupName("A"); } @Bean public Docket docket2(){ return new Docket(DocumentationType.SWAGGER_2).groupName("A"); } @Bean public Docket docket3(){ return new Docket(DocumentationType.SWAGGER_2).groupName("A"); } //配置Swagger信息=apiInfo private ApiInfo apiInfo(){ //作者信息 Contact contact = new Contact("vllos","https://blog.vllos.com","[email protected]"); return new ApiInfo("Api Documentation", "Api Documentation", "1.0", "urn:tos", DEFAULT_CONTACT, "Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0", new ArrayList<>()); } } 5:com/vllos/springboot09swagger/pojo/User @ApiModel("用户实体类") public class User { @ApiModelProperty("用户名") public String username; @ApiModelProperty("密码") public String password; } 6:resources/application.properties server.port=8080 spring.profiles.active=dev 7:resources/application-dev.properties server.port=8081 8:resources/application-pro.properties server.port=8082 ---------- ###任务 异步任务 使用Spirng注解或者多线程: 使用注解:AsyncService.java + 主方法main加上@EnableAsync//开启异步注解功能 即可 使用多线程:AsyncService.java + AysnController.java 1:vllos/service/AsyncService.java @Service public class AsyncService { //只需要告诉Spring这是一个异步的方法 @Async public void hello(){ try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("数据正在处理..."); } } 2:Springboot10TestApplication @EnableAsync //开启异步注解功能 @SpringBootApplication public class Springboot10TestApplication { public static void main(String[] args) { SpringApplication.run(Springboot10TestApplication.class, args); } } 3:如果使用多线程的话,主方法main上不加注解,还需要一个Controller: vllos/controller/AysnController.java @RestController public class AysnController { @Autowired AsyncService asyncService; @RequestMapping("/hello") public String hello(){ asyncService.hello();//停止3秒 return "OK"; } } 邮件发送任务 1:导依赖 rg.springframework.boot spring-boot-starter-mail 2:application.properties [email protected] #qq邮箱获取password spring.mail.password=... spring.mail.host=smtp.qq.com #开启加密验证 spring.mail.properties.mail.smtl.ssl.enable=true 3:Springboot10TestApplicationTests.java @SpringBootTest class Springboot10TestApplicationTests { @Autowired JavaMailSenderImpl mailSender; @Test void contextLoads() { //一个简单的邮件 SimpleMailMessage mailMessage = new SimpleMailMessage(); mailMessage.setSubject("你好啊,小碧池~"); mailMessage.setText("多吃饭,长高点啊,小碧池"); mailMessage.setTo("[email protected]"); mailMessage.setFrom("[email protected]"); mailSender.send(mailMessage); } @Test void contextLoads2() throws MessagingException { //一个复杂的邮件 MimeMessage mimeMessage = mailSender.createMimeMessage(); //组装 MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true); //正文 helper.setSubject("你好啊,小碧池~"); helper.setText("

多吃饭,长高点啊,小碧池

",true); //附件 helper.addAttachment("1.jpg",new File("C:\\Users\\DELL\\1.jpg")); helper.addAttachment("2.jpg",new File("C:\\Users\\DELL\\1.jpg")); helper.setTo("[email protected]"); helper.setFrom("[email protected]"); mailSender.send(mimeMessage); } /** * * @param html * @param subject * @param text * @throws MessagingException * @Author:vllos */ //封装成方法 /* public void sendMail(Boolean html,String subject,String text) throws MessagingException { //一个复杂的邮件 MimeMessage mimeMessage = mailSender.createMimeMessage(); //组装 MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,html); //正文 helper.setSubject(subject); helper.setText(text,true); //附件 helper.addAttachment("1.jpg",new File("C:\\Users\\DELL\\1.jpg")); helper.addAttachment("2.jpg",new File("C:\\Users\\DELL\\1.jpg")); helper.setTo("[email protected]"); helper.setFrom("[email protected]"); mailSender.send(mimeMessage); } */ } 定时任务 两个核心: TaskScheduler:任务调度者 TaskExecutor:任务执行者 还需要两个注解: @EnableScheduling //开启定时功能的注解 Scheduled //扫描时候执行 cron表达式 1:main主方法中: @EnableScheduling //开启定时功能的注解 2:vllos/service/ScheduledService.java @Service public class ScheduledService { //在一个特定的时间执行这个方法 //cron表达式 /* * 30 15 10 * * ? :代表每天10点15分30 执行依次 * 30 0/5 10,18 * * ? :代表每天10点和18点,每隔五分钟执行依次 * 0 15 10 ? * 1-6 :代表每个月的周一到周六10点15分执行一次 * */ @Scheduled(cron = "0/2 * * * * ?") public void hello(){ System.out.println("hello,你被执行了"); } } ---------- ###SpringBoot集成Redis SpringData:操作数据 SpringData JPA SpringData MongoDB SpringData Redis 在SpringBoot2.x之后,原来使用的jedis被替换成了lettuce jedis:采用的直连,多个线程操作的话,是不安全的,如果想要避免不安全,使用jedis pool 连接池。 更像BIO模式 lettuce:采用netty,实例可以在多个线程中进行共享,不存在线程不安全的情况,可以减少线程数据了。 更像Nio模式 源码分析: 对象都需要序列化, 强转: ---> 可以自定义一个redisTemplate来替换这个默认的 由于String是redis中最常用的类型,所以单独提出来了一个bean:stringRedisTemplate 1:整合: 1:导入依赖 org.springframework.boot spring-boot-starter-data-redis 2:配置连接 application.properties #配置redis spring.redis.host=127.0.0.1 spring.redis.port=6379 3:测试 test/Redis02SpringbootApplicationTests.java @SpringBootTest class Redis02SpringbootApplicationTests { @Autowired private RedisTemplate redisTemplate; @Test void contextLoads() { //redisTemplate 操作不同的数据类型,api和我们的指令是一样的 //opsForValue 操作字符串 类似String //opsForList 操作List 类似List //opsForSet //opsForHash //opsForZSet //opsForGro //opsForHyperLogLog //除了基本的操作,我们常用的方法都可以直接通过redisTemplate操作,比如事务和CRUD //获取redis连接对象 /* RedisConnection connection = redisTemplate.getConnectionFactory().getConnection(); connection.flushDb(); connection.flushAll(); */ redisTemplate.opsForValue().set("mykey","vllos"); System.out.println(redisTemplate.opsForValue().get("mykey")); } } 2:默认的序列化方式是JDK序列化, 1:vllos/config/RedisConfig.java @Configuration public class RedisConfig { } 2:vllos/pojo/User.java @Component @AllArgsConstructor @NoArgsConstructor @Data //在企业中,我们的所以pojo都会序列化! public class User implements Serializable { //implements Serializable private String name; private int age; } 3::test/Redis02SpringbootApplicationTests.java @Test public void test() throws JsonProcessingException { //真实的开发一般都是使用json来传递对象 User user = new User("小碧池","3"); String jsonUser = new ObjectMapper().writeValueAsString(user); redisTemplate.opsForValue().set("user",jsonUser); System.out.println(redisTemplate.opsForValue().get("user")); } 4:结果是JDK序列化 3:我们可能会使用Json来序列化,所以我们可以自己去定义一个配置类 1:vllos/config/RedisConfig.java @Configuration public class RedisConfig { //这是一个固定模板,在企业中,可以直接使用 //自己定义的一个redisTemplate @Bean @SuppressWarinings("all") public RedisTemplate redisTemplate(RedisConnectionFactory factory){ //我们为了自己开发方便,一般直接使用 RedisTemplate template = new RedisTemplate(); template.setConnectionFactory(factory); //序列化配置 Jackon2JsonRedisSerializer jackon2JsonRedisSerializer = new Jackon2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackon2JsonRedisSerializer.setObjectMapper(om); //String的序列化 StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); //key采用String的序列化方式 template.setKeySerializer(stringRedisSerializer); //hash的key也采用String的序列化方式 template.setHashKeySerializer(stringRedisSerializer); //value序列化方式采用jackson template.setValueSerializer(jackon2JsonRedisSerializer); //hash的value序列化方式采用jackon template.setHashKeySerializer(jackon2JsonRedisSerializer); template.afterPropertiesSet(); return template; } } 2:Redis02SpringbootApplicationTests.java @SpringBootTest class Redis02SpringbootApplicationTests { @Autowired @Qualifier("redisTemplate") private RedisTemplate redisTemplate; @Test void contextLoads() { redisTemplate.opsForValue().set("mykey","vllos"); System.out.println(redisTemplate.opsForValue().get("mykey")); } @Test public void test() throws JsonProcessingException { //真实的开发一般都是使用json来传递对象 User user = new User("小碧池","3"); String jsonUser = new ObjectMapper().writeValueAsString(user); redisTemplate.opsForValue().set("user",user); System.out.println(redisTemplate.opsForValue().get("user")); } } 4:RedisUtils:在企业中,我们80%的情况下,都不会使用这个原生的方式去编写代码,所以去写一个工具类:RedisUtils //redisTemplate 操作不同的数据类型,api和我们的指令是一样的 //opsForValue 操作字符串 类似String //opsForList 操作List 类似List //opsForSet //opsForHash //opsForZSet //opsForGro //opsForHyperLogLog //除了基本的操作,我们常用的方法都可以直接通过redisTemplate操作,比如事务和CRUD ---------- ###分布式Dubbo+Zookeeper 分布式系统(distributed system):是建立在网络之上的软件系统 分布式系统是若干独立计算机的集合,这些计算机集合对于用户来说就像单个相关系统 分布式系统是由一组通过网络进行通信、为了完成共同的任务而协调工作的计算机节点组成的系统。 分布式系统的出现是为了用廉价的、普通的机器完成单个计算机无法完成的计算、存储任务。 其目的是利用更多的机器,处理更多的数据 Dubbo文档: 网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构 势在必行,急需一个治理系统确保架构有条不紊的演进。 单一应用架构:当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。 此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。 适用于小型网站,小型管理系统,将所有功能都部署到一个功能里,简单易用。 缺点:性能扩展比较难、协同开发问题、不利于升级维护 垂直应用架构:当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的应用, 以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。 通过切分业务来实现各个模块独立部署,降低了维护和部署的难度,团队各司其职更易管理, 性能扩展也更方便,更有针对性。 缺点:公用模块无法重复利用,开发性的浪费 分布式服务架构:当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务, 逐渐形成稳定的服务中心,使前端应用能更快速的相应多变的市场需求。此时, 用于提高业务复用及整合的分布式服务框架(RPC)是关键。 流动计算架构:当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心 基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心 (SOA)[Service Oriented Architecture]是关键。 RPC:Remote Procedure Call 是指远程过程调用,是一种进程间通信方式,是一种技术思想,而不是规范。它允许程序调用另一个 地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式编码这个远程调用的细节。 即:程序员无论是调用本地的还是远程的函数,本质上编写的调用代码基本相同。 Http协议、RPC都是用来通信的,只是功能不一样 RPC两个核心:通讯、序列化(数据传输需要转换) Dubbo: Apache Dubbo ,就是一个高可用的RPC框架。微服务框架 是一个高性能、轻量级的开源java RPC框架,提供了三大核心能力: 面向接口的远程方法调用 智能容错和负载均衡 服务自动注册和发现 服务提供者(Provider):暴露服务的服务提供方,服务提供者在启动时,向注册中心注册自己提供的服务 服务消费者(Consumer):调用远程服务的服务消费方,服务消费者在启动时,向注册中心订阅自己所需的服务, 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用, 如果调用失败,再选另一台调用 注册中心(Registry):注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送 变更数据给消费者 Dubbo目前支持4种注册中心,(multicast zookeeper redis simple) 推荐使用Zookeeper注册中心, 监控中心(Monitor):服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据 到监控中心 Dubbo环境搭建与测试: 1:下载解压:dubbo-admin-master 2:打包:dubbo-admin mvn clean package -Dmaven.text.skip=true 3:执行dubbo-admin\target下的dubbo-admin-0.0.1-SNAPSHOT.jar 4:测试: 打开zookeeper服务:zkServer.cmd 打开dubbo-admin-jar:dubbo-admin-0.0.1-SNAPSHOT.jar 浏览器访问:localhost:7001 默认用户名,密码:root ,root dubbo-admin:是一个监控管理后台,查看我们注册了哪些服务,哪些服务被消费了~ 可以不要,不重要。 5:Dubbo的jar包 和 zookeeper才重要!!! ###分布式Dubbo + Zookeeper + SpringBoot 保持下面两个后台开启: zkServer.cmd dubbo-admin-0.0.1-SNAPSHOT.jar 1:写服务端 module:provider-server vllos/service 1:TicketService.java public interface TicketService { public String getTicket(); } 2:TicketServiceImpl.java //zookeeper:服务注册与发现 @Component //使用了Dubbo后 尽量不要用Service注解 public class TicketServiceImpl implements TicketService { @Override public String getTicket() { return null; } } 3:application.properties server.port=8001 #服务应用的名字 dubbo.application.name=provider-server #注册中心地址 dubbo.registry.address=zookeeper://127.0.0.1:2181 #哪些服务要被注册 dubbo.scan.base-packages=com.vllos.service 4:pom.xml org.apache.dubbo dubbo-spring-boot-starter 2.7.7 com.github.sgroschupf zkclient 0.1 org.apache.curator curator-framework 2.12.0 org.apache.curator curator-recipes 2.12.0 org.apache.zookeeper zookeeper 3.4.14 org.slf4j slf4j-log4j12 2:写客户端 module:consumer-server 1:pom.xml org.apache.dubbo dubbo-spring-boot-starter 2.7.7 com.github.sgroschupf zkclient 0.1 org.apache.curator curator-framework 2.12.0 org.apache.curator curator-recipes 2.12.0 org.apache.zookeeper zookeeper 3.4.14 org.slf4j slf4j-log4j12 2:application.properties server.port=8002 #消费者去哪里拿服务,去服务中心拿,需要暴露自己的名字 dubbo.application.name=consumer-server #注册中心的地址,可以在任何电脑上! dubbo.registry.address=zookeeper://127.0.0.1:2181 3:vllos/service 1:TicketService.java public interface TicketService { public String getTicket(); } 2:UserService.java @Service //放到容器中~ import org.springframework.stereotype.Service public class UserService { //想拿到privider-server服务端提供的票,要去注册中心拿到服务 @Reference //引用 两种方法:pom坐标 或 定义路径相同的接口名 TicketService ticketService; public void buyTicket(){ String ticket = ticketService.getTicket(); System.out.println("在注册中心拿到=>"+ticket); } } 4:Test/ConsumerServerApplicationTests.java @SpringBootTest class ConsumerServerApplicationTests { @Autowired UserService userService; @Test void contextLoads() { } } 3:步骤: 1:前提:zookeeper服务已开启 2:提供者提供服务 1:导入依赖 2:配置注册中心的地址,以及服务发现名,和要扫描的包 3:在想要被注册的服务上面增加一个注解@Service 3:消费者如何消费 1:导入依赖 2:配置注册中心的地址,配置自己的服务名 3:从远程注入服务 @Reference ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 微服务架构问题: 分布式架构会遇到的四个核心问题? 1:这么多服务,客户端该如何去访问? API网关,服务路由 2:这么多服务,服务之间如何进行通信?HTTP、RPC框架,异步调用 3:这么多服务,如何治理? 服务与注册,高可用 4:服务挂了,怎么办? 熔断机制,服务降级 解决方案: SpringCloud,是一套生态,就是来解决以上分布式架构的4个问题 想使用SpringCloud,必须要掌握SpringBoot,因为SpringCloud是基于SpringBoot的 1:第一套解决系统:Spring Cloud netFlix 一站式解决方案 1:Api网关 : zuul组件 2:Feign ---> httpClient --->基于http通信方式,同步并阻塞 3:服务注册与发现 : Eureka 4:熔断机制 : Hystrix 2018年低,netFlix宣布无限期停止维护。生态不再维护,就会脱节 2:第二套解决系统:Apache Dubbo zookeeper 1:Api网关 : 没有! 找第三方或者自己实现 2:Dubbo , RPC通信框架 3:服务注册与发现 : zookeeper 4:熔断机制 : 没有,借助了Hystrix 3:第三套解决系统:SpringCloud Alibaba 一站式解决方案 4:又提出一个新概念:服务网格,下一代微服务标准:Server Mesh 代表解决方案:istio 为什么要解决这些问题?本质:网络是不可靠的! ----------

你可能感兴趣的:(java)