2021SC@SDUSC
目录
一、简介
Sitemap是jpress.web.sitemap包下的一个完整的管理网站导航地图的子模块,其结构独立清晰,代码简洁,很有独立分析的价值。
其大体结构如下:
可以发现,SitemapProvider提供了网站导航的服务接口。MainSitemapCreater类实现了SitemapProvider接口;SitemapManager类由这个接口组合而成,也很明显依赖于这个接口;SitemapProvider接口依赖于Sitemap类。
由此分析,可以发现MainSitemapCreater为这个包主要提供网页导航类的创造器,而SitemapManager则是网页导航类的管理器,SitemapController对外提供控制处理。
二、Sitemap
整个包的核心类就是Sitemap类,其他类基本都是依赖于Sitemap类。Sitemap类中有站点地理位置、上次修改时间、更改频率、网站等级信息。Sitemap类本身在依赖关系中,依赖于SitemapUtil类,SitemapUtil类主要用于str2date或者date2str的修改。
1.SitemapUtil
SitemapUtil类本身的功能不稀奇,但它的一个设计让我大开眼界,它的formats时间格式作为一个资源,不是直接的使用
private static SimpleDateFormat formats =new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
这样的写法来作为常量,而是使用了ThreadLocal来包装formats资源,如下所示
private static ThreadLocal<SimpleDateFormat> formats =ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"));
这是因为,多线程访问同一个共享变量的时候容易出现并发问题,特别是多个线程对一个变量进行写入的时候,为了保证线程安全,一般使用者在访问共享变量的时候需要进行额外的同步措施才能保证线程安全性。ThreadLocal是除了加锁这种同步方式之外的一种保证一种规避多线程访问出现线程不安全的方法,当我们在创建一个变量后,如果每个线程对其进行访问的时候访问的都是线程自己的变量这样就不会存在线程不安全问题。
ThreadLocal是JDK包提供的,它提供线程本地变量,如果创建一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个副本,在实际多线程操作的时候,操作的是自己本地内存中的变量,从而规避了线程安全问题,如下图所示
从而避免了线程不安全的情况,这个写法和思路是值得我们学习借鉴的。
三、MainSitemapCreater
MainSitemapCreater类是Sitemap类的创造器,它实现了SitemapProvider接口,组合于MainSitemapProvider,依赖于Sitemap。
其在初始化时就通过扫描MainSitemapProvider类对象,扫描特定的类,将其添加到自身的私有属性mainSitemapProviders列表中。
public MainSitemapCreater() { List<Class<MainSitemapProvider>> cls = ClassScanner.scanSubClass(MainSitemapProvider.class, true); if (cls != null && cls.size() > 0) { cls.forEach(c -> { MainSitemapProvider provider = ClassUtil.newInstance(c); if (provider != null) { mainSitemapProviders.add(provider); } }); } }
对外它主要提供了getSitemaps()方法,提供了主域名Sitemap和自身mainSitemapProviders列表中的Sitemap。
四、SitemapManager
SitemapManager类是jpress.web.sitemap包下的管理Sitemap类的类,它依赖于Sitemap类,自身又组合于SitemapProvider类,还实现了JbootEventListener接口,监听了自身的init()事件(其实就是初始化了它自身的providers,和MainSitemapCreater类类似)。
它对外提供了获得Provider、Sitemap的方法,如下:
public SitemapProvider getProvider(String name) { return providers.get(name); } public Map<String, SitemapProvider> getProviders() { return providers; } public List<Sitemap> getIndexSitemapList() { List<Sitemap> sitemaps = new ArrayList<>(); for (SitemapProvider provider : providers.values()) { sitemaps.add(new Sitemap("/sitemap/" + provider.getName() + ".xml", provider.getLastmod())); } return sitemaps; }
五、SitemapController
SitemapController是 Sitemap包的核心类之一,是 MVC 设计模式中的控制器。是定义 Action 方法的地点,SitemapController包含多个 Action 。
默认的index方法由 host:8080/sitemap 地址访问,返回合法的xmlBuilder。其他接口则提供了xmlBuilder的包装,如下:
private void buildUrlsetFooter(StringBuilder xmlBuilder) { xmlBuilder.append("</urlset>"); buildFooter(xmlBuilder); } private void buildIndexHeader(StringBuilder xmlBuilder) { buildHeader(xmlBuilder); xmlBuilder.append("<sitemapindex "); xmlBuilder.append(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "); xmlBuilder.append(" xsi:schemaLocation=\"http://www.sitemaps.org/schemas/sitemap/0.9\" "); xmlBuilder.append(" xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\" "); xmlBuilder.append(" > "); } private void buildIndexFooter(StringBuilder xmlBuilder) { xmlBuilder.append("</sitemapindex>"); buildFooter(xmlBuilder); } private void buildHeader(StringBuilder xmlBuilder) { xmlBuilder.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); } private void buildFooter(StringBuilder xmlBuilder) { xmlBuilder.append("<!-- This sitemap was generated by JPress --> "); }