承接上篇整合 Struts2 + Hibernate 4.3 + Spring 4.2 環境,本篇著重於服務層與控制層的抽象封裝,消除重複代碼,並從 XML 配置遷移至註解驅動,提升開發效率與維護性。
public interface BaseService<T> {
void save(T entity);
void update(T entity);
void delete(int id);
T get(int id);
List<T> query();
}
@SuppressWarnings("unchecked")
public class BaseServiceImpl<T> implements BaseService<T> {
protected Class<T> entityClass;
@Autowired protected SessionFactory sessionFactory;
public BaseServiceImpl() {
// 透過反射獲取泛型實際類型
ParameterizedType type = (ParameterizedType) getClass().getGenericSuperclass();
entityClass = (Class<T>) type.getActualTypeArguments()[0];
}
protected Session getSession() {
return sessionFactory.getCurrentSession();
}
@Override public void save(T entity) { getSession().save(entity); }
@Override public void update(T entity) { getSession().update(entity); }
@Override public void delete(int id) {
String hql = "delete " + entityClass.getSimpleName() + " where id=:id";
getSession().createQuery(hql).setInteger("id", id).executeUpdate();
}
@Override public T get(int id) { return getSession().get(entityClass, id); }
@Override public List<T> query() {
return getSession().createQuery("from " + entityClass.getSimpleName()).list();
}
}
public interface CategoryService extends BaseService<Category> {
// 擴充業務方法,如:List<Category> queryHotCategories();
}
@Service("categoryService")
public class CategoryServiceImpl extends BaseServiceImpl<Category> implements CategoryService {
// 僅實現擴充方法,基礎 CRUD 繼承自 BaseServiceImpl
}
<!-- XML 殘留配置:僅定義基礎 Bean,lazy-init 避免泛型實例化錯誤 -->
<bean id="baseService" class="cn.it.shop.service.impl.BaseServiceImpl" lazy-init="true" abstract="true">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="categoryService" class="cn.it.shop.service.impl.CategoryServiceImpl" parent="baseService" />
註解驅動遷移:啟用 <context:component-scan base-package="cn.it.shop"/>,於類上標註 @Service、@Autowired,移除 XML 中對應 Bean 定義。
@Controller
public class BaseAction<T> extends ActionSupport
implements ModelDriven<T>, RequestAware, SessionAware, ApplicationAware {
// 領域模型 (ModelDriven)
protected T model;
@Override public T getModel() {
try {
ParameterizedType type = (ParameterizedType) getClass().getGenericSuperclass();
Class<T> clazz = (Class<T>) type.getActualTypeArguments()[0];
return clazz.newInstance();
} catch (Exception e) { throw new RuntimeException(e); }
}
// 作用域 Map 注入
protected Map<String, Object> request, session, application;
@Override public void setRequest(Map<String, Object> request) { this.request = request; }
@Override public void setSession(Map<String, Object> session) { this.session = session; }
@Override public void setApplication(Map<String, Object> application) { this.application = application; }
// 通用 Service 注入 (子類直接使用)
@Autowired protected CategoryService categoryService;
@Autowired protected AccountService accountService;
// ... 其他通用 Service
}
@Controller
@Scope("prototype")
@ParentPackage("shop")
@Namespace("/category")
public class CategoryAction extends BaseAction<Category> {
@Action(value = "query", results = @Result(name = "success", location = "/category/list.jsp"))
public String query() {
request.put("categoryList", categoryService.query());
return SUCCESS;
}
@Action(value = "save", results = @Result(name = "success", type = "redirectAction", params = {"actionName", "query"}))
public String save() {
categoryService.save(model);
return SUCCESS;
}
@Action(value = "update", results = @Result(name = "success", type = "redirectAction", params = {"actionName", "query"}))
public String update() {
categoryService.update(model);
return SUCCESS;
}
@Action(value = "delete", results = @Result(name = "success", type = "redirectAction", params = {"actionName", "query"}))
public String delete() {
categoryService.delete(model.getId());
return SUCCESS;
}
}
<!-- struts.xml 精簡為僅載入註解掃描包 -->
<struts>
<package name="shop" extends="struts-default">
<!-- 註解掃描由 Struts2 Convention Plugin 自動完成 -->
</package>
</struts>
// 需引入依賴
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-convention-plugin</artifactId>
<version>2.3.41</version>
</dependency>
| 維度 | 重構前 | 重構後 |
|---|---|---|
| Service 新增 | 複製介面/實現/XML,修改類名 | 繼承 BaseServiceImpl,僅寫擴充方法 |
| Action 新增 | 手寫 ModelDriven/Aware/Service 注入 | 繼承 BaseAction,直接使用 model/service |
| 配置維護 | beans.xml 膨脹,易出錯 | 註解自動掃描,近零配置 |
| 程式碼重複 | 大量 CRUD 樣板代碼 | 集中於基類,單一職責 |
BaseDao 抽取 DAO 層通用操作。PageHelper 或自定義分頁工具類統一分頁邏輯。tx:advice)。