开发颠覆者SpringBoot实战---------SpringBoot的Web学习

SpringBoot提供了spring-boot-starter-web为Web开发予以支持,Web相关的自动配置存储在org.springframework.boot.autoconfigure.web下,里面有自动配置内嵌Servlet容器、自动配置http编码、自动配置上传文件的属性、配置SpringMVC等

一、Thymeleaf模版引擎

SpringBoot提供了大量的模版引擎,包括FreeMarker、Groovy、Thymeleaf、Velcoity和Mustache,SpringBoot中推荐使用Thymeleaf作为模版引擎,因为Thymeleaf提供了完美的SpringMVC支持。

Thymeleaf是一个java类库,它是一个xml/xthml/html5的模版引擎,可以作为MVC的Web应用的View层。

  • 通过xmlns:th=”http://www.thymeleaf.org”命名空间,将页面转换为动态试图,需要进行动态处理的元素使用“th:”作为前缀。
  • 通过th:text=”${person.name}”访问model中的属性。
  • 通过th:each=”person:${people}”来做循环迭代。
  • 通过th:if=”${not #lists.isEmpty(people)}”来判断people是否为空。
  • 通过th:src=”@{jquery-3.3.1.min.js}”来访问Web中的静态资源。
  • 通过th:inline=”javascript”来添加script标签,这样在JavaScript代码中即可访问model中的属性值。
  • 通过“[[${}]]”格式来获得实际值。

在传统的SpringMVC中,若我们需要集成一个模版引擎的话,需要定义ViewResolver,在SpringMVC中讲过。而Thymeleaf提供了一个SpringTemplateEngine的类,用来驱动SpringMVC下使用Thymeleaf模版引擎。而在SpringBoot中对Thymeleaf进行了自动配置,可以在application中以spring.thymeleaf开发来进行配置,不配置的情况下模版的默认目录是templates。

在src/main/resource/static中放入需要引入的静态资源:Bootstrap和jQuery。根据默认的原则在src/main/resource/templates下创建index.html文件。

<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta content="text/html" charset="UTF-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link th:href="@{bootstrap\css\bootstrap.css}" rel="stylesheet">
    <link th:href="@{bootstrap\css\bootstrap-theme.css}" rel="stylesheet">
</head>
<body>
    <div class="panel panel-primary">
        <div class="panel-heading">
            <h3 class="panel-title">访问model</h3>
        </div>
        <div class="panel-body">
            <span th:text="${singlePerson.name}"></span>
        </div>
    </div>
    <div th:if="${not #lists.isEmpty(people)}">
        <div class="panel panel-primary">
            <div class="panel-heading">
                <h3 class="panel-title">列表</h3>
            </div>
            <div class="panel-body">
                <ul class="list-group">
                    <li class="list-group-item" th:each="person:${people}">
                        <span th:text="${person.name}"></span>
                        <span th:text="${person.age}"></span>
                        <button class="btn" th:onclick="'getName(\'' + ${person.name} + '\');'">获取名字</button>
                    </li>
                </ul>
            </div>
        </div>
    </div>
    <script th:src="@{jquery-3.3.1.min.js}" type="text/javascript"></script>
    <script th:src="@{bootstrap\js\bootstrap.js}" type="text/javascript"></script>
    <script th:inline="javascript">
        var single = [[${singlePerson}]];
        console.log(single.name + "/" + single.age);
        function getName(name) {
            console.log(name);
        }
    </script>
    </body>
</html>

准备数据:

/**
 * 模板引擎展示数据
 * @author think
 */
public class Person {
    private String name;
    private Integer age;
    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
    public Person() {
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
}

/**
 * 控制器
 * @author think
 */
@SpringBootApplication
@Controller
public class Springboot2ThymeleafApplication {
    @RequestMapping("/")
    public String index(Model model){
        Person single = new Person("single", 20);
        List<Person> people = new ArrayList<>();
        Person p1 = new Person("xx", 20);
        Person p2 = new Person("yy", 30);
        Person p3 = new Person("zz", 40);
        people.add(p1);
        people.add(p2);
        people.add(p3);
        model.addAttribute("singlePerson", single);
        model.addAttribute("people", people);
        return "index";
    }
    public static void main(String[] args) {
        SpringApplication.run(Springboot2ThymeleafApplication.class, args);
    }
}

二、Web相关配置

通过查看WebMvcAutoConfiguration及WebMvcProperties源码,可以发现SpringBoot通过了如下的自动配置:

1、自动配置的ViewResolver

1)ContentNegotiatingViewResolver

这是SpringMVC提供的一个特殊的ViewResolver,ContentNegotiatingViewResolver不是自己处理View,而是代理给不同的ViewResolver来处理不用的view,所以它有最高的优先级。

@Bean
@ConditionalOnBean({ViewResolver.class})
@ConditionalOnMissingBean(
    name = {"viewResolver"},
    value = {ContentNegotiatingViewResolver.class}
)
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
    ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
    resolver.setContentNegotiationManager((ContentNegotiationManager)beanFactory.getBean(ContentNegotiationManager.class));
    resolver.setOrder(-2147483648);
    return resolver;
}

