需求:整合ssh框架作一個保存用戶的業務,業務比較簡單,重在ssh框架整合。
建立數據庫和表java
CREATE DATABASE ssh01; USE DATABASE; 表由Hibernate建立,能夠看配置是否成功
一:導入jar包mysql
Hibernate須要jarweb
Hibernate基本jar mysql驅動 c3p0鏈接池 日誌包 jpa
Spring須要jarspring
Ioc(6個):beans,context,expression,core+兩個日誌 Aop(4個): spring-aop-4.2.4.RELEASE spring整合aspect aspectj:com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar aop聯盟:com.springsource.org.aopalliance-1.0.0.jar spring聲明式事務: spring-jdbc-4.2.4.RELEASE.jar spring-tx-4.2.4.RELEASE.jar
Spring整合websql
spring-web-4.2.4.RELEASE.jar
Spring整合Hibernate數據庫
spring-orm-4.2.4.RELEASE.jar
Spring整合Struts2express
struts2-spring-plugin-2.3.24.jar **注意** Spring整合Struts2包先不要導入,由於若是導入在項目啓動的時候, 會在ServetContext中查找spring工廠,會報錯,拋出下面異常
You might need to add the following to web.xml: <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> 17:46:11,396 ERROR Dispatcher:42 - Dispatcher initialization failed java.lang.NullPointerException
在配置ContextLoaderListener監聽器在項目啓動的時候建立spring容器的時候導入apache
最後:去除重複的jar struts2基本Jar和Hibernate基本Jar中都有
javassist-3.18.1-GA.jar,刪除一個低版本的,不然在使用的時候會出現問題。瀏覽器
二:須要的配置文件安全
Hibernate須要的配置文件
Hibernate.cfg.xml:src路徑下
<hibernate-configuration> <session-factory> <!-- 必要的配置信息:鏈接數據庫的基本參數 --> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql:///ssh01</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">root</property> <!-- Hibernate的屬性 --> <!-- Hibernate的方言:做用,根據配置的方言生成相應的SQL語句 --> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <!-- Hibernate顯示SQL語句: --> <property name="hibernate.show_sql">true</property> <!-- Hibernate格式化SQL語句: --> <property name="hibernate.format_sql">true</property> <!-- Hibernate的hbm2ddl(數據定義語言:create drop alter ...)屬性 --> <property name="hibernate.hbm2ddl.auto">update</property> <!-- 配置C3P0鏈接池 --> <property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property> <!-- Hibernate加載映射 --> <mapping resource="com/itheima/domain/Customer.hbm.xml"/> </session-factory> </hibernate-configuration>
jdbc.properties:(對數據庫參數的封裝) src下
jdbc.driver=com.mysql.jdbc.Driver; jdbc.url=jdbc:mysql://localhost:3306/ssh01 jdbc.username=root jdbc.password=root
log4J.properties日誌文件 src下
Customer.hbm.xml 須要保存客戶實體的映射文件
<hibernate-mapping package="com.itheima.domain"> <class name="Customer" table="cst_customer"> <!-- 數據庫表中的字段名和實體類屬性名相同的時候能夠省略Column屬性 --> <id name="cust_id"> <!-- 配置主鍵生成策略 --> <generator class="native"></generator> </id> <property name="cust_name"></property> <property name="cust_source"></property> <property name="cust_industry"></property> <property name="cust_level"></property> <property name="cust_phone"></property> <property name="cust_mobile"></property> </class> </hibernate-mapping>
Struts須要的配置文件
web.xml:配置Struts核心過濾器
<filter> <filter-name>Struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>Struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
struts2.xml:配置action 位置src下
<struts> <constant name="struts.devMode" value="true"></constant> <package name="ssh" extends="struts-default" namespace="/"> <action name="customer_*" class="com.itheima.web.action.CustomerAction" method="{1}"> <result name="success">/success.jsp</result> </action> </package> </struts>
三:建立service,dao,domain,action
建立Customer實體類,以及實體類對應的映射文件映射文件看上面)
僞代碼(爲屬性提供get/set方法)
public class Customer implements Serializable{ private static final long serialVersionUID = 1L; private Long cust_id; private String cust_name; private String cust_source; private String cust_industry; private String cust_level; private String cust_phone; private String cust_mobile;
CustomerService,CustomerServiceImpl
將customerService對象交給spring容器管理 service須要調用dao層的方法,進行屬性注入
public class CustomerServiceImpl implements CustomerService { //建立dao,並提供set方法,進行屬性注入 private CustomerDao customerDao; public void setCustomerDao(CustomerDao customerDao) { this.customerDao = customerDao; } @Override public void save(Customer customer) { customerDao.save(customer); } }
建立CustomerDao,CustomerDaoImpl
將CustomerDao對象交給spring容器管
public class CustomerDaoImpl implements CustomerDao { @Override public void save(Customer customer) { //獲取session對象,來操做數據庫 Configuration config = new Configuration().configure(); SessionFactory factory = config.buildSessionFactory(); Session session = factory.openSession(); Transaction tx = session.beginTransaction(); session.save(customer); tx.commit(); session.close(); } }
建立CustomerAction並在struts.xml中進行配置(struts.xml文件)
public class CustomerAction extends ActionSupport implements ModelDriven<Customer> { private static final long serialVersionUID = 1L; //使用ModelDriven模型驅動進行數據封裝,必須手動來建立對象 private Customer customer = new Customer(); @Override public Customer getModel() { return customer; } /* * 建立CustomerService對象調用service層的方法 * 由於action是由struts2建立service交給spring容器管理因此不能夠直接注入 */ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); CustomerService customerService = (CustomerService) context.getBean("customerService"); //保存用戶的方法 public String save(){ customerService.save(customer); return SUCCESS; } }
在spring容器中管理CustomerService,CustomerDao
<!-- 配置dao --> <bean id="customerDao" class="com.itheima.dao.impl.CustomerDaoImpl"></bean> <!-- 配置service並注入customerDao屬性 --> <bean id="customerService" class="com.itheima.service.impl.CustomerServiceImpl"> <property name="customerDao" ref="customerDao"></property> </bean>
建立save.jsp
<form action="${pageContext.request.contextPath}/customer_save.action" method="post"> 客戶名稱:<input type="text" name="cust_name"><br/> 客戶等級:<input type="text" name="cust_level"><br/> 客戶來源:<input type="text" name="cust_source"><br/> 客戶行業:<input type="text" name="cust_industry"><br/> 客戶電話:<input type="text" name="cust_mobile"><br/> <input type="submit" value="保存"> </form>
三:測試
在瀏覽器輸入http://localhost/ssh01/save.jsp輸入數據,點擊提交, 數據庫表建立成功,數據成功保存
這樣,最簡單最原始的ssh框架整合完成。
問題一:在CustomerAction中的獲取service
解決方案:
使用監聽器,當項目啓動的時候監聽ServletContext對象的建立, 當ServletContext對象建立的時候加載applicationContext.xml文件, 建立spring工廠初始化bean將spring容器放置在ServletContext域對象中 spring爲咱們提供了ContextLoaderListener,在web.xml文件中配置該監聽器, 它會加載applicationContext.xml,建立spring工廠, 並存放在ServletContext域對象中
代碼實現
在web.xml中進行配置
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
在CustomerService中獲取service
ServletContext servletContext = ServletActionContext.getServletContext(); WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext); CustomerService customerService = (CustomerService) context.getBean("customerService");
問題二:Action由struts2容器管理,service是交給spring容器管理,不能直接注入 如何在action中注入service 解決方案:spring整合struts 前提:導入struts-spring-plugin.jar,在項目啓動的時候建立spring工廠, 放置到context域中 方式一:Action仍是struts建立,Spring負責爲Struts中的Action注入匹配的屬性
//由spring容器爲action進行屬性注入 private CustomerService customerService; public void setCustomerService(CustomerService customerService) { this.customerService = customerService; }
緣由:爲何直接導入struts2-spring-plugin.jar包就能夠直接注入? 在default.properties配置有中struts.objectFactory.spring.autoWire = name Spring負責爲Struts中的Action注入匹配的屬性,根據屬性的名稱注入(默認,能夠修改) 方式二:將action交給spring容器來管理, action是多例的,因此須要配置scope="prototype"屬性 修改applicationContext.xml和struts.xml文件
<!-- 將action交給spring容器來管理 --> <bean id="customerAction" class="com.itheima.web.action.CustomerAction" scope="prototype"> <property name="customerService" ref="customerService"></property> </bean> 修改struts文件: <package name="ssh" extends="struts-default" namespace="/"> <action name="customer_*" class="customerAction" method="{1}"> <result name="success">/success.jsp</result> </action> </package>
問題三
在dao層,使用Hibernate操做數據庫,須要加載Hibernate.cfg.xml配置文件 獲取SessionFactory(相似於DataSource數據源)而後獲取到session對象 (相似於Connection鏈接對象,SessionFactory:是重量級而且線程安全的, 因此在項目中只存在一份即 解決方案 將SessionFactory交給spring容器管理:單例 sessionFactory是一個接口,在這裏咱們使用它的實現類LocalSessionFactoryBean 選擇Hibernate5的,由於咱們使用的Hibernate是5.0.7版本的 在applicationContext.xml中配置
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"> <!-- 須要指定hibernate的核心配置文件,由於這裏面有須要的數據庫配置參數 --> <property name="configLocation" value="classpath:hibernate.cfg.xml"></property> </bean>
使用spring提供的HibernateTemplate在dao層操做數據庫,將HibernateTemplate交給 spring容器來管理,並注入到dao層 在applicationContext.xml中進行配置 配置Hibernate模板須要注入SessionFactory,由於模板是對Hibernate的包裝,底層仍是 使用session來操做數據庫,因此須要獲取到session對象,經過SessionFactory對象
<!-- 配置HibernateTemplate模板 --> <bean id="hibernateTemplate" class="org.springframework.orm.hibernate5.HibernateTemplate"> <!-- hibernateTemplate模板底層操做數據庫是經過session對象,因此須要注入sessionFactory對象獲取到session --> <property name="sessionFactory" ref="sessionFactory"></property> </bean> <!-- 配置dao --> <bean id="customerDao" class="com.itheima.dao.impl.CustomerDaoImpl"> <!-- 爲 dao注入HibernateTemplate dao層使用HibernateTemplate來操做數據庫--> <property name="hibernateTemplate" ref="hibernateTemplate"></property> </bean>
修改以後dao層的代碼以下
public class CustomerDaoImpl implements CustomerDao { //注入HibernateTemplate來操做數據庫 private HibernateTemplate hibernateTemplate; public void setHibernateTemplate(HibernateTemplate hibernateTemplate) { this.hibernateTemplate = hibernateTemplate; } @Override public void save(Customer customer) { hibernateTemplate.save(customer); } }
這樣直接運行程序,會拋出異常,異常信息爲:
Write operations are not allowed in read-only mode (FlushMode.MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.
問題:只讀模式下(FlushMode.NEVER/MANUAL)寫操做不被容許: 把你的Session改爲FlushMode.COMMIT/AUTO或者清除事務定義中的readOnly標記 spring會將獲取到的session綁定到當前線程中, 爲了確保在一個請求中service層和dao層使用的是一個session對象 只有受spring聲明式事務的方法才具備寫權限。不然在操做的會拋出上面異常 因此還須要在applicationContext.xml中配置spring的聲明式事務
<!-- 無論是註解仍是xml文件的方式配置spring聲明式事務,都須要指定平臺事務管理器 --> <!-- Hibernate 的事務平臺管理器 --> <bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager"> <!-- 須要session對象來開啓事務session.beginTransaction() 因此須要注入SessionFactory來獲取到session對象 --> <property name="sessionFactory" ref="sessionFactory"></property> </bean> <!-- 配置加強/通知spring提供好的,咱們來指定規則 --> <tx:advice transaction-manager="transactionManager" id="my_advice"> <tx:attributes> <!-- 這些事務隔離級別,事務傳播行爲,超時信息都是默認值 當進行查詢操做的時候,能夠修改propagation="SUPPORTS" read-only="true"(查詢的時候只讀) --> <!-- 在開發中的經常使用配置 --> <!-- <tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/> <tx:method name="find*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true" timeout="-1"/> <tx:method name="get*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true" timeout="-1"/> <tx:method name="update*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/> <tx:method name="delete*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/> --> <tx:method name="save" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/> </tx:attributes> </tx:advice> <!-- aop配置 --> <aop:config> <!-- 配置切入點 事務控制在service層 --> <aop:pointcut expression="execution(* com.itheima.service.impl.*.*(..))" id="myPt"/> <aop:advisor advice-ref="my_advice" pointcut-ref="myPt"/> </aop:config>
這樣一個純xml配置整合ssh框架就完成了!!!
在實際的開發中都是註解+xml來完成的。如今咱們來對代碼進行優化。 在實際的開發中: 咱們自定義bean的建立和注入經過註解來完成(CustomerService等) 非自定義的bean交給xml文件配置(例如數據源dataSource和SessionFactory) 優化一:去除struts.xml文件(action的配置使用註解來完成) 使用註解的方式配置action必須導入jar包struts2-convention-plugin-2.3.24.jar 在CustomerAction上面添加註解:
@Namespace("/") @ParentPackage("struts-default") public class CustomerAction extends ActionSupport implements ModelDriven<Customer> { private static final long serialVersionUID = 1L; //使用ModelDriven模型驅動進行數據封裝,必須手動來建立對象 private Customer customer = new Customer(); @Override public Customer getModel() { return customer; } //由spring容器爲action進行屬性注入 private CustomerService customerService; public void setCustomerService(CustomerService customerService) { this.customerService = customerService; } //保存用戶的方法 @Action(value="customer_save",results={@Result(name="success",location="/success.jsp")}) public String save(){ customerService.save(customer); return SUCCESS; } }
優化二:全部的自定義bean都使用註解的方式進行配置,
去除applicationContext中的自定義bean
必須在applicationContext中開啓組件掃描
在applicationContext中開啓組件掃描 <!-- 開啓組件掃描 --> <context:component-scan base-package="com.itheima"></context:component-scan> CustomerAction中的代碼 @Namespace("/") @ParentPackage("struts-default") @Controller("customerAction") public class CustomerAction extends ActionSupport implements ModelDriven<Customer>{ @Autowired private CustomerService customerService; } CustomerServiceImpl中配置註解的代碼 @Service("customerService") public class CustomerServiceImpl implements CustomerService { @Autowired private CustomerDao customerDao; CustomerDaoImpl中註解的代碼 @Repository("customerDao") public class CustomerDaoImpl implements CustomerDao { //注入HibernateTemplate來操做數據庫 @Autowired private HibernateTemplate hibernateTemplate;
優化三:spring的聲明式事務使用xml+註解的方式進行配置
減小applicationContext.xml文件中的配置代碼
要在applicationContext中開啓事務註解支持
<!-- 開啓事務註解支持 --> <tx:annotation-driven/>
事務是在service層進行控制的,在service層上加上事務註解 能夠去除applicationContext中配置加強和aop配置的代碼
@Service("customerService") @Transactional public class CustomerServiceImpl implements CustomerService
優化四:去除持久化類的映射配置文件,使用註解進行代替
其它字段和屬性名相同,能夠省略@Column @Entity @Table(name="cst_customer") public class Customer implements Serializable{ private static final long serialVersionUID = 1L; //oid @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
去除持久化類的映射配置文件以後,在Hibernate.cfg.xml文件中 引入持久化類映射文件的代碼須要修改: <!-- Hibernate加載映射 --> <mapping resource="com/itheima/domain/Customer.hbm.xml"/> Customer.hbm.xml文件已經去除,修改成 <mapping class="com.itheima.domain.Customer"/>
優化五:去除Hibernate.cfg.xml
在前面applicationContext.xml中將SessionFactory交給spring容器管理的時候 <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"> <!-- 須要指定hibernate的核心配置文件,由於這裏面有須要的數據庫配合參數 --> <property name="configLocation" value="classpath:hibernate.cfg.xml"></property> </bean> 指定了核心配置文件,如今須要手動的配置數據庫參數以及Hibernate的一些基本配置 如是否顯示sql語句,是否格式化sql語句,mysql方言配置等
最終:只留下了applicationContext.xml配置文件
<?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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 開啓事務註解支持 --> <tx:annotation-driven/> <!-- 開啓組件掃描 --> <context:component-scan base-package="com.itheima"></context:component-scan> <!-- 將配置文件加載到容器中 --> <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <!-- 進行屬性的注入 --> <property name="driverClass" value="${jdbc.driver}"></property> <property name="jdbcUrl" value="${jdbc.url}"></property> <property name="user" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean> <!-- 將SessionFactory交給spring容器來管理 --> <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"> <!-- 注入數據源 --> <property name="dataSource" ref="dataSource"></property> <!-- Hibernate的基本配置 --> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">true</prop> <prop key="hibernate.hbm2ddl.auto">update</prop> </props> </property> <!-- 指定掃描包 包下具備@Entity的這些類 --> <property name="packagesToScan"> <list> <value>com.itheima.domain</value> </list> </property> </bean> <!-- 配置HibernateTemplate模板 --> <bean id="hibernateTemplate" class="org.springframework.orm.hibernate5.HibernateTemplate"> <!-- hibernateTemplate模板底層操做數據庫是經過session對象,因此須要注入sessionFactory對象獲取到session --> <property name="sessionFactory" ref="sessionFactory"></property> </bean> <!-- 無論是註解仍是xml文件的方式配置spring聲明式事務,都須要指定平臺事務管理器 --> <!-- Hibernate 的事務平臺管理器 --> <bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager"> <!-- 須要session對象來開啓事務session.beginTransaction() 因此須要注入SessionFactory來獲取到session對象 --> <property name="sessionFactory" ref="sessionFactory"></property> </bean> </beans>
這樣,以xml+註解結合方式整合ssh框架就完成了。