3.整合Web開發
3.1 aop
- 添加pom依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- 編寫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";
}
}
- 編寫測試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);
}
}
- 運行結果及代碼結構截圖
3.2 ApplicationRunner
- 編寫啓動類
@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結束>>>>>>>>>>>>>>>>");
}
}
- 啓動參數配置
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 文件上傳
- 文件上傳配置
application.properties裏配置
spring.servlet.multipart.max-file-size=1MB
- 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";
}
}
- 三種頁面文件上傳方式
單文件上傳
<!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>
- 上傳異常處理
@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 攔截器
- 自定義攔截器
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");
}
}
- 配置攔截器
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myInterceptor()).addPathPatterns("/**");
}
@Bean
MyInterceptor myInterceptor() {
return new MyInterceptor();
}
}
3.測試結果
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.測試與分析
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());
}
}