前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >springBoot开发

springBoot开发

作者头像
西红柿炒鸡蛋
发布2020-01-15 16:07:03
3780
发布2020-01-15 16:07:03
举报
文章被收录于专栏:自学笔记自学笔记

系统设计

整个博客系统包括用户管理,安全设置,博客管理,评论管理,点赞管理,分类管理,标签管理和首页搜索。前端使用boostrap,thymeleaf模板引擎,jQuery等等,后端使用springboot,springMVC,spring data,spring security管理安全,数据存储使用MySQL,H2,MongoDB,MongoDB主要是存储文件等等,其他还有ElasticSearch,这次项目就尝试使用Gradle,不用maven了。

用户管理

注册,登录,增加用户,修改用户,删除用户,搜索用户。

安全设置

角色授权,权限设置

博客管理

发表博客,编辑博客,删除博客,分类博客,设置标签, 上传图片,模糊查询,最新排行,最热排序,阅读量统计

评论管理

发表评论,删除评论,统计评论,

点赞管理

点赞,取消点赞,统计

分类管理

创建分类,编辑分类,删除分类,按分类查询

标签管理

创建,删除,查询

首页管理

全文检索,最新文章,最热文章,热门标签,热门用户,热门文章,最新发布

配置环境gradle

这个环境有点奇怪,用idea的spring initial创建会出现找不到spring boot驱动的问题,发现是gradle没有选择global环境,如果没有选择就默认是找本地,本地没有当然报错了。选了globe还有问题,出现了A problem occurs from '项目名',仔细看发现他每一层都报了错,后来尝试着直接从官网配置好了下载下来,然后又是漫长的等待,真的很漫长,不知道是电脑垃圾还是咋地,感觉配置起来是比maven简单,build-gradle里面也比maven好理解,就是等太久了,我开始还以为是死机了。

4分钟才构建好,中途我还以为是电脑垃圾。还是只能直接从官网下载,idea自己spring initial不知道为什么总是出现springbootV2.2.2驱动不能识别的问题。如果这个时候心急了直接点击运行,会出现:Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.很明显是因为数据库没配好(因为我导入的时候把jpa引入了),这个时候按照提示把数据库补上就好了。

项目结构

项目里面有一个build.gradle:

这个文件是整个项目的一个脚本,里面都是gradle语言的语法,respositries里面的mavenCentral()就是指定使用中央仓库。build整个文件就是gradle构建之后生成的,gradle目录里面有一个wrapper,这个东西是可以使其自动下载gradle,wrapper可以省去安装gradle的步骤gradle-wrapper.properties文件就是配置文件,这个文件最后一行:distributionUrl=https://services.gradle.org/distributions/gradle-6.0.1-all.zip

就是gradle的版本,src就是源码了,test测试代码。

测试一下看看行不行吧。创建一个controller类,controller类需要进行Http请求,所以需要MockMvc,注意这个MockMvc需要用AutoConfigureMockMvc注释

代码语言:javascript
复制
@SpringBootTest
@AutoConfigureMockMvc
class HelloControllerTest {

    @Autowired
    private MockMvc mockMvc;
    @Test
    void hello() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/hello").accept(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.content().string(Matchers.equalTo("Hello world!")));
    }
}

后面那一堆就是测试,请求首先判断状态是不是200,判断是不是返回了hello world。和原来的有些不一样,可能这里没有用到RunWith注解,使用RunWith注解以便在测试开始的时候自动创建Spring的应用上下文,注解了@RunWith就可以直接使用spring容器,直接使用@Test注解,不用启动spring容器,但是这里用是gradle是6版本,不支持Junit4,只支持Junit5。

集成Thymeleaf

变量表达式
{...},比如<span th:text = "
{...},比如<span th:text = "

{book}">,变量表达式里面是变量

消息表达式

#{...}, <th th:text = "#{header.address}">...</th>,也称为文本外部化,国际化等等。

选择表达式

<div th:object = "${book}"> <span th:text = "*{title}"></span> </div>与变量表达式区别,他们是在当前选择的对象而不是整个上下文变量映射上执行,比如这里的title只是变量book。

链接表达式

@{...}这个没什么好说的,但是种类很多。

分段表达式

<div th:fragent = "copy"> </div> 还有其他的一些比较大小等等。

迭代器

th:each,数组,map,list都可以用迭代器。状态变量,index这些其实就是索引。 实践一下,简单写一个crud用户管理。首先需要有控制器,控制器两个注解,@RestController,这个注解其实就是controller注解和Respondebody这两个注解的集合,还有一个RequestMapping,get,post注解都可以接受。rest风格的注解就用到两个,GetMapping,PostMapping注解。

代码语言:javascript
复制
@RestController
@RequestMapping("/users")
public class UserController {
    @Autowired
    private UserRepository userRepository;

    @GetMapping
    public ModelAndView list(Model model) {
        model.addAttribute("userList", userRepository.listUser());
        model.addAttribute("title", "用户管理");
        return new ModelAndView("users/list", "userModel", model);
    }

