日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java笔记:自己动手写javaEE

發(fā)布時間:2025/6/15 编程问答 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java笔记:自己动手写javaEE 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

寫這篇博客前,我有個技術(shù)難題想請教大家,不知道誰有很好的建議,做過互聯(lián)網(wǎng)的童鞋應(yīng)該都知道,有點規(guī)模的大公司都會做用戶行為分析系統(tǒng),而且有些大公司還會提供專業(yè)的用戶行為分析解決方案例如:百度分析,google analysis。用戶行為分析就是當(dāng)用戶訪問某個網(wǎng)站的頁面,會有專門系統(tǒng)記錄用戶的相關(guān)信息以及使用狀況,然后分析這些數(shù)據(jù)用來指導(dǎo)網(wǎng)站的運營,我們現(xiàn)在遇到一個問題:如果某的訪客訪問了www.a.com頁面,我們怎么知道這個用戶訪問過www.b.com頁面,a頁面和b頁面毫無關(guān)系,比如:某個未知訪客訪問QQ主頁,他只要打開了QQ頁面我就知道他是否訪問過sina的頁面,聽說有人把這個做出來了,但是我還沒想到,哪位高手能想到解決方案嗎?
  說到用戶行為分析,這個對于互聯(lián)網(wǎng)公司來說相當(dāng)?shù)闹匾到y(tǒng),以后有時間我會開一個系列從技術(shù)到業(yè)務(wù)的角度講講用戶行為分析系統(tǒng)是啥樣子,大伙好好交流下。感興趣的童鞋多多關(guān)注哈。
  轉(zhuǎn)入正題前還閑話幾句,本來開博客是想系統(tǒng)研究javascript技術(shù),但是最近工作比較忙,而且大量工作都是java開發(fā)任務(wù)(我們公司前端工程師太不專業(yè)了,哎),因此javascript現(xiàn)在只得暫停下,畢竟晚上的時間還是太少。因此我想就我現(xiàn)在做的技術(shù)開一個新系列:自己動手寫javaEE框架,這個和我現(xiàn)在工作有關(guān)比較好收集資料,這個系列我的想法很大,我想寫如下內(nèi)容:
1.???? struts2+ibatis+spring+js
2.???? springmvc+ibatis+spring+js
3.???? flex+ibatis+spring
  如果以上寫完還有時間的話,我會把ibatis換成hibernate,也許還會自己封裝一個js框架或者更加深入講解flex技術(shù)。哎,東西挺多了,能寫完不???天知道了。
  由于內(nèi)容比較多,我的博客里只做簡單講解,不做深入分析。好下面開始了。
  我第一個寫的是struts2+ibatis+spring+js。框架結(jié)構(gòu)是頁面+action+service+dao+數(shù)據(jù)庫,數(shù)據(jù)庫為oracle(公司電腦裝的是oracle)或者mysql(家里電腦裝的是mysql)。
今天任務(wù)是搭建好spring框架,spring里面集成好ibatis框架,然后編寫dao層,最后一個重要任務(wù)是加入junit測試框架,方便以后開發(fā)的單元測試(junit測試框架是本博文的亮點,我想許多做java童鞋看完本篇博客還是很有啟發(fā)的,因為我發(fā)現(xiàn)很多朋友都不太會做跟spring相關(guān)的單元測試,這個會了能提升不少開發(fā)效率)。我是按下面步驟寫的:
1.???? 首先是工程結(jié)構(gòu)如下圖:
?

2.使用到的jar包如下:

?

?
3.???? 我首先寫的是web.xml,代碼如下:
?
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
??? <display-name>ssiprj</display-name>
? <!-- spring配置文件位置 -->
? <context-param>
????? <param-name>contextConfigLocation</param-name>
????? <param-value>classpath:conf/applicationContext*.xml</param-value>
? </context-param>
? <!-- 將spring框架裝載進(jìn)我們的工程里 -->
? <listener>
????? <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
? </listener>
? <welcome-file-list>
??? <welcome-file>index.jsp</welcome-file>
? </welcome-file-list>
</web-app>
這里面主要是向工程里裝載spring框架。
4.???? 下面我在conf包下面建立constants.properties文件,這個文件用來放置一些經(jīng)常會變化的配置信息,例如:數(shù)據(jù)庫配置信息、文件的路徑等等,內(nèi)容如下:
#db.driverClass = oracle.jdbc.driver.OracleDriver
#db.user??????? = sharpxiajun
#db.password??? = sharpxiajun
#db.jdbcUrl???? = jdbc:oracle:thin:@127.0.0.1:1521:orcl

