前言
大學剛畢業,專業是電子信息工程。大一開始學Java,準確的說是高三最後的幾周開始的. 果然興趣是最好的老師, 在大一下學期自己從前端到後臺寫了我的個人網站:TODAY BLOG 。 從註冊域名到備案再到網站成功上線,我遇到過的困難不計其數。因爲感興趣所以我堅持了下來。第一個版本使用的純Servlet寫的。後來瞭解到Java有很多開源框架可以簡化我的開發。於是又投入到新一輪的學習之中… 學了Struts2後自己學着寫了一個小框架:TODAY WEB,幾百行搞定從解析xml定義的action到處理對應的請求。學了Spring MVC後,我寫了此項目:TODAY WEB 2.0。
簡介
🍎 today-web A Java library for building web applications.
安裝
<dependency>
<groupId>cn.taketoday</groupId>
<artifactId>today-web</artifactId>
<version>2.3.6.RELEASE</version>
</dependency>
拉取最新代碼 => https://github.com/TAKETODAY/today-web
快速入門
第一步: 新建項目
項目結構
第二步:引入依賴
<dependency>
<groupId>cn.taketoday</groupId>
<artifactId>today-web</artifactId>
<version>2.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>cn.taketoday</groupId>
<artifactId>today-context</artifactId>
<version>2.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
</dependency>
到此項目建立完畢,並不需要去管 web.xml
文件
第三步:配置控制器
/**
* @author Today <br>
* 2018-12-02 22:30
*/
@RestController
@RequestMapping("index")
public class IndexController {
@GET("{key}")
public String index(@PathVariable int key) {
return key + "";
}
@GET
@ResponseBody(false)
public String index() {
return "index";
}
}
Freemarker
模板
第四步:部署項目到Tomcat
查看效果
@GET("{key}")
@GET
案例
文檔
使用說明
函數式路由
@Component
@ResponseBody
public class FunctionController {
public String function(RequestContext request) {
return "body:" + request.method() + " requestURI -> " + request.requestURI();
}
public String test(RequestContext request) {
return "body:" + request.method() + " test -> " + request.requestURI();
}
public void script(RequestContext request) throws IOException {
ModelAndView modelAndView = new ModelAndView();
request.modelAndView(modelAndView);
modelAndView.setContentType("text/html;charset=UTF-8");
modelAndView.setView(new StringBuilder("<script>alert('HELLO, 你好 script');</script>"));
}
}
@Configuration
//@EnableDefaultMybatis
//@EnableRedissonCaching
public class WebMvcConfig implements WebMvcConfiguration {
@Autowired
private FunctionController functionController;
@Override
public void configureFunctionHandler(FunctionHandlerRegistry registry) {
registry.get("/function", functionController::function);
registry.get("/function/test", functionController::test);
registry.get("/function/script", functionController::script);
registry.get("/function/error/500", (context) -> {
context.sendError(500);
});
}
}
註解路由
//@Controller
@RestController
@RequestMapping("/users")
public class UserController {
@GET("index")
@POST("post")
@PUT("articles/{id}")
......
@RequestMapping("/users/{id}")
@RequestMapping(value = "/**", method = {RequestMethod.GET})
@RequestMapping(value = "/*.html", method = {RequestMethod.GET})
@RequestMapping(value = {"/index.action", "/index.do", "/index"}, method = RequestMethod.GET)
@Interceptor({LoginInterceptor.class, ...})
public (String|List<?>|Set<?>|Map<?>|void|File|Image|...) \\w+ (request, request, session,servletContext, str, int, long , byte, short, boolean, @Session("loginUser"), @Header("User-Agent"), @Cookie("JSESSIONID"), @PathVariable("id"), @RequestBody("users"), @Multipart("uploadFiles") MultipartFile[]) {
service...
return </>;
}
}
ViewController
@Configuration
public class WebMvcConfig implements WebMvcConfiguration {
@Override
public void configureViewController(ViewControllerHandlerRegistry registry) {
registry.addViewController("/github", "redirect:https://github.com");
registry.addRedirectViewController("/login.do", "/login");
registry.addViewController("/login.action")
.setAssetsPath("redirect:/login");
}
}
靜態資源
@Singleton
@Profile("dev")
public ResourceHandlerRegistry devRsourceMappingRegistry(@Env("site.uploadPath") String upload,
@Env("site.assetsPath") String assetsPath) //
{
final ResourceHandlerRegistry registry = new ResourceHandlerRegistry();
registry.addResourceMapping("/assets/**")//
.addLocations(assetsPath);
registry.addResourceMapping("/upload/**")//
.addLocations(upload);
registry.addResourceMapping("/logo.png")//
.addLocations("file:///D:/dev/www.yhj.com/webapps/assets/images/logo.png");
registry.addResourceMapping("/favicon.ico")//
.addLocations("classpath:/favicon.ico");
return registry;
}
@Singleton
@Profile("prod")
public ResourceHandlerRegistry prodResourceMappingRegistry() {
final ResourceHandlerRegistry registry = new ResourceHandlerRegistry();
registry.addResourceMapping(LoginInterceptor.class)//
.setPathPatterns("/assets/admin/**")//
.setOrder(Ordered.HIGHEST_PRECEDENCE)//
.addLocations("/assets/admin/");
return registry;
}
@Override
public void configureResourceHandler(ResourceHandlerRegistry registry) {
registry.addResourceMapping(LoginInterceptor.class)//
.setPathPatterns("/assets/admin/**")//
.setOrder(Ordered.HIGHEST_PRECEDENCE)//
.addLocations("/assets/admin/");
}
自定義參數轉換器
@Component
public class DateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) throws ConversionException {
...
}
}
也可以通過xml文件配置簡單視圖
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Web-Configuration PUBLIC
"-//TODAY BLOG//Web - Configuration DTD 2.0//CN"
"https://taketoday.cn/framework/web/dtd/web-configuration-2.3.7.dtd">
<Web-Configuration>
<controller prefix="/error/">
<action resource="400" name="BadRequest" status="400" />
<action resource="403" name="Forbidden" status="403" />
<action resource="404" name="NotFound" status="404" />
<action resource="500" name="ServerIsBusy" status="500" />
<action resource="405" name="MethodNotAllowed" status="405" />
</controller>
<controller>
<action resource="redirect:http://pipe.b3log.org/blogs/Today" name="today-blog-pipe" />
<action resource="redirect:https://taketoday.cn" name="today" />
<action resource="redirect:https://github.com" name="github" />
<action resource="redirect:/login" name="login.do" />
</controller>
<controller class="cn.taketoday.web.demo.controller.XMLController" name="xmlController" prefix="/xml/">
<action name="obj" method="obj" />
<action name="test" resource="test" method="test"/>
</controller>
</Web-Configuration>
登錄實例
@Controller
public class UserController {
/*
<controller prefix="/WEB-INF/view/" suffix=".ftl">
<action resource="login" name="login" />
<action resource="register" name="register" />
</controller> */
// @GET("login")
@RequestMapping(value = "/login" , method = RequestMethod.GET)
public String login() {
return "/login/login";//支持jsp,FreeMarker,Thymeleaf,自定義視圖
}
@Logger("登錄")
//@POST("/login")
//@RequestMapping(value = "/login" , method = RequestMethod.POST)
@ActionMapping(value = "/login", method = RequestMethod.POST)
public String login(HttpSession session, RedirectModel redirectModel, @Valid User user, Errors error) {
if (error.hasErrors()) {
System.err.println(error.getAllErrors());
redirectModel.attribute("msg", error.getAllErrors().toString());
return "redirect:/login";
}
User login = userService.login(user);
if (login == null) {
redirectModel.attribute("userId", user.getUserId());
redirectModel.attribute("msg", "登錄失敗");
return "redirect:/login";
}
redirectModel.attribute("msg", "登錄成功");
session.setAttribute(USER_INFO, login);
return "redirect:/user/info";
}
}
文件下載,支持直接返回給瀏覽器圖片
@RequestMapping(value = {"/download"}, method = RequestMethod.GET)
public File download(String path) {
return new File(path);
}
@GET("/display")
public final BufferedImage display(HttpServletResponse response) throws IOException {
response.setContentType("image/jpeg");
return ImageIO.read(new File("D:/taketoday.cn/webapps/upload/logo.png"));
}
@GET("captcha")
public final BufferedImage captcha(HttpServletRequest request) throws IOException {
BufferedImage image = new BufferedImage(IMG_WIDTH, IMG_HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics graphics = image.getGraphics();
graphics.setColor(Color.WHITE);
graphics.fillRect(0, 0, IMG_WIDTH, IMG_HEIGHT);
Graphics2D graphics2d = (Graphics2D) graphics;
drawRandomNum(graphics2d, request);
return image;
}
文件上傳,支持多文件
@RequestMapping(value = { "/upload" }, method = RequestMethod.POST)
public final String upload(@Multipart MultipartFile uploadFile) throws IOException {
String upload = "D:/www.yhj.com/webapps/upload/";
String path = upload + uploadFile.getFileName();
File file = new File(path);
uploadFile.save(file);
return "/upload/" + uploadFile.getFileName();
}
@POST({"/upload/multi"})
public final String multiUpload(HttpServletResponse response, @Multipart MultipartFile[] files) throws IOException {
String upload = "D:/www.yhj.com/webapps/upload/";
for (MultipartFile multipartFile : files) {
String path = upload + multipartFile.getFileName();
File file = new File(path);
System.out.println(path);
if (!multipartFile.save(file)) {
return "<script>alert('upload error !')</script>";
//response.getWriter().print("<script>alert('upload error !')</script>");
}
}
//response.getWriter().print("<script>alert('upload success !')</script>");
return "<script>alert('upload success !')</script>";
}
🙏 鳴謝
本項目的誕生離不開以下開源項目:
- Freemarker: Apache Freemarker
- Slf4j: Simple Logging Facade for Java
- Spring: Spring Framework
- EL: Java Unified Expression Language
- FastJSON: A fast JSON parser/generator for Java
- Lombok: Very spicy additions to the Java programming language
- Today Context: A Java library for dependency injection and aspect oriented programing
- Hibernate Validator: Hibernate Validator - Bean Validation 2.0 (JSR 380) Reference Implementation
📄 開源協議
請查看 GNU GENERAL PUBLIC LICENSE