1、泛型Service
以前Service写法
public abstract class BaseService<M extends Serializable> {
private BaseRepository<M> repository;
public void setRepository(BaseRepository<M> repository) {
this.repository = repository;
}
public void save(M m) {
repository.save(m);
}
}
@Service
public class UserService extends BaseService<User> {
@Autowired
public void setUserRepository(UserRepository userRepository) {
setRepository(userRepository);
}
}
@Service
public class OrganizationService extends BaseService<Organization> {
@Autowired
public void setOrganizationRepository(OrganizationRepository organizationRepository) {
setRepository(organizationRepository);
}
}
可以看到,以前必须再写一个setter方法,然后指定注入的具体类型,然后进行注入;
泛型Service的写法
public abstract class BaseService<M extends Serializable> {
@Autowired
protected BaseRepository<M> repository;
public void save(M m) {
repository.save(m);
}
}
@Service
public class UserService extends BaseService<User> {
}
@Service
public class OrganizationService extends BaseService<Organization> {
}
2、Spring boot 入门
1) 继承spring-boot-starter-parent
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
2) 引入spring-boot-starter-web包
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
3) 编写启动Class
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
4) 项目启动
有两张方式启动
在项目目录下执行mvn spring-boot:run
在DemoApplication 类中执行右键Run DemoApplication
至此SpringBoot项目已经完成
3 SpringBoot深入介绍
1) 页面渲染
静态文件放到src\main\resources目录访问时可以通过http://ip:port/cxt/static/js/***.js
页面展现文件放到src\main\resources\templates目录下
spring-boot 不推荐JSP
可以通过freemarker、velocity、thymeleaf 等模板引擎
freemarker:渲染文件以ftl结尾
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
velocity:渲染文件以vm结尾(SpringBoot 1.5.x之后已经不再支持Velocity)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-velocity</artifactId>
</dependency>
thymeleaf:渲染文件以html结尾
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
可同时支持freemarker和thymeleaf
2) 应用部署
添加插件
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
执行mvn clean package 可以得到demo-0.0.1-SNAPSHOT.jar
通过命令java -jar target/demo-0.0.1-SNAPSHOT.jar运行程序
3) 配置文件介绍
配置文件支持properties或yml文件,写法区别如下:
spring.redis.cluster.nodes[0]=192.168.0.1:6379
spring.redis.cluster.nodes[1]=192.168.0.2:6379
或
spring:
redis:
cluster:
nodes:
- 192.168.0.1:6379
- 192.168.0.2:6379
常用配置如下:
server.port=8081 #项目启动端口号,默认为8080
server.context-path=/demo #项目启动应用名称,默认为空
控制日志级别
通过 logging.level.*= LEVEL(LEVEL是TRACE,DEBUG,INFO,WARN,FATAL,OFF)中的一个
这样表示项目中所有的日志级别都是一样的.也可以精准控制到具体的一块
logging.level.org.springframework.web: DEBUG //org.springframework.web包下面的日志级别是DEBUG的
logging.level.org.hibernate: ERROR //org.hibernate包下面的日志级别是ERROR
logging.level.com.ibigsea.*: INFO //com.ibigsea包下面的日志级别是INFO
Profile配置
Profile是Spring用来针对不同环境对不同配置提供支持的,全局Profile配置使用
application-{profile}.properties 如:application-prod.properties
在本地模拟出一个开发环境和生产环境
创建application-prd.properties 和 application-dev.properties
项目结构
application-dev.properties
server.port=8081
application-prd.properties
server.port=8082
application.properties(设置启动环境)
spring.profiles.active=prd
除了上面这种配置方式,我们还有一种更简便的方式来配置多种环境,通过命令行参数配置
删除多余的properties文件
application.yml
spring:
profiles:
active: prd
---
spring :
profiles : prd
server :
port : 8081
---
spring :
profiles : dev
server :
port : 8082
将程序打包之后 通过java -jar xx.jar –spring.profiles.active=dev的方式启动不通的配置环境
4) 使用actuator
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
主要暴露的功能
HTTP方法 | 路径 | 描述 | 鉴权 |
---|---|---|---|
GET | /autoconfig | 查看自动配置的使用情况 | true |
GET | /configprops | 查看配置属性,包括默认配置 | true |
GET | /beans | 查看bean及其关系列表 | true |
GET | /dump | 打印线程栈 | true |
GET | /env | 查看所有环境变量 | true |
GET | /env/{name} | 查看具体变量值 | true |
GET | /health | 查看应用健康指标 | false |
GET | /info | 查看应用信息 | false |
GET | /mappings | 查看所有url映射 | true |
GET | /metrics | 查看应用基本指标 | true |
GET | /metrics/{name} | 查看具体指标 | true |
POST | /shutdown | 关闭应用 | true |
GET | /trace | 查看基本追踪信息 | true |
注:此时直接访问会报权限的问题需要在配置文件中配置management.security.enabled=false
5) SpringBoot集成mybatis
引入Mybatis相关联的jar
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.2.0</version>
</dependency>
<!--内存数据库可以直接使用不用配置数据源-->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
介绍使用annotation模式的Mybatis调用
编写实体类如下:
import java.io.Serializable;
/**
* @author Eddú Meléndez
*/
public class City implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String name;
private String state;
private String country;
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getState() {
return this.state;
}
public void setState(String state) {
this.state = state;
}
public String getCountry() {
return this.country;
}
public void setCountry(String country) {
this.country = country;
}
@Override
public String toString() {
return getId() + "," + getName() + "," + getState() + "," + getCountry();
}
}
编写Mapper
@Mapper
public interface CityMapper {
@Select("select * from city where state = #{state}")
City findByState(@Param("state") String state);
}
在Service层调用Mapper
@Autowired
private CityMapper cityMapper;
Mybatis调用已经完成
6) SpringBoot集成sitemesh
Sitemesh是一种页面装饰技术 :
它通过过滤器(filter)来拦截页面访问
根据被访问页面的URL找到合适的装饰模板
提取被访问页面的内容,放到装饰模板中合适的位置
最终将装饰后的页面发送给客户端。
在pom.xml中添加相应的Jar 配置文件
<dependency>
<groupId>org.sitemesh</groupId>
<artifactId>sitemesh</artifactId>
</dependency>
编写Filter类
import org.sitemesh.builder.SiteMeshFilterBuilder;
import org.sitemesh.config.ConfigurableSiteMeshFilter;
public class WebSiteMeshFilter extends ConfigurableSiteMeshFilter {
@Override
protected void applyCustomConfiguration(SiteMeshFilterBuilder builder) {
builder.addDecoratorPath("/*", "/main")
.addExcludedPath("/main")
.addExcludedPath("/login")
.addExcludedPath("/static/*");
}
}
在应用程序入口文件中此处在DemoApplication类中实现Filter注册bean
@Bean
public FilterRegistrationBean siteMeshFilter(){
FilterRegistrationBean fitler = new FilterRegistrationBean();
WebSiteMeshFilter siteMeshFilter = new WebSiteMeshFilter();
fitler.setFilter(siteMeshFilter);
return fitler;
}
最后编写如下模板文件如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>TPA-
<sitemesh:write property='title'/>
</title>
<script type="text/javascript" src="${request.contextPath}/static/js/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="${request.contextPath}/static/js/msg.js"></script>
<!-- We support more than 40 localizations -->
<script type="text/javascript" src="${request.contextPath}/static/js/i18n/grid.locale-cn.js"></script>
<!-- This is the Javascript file of jqGrid -->
<script type="text/javascript" src="${request.contextPath}/static/js/jquery.jqGrid.min.js"></script>
<script type="text/javascript" src="${request.contextPath}/static/js/jqgrid.extend.js"></script>
<!-- This is the localization file of the grid controlling messages, labels, etc.
<!-- A link to a jQuery UI ThemeRoller theme, more than 22 built-in and many more custom -->
<link rel="stylesheet" href="${request.contextPath}/static/css/bootstrap.min.css">
<link rel="stylesheet" href="${request.contextPath}/static/css/msg.css">
<#--<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">-->
<!-- The link to the CSS that the grid needs -->
<link rel="stylesheet" type="text/css" media="screen"
href="${request.contextPath}/static/css/ui.jqgrid-bootstrap.css"/>
<sitemesh:write property='head'/>
<script>
$.jgrid.defaults.width = 1140;
$.jgrid.defaults.height = 560;
$.jgrid.defaults.styleUI = 'Bootstrap';
</script>
<script type="text/javascript" src="${request.contextPath}/static/js/bootstrap.min.js"></script>
<style type="text/css">
.navbar {
margin-bottom: 0px;
}
/*选择框高度调整*/
select[multiple], select[size] {
height: 34px;
}
</style>
</head>
<body>
<div class="container">
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
data-target="#bs-example-navbar-collapse-1">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<#--<a class="navbar-brand" href="/bbs/bbs/index">-->
<#--<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1880"><path d="M832.299957 714.965621c-71.047126 88.067778-148.2126 70.077032-148.2126 70.077032 77.615729-65.686025 74.107835-233.597269 74.107835-233.597269 56.979736 6.564509 74.104765 93.439112 74.104765 93.439112 2.635013 53.857629 148.21567 93.439112 148.21567 93.439112C887.304711 721.78698 832.299957 714.965621 832.299957 714.965621zM513.173732 467.12086c0 0-26.707268-15.610536-21.554922-82.356706 0 0 43.672662-370.594433 237.145072-313.196165 0 0 128.243803 88.283696-21.580504 292.610314C707.182354 364.178303 570.780755 504.72634 513.173732 467.12086zM560.578392 504.72634c31.834033 103.172801-49.409316 210.239282-49.409316 210.239282-95.057983 32.713053-67.633377-56.756655-67.633377-56.756655 58.984393-61.013609 42.930765-130.125663 42.930765-130.125663C512.189312 506.013659 560.578392 504.72634 560.578392 504.72634zM214.740806 668.246577c0 0-15.826454-68.054979 9.243525-99.791797 0 0 19.540032 53.137221 64.86431 6.345522 0 0 13.948687-61.868069-50.226938-47.768957 0 0 21.086248-69.096704 66.274426-103.007025-82.760912-100.30652-238.370992-82.821287-238.370992-82.821287-51.842739-30.236652 0-46.715975 0-46.715975 135.454018-0.991584 222.324528 93.439112 222.324528 93.439112-23.989367-126.739543-148.21567-163.520237-148.21567-163.520237-39.818891-48.290843 17.485233-35.68371 17.485233-35.68371 126.141932 81.534991 149.874449 190.310394 154.345274 230.244918 20.531616-12.791327 45.414329-18.281365 75.196633-7.67889 0 0 156.796092 73.606415-24.702612 256.95935C362.956476 668.246577 246.483764 776.989233 214.740806 668.246577zM706.990996 513.219781c30.182416 116.948549-22.903639 201.74584-22.903639 201.74584-59.500139 125.21891-148.21567 46.719044-148.21567 46.719044 93.910856-94.708012 98.807377-256.95935 98.807377-256.95935C675.925467 502.981602 706.990996 513.219781 706.990996 513.219781z" p-id="1881"></path></svg>-->
<#--Beetl-BBS-->
<#--</a>-->
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li id="personManagerLi"><a href="${request.contextPath}/manager/classify">类型管理</a></li>
<li id="employeeManagerLi"><a href="${request.contextPath}/system/employee">用户管理</a></li>
<li id="roleManagerLi"><a href="${request.contextPath}/system/role">角色管理</a></li>
<li id="resourceManagerLi"><a href="${request.contextPath}/system/resource">资源管理</a></li>
<li id="resourceManagerLi"><a href="${request.contextPath}/system/method">方法管理</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">板块 <span
class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="#">AAA </a></li>
<li><a href="#">BBB </a></li>
<li><a href="#">CCC </a></li>
</ul>
</li>
</ul>
<form class="navbar-form navbar-left">
<div class="form-group">
<input type="text" class="form-control" id="keyword" placeholder="关键字搜索">
</div>
<button type="button" class="btn btn-default" id="serach-btn">搜索</button>
</form>
<form id="logoutForm" action='${request.contextPath}/logout' method='post'>
</form>
<ul class="nav navbar-nav navbar-right">
<li>
<a href="javascript:$('#logoutForm').submit();" data-toggle="modal"><i class="fa fa-sign-in"></i> 退出</a>
</li>
<li><a href="#reg-dialog" data-toggle="modal"><i class="fa fa-user-plus"></i> 注册</a></li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
<sitemesh:write property='body'/>
</div>
</body>
</html>
7) SpringBoot单元测试
Controller层测试如下使用MockMvc
@RunWith(SpringRunner.class)
@SpringBootTest
public class TpaSecurityApplicationTests {
@Autowired
private RoleController roleController;
private MockMvc mvc;
@Before
public void setUp() throws Exception {
mvc = MockMvcBuilders.standaloneSetup(roleController).build();
}
@Test
public void contextLoads() throws Exception {
RequestBuilder request = null;
request = get("/system/role/1");
mvc.perform(request)
.andExpect(status().isOk());
request = put("/system/role/1")
.param("name", "测试终极大师");
// 4、put修改id为1的user
mvc.perform(request);
}
}
services mapper 层测试更简单
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {
@Autowired
private CityMapper cityMapper;
@Test
public void contextLoads() {
}
@Test
public void findByStateTest() {
City city = cityMapper.findByState("CA");
assertThat(city.getName()).isEqualTo("San Francisco");
assertThat(city.getState()).isEqualTo("CA");
assertThat(city.getCountry()).isEqualTo("US");
}
}
4 IDEA Spring boot 热部署
1) CTRL + SHIFT + A –> 查找make project automatically –> 选中 此处可以更新页面展示文件
2) 使用spring-boot-1.3开始有的热部署功能
加maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
开启热部署
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
</configuration/>
</plugin>
</plugins>
</build>
注:此时热部署更改文件class文件之后整个项目重启,时间可能比较长但是启动比较彻底
3) 使用springreload插件重启,
project 节点下添加:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
<version>1.2.6.RELEASE</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
注:修改class类时热部署,不会整个项目重启,修改比较快速,但是需要mvn spring-boot:run启动项目才可以热部署