db.driverClass = com.mysql.jdbc.Driver
db.user??????? = root
db.password??? = root
db.jdbcUrl???? = jdbc\:mysql\://localhost\:3306/sq_xidi?useUnicode\=true&characterEncoding\=utf-8
5.?????? 接下來寫的是applicationContext.xml文件,這里首先是掃描spring組件和導(dǎo)入constants.properties文件的內(nèi)容,接下來配置數(shù)據(jù)源(oracle或mysql),然后在spring里裝載ibatis框架和定義ibatis操作模板類,最后定義事物管理,這些做javaee開發(fā)的工程師都很清楚,我就不具體講解,實際使用copy就行了。內(nèi)容如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
? xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
? xmlns:context="http://www.springframework.org/schema/context"
? xmlns:aop="http://www.springframework.org/schema/aop"
? xmlns:tx="http://www.springframework.org/schema/tx"
? xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
???????
??????? <!-- 掃描該路徑下的spring組件 -->
??????? <context:component-scan base-package="cn.com.sharpxiajun" />
???????
??????? <!-- 讀取資源文件 -->
??????? <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
??????????? <property name="locations">
??????????????? <list>
??????????????????? <value>classpath:conf/constants.properties</value>
??????????????? </list>
??????????? </property>
??????? </bean>
???????
??????? <!-- 配置數(shù)據(jù)源 -->
???????? <!--? <bean id="myDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
???????????? <property name="driverClass" value="${db.driverClass}"/>
???????????? <property name="jdbcUrl" value="${db.jdbcUrl}"/>
???????????? <property name="user" value="${db.user}"/>
???????????? <property name="password" value="${db.password}"/>
???????? </bean>-->
????????
???????? <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
???????????? <property name="driverClassName" value="${db.driverClass}"/>
???????????? <property name="url" value="${db.jdbcUrl}"/>
???????????? <property name="username" value="${db.user}"/>
???????????? <property name="password" value="${db.password}"/>
???????? </bean>
???????
??????? <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
??????????? <property name="configLocation">
??????????????? <value>classpath:conf/SqlMapConfig.xml</value>
??????????? </property>
??????????? <property name="dataSource" ref="myDataSource"/>
??????? </bean>
???????
??????? <bean id="sqlMapClientTemplate" class="org.springframework.orm.ibatis.SqlMapClientTemplate">
??????????? <property name="sqlMapClient">
??????????????? <ref local="sqlMapClient"/>
??????????? </property>
??????? </bean>
???????
??????? <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
??????????? <property name="dataSource">
??????????????? <ref local="myDataSource"/>
??????????? </property>
??????? </bean>

</beans>
6.???? 接著我在文件路徑cn/com/sharpxiajun/dao/sqlmap/下編寫了USERS.xml配置文件,內(nèi)容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap namespace="USERS">
??? <select id="queryUserList" parameterClass="java.util.Map" resultClass="java.util.HashMap">
??????? select t.username,t.password,t.enabled from users t
??? </select>
</sqlMap>


里面我只寫了一個查詢方法,parameterClass="java.util.Map"代表傳入?yún)?shù)是map,resultClass="java.util.HashMap"表示返回的結(jié)果是一個map,這里正好應(yīng)用了我前一篇博文里面談到的各個邏輯層用map來做為傳輸?shù)慕橘|(zhì)。
7.???? 然后我在路徑cn.com.sharpxiajun.dao下編寫接口UsersDao,內(nèi)容如下:
package cn.com.sharpxiajun.dao;

import java.util.List;
import java.util.Map;

public interface UsersDao {
???
??? public static final String QUERY_USERS_SQL = "USERS.queryUserList";
???
??? public List<Map<String, Object>> queryUserList(Map<String, Object> map) throws Exception;

}
在路徑cn.com.sharpxiajun.dao.impl下實現(xiàn)了UsersDaoImpl接口,代碼如下:
package cn.com.sharpxiajun.dao.impl;

import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Scope;
import org.springframework.orm.ibatis.SqlMapClientTemplate;
import org.springframework.stereotype.Repository;

import cn.com.sharpxiajun.dao.UsersDao;

@SuppressWarnings("unchecked")
@Scope("prototype")
@Repository("usersDao")
public class UsersDaoImpl implements UsersDao {
??? @Autowired
??? @Qualifier("sqlMapClientTemplate")
??? private SqlMapClientTemplate sqlMapClientTemplate = null;

??? public List<Map<String, Object>> queryUserList(Map<String, Object> map)
??????????? throws Exception {
??????? return sqlMapClientTemplate.queryForList(QUERY_USERS_SQL, map);
??? }

}
@Scope("prototype"):多線程, 生成多個實例。
@Repository("usersDao")將UsersDaoImpl注冊為spring的bean對象,Repository標(biāo)簽只用于dao層,因為Repository標(biāo)簽里面還封裝了dao層拋出的異常類型。
8.???? 寫好了dao層我接下來編寫了SqlMapConfig.xml文件,內(nèi)容如下:
<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE sqlMapConfig PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-config-2.dtd">
<sqlMapConfig>
??? <settings cacheModelsEnabled="true" enhancementEnabled="true" lazyLoadingEnabled="true" errorTracingEnabled="true"
??????????????? maxRequests="64" maxSessions="20" maxTransactions="10"
??????????????? useStatementNamespaces="true"/>
??? <sqlMap resource="cn/com/sharpxiajun/dao/sqlmap/USERS.xml"/>
</sqlMapConfig>
9.???? 最后我們編寫該UserDao單元測試類,代碼如下:
package cn.com.sharpxiajun.junittest.dao;