2)BeanNameViewResolver

在控制器中的一个方法返回的字符串值会根据BeanNameViewResolver去查找Bean的名称为返回的字符串的view来进行视图渲染。

/**
 * 这是源码
 */
@Bean
@ConditionalOnBean({View.class})
@ConditionalOnMissingBean
public BeanNameViewResolver beanNameViewResolver() {
    BeanNameViewResolver resolver = new BeanNameViewResolver();
    resolver.setOrder(2147483637);
    return resolver;
}

/**
 * 控制器
 * @author think
 */
@SpringBootApplication
@Controller
public class Springboot2ThymeleafApplication {
    @Bean
    public MappingJackson2JsonView jsonView(){
        return new MappingJackson2JsonView();
    }
    @RequestMapping(value = "/json", produces = {MediaType.APPLICATION_JSON_VALUE})
    public String index(Model model){
        Person single = new Person("single", 20);
        model.addAttribute("singlePerson", single);
        return "jsonView";//返回的就是上面定义的Bean
    }
    public static void main(String[] args) {
        SpringApplication.run(Springboot2ThymeleafApplication.class, args);
    }
}

3)InternalResourceViewResolver

这是一个极为常用的ViewResolver,主要通过设置前缀、后缀、以及控制器中的方法来返回视图名的字符串,以得到实际页面。

@Bean
@ConditionalOnMissingBean
public InternalResourceViewResolver defaultViewResolver() {
    InternalResourceViewResolver resolver = new InternalResourceViewResolver();
    resolver.setPrefix(this.mvcProperties.getView().getPrefix());
    resolver.setSuffix(this.mvcProperties.getView().getSuffix());
    return resolver;
}

2、自动配置的静态资源

将静态文件放置在resources/static、resources/public、resources/resources中,直接遍可访问静态资源。

3、接管SpringBoot的Web配置

通常情况下,SpringBoot的自动配置是符合我们大多少要求的,但是想要增加自己的额外配置时候,可以继承WebMvcConfigurerAdapter(貌似已经过时了),不需要使用@EnableWebMvc。

4、注册Servlet、Filter、Listener

当使用嵌入式Servlet容器时,我们通过将Servlet、Filter、Listener声明为Spring Bean的方式,或者注册。

/**
 * 直接注册Bean
 * @author think
 */
@Bean
public XXServlet xxServlet(){
    return new XXServlet();
}
@Bean
public YYFilter yyFilter(){
    return new YYFilter();
}
@Bean
public ZZListener zzListener(){
    return new ZZListener();
}

/**
 * 通过RegistrationBean
 * @author think
 */
 @Bean
public ServletRegistrationBean servletRegistrationBean(){
    return new ServletRegistrationBean(new XXServlet(), "/xx/*");
}
@Bean
public FilterRegistrationBean filterRegistrationBean(){
    FilterRegistrationBean bean = new FilterRegistrationBean();
    bean.setFilter(new YYFilter());
    bean.setOrder(2);
    return bean;
}
@Bean
public ServletListenerRegistrationBean servletListenerRegistrationBean(){
    return new servletListenerRegistrationBean<ZZListener>(new XXServlet());
}

