前言 由于现在开发中使用注解的比较多,下面演示示例以注解为主 SpringBoot 基于 Spring 开发。继承了Spring框架原有的优秀特性和 Spring 框架紧密 结合进一步简化了Spring应用的整个搭建和开发过程。其设计目的是用来简化 Spring 应用的初始搭建以及开发过程。大部分的 SpringBoot 应用都只需要非常少量的配置代码,开发者能够更加专注于业务逻辑。
springboot基础 springboot的默认结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 src └── main ├── java │ └── com │ └── example │ └── demo │ ├── config │ ├── controller │ ├── model │ ├── service │ └── DemoApplication.java ├── resources │ ├── application.properties//全局的配置文件 │ ├── static │ ├── templates//spring默认视图路径以 /templates/ 为前缀,以 .html 为后缀 │ └── ... └── ... 生成的classes目录中com与resources下的子文件平行
主程序类 @SpringBootApplication ,它是一个复合注解。可以用下面三个代替
@SpringBootConfiguration 通过@Configuration 与@Bean结合,注册到Spring ioc 容器。
@ComponentScan 通过范围扫描的方式,扫描特定注解类,将其注册到Spring ioc 容器。如果不写则扫描@SpringBootApplication注解的所在的包及其下级包
@EnableAutoConfiguration 通过spring.factories的配置,来实现bean的注册到Spring ioc 容器。
全局的配置文件 配置文件可以是端口配置,数据库设计,日志设计 application.properties 语法结构 :key=value server.port=8081
application.yml 语法结构 : server: port: 8081
配置类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @Configuration(proxyBeanMethods = false) public class MyConfig { @Bean public User user01 () { User zhangsan = new User("zhangsan" , 18 ); zhangsan.setPet(tomcatPet()); return zhangsan; } @Bean("tom") public Pet tomcatPet () { return new Pet("tomcat" ); } }
路由设计 SpringMVC @Component:注解是一个通用的注解,被Spring容器扫描并纳入管理,下面三个可以看作其的具体划分 @Controller:修饰web层的类 @Service:修饰service层的类 @Repository:修饰dao层的类
1 2 3 4 5 6 7 8 9 10 11 <context:component-scan base-package ="com.ssl.controller" /> <mvc:default-servlet-handler /> <mvc:annotation-driven /> <bean class ="org.springframework.web.servlet.view.InternalResourceViewResolver" id ="internalResourceViewResolver" > <property name ="prefix" value ="/WEB-INF/jsp/" /> <property name ="suffix" value =".jsp" /> </bean >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Controller @RequestMapping("/controller") public class HelloController { @GetMapping("/hello") @ResponseBody public String hello (User user) { return user.getDepartment().getName1().contains("njust" ); }
Interceptor 自定义拦截器必须实现HandlerInterceptor接口或者继承Handler InterceptorAdapter类,功能与Servlet Filter类似
1 2 3 4 5 6 7 8 9 public interface HandlerInterceptor {return false 不放行,不会执行controller中的方法 boolean preHandle (HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception ; void postHandle (HttpServletRequest var1, HttpServletResponse var2, Object var3, ModelAndView var4) throws Exception ; void afterCompletion (HttpServletRequest var1, HttpServletResponse var2, Object var3, Exception var4) throws Exception ; }
Swagger2 不同与Structs框架,springmvc的路由多有注解实现,不便于通用管理,于是需要有一个维护接口的组件 ,swagger应用而生,它可以快速帮助我们编写最新的API接口文档,从而提升了团队开发的沟通效率。
创建Swagger配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Configuration @EnableSwagger2 @Profile({"dev", "test"}) public class Swagger2Api { @Bean Docket createApi () { Docket build = new Docket(DocumentationType.SWAGGER_2) .apiInfo(getApiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.wenbin.springboot" )) .paths(PathSelectors.any()) .build(); return build;
在要分析的control类前加上一下注释
1 2 3 4 @Api(value = "helloController的api文档",description = "helloController的api文档2") @ApiOperation(value = "hello的入口方法,基本数据类型参数") @ApiParam(name = "name", value = "姓名",required = true) @ApiImplicitParam(name = "id",value = "用户id",paramType = "path",dataType = "Long",required = true)
访问http://localhost:8080/swagger-ui.html
即可看到分析页面
上线系统时,修改application.yml文件防止不必要的信息泄露
1 2 3 4 spring: profiles: active: dev
spring的编程设计 spring有两个重要的设计思想,分别为控制反转(IOC) ,面向切面编程(AOP)
ioc IoC的核心思想是将对象之间的依赖关系 交给容器来管理,通过配置文件(bean)或注解 等方式告诉容器哪些对象需要被创建和注入,容器会根据配置信息自动创建对象,并将依赖注入到相应的位置。这样可以降低对象之间的耦合度 (两个子系统或类之间的关联程度),提高代码的可维护性和可扩展性。 IOC是一种编程思想,由主动的编程 变成被动的接收 。 类似与thinkphp中的利用__call根据类名调用自动加载
依赖注入 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 public class Customer { private String name; private int age; public void setName (String name) { this .name = name; } public void setAge (int age) { this .age = age; } @Override public String toString () { return "Customer{" + "name='" + name + '\'' + ", age=" + age + '}' ; } } <bean id="customerBean" class ="com.powernode.spring6.beans.Customer" p:name="zhangsan" p:age="20" /> @Test public void testP () { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-p.xml" ); Customer customerBean = applicationContext.getBean("customerBean" , Customer.class); System.out.println(customerBean); } public class MyTime { private int year; private int month; private int day; public MyTime (int year, int month, int day) { this .year = year; this .month = month; this .day = day; } @Override public String toString () { return "MyTime{" + "year=" + year + ", month=" + month + ", day=" + day + '}' ; } } <bean id="myTimeBean" class ="com.powernode.spring6.beans.MyTime" c:_0="2008" c:_1="8" c:_2="8" />
使用注解实现自动装配
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @Repository("userDao") public class UserDaoImpl implements UserDao { @Value("李四") private String name; @Override public void save () { System.out.println("DAOImpl保存用户的方法执行了。。。。" + name); } } @Service("UserService") public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Override public void save () { System.out.println("UserServiceImpl执行了" ); userDao.save(); } }
AOP AOP可以让开发者在不修改原代码的情况下,在程序执行过程中插入一些行为,从而实现某些横向功能的需求 这个类似与thinkphp中的hook,servlet的fliter,常用于日志,行为检测,插件开发等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class DiyPointCut { public void before () { System.out.println("======方法执行前======" ); } public void after () { System.out.println("======方法执行后======" ); } } <aop:pointcut id="point" expression="execution(* com.kuang.service.UserServiceImpl.*(..))" /> <aop:before method="before" pointcut-ref="point" /> <aop:after method="after" pointcut-ref="point" /> @Aspect public class AnnotationPointCut { @Before("execution(* com.kuang.service.UserServiceImpl.*(..))") public void before () { System.out.println("====方法执行前====" ); } @After("execution(* com.kuang.service.UserServiceImpl.*(..))") public void after () { System.out.println("====方法执行后====" ); } }
Spring Boot actuator Actuator 是 springboot 提供的用来对应用系统进行自省和监控的功能模块,借助于 Actuator 开发者可以很方便地对应用系统某些监控指标进行查看、统计等。
编写配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 management: server: port: 8080 endpoints: web: exposure: include: "*" base-path: /actuator endpoint: shutdown: enabled: true health: show-details: always
端点总结 Spring Boot 1.x 版本的 Actuator 端点在根 URL 下注册 Spring Boot 2.x 版本的 Actuator 端点移动到 /actuator/ 路径下 Spring Boot < 1.5 默认未授权访问所有端点; Spring Boot >= 1.5 默认只允许访问 /health 和 /info 端点
1 2 3 4 5 6 7 8 9 10 /actuator/env 露出Spring的属性的各种环境变量 /actuator/refresh 通过调用这个端点来更新应用中的配置属性,而不需要重启应用 /actuator/restart /actuator/heapdump 返回的 GZip 压缩 堆转储文件,可使用 Eclipse MemoryAnalyzer 加载,通过站点泄露的内存信息,有机会查看到后台账号信息和数据库账号等 /actuator/mappings 显示所有@RequestMapping路径的整理列表 /actuator/httptrace 显示最近的HTTP请求和响应信息,路径包含一些 http 请求包访问跟踪信息,有可能在其中发现内网应用系统的一些请求信息详情、以及有效用户或管理员的 authorization(token、JWT、cookie)等字段 /actuator/info 显示任意应用信息,是在配置文件里自己定义的 /actuator/health 显示应用健康信息 /actuator/jolokia 通过这个端点,你可以以 JSON 格式与 JMX MBeans 进行交互,从而监控和管理应用程序的各个方面,查看/actuator/jolokia/list 是否存在关键词
Thymeleaf模板引擎 Spring Boot默认是不支持JSP的,而是推荐使用Thymeleaf、Freemarker等模板引擎,默认情况下,如果你添加了 Thymeleaf 或 FreeMarker 的依赖,Spring Boot 将自动配置对应的模板引擎。 SpringBoot默认在static 目录中存放静态资源,而 templates 中放动态页面。 我们只需要把我们的html页面放在类路径下的templates下,thymeleaf就可以帮我们自动渲染了。
如果页面是Thymeleaf生成的就会在html上加上这样的标识<html xmlns:th="http://www.thymeleaf.org">
SpEL表达式 使用场景 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class user { @Value("${spring.user.username}") private String name; @Value("${spring.user.age} }") private Long age; } public String getindex (Model model) { user user1=new user("bigsai" ,22 ); model.addAttribute("user" ,user1); return "index" ; } <td th:text="${user.name}" ></td> <td th:text="${user['name']}" ></td> <td th:text="${user.getname()}" ></td>
1 2 3 4 <bean id ="xxx" class ="com.java.XXXXX.xx" > <property name ="arg" value ="#{表达式}" > </bean >
在注解@Value里面
用法 1 2 3 4 5 6 ${} 用于从上下文中获取数据(如模型对象的属性)。 #{} 用于调用 Spring bean 的方法或访问特定功能。 @{} 用于引用外部资源 #{…} 和${…} 可以混合使用,但是必须#{}外面,${}在里面,#{ ‘${}’ } ,注意单引号,注意不能反过来" 基础用法
1 2 3 #{12 *12 } #{T(java.lang.Runtime).getRuntime().exec("calc" )} #{new java.lang.ProcessBuilder('cmd' ,'/c' ,'calc' ).start()}
运算符
1 2 3 4 #{my name is+' ' +user} #{shape.kind == 'circle' and shape.perimeter gt 10000 } #{songSelector.selectSong() == 'May Rain' ? piano:saxphone} #{admin.email matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.com' }
SpringSecurity Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架 配置类授权
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure (AuthenticationManagerBuilder auth) throws Exception { auth. inMemoryAuthentication() .withUser("张三" ) .password("123456" ) .roles("vip1" ) .authorities("user:select" ,"user:delete" ,"user:insert" ,"user:update" ) .and() .withUser("李四" ) .password("123456" ) .roles("vip2" ) .authorities("user:select" ,"user:export" ); } @Override protected void configure (HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/" ).permitAll() .antMatchers("/level1/**" ).hasAnyRole("vip1" ) .antMatchers("/level2/**" ).hasAnyRole("vip2" ) .antMatchers("/level3/**" ).hasAnyRole("vip3" ); http.csrf().disable(); } }
身份校验
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @GetMapping("info") public Authentication info () { SecurityContext context = SecurityContextHolder.getContext(); Authentication authentication = context.getAuthentication(); UserDetails principal = (UserDetails) authentication.getPrincipal(); System.out.println(principal.getUsername()); return authentication; } @GetMapping("select") @PreAuthorize("hasAuthority('user:select')") public String select () { System.out.println("查询用户" ); return "查询用户" ; }
thymeleaf注入 在<= thymeleaf-spring5:3.0.12 组件中,thymeleaf结合模板注入中的特定场景可能会导致远程代码执行。详细描述参见 https://github.com/thymeleaf/thymeleaf-spring/issues/256
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @RequestMapping("/hello") public class HelloController { @RequestMapping("/whoami/{name}/{sex}") public String hello (@PathVariable("name") String name, @PathVariable("sex") String sex) { return "Hello" + name + sex; } } @PostMapping("/getNames") public String getCacheNames (String fragment, ModelMap mmap) { mmap.put("cacheNames" , cacheService.getCacheNames()); return prefix + "/cache::" + fragment; }
shiro 简单配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [main] shiro.loginUrl = /login.jsp[users] root = secret,adminguest = guest,guest[roles] admin = *[urls] /user/** = authc /admin/list = authc, roles[admin] /admin/** = authc /audit/** = authc, perms["audit:list"] /** = anon