Springboot之3.整合Web開發

3.整合Web開發

3.1 aop

  1. 添加pom依賴
 <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  1. 編寫aop切面
@Component
@Aspect
public class LogComponent {
    @Pointcut("execution(* org.javaboy.aop.service.*.*(..))")
    public void pc1() {
    }

    @Before(value = "pc1()")
    public void before(JoinPoint jp) {
        String name = jp.getSignature().getName();
        System.out.println("before--" + name);
    }

    @After(value = "pc1()")
    public void after(JoinPoint jp) {
        String name = jp.getSignature().getName();
        System.out.println("after--" + name);
    }

    @AfterReturning(value = "pc1()", returning = "result")
    public void afterReturning(JoinPoint jp, Object result) {
        String name = jp.getSignature().getName();
        System.out.println("afterReturning----" + name + "-----" + result);
    }

    @AfterThrowing(value = "pc1()",throwing = "e")
    public void afterThrowing(JoinPoint jp,Exception e) {
        String name = jp.getSignature().getName();
        System.out.println("afterThrowing---"+name+"----"+e.getMessage());
    }

    //這個方法的返回值會覆蓋切點方法的返回值
    @Around("pc1()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        Object proceed = pjp.proceed();
        return "www.javaboy.org";
    }
}

  1. 編寫測試service和controller
@Service
public class UserService {
    public String getUsernameById(Integer id) {
        System.out.println("getUsernameById");
        return "javaboy";
    }

    public void deleteUserById(Integer id) {
        System.out.println("deleteUserById");
    }
}
@RestController
public class UserController {
    @Autowired
    UserService userService;

    @GetMapping("/test1")
    public String getUsernameById(Integer id) {
        return userService.getUsernameById(id);
    }

    @GetMapping("/test2")
    public void deleteUserById(Integer id) {
        userService.deleteUserById(id);
    }
}

  1. 運行結果及代碼結構截圖
    在這裏插入圖片描述

3.2 ApplicationRunner

  1. 編寫啓動類
@Component
@Order(99)
public class MyApplicationRunner01 implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        String[] sourceArgs = args.getSourceArgs();//獲取啓動的所有參數
        System.out.println("sourceArgs:" + Arrays.toString(sourceArgs));
        List<String> nonOptionArgs = args.getNonOptionArgs();
        System.out.println("nonOptionArgs:" + nonOptionArgs);
        Set<String> optionNames = args.getOptionNames();
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>");
        for (String optionName : optionNames) {
            System.out.println(optionName + ":" + args.getOptionValues(optionName));
        }
        System.out.println(">>>>>>>>>>>>>>>MyApplicationRunner01結束>>>>>>>>>>>>>>>>");
    }
}
  1. 啓動參數配置
    在這裏插入圖片描述

3.運行結果及分析
@Order值越小,對應的ApplicationRunner越先執行

在這裏插入圖片描述

3.3 CommandLineRunner

與ApplicationRunner類似
1.用法

@Component
@Order(98)
public class MyCommandLineRunner2 implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("MyCommandLineRunner2>>>"+Arrays.toString(args));
    }
}

2.示例
在這裏插入圖片描述
3.運行結果及分析
@Order值越小,對應的CommandLineRunner越先執行
可以在springboot啓動後根據參數做一些自定義的任務,如執行sql等

3.4 ControllerAdvice

1.問題背景
假設一個post表單要提交兩個對應實體(Author,Book)的字段,如果這兩個實體(Author,Book)具有相同的字段名稱name,那麼contoller如何識別name字段是屬於哪個實體?
Book實體類

public class Book {
    private String name;
    private Double price;
    }

Author實體類

public class Author {
    private String name;
    private Integer age;

2.ControllerAdvice的配置

@ControllerAdvice
public class GlobalData {
    @ModelAttribute(value = "info")
    public Map<String,Object> mydata() {
        Map<String, Object> map = new HashMap<>();
        map.put("name", "javaboy");
        map.put("address", "www.javaboy.org");
        return map;
    }