三、tomcat配置

SpringBoot默认内嵌的servlet容器为tomcat,通用的servlet配置都以“servlet”为前缀,而tomcat的配置都以“servlet.tomcat”为前缀。

//配置访问路径
server.servlet.context-path=/
//配置端口号
server.port=8088
//配置session过期时间,以秒为单位
server.servlet.session.timeout=600
//tomcat默认编码
server.tomcat.uri-encoding=UTF-8

SSL配置:

server.ssl.key-store=214525419750767.pfx
server.ssl.key-store-password=214525419750767
server.ssl.key-store-type=PKCS12
server.ssl.key-alias=tomcat

代码配置tomcat:

@Bean
public ServletWebServerFactory servletFactory(){
    TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(){
        //重载方法设置https访问
        @Override
        protected void postProcessContext(Context context) {
            SecurityConstraint constraint = new SecurityConstraint();
            constraint.setUserConstraint("CONFIDENTIAL");
            SecurityCollection collection = new SecurityCollection();
            collection.addPattern("/*");
            constraint.addCollection(collection);
            context.addConstraint(constraint);
            super.postProcessContext(context);
        }
    };
    factory.setPort(8088);
    factory.setContextPath("/mySpringBoot");
    //配置错误页面,将错误页面放置在resource/static静态文件夹下即可
    factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/404.html"));
    factory.addAdditionalTomcatConnectors(httpConnertor());
    return factory;
}
@Bean
public Connector httpConnertor(){
    Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
    connector.setScheme("http");
    connector.setPort(8080);
    connector.setSecure(false);
    connector.setRedirectPort(8443);
    return connector;
}

https的设置方法与tomcat服务器的server.xml文件设置基本一致,下面是我自己设置过的tomcat服务器,可以做一下比较一下:

<Connector port="8081" 
    protocol="org.apache.coyote.http11.Http11NioProtocol" 
    redirectPort="8443"
    ....
    />

<Connector port="8443" 
    protocol="org.apache.coyote.http11.Http11NioProtocol"
    maxThreads="150" 
    SSLEnabled="true" 
    scheme="https" 
    secure="true"
    clientAuth="false" 
    sslProtocol="TLS" 
    keystoreFile="F:\server\pingdian\apache-tomcat-7.0.81\conf\cert\214525419750767\214525419750767.pfx"
    keystorePass="214525419750767"
    keystoreType="PKCS12"/>

四、Favicon设置

SpringBoot提供一个默认的Favicon,每次访问都可以看到,可以在application.properties中设置关闭:

spring.mvc.favicon.enabled=false

需要设置自己Facicon,则只要把自己的文件(文件名不能变)放置在类路径/resource/static下就可以。

五、WebSocket

WebSocket为向浏览器和服务器提供了双工异步通信的功能,即浏览器可以向服务器发送消息,服务器也可以向浏览器发送消息。它是通过一个socket来实现双工异步功能,下面的代码中使用它的子协议STOMP,一个更高级的协议,减少开发难度。

1、广播式

广播式即订阅模式,会发送消息给所有连接了当前节点的浏览器。

1)配置WebSocket

/**
 * 配置WebSocket
 * @author think
 */
@Configuration
@EnableWebSocketMessageBroker//开启使用STOMP协议来传输基于代理的消息
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    //注册STOMP协议节点endpoint,指定使用SockJS协议,并映射指定的url
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/endpointWisely").withSockJS();
    }
    /*@Override
    //配置消息代理
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/topic");
    }*/
}

2)两种实体类

/**
 * 服务端向浏览器发送的消息使用此类接受
 * @author think
 */
public class WiselyResponse {
    private String responseMessage;
    public WiselyResponse(String responseMessage) {
        this.responseMessage = responseMessage;
    }
    public String getResponseMessage() {
        return responseMessage;
    }
}

/**
 * 浏览器向服务端发送的消息使用此类接受
 * @author think
 */
public class WiselyMessage {
    private String name;
    public String getName(){
        return name;
    }
}

3)控制器

/**
 * 控制器
 * @author think
 */
