From 63ac1ff3ed596680494cc4d5bb3ae250e51bd1e3 Mon Sep 17 00:00:00 2001 From: Wendal Chen Date: Wed, 17 Apr 2019 19:38:16 +0800 Subject: [PATCH] =?UTF-8?q?add:=20=E9=87=8D=E6=96=B0=E5=AE=9E=E7=8E=B0star?= =?UTF-8?q?ter-thymeleaf,=E5=B9=B6=E6=B7=BB=E5=8A=A0thymeleaf+shiro?= =?UTF-8?q?=E7=9A=84demo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refer: https://nutz.cn/yvr/t/7gareesackiftr1bgo2tadmobq --- .../pom.xml | 60 ++++++++ .../io/nutz/demo/simple/MainLauncher.java | 60 ++++++++ .../java/io/nutz/demo/simple/bean/User.java | 50 +++++++ .../nutz/demo/simple/module/UserModule.java | 58 ++++++++ .../simple/shiro/SimpleAuthorizingRealm.java | 66 +++++++++ .../src/main/resources/application.properties | 11 ++ .../src/main/resources/log4j.properties | 8 ++ .../src/main/resources/template/home.html | 10 ++ .../src/main/resources/template/index.html | 19 +++ .../src/main/resources/template/login.html | 36 +++++ nutzboot-demo/nutzboot-demo-simple/pom.xml | 2 +- .../boot/starter/thymeleaf/ThymeleafView.java | 49 ++----- .../thymeleaf/ThymeleafViewMakerStarter.java | 135 ++++++++++++++++-- 13 files changed, 508 insertions(+), 56 deletions(-) create mode 100644 nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/pom.xml create mode 100644 nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/java/io/nutz/demo/simple/MainLauncher.java create mode 100644 nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/java/io/nutz/demo/simple/bean/User.java create mode 100644 nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/java/io/nutz/demo/simple/module/UserModule.java create mode 100644 nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/java/io/nutz/demo/simple/shiro/SimpleAuthorizingRealm.java create mode 100644 nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/resources/application.properties create mode 100644 nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/resources/log4j.properties create mode 100644 nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/resources/template/home.html create mode 100644 nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/resources/template/index.html create mode 100644 nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/resources/template/login.html diff --git a/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/pom.xml b/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/pom.xml new file mode 100644 index 00000000..06132880 --- /dev/null +++ b/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/pom.xml @@ -0,0 +1,60 @@ + + 4.0.0 + + org.nutz + nutzboot-demo-simple + 2.3.5-SNAPSHOT + + nutzboot-demo-simple-thymeleaf-shiro + + + org.nutz + nutzboot-starter-thymeleaf + + + nz.net.ultraq.thymeleaf + thymeleaf-layout-dialect + 2.2.2 + + + org.nutz + nutzboot-starter-nutz-mvc + + + org.nutz + nutzboot-starter-nutz-dao + + + org.nutz + nutzboot-starter-jdbc + + + org.nutz + nutzboot-starter-shiro + + + org.nutz + nutzboot-starter-jetty + + + org.slf4j + slf4j-log4j12 + + + org.nutz + nutzboot-starter-jdbc + + + com.h2database + h2 + 1.4.196 + + + com.github.theborakompanioni + thymeleaf-extras-shiro + 2.0.0 + + + diff --git a/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/java/io/nutz/demo/simple/MainLauncher.java b/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/java/io/nutz/demo/simple/MainLauncher.java new file mode 100644 index 00000000..32df4598 --- /dev/null +++ b/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/java/io/nutz/demo/simple/MainLauncher.java @@ -0,0 +1,60 @@ +package io.nutz.demo.simple; + +import java.util.Date; + +import javax.servlet.http.HttpSession; + +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.crypto.hash.Sha256Hash; +import org.apache.shiro.subject.Subject; +import org.nutz.boot.NbApp; +import org.nutz.dao.Dao; +import org.nutz.dao.util.Daos; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.random.R; +import org.nutz.lang.util.NutMap; +import org.nutz.mvc.annotation.At; +import org.nutz.mvc.annotation.Ok; + +import io.nutz.demo.simple.bean.User; + +@IocBean +public class MainLauncher { + + @Inject + protected Dao dao; + + @At({"/", "/index"}) + @Ok("th:/index.html") + public NutMap index() { + return NutMap.NEW().setv("name", "NB").setv("age", 18); + } + + @Ok("raw") + @At("/shiro/test") + public boolean isAuthenticated(HttpSession session) { + Subject subject = SecurityUtils.getSubject(); + return subject.isAuthenticated(); + } + + public void init() { + Daos.createTablesInPackage(dao, User.class, false); + dao.insert(newUser("admin", "123456")); + dao.insert(newUser("wendal", "123123")); + } + + protected static User newUser(String name, String password) { + User user = new User(); + user.setName(name); + user.setSalt(R.UU32()); + user.setPassword(new Sha256Hash(password, user.getSalt()).toHex()); + user.setCreateTime(new Date()); + return user; + } + + public static void main(String[] args) throws Exception { + new NbApp().run(); + } + +} diff --git a/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/java/io/nutz/demo/simple/bean/User.java b/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/java/io/nutz/demo/simple/bean/User.java new file mode 100644 index 00000000..2fbd97f9 --- /dev/null +++ b/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/java/io/nutz/demo/simple/bean/User.java @@ -0,0 +1,50 @@ +package io.nutz.demo.simple.bean; + +import java.util.Date; + +import org.nutz.dao.entity.annotation.Id; +import org.nutz.dao.entity.annotation.Name; +import org.nutz.dao.entity.annotation.Table; + +@Table("t_user") +public class User { + + @Id + private long id; + @Name + private String name; + private String password; + private String salt; + private Date createTime; + public long getId() { + return id; + } + public void setId(long id) { + this.id = id; + } + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + public String getPassword() { + return password; + } + public void setPassword(String password) { + this.password = password; + } + public String getSalt() { + return salt; + } + public void setSalt(String salt) { + this.salt = salt; + } + public Date getCreateTime() { + return createTime; + } + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + +} diff --git a/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/java/io/nutz/demo/simple/module/UserModule.java b/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/java/io/nutz/demo/simple/module/UserModule.java new file mode 100644 index 00000000..f1ec3ab7 --- /dev/null +++ b/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/java/io/nutz/demo/simple/module/UserModule.java @@ -0,0 +1,58 @@ +package io.nutz.demo.simple.module; + +import javax.servlet.http.HttpSession; + +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.crypto.hash.Sha256Hash; +import org.apache.shiro.subject.Subject; +import org.nutz.dao.Dao; +import org.nutz.integration.shiro.SimpleShiroToken; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.mvc.annotation.At; +import org.nutz.mvc.annotation.Fail; +import org.nutz.mvc.annotation.GET; +import org.nutz.mvc.annotation.Ok; +import org.nutz.mvc.annotation.POST; +import org.nutz.mvc.annotation.Param; + +import io.nutz.demo.simple.bean.User; + +@At("/user") +@IocBean +public class UserModule { + + @Inject + Dao dao; + + @GET + @At("/login") + @Ok(">>:/login.html") + public void loginPage() {} + + + @Ok("json") + @Fail("http:500") + @POST + @At("/login") + public boolean login(@Param("username")String username, @Param("password")String password, HttpSession session) { + User user = dao.fetch(User.class, username); + if (user == null) + return false; + Sha256Hash hash = new Sha256Hash(password, user.getSalt()); + if (!hash.toHex().equals(user.getPassword())) { + return false; + } + Subject subject = SecurityUtils.getSubject(); + subject.login(new SimpleShiroToken(user.getId())); + return true; + } + + @Ok(">>:/index.html") + @At + public void logout() { + Subject subject = SecurityUtils.getSubject(); + if (subject.isAuthenticated()) + subject.logout(); + } +} diff --git a/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/java/io/nutz/demo/simple/shiro/SimpleAuthorizingRealm.java b/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/java/io/nutz/demo/simple/shiro/SimpleAuthorizingRealm.java new file mode 100644 index 00000000..4ab0b00e --- /dev/null +++ b/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/java/io/nutz/demo/simple/shiro/SimpleAuthorizingRealm.java @@ -0,0 +1,66 @@ +package io.nutz.demo.simple.shiro; + +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.SimpleAccount; +import org.apache.shiro.authc.credential.CredentialsMatcher; +import org.apache.shiro.authz.AuthorizationException; +import org.apache.shiro.authz.AuthorizationInfo; +import org.apache.shiro.authz.SimpleAuthorizationInfo; +import org.apache.shiro.cache.CacheManager; +import org.apache.shiro.subject.PrincipalCollection; +import org.nutz.integration.shiro.AbstractSimpleAuthorizingRealm; +import org.nutz.integration.shiro.SimpleShiroToken; +import org.nutz.ioc.loader.annotation.IocBean; + +import io.nutz.demo.simple.bean.User; + +@IocBean(name="shiroRealm", fields="dao") +public class SimpleAuthorizingRealm extends AbstractSimpleAuthorizingRealm { + + + @Override + protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { + // null usernames are invalid + if (principals == null) { + throw new AuthorizationException("PrincipalCollection method argument cannot be null."); + } + long userId = ((Number) principals.getPrimaryPrincipal()).longValue(); + User user = dao().fetch(User.class, userId); + if (user == null) + return null; + SimpleAuthorizationInfo auth = new SimpleAuthorizationInfo(); + auth.addRole(user.getName()); + auth.addStringPermission("user:list"); + return auth; + } + + @Override + protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { + SimpleShiroToken upToken = (SimpleShiroToken) token; + + User user = dao().fetch(User.class, (Long)upToken.getPrincipal()); + if (user == null) + return null; + return new SimpleAccount(user.getId(), user.getPassword(), getName()); + } + + public SimpleAuthorizingRealm() { + this(null, null); + } + + public SimpleAuthorizingRealm(CacheManager cacheManager, CredentialsMatcher matcher) { + super(cacheManager, matcher); + setAuthenticationTokenClass(SimpleShiroToken.class); + } + + public SimpleAuthorizingRealm(CacheManager cacheManager) { + this(cacheManager, null); + } + + public SimpleAuthorizingRealm(CredentialsMatcher matcher) { + this(null, matcher); + } + +} diff --git a/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/resources/application.properties b/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/resources/application.properties new file mode 100644 index 00000000..d074544b --- /dev/null +++ b/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/resources/application.properties @@ -0,0 +1,11 @@ +server.port=8080 +server.host=0.0.0.0 + +jdbc.url=jdbc:h2:mem:~ +shiro.ini.urls: +/rs = anon +/home.html = authc +#end + +thymeleaf.dialects=nz.net.ultraq.thymeleaf.LayoutDialect +thymeleaf.dialects.shiro=at.pollux.thymeleaf.shiro.dialect.ShiroDialect diff --git a/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/resources/log4j.properties b/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/resources/log4j.properties new file mode 100644 index 00000000..d8217d50 --- /dev/null +++ b/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/resources/log4j.properties @@ -0,0 +1,8 @@ +log4j.rootLogger=debug,Console + +log4j.logger.org.eclipse.jetty=info +log4j.logger.org.thymeleaf=debug + +log4j.appender.Console=org.apache.log4j.ConsoleAppender +log4j.appender.Console.layout=org.apache.log4j.PatternLayout +log4j.appender.Console.layout.ConversionPattern=[%-5p] %d{HH:mm:ss.SSS} %l - %m%n diff --git a/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/resources/template/home.html b/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/resources/template/home.html new file mode 100644 index 00000000..b756f99d --- /dev/null +++ b/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/resources/template/home.html @@ -0,0 +1,10 @@ + + + + +用户个人主页 + + +

