需求:整合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框架就完成了。