    @GetMapping("/delete/{id}")
    public ModelAndView delete(@PathVariable("id") Long id){
        userRepository.deleteUser(id);
        return new ModelAndView("redirect:/users");
    }

    @GetMapping("{id}")
    public ModelAndView view(@PathVariable("id") Long id, Model model) {
        User user = userRepository.getUserById(id);
        model.addAttribute("user", user);
        model.addAttribute("title", "查看用户");
        return new ModelAndView("users/view", "userModel", model);
    }

    @GetMapping("/modify/{id}")
    public ModelAndView modify(@PathVariable("id") Long id, Model model) {
        User user = userRepository.getUserById(id);
        model.addAttribute("user", user);
        model.addAttribute("title", "修改用户");
        return new ModelAndView("users/form", "userModel", model);
    }


    @GetMapping("/form")
    public ModelAndView createForm(Model model) {
        model.addAttribute("user", new User());
        model.addAttribute("title", "创建用户");
        return new ModelAndView("users/form", "userModel", model);
    }

    @PostMapping
    public ModelAndView saveOrUpdateUser(User user) {
        userRepository.saveOrUpdate(user);
        return new ModelAndView("redirect:/users");
    }
}

ModelAndView最后返回redirect:/users是重定向,其实就是又回到某个Controller,如果是直接返回users/form就是返回页面。然后就是html页面的编写:

代码语言:javascript
复制
<!DOCTYPE html>
<html  xmlns:th="http://www.thymeleaf.org"
       xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
    <meta charset="UTF-8">
    <title>Thymeleaf in action</title>
</head>
<body>
<div th:replace="~{fragements/header :: header}"></div>
<h3 th:text="${userModel.title}">greenArrow</h3>
<div>
    <a href="/users/form.html" th:href="@{/users/form}">创建用户 </a>
</div>
<table border="1">
    <thead>
    <tr>
        <td>ID</td>
        <td>Email</td>
        <td>Name</td>
    </tr>
    </thead>
    <tbody>
    <tr th:if="${userModel.userList.size()} eq 0">
        <td colspan="3">无用户信息</td>
    </tr>
    <tr th:each="user : ${userModel.userList}">
        <td th:text="${user.id}"></td>
        <td th:text="${user.email}"></td>
        <td><a th:href="@{'/users/' + ${user.id}}" th:text="${user.name}"></a></td>

    </tr>
    </tbody>
</table>
<div th:replace="~{fragements/footer :: footer}"></div>
</body>
</html>

主要就是熟悉几个标签而已,片段th:replace等等。

ElasticSearch

主要就是应用全文检索,建立本库-》建立索引-》执行搜索-》过滤结果。有点像lucene检索,除了ElasticSearch还有solr,也是一个级别的检索,但都是基于lucene来进行实现的。ES高度可扩展的开源全文搜索和分析引擎,快速的,近实时的对大数据进行存储,搜索和分析。ES特点:首先他是分布式,会把内容分开到多个分片上, 高可用,多类型。近实时:就是接近实时而不是正在的实时,在搜索和可搜索文档之间延时1s,Lucene是可以做到的,这种实时要不是牺牲索引效率,每次索引都要刷新一次,要么牺牲查询效率,每次查询前都要进行刷新,而ES是固定时间刷新一次,一般设置一秒。索引建立之后不会直接写入磁盘,而是通过刷新同步到磁盘里面去。集群,就是一个或者多个节点的集合,保存应用的全部数据,提供基于全部节点的集成似的索引功能,默认就是index elaticsearch。索引,每个索引都有一个名称,通过这个名称可以对文档进行crud操作,单个集群可以定义容易数量的索引。分片,企业中索引存储比较大,一般会超过单个索引节点所能处理的范围,而ES是可以分片也可以聚合,对于分片数据还要建立一个副本。

集成springboot和ElasticSearch

首先需要一个ES服务器,需要spring data es的支持,还需要JNA的一个库。

定义一个pojo,作为索引,而在es中索引的最小单位是 document文档,所以这个类要设置成document:

代码语言:javascript
复制
@Document(indexName = "blog", type = "blog")
public class EsBlog implements Serializable {
    @Id
    private String id;
    private String title;
    private String summary;
    private String content;

    protected EsBlog() {
    }

    public EsBlog(String title, String summary, String content) {
        this.title = title;
        this.summary = summary;
        this.content = content;
    }


    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getSummary() {
        return summary;
    }