import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;

import cn.com.sharpxiajun.dao.UsersDao;

import junit.framework.TestCase;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:conf/applicationContext.xml"})
@TransactionConfiguration(defaultRollback = false)
public class UsersDaoImplTest extends AbstractTransactionalJUnit4SpringContextTests{
???
??? @Autowired
??? private UsersDao usersDao;
???
??? public UsersDaoImplTest()
??? {
??????? System.out.println("初始化測試類....");
??? }
???
??? @Before
??? public void setUp() throws Exception
??? {
??????? System.out.println("測試開始....");
??? }
???
??? @After
??? public void tearDown() throws Exception
??? {
??????? System.out.println("測試結(jié)束!!");
??? }
???
??? @Test
??? public void testQueryUserList()
??? {
??????? Map<String, Object> map = new HashMap<String, Object>();
??????? try {
??????????? List<Map<String, Object>> list = usersDao.queryUserList(map);
??????????? System.out.println(list);
??????? } catch (Exception e) {
??????????? // TODO Auto-generated catch block
??????????? e.printStackTrace();
??????? }
???????
??? }

}
運行結(jié)果如下:
初始化測試類....
2011-10-9 23:22:22 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [conf/applicationContext.xml]
2011-10-9 23:22:23 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.GenericApplicationContext@290fbc: startup date [Sun Oct 09 23:22:23 CST 2011]; root of context hierarchy
2011-10-9 23:22:23 org.springframework.core.io.support.PropertiesLoaderSupport loadProperties
信息: Loading properties file from class path resource [conf/constants.properties]
2011-10-9 23:22:23 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@d16fc1: defining beans [usersDao,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,propertyConfigurer,myDataSource,sqlMapClient,sqlMapClientTemplate,transactionManager]; root of factory hierarchy
2011-10-9 23:22:23 org.springframework.test.context.transaction.TransactionalTestExecutionListener startNewTransaction
信息: Began transaction (1): transaction manager [org.springframework.jdbc.datasource.DataSourceTransactionManager@1b7c76]; rollback [false]
測試開始....
[{enabled=false, username=admin, password=admin}, {enabled=false, username=test, password=test}]
測試結(jié)束!!
2011-10-9 23:22:23 org.springframework.test.context.transaction.TransactionalTestExecutionListener endTransaction
信息: Committed transaction after test execution for test context [[TestContext@12b19c5 testClass = UsersDaoImplTest, locations = array<String>['classpath:conf/applicationContext.xml'], testInstance = cn.com.sharpxiajun.junittest.dao.UsersDaoImplTest@a8e586, testMethod = testQueryUserList@UsersDaoImplTest, testException = [null]]]
寫完了,寫的累死人了,這里用junit做spring組件的單元測試非常有用,希望很多做java的童鞋可以試試,另外我寫的junit測試類是用注解的方式,這是junit4里才有的,有興趣的童鞋可以學(xué)習(xí)下哈。




前一篇博文里有三位童鞋留言了,第一位童鞋問道我提出的那個技術(shù)難題,我得到一個答案,但是我比較懷疑這個方法的技術(shù)實現(xiàn),以后我會驗證下,還有位童鞋問道源碼,我現(xiàn)在還沒有寫完,寫完后我會把源碼發(fā)到博客里的,最后一位童鞋的問題我要著重講講。其實開起這個系列時我是想過用什么題目,例如用ssh或者 ssi等等,但是這種命名就局限了,因為這里面每一個單詞都是指一個技術(shù)框架,而我想用到的框架比較多,這樣的標(biāo)題不能代表我寫的所有內(nèi)容。用 javaEE是有理由的,javaEE是j2ee的新名稱,(注意:為了嚴(yán)謹(jǐn)我下面的理解是我自己經(jīng)驗得來的理解,寫下面內(nèi)容時候我沒有查閱相關(guān)資料,假如不正確,大家可以直接指出),sun公司出品的java語言,當(dāng)然現(xiàn)在是甲骨文的產(chǎn)品了,一共包括三大部分,j2se,j2ee和j2me,而j2ee 是什么了?是sun運用java語言為企業(yè)級開發(fā)提供的一套解決方案,j2ee也可以說是一套框架,這個框架里面主要是定義了java為企業(yè)級開發(fā)提供了那些技術(shù),但是這些技術(shù)只是提供了接口和規(guī)范,而非是具體實現(xiàn),最開始sun出品了ejb,但是ejb太繁瑣,最后就有類似ssh框架的實現(xiàn),但是 j2ee的范疇很大,我們平時開發(fā)時候只是實現(xiàn)了其中的部分功能,例如一個頁面發(fā)出請求到后臺程序運算最后查詢數(shù)據(jù)庫,返回數(shù)據(jù)到頁面展示,這是j2ee 的一個子集實現(xiàn),j2ee還有很多,比如jms,webservice,xml等等技術(shù),我這里想寫的框架絕不是簡單的針對頁面請求響應(yīng),我還想引入很多功能到這些框架,比如webservice,mq,velocity,spring的調(diào)度,ant,緩存等等,要不我寫一個大眾化的框架放到博客里意義不大,就是重復(fù)。而且后面那些技術(shù)并不是能常用到,但很有可能會成為你面試的軟肋,因為有些現(xiàn)在我用過忘了,有些你沒用過別人會給你印象減分,這是功力的說法了,但是接觸的技術(shù)越到你做項目的余地越大,所以我用javaEE左標(biāo)題,我想做一個盡量完善的j2ee規(guī)范實現(xiàn)的子集。
  轉(zhuǎn)入正題還是要閑話下,最近在為公司的平臺做權(quán)限開發(fā)設(shè)計,由于人力和時間的原因這個功能復(fù)雜度的要求被降低了,就算降低了,這次權(quán)限設(shè)計還是和我以前的經(jīng)驗比較起來有很大不同,我們做的是到程序方法級別的控制和數(shù)據(jù)級別的權(quán)限控制,數(shù)據(jù)級別的權(quán)限控制我以后再說了,這個主要是從系統(tǒng)設(shè)計塊實現(xiàn),技術(shù)沒有特別之處。前者會用到spring的aop,這個和我現(xiàn)在寫的這個系列有關(guān),所以我這里要提到這個問題。
  今天我將寫service層,然后寫service的測試類,然后我會加入一個攔截器:攔截service的請求,這個攔截器是針對方法的攔截,這個攔截器里面我們可以知道調(diào)用到了那個service類,那個method,可以截獲到傳入的參數(shù),也能截獲到返回值。我在公司的項目里的aop就會攔截到 service的方法,大家也許會很奇怪,為什么不做到action而是service,哎,這個沒法子,我們前臺用的是flex,而flex調(diào)用 java跟rpc很像,就是flex直接調(diào)用service的方法,因此控制層在前臺,和前臺的耦合度太高,只得做service方法級別的攔截了。