@Controller
public class WsController {
    @MessageMapping("/welcome")//当浏览器向服务端发送请求时,通过此注解映射welcome的地址,类似@RequestMapping
    @SendTo("/topic/getResponse")//当服务端有消息时,会对订阅了此路径的浏览器发送消息
    public WiselyResponse say(WiselyMessage message) throws InterruptedException {
        Thread.sleep(3000);
        return new WiselyResponse("welcome, " + message.getName());
    }
    //访问路径映射页面
   @RequestMapping("/ws")
    public String turn2WS(){
        return "ws";
    }
}

4)在resource/templates下新建ws.html,其中需要两个js文件,可以自己下载

<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta content="text/html" charset="UTF-8">
    <title>广播式</title>
</head>
<body onload="disconnect()">
<noscript><h2 style="color: #00a0e9">貌似你的浏览器不支持websocket</h2></noscript>
<div>
    <div>
        <button id="connect" onclick="connect()">连接</button>
        <button id="disconnect" onclick="disconnect()">断开连接</button>
    </div>
    <div id="conversationDiv">
        <label>输入你的名字</label>
        <input type="text" id="name">
        <button id="sendName" onclick="sendName()">发送</button>
        <p id="response"></p>
    </div>
</div>
<script th:src="@{sockjs.js}"></script>
<script th:src="@{stomp.js}"></script>
<script type="text/javascript">
    var stompClient = null;
    function setConnected(connected) {
        document.getElementById('conversationDiv').style.visibility = connected ? 'visible' : 'hidden';
        document.getElementById('response').innerHTML = '';
    }
    function connect() {
        //连接SockJS的endpoint名称为'/endpointWisely'
        var socket = new SockJS('/endpointWisely');
        //使用STOMP子协议的WebSocket客户端
        stompClient = Stomp.over(socket);
        //连接客户端
        stompClient.connect({}, function (frame) {
            setConnected(true);
            console.log('Connected:' + frame);
            //订阅/topic/getResponse
            stompClient.subscribe('/topic/getResponse', function (response) {
                showResponse(JSON.parse(response.body).responseMessage);
            });
        });
    }
    function disconnect() {
        if (stompClient != null){
            stompClient.disconnect();
        }
        setConnected(false);
        console.log("Disconnected")
    }
    function sendName() {
        var name = document.getElementById('name').value;
        //向服务器发送消息,控制器中定义的
        stompClient.send("/welcome", {}, JSON.stringify({'name':name}));
    }
    function showResponse(message) {
        document.getElementById('response').innerHTML = message;
    }
</script>
</body>
</html>

运行后即可在所有浏览器窗口接收到其中一个浏览器发送的信息。

2、点对点

即一对一的聊天式,需要先引入Spring Security。

1)Security的简单配置

/**
 * Security的简单配置
 * @author think
 */
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/", "/login").permitAll()//对“/”和“/login”路径不拦截
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login")//登陆后的访问路径是“login”
                .defaultSuccessUrl("/chat")//登录成功后转向“/chat”
                .permitAll()
                .and()
                .logout()
                .permitAll();
        super.configure(http);
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //配置两个用户
        auth.inMemoryAuthentication()
                .withUser("wyf").password("wyf").roles("USER")
                .and()
                .withUser("wisely").password("wisely").roles("USER");
        super.configure(auth);
    }
    @Override
    public void configure(WebSecurity web) throws Exception {
        //对静态资源不拦截
        web.ignoring().antMatchers("/resources/static/**");
        super.configure(web);
    }
}

2)配置WebSocket

/**
 * 配置WebSocket
 * @author think
 */
@Configuration
@EnableWebSocketMessageBroker//开启使用STOMP协议来传输基于代理的消息
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    //注册STOMP协议节点endpoint,指定使用SockJS协议,并映射指定的url
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/endpointChat").withSockJS();
    }
    @Override
    //配置消息代理
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/queue");
    }
}

3)控制配置

/**
 * 控制器
 * @author think
 */
