構建一個高效能的 Web 橫幅廣告輪播系統:隨機展示廣告圖片、點擊跳轉目標鏈接、記錄展示/點擊統計、支持加權輪播。技術棧:Apache Tomcat 4.1+、MySQL 4.0+、JDBC (MM MySQL 驅動)、Java Servlet。
[TOMCAT_HOME]/webapps/banner/ ├── WEB-INF/ │ ├── classes/ # Servlet 編譯類 │ ├── lib/ # JDBC 驅動 JAR │ └── web.xml # Servlet 映射配置 ├── images/ # 廣告圖片存放 └── index.html # 測試頁面
<Context path="/banner" docBase="webapps/banner" debug="0" reloadable="true"/>
重啟 Tomcat 生效。訪問 https://localhost:8080/banner/servlet/BannerServlet。
mm.mysql-2.0.14-bin.jar (或更新版 mysql-connector-java) 放入 WEB-INF/lib/。org.gjt.mm.mysql.Driver (舊版) / com.mysql.cj.jdbc.Driver (新版)。jdbc:mysql://localhost:3306/BANNER?useSSL=false。CREATE DATABASE BANNER DEFAULT CHARACTER SET utf8mb4; USE BANNER; CREATE TABLE ADS ( IMAGEFILE VARCHAR(255) NOT NULL COMMENT '圖片相對路徑', URL VARCHAR(500) NOT NULL COMMENT '點擊跳轉鏈接', CUSTOMERNAME VARCHAR(100) COMMENT '廣告主', NUMBEROFCLICKSPURCHASED INT DEFAULT 0 COMMENT '購買點擊數', NUMBEROFCLICKSREMAINING INT NOT NULL DEFAULT 0 COMMENT '剩餘點擊數', NUMBEROFIMPRESSIONS INT NOT NULL DEFAULT 0 COMMENT '展示次數', BANNERWEIGHT INT NOT NULL DEFAULT 10 COMMENT '權重,越大越易展示', PRIMARY KEY (IMAGEFILE) ) ENGINE=InnoDB;
展示概率 = BannerWeight / SUM(BannerWeight) * 100%。實現:計算總權重 → 隨機數落區間 → 對應廣告。
| 類 | 職責 | 復用性 |
|---|---|---|
Logger | 文本日志記錄,支持分隔符、方法入口/出口日志 | 高 (通用) |
DBHandler | JDBC 封裝:連接管理、PreparedStatement 執行、ResultSet 映射 | 高 (通用) |
Banner | POJO,對應 ADS 表字段,Getter/Setter | 中 (業務綁定) |
BannerServlet | 核心控制器:初始化緩存、處理 image/link 請求、統計更新 | 低 (業務核心) |
public class DBHandler {
private Connection conn;
private PreparedStatement pstmt;
private String queryString;
private Logger logger;
public DBHandler(String driver, String url, Logger logger) { ... }
public void setQueryString(String sql) { this.queryString = sql; }
public ResultSet lookup(Object... params) { ... } // SELECT
public int executeUpdate(Object... params) { ... } // INSERT/UPDATE/DELETE
public void close() { ... }
}
public class BannerServlet extends HttpServlet {
private Map bannerCache = new HashMap<>();
private int totalWeight = 0;
private Logger logger = new Logger("banner.log");
private String dbUrl = "jdbc:mysql://localhost/BANNER";
private String driver = "org.gjt.mm.mysql.Driver";
public void init() {
// 啟動時載入所有廣告到內存 HashMap,計算 totalWeight
DBHandler db = new DBHandler(driver, dbUrl, logger);
db.setQueryString("SELECT * FROM ADS");
ResultSet rs = db.lookup();
while (rs.next()) {
Banner b = mapRow(rs);
bannerCache.put(bannerCache.size(), b);
totalWeight += b.getBannerWeight();
}
db.close();
}
protected void service(HttpServletRequest req, HttpServletResponse resp) {
String type = req.getParameter("type");
if ("image".equals(type)) {
Banner b = getRandomBanner();
req.getSession().setAttribute("banner", b); // 存入 Session
increaseImpressions(b);
resp.sendRedirect(b.getImageFile()); // 重定向到圖片
} else if ("link".equals(type)) {
Banner b = (Banner) req.getSession().getAttribute("banner");
if (b != null) {
decreaseClicksRemaining(b);
resp.sendRedirect(b.getUrl()); // 跳轉目標鏈接
}
}
}
private Banner getRandomBanner() {
int r = (int) (Math.random() * totalWeight);
int sum = 0;
for (Banner b : bannerCache.values()) {
sum += b.getBannerWeight();
if (r < sum) return b;
}
return bannerCache.values().iterator().next(); // fallback
}
private void increaseImpressions(Banner b) {
DBHandler db = new DBHandler(driver, dbUrl, logger);
db.setQueryString("UPDATE ADS SET NUMBEROFIMPRESSIONS=NUMBEROFIMPRESSIONS+1 WHERE IMAGEFILE=?");
db.executeUpdate(b.getImageFile());
db.close();
init(); // 重新載入緩存 (簡單但低效,生產建議增量更新)
}
// decreaseClicksRemaining 同理
}
<!-- 頁面嵌入廣告位 --> <a href="/banner/servlet/BannerServlet?type=link" target="_blank"> <img src="/banner/servlet/BannerServlet?type=image" border="0" /> </a>
DBHandler 整合 HikariCP/Druid,移除 init() 重載緩存,改用定時任務刷新。