我是按下面順序開發(fā)的:
1.先看看我新的目錄結(jié)構(gòu)

還要加入三個jar包,都是AspectJ相關(guān)的,如下圖:


2.在cn.com.sharpxiajun.service包下新建接口UsersService,代碼如下:

package cn.com.sharpxiajun.service;import java.util.List; import java.util.Map;public interface UsersService {public List<Map<String, Object>> queryUsersList(Map<String, Object> map) throws Exception;}

3.實現(xiàn)UsersService接口,在cn.com.sharpxiajun.service.impl包下面新建類UsersServiceImpl,代碼如下:

package cn.com.sharpxiajun.service.impl;import java.util.List; import java.util.Map;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Service;import cn.com.sharpxiajun.dao.UsersDao; import cn.com.sharpxiajun.service.UsersService;@SuppressWarnings("unchecked") @Scope("prototype") @Service("userService") public class UsersServiceImpl implements UsersService {@Autowired@Qualifier("usersDao")private UsersDao usersDao = null;@Overridepublic List<Map<String, Object>> queryUsersList(Map<String, Object> map)throws Exception {return usersDao.queryUserList(map);}}

大家可以看到service注解是@service,其他和dao差不多(我感覺要寫篇文章好好介紹下spring相關(guān)注解,只有這樣才能對框架有深刻理解)

4.接下來編寫方法攔截器,這個類放在cn.com.sharpxiajun.common.aop包下,類名是:MethodServiceAdvisor,代碼如下:

package cn.com.sharpxiajun.common.aop;import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation;import cn.com.sharpxiajun.service.UsersService;public class MethodServiceAdvisor implements MethodInterceptor {@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {Object obj = null;System.out.println("進(jìn)入到了方法攔截器。。。。");System.out.println("調(diào)用的service:");System.out.println(invocation.getThis());System.out.println("調(diào)用的方法:");System.out.println(invocation.getMethod());System.out.println("參數(shù)是:");for (int i = 0;i < invocation.getArguments().length;i++){Object[] objs = invocation.getArguments();System.out.println(objs[i]);}obj = invocation.proceed();System.out.println("返回結(jié)果是:");System.out.println(obj);System.out.println("攔截器執(zhí)行結(jié)束!!");return obj;}}

攔截器的注解我今天不寫,太晚了,而且記得不清,下一篇里我會補上這些內(nèi)容。