@Controller
public class WsController {
    //通过SimpMessagingTemplate向浏览器发送消息
    @Autowired
    private SimpMessagingTemplate messagingTemplate;
    @MessageMapping("/chat")
    public void handleChat(Principal principal, String msg){
        //如果用户是xiaoming,则信息发给xiaowang,反之
        if (principal.getName().equals("wyf")){
            //发送消息,第一个参数用户,第二个浏览器订阅地址,第三个消息本身
            messagingTemplate.convertAndSendToUser("wisely", "/queue/notifications", principal.getName() + "-send:" + msg);
        }else {
            messagingTemplate.convertAndSendToUser("wyf", "/queue/notifications", principal.getName() + "-send:" + msg);
        }
    }
    //访问路径映射页面
    @RequestMapping("/login")
    public String login(){
        return "login";
    }
    @RequestMapping("/chat")
    public String chat(){
        return "chat";
    }
}

4)在resource/templates下新建两个html:login.html和chat.html

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
    <meta content="text/html" charset="UTF-8">
    <title>登录</title>
</head>
<body>
<div th:if="${param.error}">
    无效的账户和密码
</div>
<div th:if="${param.logout}">
    您已注销
</div>
<form th:action="@{/login}" method="post">
    <div><label>帐号:<input type="text" name="username"></label></div>
    <div><label>密码:<input type="password" name="password"></label></div>
    <div><input type="submit" value="登录"></div>
</form>
</body>
</html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta content="text/html" charset="UTF-8">
    <title>聊天</title>
</head>
<body>
<p>聊天室</p>
<form id="wiselyForm">
    <textarea rows="4" cols="60" name="text"></textarea>
    <input type="submit">
</form>
<div id="output"></div>
<script th:src="@{sockjs.js}"></script>
<script th:src="@{stomp.js}"></script>
<script th:inline="javascript">
    document.getElementById('wiselyForm').onsubmit(function (e) {
        e.preventDefault();
        var text = document.getElementById('wiselyForm').getElementsByTagName('textarea').value;
        sendSpittle(text);
    });
    var sock = new SockJS('endpointChat');
    var stomp = Stomp.over(sock);
    stomp.connect('guest', 'guest', function (frame) {
        stomp.subscribe('user/queue/notifications', handleNotification)
    })
    function handleNotification(message) {
        document.getElementById('output').innerHTML = '<b>Received:' + message.body + '</b><br/>'
    }
    function sendSpittle(text) {
        stomp.send('/chat', {}, text);
    }
</script>
</body>
</html>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当前课程中博客项目的实战源码是我在 GitHub上开源项目 My-Blog,目前已有 3000 多个 star:本课程是一个 Spring Boot 技术栈的实战类课程,课程共分为 3 大部分,前面两个部分为基础环境准备和相关概念介绍,第三个部分是 Spring Boot 个人博客项目功能的讲解,通过本课程的学习,不仅仅让你掌握基本的 Spring Boot 开发能力以及 Spring Boot 项目的大部分开发使用场景,同时帮你提前甄别和处理掉将要遇到的技术难点,认真学完这个课程后,你将会对 Spring Boot 有更加深入而全面的了解,同时你也会得到一个大家都在使用的博客系统源码,你可以根据自己的需求和想法进行改造,也可以直接使用它来作为自己的个人网站,这个课程一定会给你带来巨大的收获。作者寄语本课程录制于 2020 年,代码基于 Spring Boot 2.x 版本。到目前为止,Spring Boot 技术栈也有一些版本升级,比如 Spring Boot 2.7 发版、Spring Boot 3.x 版本发布正式版本。对于这些情况,笔者会在本课程实战项目的开源仓库中创建不同的代码分支,保持实战项目的源码更新,保证读者朋友们不会学习过气的知识点。课程特色 课程内容紧贴 Spring Boot 技术栈,涵盖大部分 Spring Boot 使用场景。开发教程详细完整、文档资源齐全、实验过程循序渐进简单明了。实践项目页面美观且实用,交互效果完美。包含从零搭建项目、以及完整的后台管理系统和博客展示系统两个系统的功能开发流程。技术栈新颖且知识点丰富,学习后可以提升大家对于知识的理解和掌握,对于提升你的市场竞争力有一定的帮助。实战项目预览    

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值