    @InitBinder("a")
    public void initA(WebDataBinder binder) {
        binder.setFieldDefaultPrefix("a.");
    }
    @InitBinder("b")
    public void initB(WebDataBinder binder) {
        binder.setFieldDefaultPrefix("b.");
    }
}

3.Controller中的用法

@RestController
public class BookController {
    @PostMapping("/book")
    public void addBook(@ModelAttribute("b") Book book, @ModelAttribute("a") Author author) {
        System.out.println(book);
        System.out.println(author);
    }
}

查看全局數據info

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello(Model model) {
        Map<String, Object> map = model.asMap();
        Set<String> keySet = map.keySet();
        for (String key : keySet) {
            System.out.println(key + ":" + map.get(key));
        }
        return "hello";
    }
}

4.測試結果
Postman發起post請求 /book
在這裏插入圖片描述
在這裏插入圖片描述
測試全局數據info

在這裏插入圖片描述

3.5 Cors跨域訪問配置

方式1:在Controller類上添加註釋,例如@CrossOrigin(origins = "http://localhost:8081")

@RestController
@CrossOrigin(origins = "http://localhost:8081")
public class HelloController {
    @GetMapping("/hello")
    public String hello() {
        return "hello cors!";
    }
    @PutMapping("/doput")
    public String doPut() {
        return "doput";
    }
}

方式2:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**").allowedOrigins("http://localhost:8081")
                .allowedHeaders("*")
                .allowedMethods("*")
                .maxAge(30 * 1000);
    }
}

模擬跨域測試
在這裏插入圖片描述

3.6 exception 異常處理配置

  • 默認配置:
    1.優先訪問templates/error/5xx.html 或4xx.html
    2.其次訪問static/error/5xx.html 或4xx.html
  • 自定義配置
    1.自定義異常文件訪問路徑,如下配置爲javaboy.html
@Component
public class MyErrorViewResolver extends DefaultErrorViewResolver {
    /**
     * Create a new {@link DefaultErrorViewResolver} instance.
     *
     * @param applicationContext the source application context
     * @param resourceProperties resource properties
     */
    public MyErrorViewResolver(ApplicationContext applicationContext, ResourceProperties resourceProperties) {
        super(applicationContext, resourceProperties);
    }

    @Override
    public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
        ModelAndView mv = new ModelAndView();
        mv.setViewName("javaboy");
        mv.addAllObjects(model);
        return mv;
    }
}

2.自定義異常消息

@Component
public class MyErrorAttribute extends DefaultErrorAttributes {
    @Override
    public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
        Map<String, Object> map = super.getErrorAttributes(webRequest, includeStackTrace);
        map.put("myerror", "這是我自定義的異常信息!");
        return map;
    }
}

3.自定義的javaboy消息頁面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>javaboy-5xx</h1>
<table border="1">
    <tr>
        <td>path</td>
        <td th:text="${path}"></td>
    </tr>
    <tr>
        <td>timestamp</td>
        <td th:text="${timestamp}"></td>
    </tr>
    <tr>
        <td>message</td>
        <td th:text="${message}"></td>
    </tr>
    <tr>
        <td>error</td>
        <td th:text="${error}"></td>
    </tr>
    <tr>
        <td>status</td>
        <td th:text="${status}"></td>
    </tr>
    <tr>
        <td>myerror</td>
        <td th:text="${myerror}"></td>
    </tr>
</table>
</body>
</html>

4.測試效果

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello() {
        int i = 1 / 0;
        return "hello";
    }
}

在這裏插入圖片描述
在這裏插入圖片描述

3.7 fildupload 文件上傳

  1. 文件上傳配置
    application.properties裏配置
spring.servlet.multipart.max-file-size=1MB
  1. Controller
@RestController
public class FileUploadController {

    SimpleDateFormat sdf = new SimpleDateFormat("/yyyy/MM/dd/");

    @PostMapping("/upload")
    public String upload(MultipartFile file, HttpServletRequest req) {
        String format = sdf.format(new Date());
        String realPath = req.getServletContext().getRealPath("/img") + format;
        System.out.println(realPath);
        File folder = new File(realPath);
        if (!folder.exists()) {
            folder.mkdirs();
        }
        String oldName = file.getOriginalFilename();
        String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf("."));
        try {
            file.transferTo(new File(folder, newName));

            String url = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort() + "/img" + format + newName;
            return url;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "error";
    }