5.下面是修改后的applicationContext.xml配置文件,內(nèi)容如下:

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"><!-- 掃描該路徑下的spring組件 --><context:component-scan base-package="cn.com.sharpxiajun" /><!-- 讀取資源文件 --><bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"><property name="locations"><list><value>classpath:conf/constants.properties</value></list></property></bean><!-- 配置數(shù)據(jù)源 --><!-- <bean id="myDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><property name="driverClass" value="${db.driverClass}"/><property name="jdbcUrl" value="${db.jdbcUrl}"/><property name="user" value="${db.user}"/><property name="password" value="${db.password}"/></bean>--><bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"><property name="driverClassName" value="${db.driverClass}"/><property name="url" value="${db.jdbcUrl}"/><property name="username" value="${db.user}"/><property name="password" value="${db.password}"/></bean><bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"><property name="configLocation"><value>classpath:conf/SqlMapConfig.xml</value></property><property name="dataSource" ref="myDataSource"/></bean><bean id="sqlMapClientTemplate" class="org.springframework.orm.ibatis.SqlMapClientTemplate"><property name="sqlMapClient"><ref local="sqlMapClient"/></property></bean><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource"><ref local="myDataSource"/></property></bean><!-- 將我自己定義的攔截器生成bean --><bean id="methodServiceAdvisor" class="cn.com.sharpxiajun.common.aop.MethodServiceAdvisor"/><aop:config><!--配置規(guī)則,滿足以下規(guī)則的將攔截,第一個*表示所有返回類型,第二個表示service包下的所有class,第三個表示所有方法--><aop:pointcut id="baseServiceMethods" expression="execution(* cn.com.sharpxiajun.service.*.*(..))"/><!-- 符合上面規(guī)則的攔截器都會調(diào)用到methodServiceAdvisor --><aop:advisor advice-ref="methodServiceAdvisor" pointcut-ref="baseServiceMethods"/></aop:config></beans>

?

這里用到了aop:config這是spring為了迎合AspectJ,對于AspectJ這個以后有機(jī)會我也想研究下,寫篇文章。

6.最后編寫針對UsersService的測試類UsersServiceImplTest,所在包是:cn.com.sharpxiajun.junittest.service,代碼如下:

package cn.com.sharpxiajun.junittest.service;import java.util.HashMap; import java.util.List; import java.util.Map;import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.transaction.TransactionConfiguration;import cn.com.sharpxiajun.service.UsersService;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations={"classpath:conf/applicationContext.xml"}) @TransactionConfiguration(defaultRollback = false) public class UsersServiceImplTest extendsAbstractTransactionalJUnit4SpringContextTests {@Autowiredprivate UsersService usersService = null;public UsersServiceImplTest(){System.out.println("初始化測試類....");}@Beforepublic void setUp() throws Exception{System.out.println("測試開始....");}@Afterpublic void tearDown() throws Exception{System.out.println("測試結(jié)束!!");}@Testpublic void testQueryUserList(){Map<String, Object> map = new HashMap<String, Object>();map.put("username", "sharpxiajun");try {List<Map<String, Object>> list = usersService.queryUsersList(map);System.out.println(list);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}}

運行結(jié)果如下:

初始化測試類.... 2011-10-11 23:44:45 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [conf/applicationContext.xml] 2011-10-11 23:44:45 org.springframework.context.support.AbstractApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.support.GenericApplicationContext@290fbc: startup date [Tue Oct 11 23:44:45 CST 2011]; root of context hierarchy 2011-10-11 23:44:46 org.springframework.core.io.support.PropertiesLoaderSupport loadProperties 信息: Loading properties file from class path resource [conf/constants.properties] 2011-10-11 23:44:46 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons 信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@922804: defining beans [usersDao,userService,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,propertyConfigurer,myDataSource,sqlMapClient,sqlMapClientTemplate,transactionManager,methodServiceAdvisor,org.springframework.aop.config.internalAutoProxyCreator,baseServiceMethods,org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0]; root of factory hierarchy 2011-10-11 23:44:46 org.springframework.test.context.transaction.TransactionalTestExecutionListener startNewTransaction 信息: Began transaction (1): transaction manager [org.springframework.jdbc.datasource.DataSourceTransactionManager@8de972]; rollback [false] 測試開始.... 進(jìn)入到了方法攔截器。。。。 調(diào)用的service: cn.com.sharpxiajun.service.impl.UsersServiceImpl@15fc672 調(diào)用的方法: public abstract java.util.List cn.com.sharpxiajun.service.UsersService.queryUsersList(java.util.Map) throws java.lang.Exception 參數(shù)是: {username=sharpxiajun} 返回結(jié)果是: [{enabled=false, username=admin, password=admin}, {enabled=false, username=test, password=test}] 攔截器執(zhí)行結(jié)束!! [{enabled=false, username=admin, password=admin}, {enabled=false, username=test, password=test}] 測試結(jié)束!! 2011-10-11 23:44:46 org.springframework.test.context.transaction.TransactionalTestExecutionListener endTransaction 信息: Committed transaction after test execution for test context [[TestContext@58e2a1 testClass = UsersServiceImplTest, locations = array<String>['classpath:conf/applicationContext.xml'], testInstance = cn.com.sharpxiajun.junittest.service.UsersServiceImplTest@186f3b3, testMethod = testQueryUserList@UsersServiceImplTest, testException = [null]]]

看來達(dá)到我們預(yù)期的結(jié)果了!!!

總結(jié)下了:這樣寫程序蠻有成就感,而且一步步來感覺很清晰。工作永遠(yuǎn)是匆忙的,總是快的讓人無法思考,我想這就是聰明的中國為什么沒有那么多優(yōu)秀開源框架的原因把。今天我又得到一個js開源框架,獲得源碼,它能做出yfiles一樣的效果,非常強大,也是老外寫的,牛的不行啊,我們平臺開發(fā)本來對前端要求很高的,要求圖形化實現(xiàn),真正做圖形化才知道他的復(fù)雜度之高,以后我會介紹下這個圖形化的框架,他能實現(xiàn)畫圖,自定義工作流,還能為關(guān)系復(fù)雜的圖形進(jìn)行布局合理的自動繪制,太牛了。

下個階段可能要回歸javascript了,java框架暫停一下

作者:夏天的森林


最近忙得要死,昨晚寫著寫著居然睡著了。哎,還是接著寫java框架吧。
??? 任何系統(tǒng)里,日志和一定的監(jiān)控是相當(dāng)重要的,在一個軟件整個生命周期里維護(hù)永遠(yuǎn)是大頭同時是痛苦的,而日志和監(jiān)控就是為后期維護(hù)提供了良好的基礎(chǔ)和手段,在java工程里面大多使用log4j來記錄系統(tǒng)日志,這個技術(shù)幾乎所有的java工程師都很熟悉,不太明白了,大家可以查查百度。這里我打算引入一個能監(jiān)控JDBC執(zhí)行語句的框架到我寫的java框架里面,這個框架非常的好用,他就是p6spy。

 “如果優(yōu)化SQL語句,如何進(jìn)行系統(tǒng)調(diào)優(yōu)”,這樣的問題我想很多程序員都聽到過,優(yōu)化和調(diào)優(yōu)是一個高難度的技術(shù)技能,只有具有扎實的技術(shù)功底和多年的項目經(jīng)驗才把他做好。我以前遇到這樣的提問,思路很機(jī)械,都是從SQL語法,索引,分區(qū),算法來考慮,其實優(yōu)化和調(diào)優(yōu)程序環(huán)境的搭配也是很重要的,例如,現(xiàn)在做java企業(yè)級項目,大多使用了orm技術(shù),很少會直接去用jdbc操作數(shù)據(jù)庫,而orm技術(shù)對jdbc的封裝讓最終執(zhí)行的sql語句的原型離程序員越來越遠(yuǎn),因此對jbdc執(zhí)行的原生態(tài)的sql語句的掌控是相當(dāng)重要的。

?? 在這里我可以分享一下我的經(jīng)驗。我現(xiàn)在比較推崇ibatis,它既實現(xiàn)了orm思想,又保留了sql語句的使用而不是像hibernate那樣對數(shù)據(jù)庫做完全面向?qū)ο蟮挠成?#xff0c;而產(chǎn)生了很多新的東西,比如hql。這是一個簡便的設(shè)計,我覺得系統(tǒng)設(shè)計最佳的方案就是新老兼容。其實不同的程序員擅長的技術(shù)也不同,做java的程序員當(dāng)然對java更熟悉,開發(fā)數(shù)據(jù)庫的程序員對數(shù)據(jù)庫很在行,假如你現(xiàn)在開發(fā)一個后臺數(shù)據(jù)庫數(shù)據(jù)量很大,業(yè)務(wù)操作很復(fù)雜的系統(tǒng),我這里會首推ibatis技術(shù)做orm層,因為這樣的大系統(tǒng)到了數(shù)據(jù)庫層面和dba打交道很多,對sql語句以及數(shù)據(jù)庫優(yōu)化很多,而ibatis直接寫sql語句的優(yōu)點就很明顯了,java程序員和dba的溝通和程序的交互也就方便多了。我最近做了幾個這樣的系統(tǒng),系統(tǒng)做完后我都會把重要的sql語句從系統(tǒng)里抽取出來給dba優(yōu)化,而dba優(yōu)化后我基本只要拷貝到程序里就可以,為整個工作帶來了便利。此外系統(tǒng)上線,如果操作數(shù)據(jù)庫報錯,在日志里提取sql語句,進(jìn)行檢查,檢查后維護(hù)程序也是有很大的便利,最后用sql嵌入orm,程序架構(gòu)的變遷所帶來的問題也會少很多,因此我以后開發(fā)系統(tǒng)orm技術(shù)的首選就是ibatis了。

?? 但是ibatis執(zhí)行jdbc是使用prepareStatement,所以最終打印出來的sql語句是下面的格式:

2011-10-13 11:17:36 Connection - {conn-100000} Connection 2011-10-13 11:17:36 Connection - {conn-100000} Preparing Statement: select t.username,t.password,t.enabled from users t where t.username = ? 2011-10-13 11:17:36 PreparedStatement - {pstm-100001} Executing Statement: select t.username,t.password,t.enabled from users t where t.username = ? 2011-10-13 11:17:36 PreparedStatement - {pstm-100001} Parameters: [sharpxiajun] 2011-10-13 11:17:36 PreparedStatement - {pstm-100001} Types: [java.lang.String]



我們拷貝出sql語句還要改寫,真是煩死人了,那么p6spy就能解決這個問題,它會把?替換成參數(shù),看看我加入了p6spy后執(zhí)行的效果吧。

?

|statement| select t.username,t.password,t.enabled from users t where t.username = 'sharpxiajun'