登录成功才能看到我,现在登出

+ + \ No newline at end of file diff --git a/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/resources/template/index.html b/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/resources/template/index.html new file mode 100644 index 00000000..e8baa6c0 --- /dev/null +++ b/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/resources/template/index.html @@ -0,0 +1,19 @@ + + + + +Shiro测试页 + + +
+

+ 点我,访问一个未授权路径,会跳转到登录页面 +

+
+
+

+ 当前登录状态: 已登录 未登录 +

+
+ + \ No newline at end of file diff --git a/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/resources/template/login.html b/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/resources/template/login.html new file mode 100644 index 00000000..aa315dc0 --- /dev/null +++ b/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-thymeleaf-shiro/src/main/resources/template/login.html @@ -0,0 +1,36 @@ + + + + +这是登录页面 + + +
+ 账号 + 密码 + +
+ + + + \ No newline at end of file diff --git a/nutzboot-demo/nutzboot-demo-simple/pom.xml b/nutzboot-demo/nutzboot-demo-simple/pom.xml index b06f6882..616e6c08 100644 --- a/nutzboot-demo/nutzboot-demo-simple/pom.xml +++ b/nutzboot-demo/nutzboot-demo-simple/pom.xml @@ -58,7 +58,7 @@ nutzboot-demo-simple-ftp nutzboot-demo-simple-fastdfs nutzboot-demo-simple-sqlxmltpl - + nutzboot-demo-simple-thymeleaf-shiro diff --git a/nutzboot-starter/nutzboot-starter-thymeleaf/src/main/java/org/nutz/boot/starter/thymeleaf/ThymeleafView.java b/nutzboot-starter/nutzboot-starter-thymeleaf/src/main/java/org/nutz/boot/starter/thymeleaf/ThymeleafView.java index 0938e133..6fe96ddc 100644 --- a/nutzboot-starter/nutzboot-starter-thymeleaf/src/main/java/org/nutz/boot/starter/thymeleaf/ThymeleafView.java +++ b/nutzboot-starter/nutzboot-starter-thymeleaf/src/main/java/org/nutz/boot/starter/thymeleaf/ThymeleafView.java @@ -1,50 +1,32 @@ package org.nutz.boot.starter.thymeleaf; -import java.lang.reflect.InvocationTargetException; import java.util.Locale; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.nutz.lang.util.Context; -import org.nutz.lang.util.NutMap; import org.nutz.log.Log; import org.nutz.log.Logs; import org.nutz.mvc.Mvcs; import org.nutz.mvc.view.AbstractPathView; import org.thymeleaf.TemplateEngine; import org.thymeleaf.context.WebContext; -import org.thymeleaf.dialect.IDialect; -import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver; -import org.thymeleaf.templateresolver.ITemplateResolver; public class ThymeleafView extends AbstractPathView { private static final Log log = Logs.get(); - private TemplateEngine templateEngine = new TemplateEngine(); + protected TemplateEngine templateEngine; - private String contentType; - - private String encoding; - - public ThymeleafView(ClassLoader classLoader, NutMap prop, String path) { + protected String contentType; + protected String encoding; + + public ThymeleafView(TemplateEngine templateEngine, String path, String contentType, String encoding) { super(path); - templateEngine.setTemplateResolver(initializeTemplateResolver(classLoader, prop)); - - encoding = prop.getString("encoding", "UTF-8"); - contentType = prop.getString("contentType", "text/html") + "; charset=" + encoding; - - prop.getList("dialects", String.class).forEach(dialect -> { - try { - Object obj = Class.forName(dialect).getDeclaredConstructor().newInstance(); - if (obj instanceof IDialect) { - log.debugf("在 thymeleaf 视图中加载 %s 类。", dialect); - templateEngine.setDialect((IDialect) obj); - } - } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | ClassNotFoundException e) { - log.debugf("未在 thymeleaf 视图中加载 %s 类,因为其并不是 org.thymeleaf.dialect.IDialect 合法的实现类。", dialect); - } - }); + this.templateEngine = templateEngine; + this.contentType = contentType; + this.encoding = encoding; } @Override @@ -65,17 +47,4 @@ public class ThymeleafView extends AbstractPathView { throw e; } } - - private ITemplateResolver initializeTemplateResolver(ClassLoader classLoader, NutMap prop) { - ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver(classLoader); - - templateResolver.setTemplateMode(prop.getString("mode", "HTML")); - templateResolver.setPrefix(prop.getString("prefix", "template/")); - templateResolver.setSuffix(prop.getString("suffix", ".html")); - templateResolver.setCharacterEncoding(prop.getString("encoding", "UTF-8")); - templateResolver.setCacheable(prop.getBoolean("cache", true)); - templateResolver.setCacheTTLMs(prop.getLong("cacheTTLMs", 3600000L)); - - return templateResolver; - } } diff --git a/nutzboot-starter/nutzboot-starter-thymeleaf/src/main/java/org/nutz/boot/starter/thymeleaf/ThymeleafViewMakerStarter.java b/nutzboot-starter/nutzboot-starter-thymeleaf/src/main/java/org/nutz/boot/starter/thymeleaf/ThymeleafViewMakerStarter.java index 4b803289..7597254b 100644 --- a/nutzboot-starter/nutzboot-starter-thymeleaf/src/main/java/org/nutz/boot/starter/thymeleaf/ThymeleafViewMakerStarter.java +++ b/nutzboot-starter/nutzboot-starter-thymeleaf/src/main/java/org/nutz/boot/starter/thymeleaf/ThymeleafViewMakerStarter.java @@ -1,25 +1,64 @@ package org.nutz.boot.starter.thymeleaf; -import java.util.Arrays; -import java.util.stream.Collectors; +import java.io.File; import org.nutz.boot.AppContext; +import org.nutz.boot.annotation.PropDoc; import org.nutz.ioc.Ioc; import org.nutz.ioc.impl.PropertiesProxy; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.Lang; +import org.nutz.lang.Mirror; import org.nutz.lang.Strings; import org.nutz.lang.util.NutMap; import org.nutz.log.Log; import org.nutz.log.Logs; import org.nutz.mvc.View; import org.nutz.mvc.ViewMaker; +import org.thymeleaf.TemplateEngine; +import org.thymeleaf.dialect.IDialect; +import org.thymeleaf.templateresolver.AbstractConfigurableTemplateResolver; +import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver; +import org.thymeleaf.templateresolver.FileTemplateResolver; -@IocBean(name="$views_thymeleaf", create="init") +@IocBean(name = "$views_thymeleaf", create = "init") public class ThymeleafViewMakerStarter implements ViewMaker { private static final Log log = Logs.get(); + public static String PRE = "thymeleaf."; + + @PropDoc(value = "渲染模式", defaultValue = "html") + public static final String PROP_MODE = PRE + "mode"; + + @PropDoc(value = "路径前缀", defaultValue = "template/") + public static final String PROP_PREFIX = PRE + "prefix"; + + @PropDoc(value = "模板文件后缀", defaultValue = ".html") + public static final String PROP_SUFFIX = PRE + "suffix"; + + @PropDoc(value = "模板文件编码", defaultValue = "UTF-8") + public static final String PROP_ENCODING = PRE + "encoding"; + + @PropDoc(value = "启用模板缓存", defaultValue = "true") + public static final String PROP_CACHE_ENABLE = PRE + "cache.enable"; + + @PropDoc(value = "模板缓存生存时长", defaultValue = "3000") + public static final String PROP_CACHE_TTL_MS = PRE + "cache.ttl"; + + @PropDoc(value = "模板目录的绝对路径,若不存在,回落到'模板目录的路径'") + public static final String PROP_TEMPLATE_ROOT_LOCAL = PRE + "resolver.rootLocal"; + + @PropDoc(value = "加载dialects,需要完整类名,逗号分隔") + public static final String PROP_DIALECTS = PRE + "dialects"; + + @PropDoc(value = "带前缀加载dialect,需要完整类名,逗号分隔") + public static final String PROP_DIALECTS_XXX = PRE + "dialects.xx"; + + @PropDoc(value = "响应的默认类型", defaultValue="text/html") + public static final String PROP_CONTENT_TYPE = PRE + "contentType"; + @Inject protected PropertiesProxy conf; @@ -28,29 +67,95 @@ public class ThymeleafViewMakerStarter implements ViewMaker { protected NutMap prop = NutMap.NEW(); + protected TemplateEngine templateEngine = new TemplateEngine(); + + protected String contentType; + + protected String encoding; + + protected AbstractConfigurableTemplateResolver setupTemplateResolver(AbstractConfigurableTemplateResolver templateResolver) { + + templateResolver.setTemplateMode(conf.get(PROP_MODE, "HTML")); + templateResolver.setPrefix(conf.get(PROP_PREFIX, "template/")); + templateResolver.setSuffix(conf.get(PROP_SUFFIX, ".html")); + templateResolver.setCharacterEncoding(encoding); + templateResolver.setCacheable(conf.getBoolean(PROP_CACHE_ENABLE, true)); + templateResolver.setCacheTTLMs(conf.getLong(PROP_CACHE_TTL_MS, 3000L)); + + return templateResolver; + } + + protected ClassLoaderTemplateResolver createClassLoaderTemplateResolver() { + ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver(appContext.getClassLoader()); + setupTemplateResolver(templateResolver); + return templateResolver; + } + + protected FileTemplateResolver createFileTemplateResolver(String root) { + FileTemplateResolver templateResolver = new FileTemplateResolver(); + setupTemplateResolver(templateResolver); + templateResolver.setPrefix(root); + templateResolver.setCacheable(false); + return templateResolver; + } + public void init() { - if (conf == null) { - return; - } log.debug("thymeleaf init ...."); + + contentType = conf.get(PROP_CONTENT_TYPE, "text/html"); + encoding = conf.get(PROP_ENCODING, "UTF-8"); + + if (conf.has("thymeleaf.dialects")) { + addDialect(null, conf.get("thymeleaf.dialects")); + } for (String key : conf.keySet()) { - if (key.startsWith("thymeleaf.")) { - if (key.startsWith("thymeleaf.dialects.")) { - prop.put("dialects", Arrays.stream(conf.get(key).split(",")).map(Strings::trim).collect(Collectors.toList())); - } else { - prop.put(key.substring("thymeleaf.".length()), conf.get(key)); - } + if (key.startsWith("thymeleaf.dialects.")) { + String prefix = key.substring("thymeleaf.dialects.".length()); + if ("default".equals(prefix)) + prefix = null; + String dialects = conf.get(key); + addDialect(prefix, dialects); } } + if (conf.has(PROP_TEMPLATE_ROOT_LOCAL)) { + String path = conf.get(PROP_TEMPLATE_ROOT_LOCAL); + File f = new File(path); + if (f.exists()) { + log.debugf("add local template path = %s", f.getAbsolutePath()); + FileTemplateResolver resolver = createFileTemplateResolver(f.getAbsolutePath()); + resolver.setCacheable(false); + templateEngine.addTemplateResolver(resolver); + } + } + templateEngine.addTemplateResolver(createClassLoaderTemplateResolver()); log.debug("thymeleaf init complete"); } + public void addDialect(String prefix, String klassNames) { + for (String dialect : Strings.splitIgnoreBlank(klassNames)) { + log.debugf("loading thymeleaf dialect " + dialect); + Class klass = Lang.loadClassQuite(dialect); + if (klass == null) { + log.info("no such thymeleaf dialect class = " + dialect); + continue; + } + Object obj = Mirror.me(klass).born(); + if (obj instanceof IDialect) { + templateEngine.addDialect(prefix, (IDialect) obj); + } + } + } + @Override public View make(Ioc ioc, String type, String value) { if ("th".equalsIgnoreCase(type)) { - return new ThymeleafView(appContext.getClassLoader(), prop, value); - } else { - return null; + return new ThymeleafView(templateEngine, value, contentType, encoding); } + return null; + } + + @IocBean(name = "thymeleafTemplateEngine") + public TemplateEngine getTemplateEngine() { + return templateEngine; } }