    @PostMapping("/uploads")
    public String uploads(MultipartFile[] files, HttpServletRequest req) {
        String format = sdf.format(new Date());
        String realPath = req.getServletContext().getRealPath("/img") + format;
        File folder = new File(realPath);
        if (!folder.exists()) {
            folder.mkdirs();
        }
        for (MultipartFile file : files) {
            String oldName = file.getOriginalFilename();
            String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf("."));
            try {
                file.transferTo(new File(folder, newName));
                String url = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort() + "/img" + format + newName;
                System.out.println(url);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return "success";
    }
}
  1. 三種頁面文件上傳方式
    單文件上傳
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
    <input type="file" name="file">
    <input type="submit" value="提交">
</form>
</body>
</html>

單文件上傳jquery -ajax方式

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="jquery3.3.1.js"></script>
</head>
<body>
<div id="result"></div>
<input type="file" id="file">
<input type="button" value="上傳" onclick="uploadFile()">
<script>
    function uploadFile() {
        var file = $("#file")[0].files[0];
        var formData = new FormData();
        formData.append("file", file);
        $.ajax({
            type:'post',
            url:'/upload',
            processData:false,
            contentType:false,
            data:formData,
            success:function (msg) {
                $("#result").html(msg);
            }
        })
    }
</script>
</body>
</html>

多文件上傳

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/uploads" method="post" enctype="multipart/form-data">
    <input type="file" name="files" multiple>
    <input type="submit" value="提交">
</form>
</body>
</html>
  1. 上傳異常處理
@ControllerAdvice
public class MyCustomException {
//    @ExceptionHandler(MaxUploadSizeExceededException.class)
//    public void myexception(MaxUploadSizeExceededException e, HttpServletResponse resp) throws IOException {
//        resp.setContentType("text/html;charset=utf-8");
//        PrintWriter out = resp.getWriter();
//        out.write("上傳文件大小超出限制!");
//        out.flush();
//        out.close();
//    }
    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public ModelAndView myexception(MaxUploadSizeExceededException e) throws IOException {
        ModelAndView mv = new ModelAndView("myerror");
        mv.addObject("error", "上傳文件大小超出限制!");
        return mv;
    }
}

異常處理視圖 myerror.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1 th:text="${error}"></h1>
</body>
</html>

3.8 Interceptor 攔截器

  1. 自定義攔截器
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion");
    }
}

  1. 配置攔截器
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(myInterceptor()).addPathPatterns("/**");
    }
    @Bean
    MyInterceptor myInterceptor() {
        return new MyInterceptor();
    }
}

3.測試結果

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-NVDyBEPE-1585797170383)(Springboot之3.整合Web開發.resources/E69C8668-17CF-483D-85D6-EB6DABFA50D4.png)]

4.總結
攔截器針對的是網絡請求路徑。
而AOP針對的是類中的方法 TODO

3.9 json

1.添加pom依賴,如果不使用原生json(默認是jackson),請打開下面的註解,選用Gson或fastjson

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
<!--            <exclusions>-->
<!--                <exclusion>-->
<!--                    <groupId>org.springframework.boot</groupId>-->
<!--                    <artifactId>spring-boot-starter-json</artifactId>-->
<!--                </exclusion>-->
<!--            </exclusions>-->
        </dependency>

<!--        Gson-->
<!--        <dependency>-->
<!--            <groupId>com.google.code.gson</groupId>-->
<!--            <artifactId>gson</artifactId>-->
<!--        </dependency>-->

<!--        fastjson-->
<!--        <dependency>-->
<!--            <groupId>com.alibaba</groupId>-->
<!--            <artifactId>fastjson</artifactId>-->
<!--            <version>1.2.49</version>-->
<!--        </dependency>-->

2.對應的json配置, Jackson,Gson和Fastjson

@Configuration
public class WebMvcConfig {

    //springboot默認使用的jackson,下面是自定義配置
    @Bean
    MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        ObjectMapper om = new ObjectMapper();
        om.setDateFormat(new SimpleDateFormat("yyyy/MM/dd"));
        converter.setObjectMapper(om);
        return converter;
    }

        @Bean
    ObjectMapper objectMapper() {
        ObjectMapper om = new ObjectMapper();
        om.setDateFormat(new SimpleDateFormat("yyyy/MM/dd"));
        return om;
    }


//   ////下面是gson的配置
//    @Bean
//    GsonHttpMessageConverter gsonHttpMessageConverter() {
//        GsonHttpMessageConverter converter = new GsonHttpMessageConverter();
//        converter.setGson(new GsonBuilder().setDateFormat("yyyy/MM/dd").create());
//        return converter;
//    }
//
//    //這個可以不用配置
//    @Bean
//    Gson gson() {
//        return new GsonBuilder().setDateFormat("yyyy/MM/dd").create();
//    }

    ////下面是fastjson的配置