P6spy使用很簡單,只要完成下面步驟就行:
1.將p6spy.jar包放到應(yīng)用的classpath所在的路徑中;
2.修改連接池或者連接配置的jdbc的驅(qū)動為p6spy所提供的保證后的驅(qū)動,com.p6spy.engine.spy.P6SpyDriver
3.修改spy.properties并將其放到類搜索目錄.

下面我們開始開發(fā)了。

1.新的工程結(jié)構(gòu)圖如下:

新添兩個jar包:log4j-1.2.12.jar和p6spy.jar

2.將log4j.properties和spy.properties拷貝到src下面(暫時拷貝到src下面,其實應(yīng)該放到conf下面,因為現(xiàn)在都是在本地測試,沒有發(fā)布到tomcat下面,就把兩個文件拷貝到默認(rèn)的路徑下)

log4j.properties內(nèi)容如下:

log4j.debug=true log4j.rootLogger=INFO,CONSOLE,STDOUT#-----CONSOLE----- log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %c{1} - %m%n#-----SQL LOG----- log4j.logger.java.sql.Connection=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUGlog4j.logger.com.ibatis=debug log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=debug log4j.logger.com.ibatis.common.jdbc.ScriptRunner=debug log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=debug

日志打印到控制臺里,SQL LOG下的配置表示打印出jbdc以及ibatis的日志。

spy.properties內(nèi)容如下:

module.log=com.p6spy.engine.logging.P6LogFactory realdriver=oracle.jdbc.driver.OracleDriver deregisterdrivers=true executionthreshold= outagedetection=false outagedetectioninterval= filter=false include = exclude = sqlexpression = autoflush = true dateformat= includecategories= excludecategories=info,debug,result,batch stringmatcher= stacktrace=false stacktraceclass= reloadproperties=false reloadpropertiesinterval=60 useprefix=false appender=com.p6spy.engine.logging.appender.StdoutLogger append=true log4j.appender.STDOUT=org.apache.log4j.ConsoleAppender log4j.appender.STDOUT.layout=org.apache.log4j.SimpleLayout log4j.appender.STDOUT.layout.ConversionPattern=p6spy log4j.logger.p6spy=DEBUG,STDOUT

這里只要修改下realdriver=oracle.jdbc.driver.OracleDriver就行。

3.接下來我們只要更改下數(shù)據(jù)庫的驅(qū)動就行了,修改下constants.properties,內(nèi)容如下:

#db.driverClass = oracle.jdbc.driver.OracleDriver db.driverClass = com.p6spy.engine.spy.P6SpyDriver db.user = sharpxiajun db.password = sharpxiajun db.jdbcUrl = jdbc:oracle:thin:@127.0.0.1:1521:orcl#db.driverClass = com.mysql.jdbc.Driver #db.user = root #db.password = root #db.jdbcUrl = jdbc\:mysql\://localhost\:3306/sq_xidi?useUnicode\=true&characterEncoding\=utf-8

4.最后修改下USERS.xml配置文件,讓查詢方法接收到參數(shù),如下:

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd"> <sqlMap namespace="USERS"><select id="queryUserList" parameterClass="java.util.Map" resultClass="java.util.HashMap">select t.username,t.password,t.enabled from users t<dynamic prepend="where"><isNotEmpty prepend="and" property="username">t.username = #username#</isNotEmpty> </dynamic></select> </sqlMap>

執(zhí)行結(jié)果如下:

log4j: Parsing for [root] with value=[INFO,CONSOLE,STDOUT]. log4j: Level token is [INFO]. log4j: Category root set to INFO log4j: Parsing appender named "CONSOLE". log4j: Parsing layout options for "CONSOLE". log4j: Setting property [conversionPattern] to [%d{yyyy-MM-dd HH:mm:ss} %c{1} - %m%n]. log4j: End of parsing for "CONSOLE". log4j: Parsed "CONSOLE" options. log4j: Parsing appender named "STDOUT". log4j:ERROR Could not find value for key log4j.appender.STDOUT log4j:ERROR Could not instantiate appender named "STDOUT". log4j: Parsing for [com.ibatis.common.jdbc.SimpleDataSource] with value=[debug]. log4j: Level token is [debug]. log4j: Category com.ibatis.common.jdbc.SimpleDataSource set to DEBUG log4j: Handling log4j.additivity.com.ibatis.common.jdbc.SimpleDataSource=[null] log4j: Parsing for [java.sql.Connection] with value=[DEBUG]. log4j: Level token is [DEBUG]. log4j: Category java.sql.Connection set to DEBUG log4j: Handling log4j.additivity.java.sql.Connection=[null] log4j: Parsing for [com.ibatis] with value=[debug]. log4j: Level token is [debug]. log4j: Category com.ibatis set to DEBUG log4j: Handling log4j.additivity.com.ibatis=[null] log4j: Parsing for [java.sql.Statement] with value=[DEBUG]. log4j: Level token is [DEBUG]. log4j: Category java.sql.Statement set to DEBUG log4j: Handling log4j.additivity.java.sql.Statement=[null] log4j: Parsing for [com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate] with value=[debug]. log4j: Level token is [debug]. log4j: Category com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate set to DEBUG log4j: Handling log4j.additivity.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=[null] log4j: Parsing for [com.ibatis.common.jdbc.ScriptRunner] with value=[debug]. log4j: Level token is [debug]. log4j: Category com.ibatis.common.jdbc.ScriptRunner set to DEBUG log4j: Handling log4j.additivity.com.ibatis.common.jdbc.ScriptRunner=[null] log4j: Parsing for [java.sql.PreparedStatement] with value=[DEBUG]. log4j: Level token is [DEBUG]. log4j: Category java.sql.PreparedStatement set to DEBUG log4j: Handling log4j.additivity.java.sql.PreparedStatement=[null] log4j: Finished configuring. 初始化測試類.... 2011-10-13 11:17:35 XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [conf/applicationContext.xml] 2011-10-13 11:17:35 GenericApplicationContext - Refreshing org.springframework.context.support.GenericApplicationContext@2808b3: startup date [Thu Oct 13 11:17:35 CST 2011]; root of context hierarchy 2011-10-13 11:17:35 PropertyPlaceholderConfigurer - Loading properties file from class path resource [conf/constants.properties] 2011-10-13 11:17:35 DefaultListableBeanFactory - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1de17f4: defining beans [usersDao,userService,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,propertyConfigurer,myDataSource,sqlMapClient,sqlMapClientTemplate,transactionManager,methodServiceAdvisor,org.springframework.aop.config.internalAutoProxyCreator,baseServiceMethods,org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0]; root of factory hierarchy 2011-10-13 11:17:35 MLog - MLog clients using log4j logging. 2011-10-13 11:17:35 C3P0Registry - Initializing c3p0-0.9.1.2 [built 21-May-2007 15:04:56; debug? true; trace: 10] 2011-10-13 11:17:36 AbstractPoolBackedDataSource - Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 2wyjv28i1xq0ktewu9ral|131303f, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.p6spy.engine.spy.P6SpyDriver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 2wyjv28i1xq0ktewu9ral|131303f, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:oracle:thin:@127.0.0.1:1521:orcl, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 15, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> null, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ] 2011-10-13 11:17:36 TransactionalTestExecutionListener - Began transaction (1): transaction manager [org.springframework.jdbc.datasource.DataSourceTransactionManager@6db33c]; rollback [false] 測試開始.... 進(jìn)入到了方法攔截器。。。。 調(diào)用的service: cn.com.sharpxiajun.service.impl.UsersServiceImpl@23bdd1 調(diào)用的方法: public abstract java.util.List cn.com.sharpxiajun.service.UsersService.queryUsersList(java.util.Map) throws java.lang.Exception 參數(shù)是: {username=sharpxiajun} 2011-10-13 11:17:36 Connection - {conn-100000} Connection 2011-10-13 11:17:36 Connection - {conn-100000} Preparing Statement: select t.username,t.password,t.enabled from users t where t.username = ? 2011-10-13 11:17:36 PreparedStatement - {pstm-100001} Executing Statement: select t.username,t.password,t.enabled from users t where t.username = ? 2011-10-13 11:17:36 PreparedStatement - {pstm-100001} Parameters: [sharpxiajun] 2011-10-13 11:17:36 PreparedStatement - {pstm-100001} Types: [java.lang.String] |statement| select t.username,t.password,t.enabled from users t where t.username = 'sharpxiajun' 返回結(jié)果是: [] 攔截器執(zhí)行結(jié)束!! [] 測試結(jié)束!! |commit| 2011-10-13 11:17:36 TransactionalTestExecutionListener - Committed transaction after test execution for test context [[TestContext@1fac852 testClass = UsersServiceImplTest, locations = array<String>['classpath:conf/applicationContext.xml'], testInstance = cn.com.sharpxiajun.junittest.service.UsersServiceImplTest@1758cd1, testMethod = testQueryUserList@UsersServiceImplTest, testException = [null]]] 2011-10-13 11:17:36 GenericApplicationContext - Closing org.springframework.context.support.GenericApplicationContext@2808b3: startup date [Thu Oct 13 11:17:35 CST 2011]; root of context hierarchy 2011-10-13 11:17:36 DefaultListableBeanFactory - Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1de17f4: defining beans [usersDao,userService,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,propertyConfigurer,myDataSource,sqlMapClient,sqlMapClientTemplate,transactionManager,methodServiceAdvisor,org.springframework.aop.config.internalAutoProxyCreator,baseServiceMethods,org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0]; root of factory hierarchy

看到了這句話了吧|statement| select t.username,t.password,t.enabled from users t where t.username = 'sharpxiajun' ,ok,寫完了。

總結(jié)下了:java框架的dao和service這塊寫完了,這個系列下一篇是針對前三篇的技術(shù)要點做一下比較詳細(xì)的解釋,做程序不僅要知其然還要知其所以然,這樣才能提高。不過下面的博文我會回到j(luò)avascript,加固一下javascript我在學(xué)習(xí)中感覺比較難理解的基礎(chǔ)知識,之后要繼續(xù)研究jquery了,這個才是我的重點

作者:夏天的森林


總結(jié)

以上是生活随笔為你收集整理的java笔记:自己动手写javaEE的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。