模仿博客園核心功能:用戶註冊/登入、隨筆發布/閱讀、評論互動、定時任務排名。三表設計:user、essay、comment。技術棧:IntelliJ IDEA + Maven + Spring 4.2.4 + MyBatis 3.3 + MySQL + Jetty 9.3 + Jersey 1.19。
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${db.mysql.driverClass}" />
<property name="jdbcUrl" value="${db.mysql.jdbcUrl}" />
<!-- c3p0 核心池參數 -->
<property name="minPoolSize" value="10" />
<property name="maxPoolSize" value="100" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:mybatis.xml" />
<property name="mapperLocations" value="classpath*:com/example/*Mapper.xml" />
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.example.dao" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
Service 類標註 @Transactional,發表評論同時更新 comment、essay.comment_count、user.score 三表,保證原子性。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"/spring.xml"})
public class TestUserService extends AbstractTransactionalJUnit4SpringContextTests {
@Resource UserService userService;
@Test
@Rollback(false) // 驗證入庫
public void testAddUser() {
userService.addUser(new User("test", "123456"));
}
}
# log4j.properties 關鍵配置
log4j.rootCategory = INFO, R, stdout
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout.ConversionPattern = [%p]-[%t]-[%l]-[%d{yyyyMMdd HH:mm:ss}]%n%m%n
log4j.appender.R = org.apache.log4j.DailyRollingFileAppender
log4j.appender.R.File = /logs/output.log
log4j.appender.R.DatePattern = '.'yyyy-MM-dd
代碼中使用佔位符避免字串拼接開銷:log.info("用戶 {} 登入耗時 {} ms", username, cost);
<!-- spring-task: 簡單輕量 -->
<task:annotation-driven executor="executor" scheduler="scheduler"/>
@Component
public class SpringTask {
@Scheduled(cron = "0/20 * * * * ?")
public void rankingJob() { userDao.rankingByScore(); }
}
<!-- Quartz: 功能強大、支援叢集 -->
<bean id="quartzTask" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="quartzTaskBean"/>
<property name="targetMethod" value="rankingByScore"/>
</bean>
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="cronExpression" value="0/10 * * * * ?"/>
</bean>
public class JettyLauncher {
public static void main(String[] args) throws Exception {
Server server = new Server(8080);
WebAppContext ctx = new WebAppContext();
ctx.setContextPath("/");
ctx.setWar("src/main/webapp");
server.setHandler(ctx);
server.start();
server.join();
}
}
@Path("/user")
@Component
public class UserResource {
@Resource UserService userService;
@GET @Path("/{username}") @Produces(MediaType.APPLICATION_JSON)
public User get(@PathParam("username") String username) {
return userService.getByUsername(username);
}
}
src/
├── main/
│ ├── java/com/example/
│ │ ├── entity/ dao/ service/ task/ jetty/ action/
│ │ └── util/SpringContextUtils.java
│ ├── resources/
│ │ ├── spring.xml, spring-mybatis.xml, spring-task.xml, spring-jetty.xml
│ │ ├── mybatis.xml, db.properties, log4j.properties
│ │ └── com/example/*Mapper.xml
│ └── webapp/WEB-INF/web.jsp
└── test/...TestUserService.java
完整代碼:GitHub Repository