//    @Bean
//    FastJsonHttpMessageConverter fastJsonHttpMessageConverter() {
//        FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
//        FastJsonConfig config = new FastJsonConfig();
//        config.setDateFormat("yyyy-MM-dd");
//        converter.setFastJsonConfig(config);
//        return converter;
//    }
}

3.實體類及controller測試

public class User {
    private Integer id;
    private String username;
    private String address;

//    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date birthday;

  //省略getter和setter
}

//@Controller
@RestController
public class UserController {
    //    @ResponseBody
    @GetMapping("/user")
    public List<User> getAllUser() {
        List<User> users = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            User user = new User();
            user.setAddress("www.javaboy.org>>" + i);
            user.setUsername("javaboy>>" + i);
            user.setId(i);
            user.setBirthday(new Date());
            users.add(user);

        }
        return users;
    }
}

3.10 paramconverter

1.自定義日期轉換器

@Component
public class DateConverter implements Converter<String,Date> {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    @Override
    public Date convert(String source) {
        if (source != null && !"".equals(source)) {
            try {
                return sdf.parse(source);
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

2.測試

@RestController
public class UserController {
    @GetMapping("/hello")
    public String hello(Date birth) {
        System.out.println(birth);
        return birth.toString();
    }
}

瀏覽器輸入http://localhost:8080/hello?birth=2020-2-2,
頁面和控制檯返回Sun Feb 02 00:00:00 CST 2020
證明轉換器可以將字符串類的參數轉化爲日期類型。

3.11 pathmapping 路徑映射

1.定義路徑映射

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/javaboy").setViewName("hello");
    }
}

作用:將訪問/javaboay的請求返回hello這個視圖
2.測試與分析
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-56qOIqtW-1585797170384)(Springboot之3.整合Web開發.resources/85E0CA34-12FB-4EE8-9D1D-392631B1A87E.png)]
Controller中的路徑配置會覆蓋addViewControllers方法中的配置。

3.12 servlet

1.servlet配置

@WebServlet(urlPatterns = "/myservlet")
public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("MyServlet");
    }
}

2.listener配置

public class MyRequestListener implements ServletRequestListener {
    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        System.out.println("requestDestroyed");
    }

    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        System.out.println("requestInitialized");
    }
}

3.filter配置

@WebFilter(urlPatterns = "/*")
public class MyFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("MyFilter");
        filterChain.doFilter(servletRequest, servletResponse);
    }
}

4.請求的生命週期
requestInitialized
MyFilter
MyServlet
requestDestroyed

在這裏插入圖片描述

3.13 static resources 靜態資源

1.靜態資源的默認加載順序,排在上面的優先加載。
src/main/resources/resources
src/main/resources/static
src/main/resources/public
2.自定義靜態資源路徑
如想要設置下面路徑爲靜態資源路徑
src/main/resources/javaboy
第一種方式 application.properties中配置

spring.resources.static-locations=classpath:/javaboy/
spring.mvc.static-path-pattern=/**

第二種方式 java文件中配置

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**").addResourceLocations("classpath:/javaboy/");
    }
}

3.14 welcom 歡迎頁面

1.關閉默認錯誤顯示頁配置
application.properties

spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration

2.自定義頁面的頁籤圖標
設置src/main/resources/favicon.ico即可
3.靜態資源習慣放在src/main/resources/static/hello.js目錄下
4.頁面模板資源習慣放在src/main/resources/templates/index.html目錄下
5.測試Controller

@Controller
public class HelloController {
    @GetMapping("/index")
    public String hello() {
        return "index";
    }
}

3.15 xml方式定義和訪問bean

1.bean實體類

public class SayHello {
    public String sayHello() {
        return "hello xml";
    }
}

2.beans.xml中配置實體 src/main/resources/beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="org.javaboy.xml.SayHello" id="sayHello"/>
</beans>

3.測試用例

@RunWith(SpringRunner.class)
@SpringBootTest
public class XmlApplicationTests {

    @Autowired
    SayHello sayHello;
    @Test
    public void contextLoads() {
        System.out.println(sayHello.sayHello());
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章