spring boot 入门学习笔记
1 简介
1.1 概述
使用 Spring Boot 可以很容易地创建出能直接运行的独立的、生产级别的基于 Spring 的应用。我们对 Spring 平台和第三方类库有自己的考虑,因此您可以从最基本的开始。大多数 Spring Boot 应用只需要很少的 Spring 配置。您可以使用 Spring Boot 来创建一个可以使用 java -jar
命令来运行或者基于传统的 war 包部署的应用程序。我们还提供了一个用于运行 spring scripts 的命令行工具。
- 为所有 Spring Boot 开发提供一个更快、更全面的入门体验。
- 坚持自我虽好,但当需求出现偏离,您需要能迅速摆脱出来。
- 提供大量非功能性特性相关项目(例如:内嵌服务器、安全、指标、健康检查、外部配置)。
- 绝对没有代码生成,也不要求 XML 配置。
1.2 初步了解
pom.xml
pom.xml的父工程的父工程pring-boot-dependencies 是核心依赖。我们在引入一些依赖的时候不需要指定版本,正是因为有这些版本仓库。
启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
启动器:springboot的启动场景。spring会将所有的应用场景变成一个个的启动器,需要什么功能,就只需要找到对应的启动器。Developing with Spring Boot
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
主程序
//@SpringBootApplication 标注这个类是一个springboot的应用 启动类下的所有资源被导入
@SpringBootApplication
public class SpringBoot01Application {
public static void main(String[] args) {
//将springboot 应用启动
SpringApplication.run(SpringBoot01Application.class, args);
}
}
@SpringBootConfiguration springboot的配置
@Configuration spring配置类
@Component 说明这也是spring的一个组件
@EnableAutoConfiguration 自动配置
@AutoConfigurationPackage 自动配置包
@Import({Registrar.class}) 自动配置 包注册
@Import({AutoConfigurationImportSelector.class}) 自动配置导入选择
获取所有的配置
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
META-INF/spring.factories 自动配置的核心文件。
自动装配
@Configuration( //表示这是一个配置类
proxyBeanMethods = false
)
//自动配置属性 ServerProperties
@EnableConfigurationProperties({ServerProperties.class})
//spring 底层注解 根据不同的条件,来判断当前配置或者类是否生效。
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@ConditionalOnClass({CharacterEncodingFilter.class})
@ConditionalOnProperty(
prefix = "server.servlet.encoding",
value = {"enabled"},
matchIfMissing = true
)
public class HttpEncodingAutoConfiguration {
}
@Conditional的扩展注解
一旦配置生效,这个配置类就会给容器中添加各种组件,这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的。
-
sping boot 启动会加载大量的自动配置类(spring.factories )
-
看需要的功能有没有在springboot默认写好的自动配置中
-
在看这个自动配置类中到底配置了那些组件
-
给容器中的自动配置类添加组件的时候,会从properties类中获取某些属性,我们只需在配置文件中指定这些属性的值即可。
xxxxAutoConfiguraryion 自动配置类 给容器添加组件
xxxxProperties 封装配置文件中相关的属性
启动
SpringApplication类
- 推断应用的类型是普通的项目还是web项目
- 查找并加载所有可用初始化器。设置到initializers属性中
- 找出所有应用程序监听器,设置到listeners属性中
- 推断并设置main方法的定义类,找到并运行的主类
2 yaml 语法
springboot使用一个全局配置文件,配置文件名是固定的:application.xxx
application.properties key=value
application.yaml key: 空格 value
#普通键值对
name: ddd
#对象
student:
name: aaa
age: 12
#行内写法
student1: {name: ccc,age: 13}
#数组
pets:
- cat
- dog
pets1: [cat,dog]
松散绑定:
JSR-303
@Validated //数据校验
public class Student {
@NonNull
private String name;
private int age;
多环境配置
---
spring:
profiles:
active: dev
---
spring:
profiles: dev
---
spring:
profiles: test
3 springboot web开发
3.1 静态资源
//添加资源管理器
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//如果静态资源属性已经被自定义, 故如果此处定义了,则下面的默认的staticLocations和 staticPathPattern就不会生效。
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
} else {
//注册,添加静态资源到 webjars/**
this.addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
//添加注册 静态资源即如下的的staticLocations和 staticPathPattern
this.addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
registration.addResourceLocations(this.resourceProperties.getStaticLocations());
if (this.servletContext != null) {
ServletContextResource resource = new ServletContextResource(this.servletContext, "/");
registration.addResourceLocations(new Resource[]{resource});
}
});
}
}
静态资源配置
spring:
mvc:
static-path-pattern: /**
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"};
private String[] staticLocations;
//故 根据数组访问顺序 资源获取resources>static>public
3.2 首页定制
WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, this.getWelcomePage(), this.mvcProperties.getStaticPathPattern());
private Resource getWelcomePage() {
Resource indexHtml = this.getIndexHtml(location);
private Resource getIndexHtml(Resource location) {
Resource resource = location.createRelative("index.html");
// 会从静态资源处 找到第一个 index.html
4 扩展springmvc
If you want to keep those Spring Boot MVC customizations and make more MVC customizations (interceptors, formatters, view controllers, and other features), you can add your own @Configuration
class of type WebMvcConfigurer
but without @EnableWebMvc
.
If you want to provide custom instances of RequestMappingHandlerMapping
, RequestMappingHandlerAdapter
, or ExceptionHandlerExceptionResolver
, and still keep the Spring Boot MVC customizations, you can declare a bean of type WebMvcRegistrations
and use it to provide custom instances of those components.
If you want to take complete control of Spring MVC, you can add your own @Configuration
annotated with @EnableWebMvc
, or alternatively add your own @Configuration
-annotated DelegatingWebMvcConfiguration
as described in the Javadoc of @EnableWebMvc
.
@Configuration
public class ExtendMVCConfig implements WebMvcConfigurer {
// 实现了视图解析器接口的类,就可以看作为视图解析器
@Bean
public ViewResolver myViewResolver(){
return new MyViewResolver();
}
//自定义一个视图解析器
public static class MyViewResolver implements ViewResolver{
@Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
return null;
}
}
}
5 web开发
5.1 国际化
- 设置文件编码全部为UTF-8
- 编写资源配置文件
- 自定义地域解析
public class MyLocalResolver implements LocaleResolver {
//获取请求解析
@Override
public Locale resolveLocale(HttpServletRequest request) {
//获取请求解析中的语言参数
String la=request.getParameter("l");
//拿到默认的,如果没有就使用默认的。
Locale locale=Locale.getDefault();
//如果请求的携带了国际化的参数
if(!StringUtils.isEmpty(la)){
String[] split=la.split("_");
return new Locale(split[0], split[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
}
}
//自定义国际化组件就生效了
@Bean
public LocaleResolver localResolver(){
return new MyLocalResolver();
}
5.2 登录拦截
public class LoginHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestURI = request.getRequestURI();
String name= (String) request.getSession().getAttribute("name");
if(name==null){
request.setAttribute("msg","没有权限");
request.getRequestDispatcher("/").forward(request,response);
return false;
}
return true;
}
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**")
.excludePathPatterns("index.html","/","/user/login");
}
6 Data
6.1 连接mysql
依赖引入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
数据库配置
datasource:
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/myemployees?useUnicode=utf-8&characterEncoding=utf-8
连接测试
@Autowired
DataSource dataSource;
@Test
void contextLoads() {
try {
System.out.println(dataSource.getConnection());
} catch (SQLException e) {
e.printStackTrace();
}
}
打印出::HikariProxyConnection@631851861 wrapping com.mysql.cj.jdbc.ConnectionImpl@6adc5b9c
6.2 Druid
依赖
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
配置
datasource:
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/myemployees?useUnicode=utf-8&characterEncoding=utf-8
type: com.alibaba.druid.pool.DruidDataSource
#配置监控统计拦截器的fiters ,stat监控统计,wall:防御sql注入,log4j:日志记录
filters: stat,wall,log4j
package com.fadaiyu.config;
import com.alibaba.druid.support.http.StatViewServlet;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.repository.NoRepositoryBean;
import java.util.HashMap;
@Configuration
public class DruidDatasSource {
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DruidDatasSource datasSource(){
return new DruidDatasSource();
}
//后台监控
@Bean
public ServletRegistrationBean registrationBean(){
ServletRegistrationBean<StatViewServlet> bean=new ServletRegistrationBean<>(new StatViewServlet(),"/druid/*");
//后台需要有人登录,账号密码配置
HashMap<String,String> initParameters=new HashMap<>();
//增加配置
initParameters.put("loginUsername","admin");
initParameters.put("loginPassword","123456");
//允许谁可以访问
initParameters.put("allow","");
//禁止谁能访问
initParameters.put("xxx","xxx");
//设置初始化参数
bean.setInitParameters(initParameters);
return bean;
}
}
6.3 整合mybatis
依赖
<!-- 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.2.1</version>
</dependency>
配置
mybatis:
type-aliases-package: com.fadaiyu.pojo
mapper-locations: classpath:mapper/*.xml
使用
@Mapper
public interface JobsMapper {
@Select("SELECT* FROM jobs")
List<Jobs> queryJobList();
Jobs queryJobsById(String jo_id);
}
<?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.fadaiyu.mapper.JobsMapper" >
<select id="queryJobsById" resultType="Jobs">
SELECT* FROM jobs WHERE job_id = #{job_id}
</select>
</mapper>
7 安全
7.1 springsecurity
Spring Security是一个专注于为Java应用程序提供身份验证和授权的框架。与所有Spring项目一样,Spring Security的真正强大之处在于它可以很容易地扩展以满足定制需求.
WebSecurityConfigurerAdapter 自定义security 策略
AuthenticationManagerBuilder 自定义认证策略
@EnableWebSecurity 开启WebSecurity模式
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity5 -->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
配置
@EnableWebSecurity //aop 拦截器
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
//首页所有人都可以访问 功能页只有对应有权限的人才能访问
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("//level1/*").hasRole("vip1");
//没有登录权限,跳到登录页
http.formLogin();
//注销成功后返回首页
http.logout().logoutSuccessUrl("/");
}
//认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.passwordEncoder(new BCryptPasswordEncoder())
.withUser("ding1").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1");
}
}
thymeleaf springsecurity5
<!DOCTYPE html>
<html lang="en"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>index</h1>
<!--
sec:authorize :判断信息是否存在
sec:authentication:取出相应的值
-->
<div sec:authorize="isAuthenticated()">
<h1>已经登录</h1>
</div>
<a th:href="@{/level1/view1}">level1--view1</a>
<a th:href="@{/logout}">logout</a>
</body>
</html>
登录定制
//定制login页面,登录页面路径,登录请求路径,参数名
// http.formLogin().loginPage("/toLogin").usernameParameter("userName").passwordParameter("passWodr");
http.rememberMe().rememberMeParameter("rememberMe");
7.2 shiro
7.2.1 简介[1]
Apache Shiro 是 Java 的一个安全框架。目前,使用 Apache Shiro 的人越来越多,因为它相当简单,对比 Spring Security,可能没有 Spring Security 做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的 Shiro 就足够了。对于它俩到底哪个好,这个不必纠结,能更简单的解决项目问题就好了。
本教程只介绍基本的 Shiro 使用,不会过多分析源码等,重在使用。
Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在 JavaSE 环境,也可以用在 JavaEE 环境。Shiro 可以帮助我们完成:认证、授权、加密、会话管理、与 Web 集成、缓存等。这不就是我们想要的嘛,而且 Shiro 的 API 也是非常简单;其基本功能点如下图所示:
- Authentication:身份认证 / 登录,验证用户是不是拥有相应的身份;
- Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
- Session Management:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通 JavaSE 环境的,也可以是如 Web 环境的;
- Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
- Web Support:Web 支持,可以非常容易的集成到 Web 环境;
- Caching:缓存,比如用户登录后,其用户信息、拥有的角色 / 权限不必每次去查,这样可以提高效率;
- Concurrency:shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
- Testing:提供测试支持;
- Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
- Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。
记住一点,Shiro 不会去维护用户、维护权限;这些需要我们自己去设计 / 提供;然后通过相应的接口注入给 Shiro 即可。
接下来我们分别从外部和内部来看看 Shiro 的架构,对于一个好的框架,从外部来看应该具有非常简单易于使用的 API,且 API 契约明确;从内部来看的话,其应该有一个可扩展的架构,即非常容易插入用户自定义实现,因为任何框架都不能满足所有需求。
首先,我们从外部来看 Shiro 吧,即从应用程序角度的来观察如何使用 Shiro 完成工作。如下图:
可以看到:应用代码直接交互的对象是 Subject,也就是说 Shiro 的对外 API 核心就是 Subject;其每个 API 的含义:
Subject:主体,代表了当前 “用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是 Subject,如网络爬虫,机器人等;即一个抽象概念;所有 Subject 都绑定到 SecurityManager,与 Subject 的所有交互都会委托给 SecurityManager;可以把 Subject 认为是一个门面;SecurityManager 才是实际的执行者;
SecurityManager:安全管理器;即所有与安全有关的操作都会与 SecurityManager 交互;且它管理着所有 Subject;可以看出它是 Shiro 的核心,它负责与后边介绍的其他组件进行交互,如果学习过 SpringMVC,你可以把它看成 DispatcherServlet 前端控制器;
Realm:域,Shiro 从 Realm 获取安全数据(如用户、角色、权限),就是说 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色 / 权限进行验证用户是否能进行操作;可以把 Realm 看成 DataSource,即安全数据源。
也就是说对于我们而言,最简单的一个 Shiro 应用:
- 应用代码通过 Subject 来进行认证和授权,而 Subject 又委托给 SecurityManager;
- 我们需要给 Shiro 的 SecurityManager 注入 Realm,从而让 SecurityManager 能得到合法的用户及其权限进行判断。
从以上也可以看出,Shiro 不提供维护用户 / 权限,而是通过 Realm 让开发人员自己注入。
接下来我们来从 Shiro 内部来看下 Shiro 的架构,如下图所示:
- Subject:主体,可以看到主体可以是任何可以与应用交互的 “用户”;
- SecurityManager:相当于 SpringMVC 中的 DispatcherServlet 或者 Struts2 中的 FilterDispatcher;是 Shiro 的心脏;所有具体的交互都通过 SecurityManager 进行控制;它管理着所有 Subject、且负责进行认证和授权、及会话、缓存的管理。
- Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得 Shiro 默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;
- Authorizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;
- Realm:可以有 1 个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是 JDBC 实现,也可以是 LDAP 实现,或者内存实现等等;由用户提供;注意:Shiro 不知道你的用户 / 权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的 Realm;
- SessionManager:如果写过 Servlet 就应该知道 Session 的概念,Session 呢需要有人去管理它的生命周期,这个组件就是 SessionManager;而 Shiro 并不仅仅可以用在 Web 环境,也可以用在如普通的 JavaSE 环境、EJB 等环境;所以呢,Shiro 就抽象了一个自己的 Session 来管理主体与应用之间交互的数据;这样的话,比如我们在 Web 环境用,刚开始是一台 Web 服务器;接着又上了台 EJB 服务器;这时想把两台服务器的会话数据放到一个地方,这个时候就可以实现自己的分布式会话(如把数据放到 Memcached 服务器);
- SessionDAO:DAO 大家都用过,数据访问对象,用于会话的 CRUD,比如我们想把 Session 保存到数据库,那么可以实现自己的 SessionDAO,通过如 JDBC 写到数据库;比如想把 Session 放到 Memcached 中,可以实现自己的 Memcached SessionDAO;另外 SessionDAO 中可以使用 Cache 进行缓存,以提高性能;
- CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到缓存中后可以提高访问的性能
- Cryptography:密码模块,Shiro 提供了一些常见的加密组件用于如密码加密 / 解密的。
7.2.2 快速入门
引入依赖
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-core -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.5.6</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>2.0.0-alpha6</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
配置 和快速启动类
https://github.com/apache/shiro/tree/shiro-root-1.8.0/samples/quickstart/src (1.8)
7.2.3 与spring boot集成
依赖
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.8.0</version>
</dependency>
配置
package com.fadaiyu.config.shiro;
import com.fadaiyu.mapper.UserMapper;
import com.fadaiyu.pojo.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
public class UserRealm extends AuthorizingRealm {
@Autowired
UserMapper userMapper;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了授权");
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
info.addStringPermission("user:add");
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行了认证");
//用户名 密码 从数据库中取出
UsernamePasswordToken token= (UsernamePasswordToken) authenticationToken;
User user = userMapper.queryByName(token.getUsername());
//用户名认证
if(user==null){
return null; //抛出异常 UnknownAccountException
}
//密码认证 由shrio 做
return new SimpleAuthenticationInfo("",user.getPassword(),"");
}
}
package com.fadaiyu.config.shiro;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
factoryBean.setSecurityManager(defaultWebSecurityManager);
//添加shiro 的内置过滤器
/**
* anon : 无需认真即可访问
* authc: 必须认证才能访问
* user: 必须拥有 记住我 功能才能访问
* perms: 拥有对某个资源的权限才能访问
* role: 拥有某个角色的权限才能访问
*/
Map<String,String> map=new LinkedHashMap<>();
//授权,未授权用户户 会跳转到未授权页面
map.put("/level1/*","perms[user:add]");
factoryBean.setFilterChainDefinitionMap(map);
factoryBean.setLoginUrl("/toLogin");
//设置未授权页面
factoryBean.setUnauthorizedUrl("/unauthorized");
return factoryBean;
}
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
//管理realm
manager.setRealm(userRealm);
return manager;
}
//创建realm对象
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
//整合shirodialect 用来整合shiro thymeleaf
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
}
8 swagger
8.1 简介
Swagger 是一个规范且完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。
Swagger 的目标是对 REST API 定义一个标准且和语言无关的接口,可以让人和计算机拥有无须访问源码、文档或网络流量监测就可以发现和理解服务的能力。当通过 Swagger 进行正确定义,用户可以理解远程服务并使用最少实现逻辑与远程服务进行交互。与为底层编程所实现的接口类似,Swagger 消除了调用服务时可能会有的猜测。
8.2 使用
依赖引入
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>3.0.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>3.0.0</version>
</dependency>
如果发生 documentxxxxxxx xxx错误,,添加如下配置:
spring:
mvc:
pathmatch:
matching-strategy: ANT_PATH_MATCHER
配置
@Configuration
@EnableSwagger2 //开启swagger
public class SwaggerConfig {
//配置swagger的bean实列
@Bean
public Docket docket(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(this.apiInfo());
}
public ApiInfo apiInfo() {
//作者信息
Contact DEFAULT_CONTACT = new Contact("发呆鱼", "", "");
return new ApiInfo("api 文档",
"啦啦啦",
"1.0",
"urn:tos",
DEFAULT_CONTACT,
"Apache 2.0",
"http://www.apache.org/licenses/LICENSE-2.0",
new ArrayList());
}
}
8.3 进一步配置
配置扫描接口
@Configuration
@EnableSwagger2 //开启swagger
public class SwaggerConfig {
//可是设置多个分组
@Bean
public Docket docket1(){
return new Docket(DocumentationType.SWAGGER_2).groupName("1");
}
//配置swagger的bean实列
@Bean
public Docket docket(Environment environment){
//设置要显示的swagger 环境
Profiles profiles=Profiles.of("dev");
//通过environment.acceptsProfiles 判读是否处于自己设定的环境中
boolean flag = environment.acceptsProfiles(profiles);
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(this.apiInfo())
.select()
//RequestHandlerSelectors配置要扫描接口的方式
//basePackage 指定要扫描的包
//any 扫描全部
// none 不扫描
//withClassAnnotation 扫描类上的注解
//withMethodAnnotation 扫描方法上的注解
.apis(RequestHandlerSelectors.basePackage("com.fadaiyu.controller"))
//.paths(PathSelectors.any()) 过滤什么路径
.build()
.groupName("A") //设置分组名
.enable(flag); //是否启动swagger
}
public ApiInfo apiInfo() {
//作者信息
Contact DEFAULT_CONTACT = new Contact("发呆鱼", "", "");
return new ApiInfo("api 文档",
"啦啦啦",
"1.0",
"urn:tos",
DEFAULT_CONTACT,
"Apache 2.0",
"http://www.apache.org/licenses/LICENSE-2.0",
new ArrayList());
}
}
实体类配置
//只要返回的接口中,返回值中存在实体类,他就会被扫描到swagger中
@GetMapping("/a2")
public User userTest(){
return new User();
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("用户实体类")
public class User {
@ApiModelProperty("用户编号")
private int id;
private String name;
private String password;
}
接口注释
package com.fadaiyu.controller;
import com.fadaiyu.pojo.User;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.lang.annotation.Target;
@Api(tags = "hello 控制类")
@RestController
@RequestMapping("/hello")
public class HelloCon {
@ApiOperation("接口a1")
@PostMapping("/a1")
public String test1(){
return "aaa";
}
//只要返回的接口中,返回值中存在实体类,他就会被扫描到swagger中
@GetMapping("/a2")
public User userTest(){
return new User();
}
@GetMapping("/a3")
public String test2(@ApiParam("用户名") String username){
return username;
}
}
9 任务
9.1 异步任务
@SpringBootApplication
@EnableAsync //开启异步注解功能
public class SpringBoot01Application {
public static void main(String[] args) {
//将springboot 应用启动
SpringApplication.run(SpringBoot01Application.class, args);
}
}
//告诉spring 这是一个异步方法
@Async
public void hello(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
9.2 发送邮件
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
配置
spring:
mail:
username: 邮箱名
password: 密钥
host: smtp.163.com
简单使用
@Test
public void mailTest(){
//一个简单的邮件
SimpleMailMessage simpleMessage=new SimpleMailMessage();
simpleMessage.setSubject("发呆鱼");
simpleMessage.setText("aaaaaaaa");
simpleMessage.setTo("2305932120@qq.com");
simpleMessage.setFrom("dingthefist@163.com");
javaMailSender.send(simpleMessage);
}
@Test
public void mailTest2(){
//一个复杂邮件
MimeMessage mimeMessage=javaMailSender.createMimeMessage();
//组装
try {
MimeMessageHelper mimeMessageHelper=new MimeMessageHelper(mimeMessage,true);
//正文
mimeMessageHelper.setSubject("复杂邮件");
mimeMessageHelper.setText("<p stytle='color:red'> 啦啦啦啦啦啦啦</p>",true);
//附件
mimeMessageHelper.addAttachment("favicon.ico",new File("D:\\WorkeSpace_Study\\Java\\Spring_St\\Spring-Boot-01\\src\\main\\resources\\favicon.ico"));
mimeMessageHelper.setTo("2305932120@qq.com");
mimeMessageHelper.setFrom("dingthefist@163.com");
} catch (MessagingException e) {
e.printStackTrace();
}
javaMailSender.send(mimeMessage);
}
9.3 定时任务
TaskScheduler 任务调度者
TaskExecutor 任务执行者
@EnableScheduling 开启定时功能注解
@Scheduler 什么时候执行
/**
* *:代表任何时间
* #, :代表连续时间
* #-:代表持续范围
* * /n :代表每隔多久执行一次
* 第1* 一分钟的第多少秒
* 第2*:一个小时当中的第几分钟
* 第3*:一天中的第几个小时
* 第4*:一个月当中的第几天
* 第5*:一年之中第几个月
* 第6*:一周当中的星期几 (0-7 0和7都是星期日)
*/
//在一个特定时间执行
@Scheduled(cron = "0 42 10 * * ?")
public void schedulerTest1(){
System.out.println("----------------------------------------------");
}
10 整合redis
导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
在springboot 2.后,原来使用的jedis 被替换为lettuce
jedis :采用直连,多个线程操作的化,是不安全的。如果想要避免,使用jedis pool 连接池。bio
lettuce:采用netiy 实例可以再多个线程中共享,不存在线程不安全情况。 nio
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
public RedisAutoConfiguration() {
}
//提供了 redistemplate ,如果不需要,可以进行自定义
@Bean
@ConditionalOnMissingBean(
name = {"redisTemplate"}
)
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
//默认的RedisTemplate 没有过多的设置, redis 对象都是需要序列化。
//两个泛型都是 object object 的类型,使用后需要转为string object
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean //
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
return new StringRedisTemplate(redisConnectionFactory);
}
}
配置
spring:
redis:
port: 6379
host: 127.0.0.1
测试
//操作不同的数据类型
//redisTemplate.opsForValue();// 操作字符串 类似 string
//redisTemplate.opsForList(); //操作list
//redisTemplate.opsForHash(); //操作哈希
// redisTemplate.opsForValue().set("name","fadaiyu");
// System.out.println(redisTemplate.opsForValue().get("name"));
自定义redisTemplate
package com.fadaiyu.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
//编写自己的redisTemplate
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<String, Object> template=new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
//json 序列化方式
Jackson2JsonRedisSerializer objectJackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om=new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
objectJackson2JsonRedisSerializer.setObjectMapper(om);
//string 序列化
StringRedisSerializer stringRedisSerializer=new StringRedisSerializer();
//key采用string序列化方式
template.setKeySerializer(stringRedisSerializer);
//hash 的key 采用string 序列化方式
template.setHashKeySerializer(stringRedisSerializer);
//value 也采用string序列化方式
template.setValueSerializer(stringRedisSerializer);
//hash 的value 序列化采用jackson
template.setHashValueSerializer(objectJackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
跟随《遇见狂神说》学习 未完待续
Shiro 简介_w3cschool :https://www.w3cschool.cn/shiro/co4m1if2.html ↩︎
本文作者:发呆鱼
本文链接:https://www.cnblogs.com/dyiblog/articles/15927808.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步