    public void setSummary(String summary) {
        this.summary = summary;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    @Override
    public String toString() {
        return "EsBlog{" +
                "id='" + id + '\'' +
                ", title='" + title + '\'' +
                ", summary='" + summary + '\'' +
                ", content='" + content + '\'' +
                '}';
    }
}

然后建立索引repository:

代码语言:javascript
复制
public interface EsBlogRepository extends ElasticsearchRepository<EsBlog, String> {
    Page<EsBlog> findDistinctEsBlogByTitleContainingOrSummaryContainingOrContentContaining(String title, String summary, String Content, Pageable pageable);
}

还要下载elasticsearch,Mac不需要安装,直接在bin目录下打开运行sh ./elasticsearch,如果打开localhost:9200在浏览器出现:

9200端口是HTTP访问的端口,而9300端口是jar或者集群之间的通信端口,所以如果浏览器访问需要9200端口,而在gradle中配置需要配置9300端口:

写一个简单的测试用例:

代码语言:javascript
复制
@SpringBootTest
class EsBlogRepositoryTest {
    @Autowired
    private EsBlogRepository esBlogRepository;

    @BeforeEach
    public void initRepository() {
        esBlogRepository.deleteAll();
        esBlogRepository.save(new EsBlog("asxaxs", "qsqdwq", "kswwqd"));
        esBlogRepository.save(new EsBlog("awews喂喂喂xaxs", "qsdwfwqdwq", "kswwqw我弟弟qd"));
        esBlogRepository.save(new EsBlog("w驱蚊器无eweasxa", "qwewfesqdwq", "wefwefkssdsfwwqd"));
    }

    @Test
    void findDistinctEsBlogByTitleContainingOrSummaryContainingOrContentContaining() {
        Pageable pageable = PageRequest.of(0, 20);
        String title = "xs";
        String summary = "喂喂";
        String content = "wjke";
        Page<EsBlog> page = esBlogRepository.findDistinctEsBlogByTitleContainingOrSummaryContainingOrContentContaining(title, summary, content, pageable);
        org.assertj.core.api.Assertions.assertThat(page.getTotalElements()).isEqualTo(2);
        for (EsBlog blog : page.getContent()) {
            System.out.println(blog.toString());
        }
    }
}

运行前肯定需要先初始化es,所以加上一个@BeforeEach,Junit4用@Before,BeforeEach是Junit5。 接下来完成一下控制层,刚刚基本服务已经测试过了:

代码语言:javascript
复制
@RestController
@RequestMapping("/blogs")
public class BlogController {
    @Autowired
    private EsBlogRepository esBlogRepository;

    @GetMapping("/list")
    public List<EsBlog> list(@RequestParam(value = "title") String title,
                             @RequestParam(value = "summary") String summary,
                             @RequestParam(value = "content") String content,
                             @RequestParam(value = "pageIndex", defaultValue = "0") int pageIndex,
                             @RequestParam(value = "pageSize", defaultValue = "10") int pageSize) {
        Pageable pageable = PageRequest.of(pageIndex, pageSize);
        Page<EsBlog> page = esBlogRepository.findDistinctEsBlogByTitleContainingOrSummaryContainingOrContentContaining(title, summary, content, pageable);
        return page.getContent();
    }
}

使用postman测试一下:

架构设计与分层

如果一个架构不分层,那么就可能存在JSP访问数据库的例子,代码不清晰,难以维护,而且这种各方都混杂在一起职责不够清晰,代码也没有分工。博客系统职责划分,博客系统分为了两套系统,纯粹的博客系统和文件管理系统,文件管理比如图片等等吧,这也是两个主要核心子系统,博客系统需要的肯定是关系型数据库,比如MySQL,Oracle,当然也有非关系型,比如ES就是;而文件系统就一般用nosql了,比如MongoDB,通过restful api进行交互。

spring security

核心领域的概念
  • 认证:认证是建立主体的过程,主体通常是可以在应用程序中执行操作的用户,设备或其他系统,不一定是人。
  • 授权:或称为访问控制,授权是指是否允许在应用程序中执行操作。 首先进行安全配置。 需要创建一个类 :
代码语言:javascript
复制
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/css/**", "/js/**", "/fonts/**", "/index").permitAll()
                .antMatchers("/users/**").hasRole("ADMIN")
                .and()
                .formLogin()
                .loginPage("/login").failureUrl("/login-error");
    }
}

重写权限配置方法,静态资源比如css,js,font文件夹下面的不用拦截,而users路径下的需要拦截,表单验证方法。上面是系统的权限管理,然后还有主体的权限管理:

代码语言:javascript
复制
    /**
     * 认证信息管理
     */
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("greenArrow").password("qazwsxedc").roles("ADMIN");
    }
}

greenArrow有权限ADMIN,和上面的hasRoles保持一致。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 系统设计
  • 配置环境gradle
  • 项目结构
  • 集成Thymeleaf
  • ElasticSearch
  • 架构设计与分层
  • spring security
相关产品与服务
访问管理
访问管理(Cloud Access Management,CAM)可以帮助您安全、便捷地管理对腾讯云服务和资源的访问。您可以使用CAM创建子用户、用户组和角色,并通过策略控制其访问范围。CAM支持用户和角色SSO能力,您可以根据具体管理场景针对性设置企业内用户和腾讯云的互通能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档