SSH框架整合项目理论及各个框架的一些应用机制知识补充

版权声明:转载请标明出处: https://blog.csdn.net/gaofengyan/article/details/86607207

        目录

底层数据库数据持久化操作层:

业务逻辑层(service)

业务表示层(Action)


        这篇文章是对自己做过的SSH(hibernate3.6.10、spring-3.2.2、struts2-2.3.1.2)框架项目的一些理论知识进行基础梳理,说真的,自己对SSH框架的整合还是不太理想,相对来说,跟现在流行的框架相比,在使用的时候,不是太适应,可是很多知识也是相通的,都具备相似的理论基础和应用机制,不同的企业在使用的时候也有不同的风格,所以不能让我们去改变企业,是要随流去不断学习,适应企业的工作制度。好吧,言归正传,下面将从最底层自己在工作中的笔记进行整理。

底层数据库数据持久化操作层:

        关于hibernate框架的介绍,这里就不再赘述了,可以查看官方文档【http://hibernate.org/】进行学习。还记得我们的pojo实体类需要继承Serializable接口吧,可能很多初学者在关于IO流学习序列化的时候接触过Serializable接口,定义是:只有实现了接口的类才能被序列化。

       【那我们实际开发中,实体类实现Serializable接口的目的是什么呢?

         hibernate有三级缓存机制(一级缓存是session;二级缓存是sessionFactory;三级缓存是对象查询缓存),并且框架使用的是反射机制,缓存会将对象写进硬盘,所以需要序列化的输入输出,以及兼容对象在网络中的传输 等等,因此类需要实现序列化Serializable接口,而这不是绝对需要的,只是在实际开发中为了后期的可扩展性,是很有必要的。

        【至于Serializable为什么能用于网络传输

          Serializable只是一个标识接口,这个序列化接口没有任何方法和域,仅用于标识序列化的语意。实现 java.io.Serializable 接口的类是可序列化的。没有实现此接口的类将不能使它们的任一状态被序列化或逆序列化。序列化类的所有子类本身都是可序列化的。

          确切的说应该是对象的序列化,一般程序在运行时,产生对象,这些对象随着程序的停止运行而消失,但如果我们想把某些对象(因为是对象,所以有各自 不同的特性)保存下来,在程序终止运行后,这些对象仍然存在,可以在程序再次运行时读取这些对象的值,或者在其他程序中利用这些保存下来的对象。这种情况 下就要用到对象的序列化。只有序列化的对象才可以存储在存储设备上。为了对象的序列化而需要继承的接口也只是一个象征性的接口而已,也就是说继承这个接口说明这个对象可以 被序列化了,没有其他的目的。之所以需要对象序列化,是因为有时候对象需要在网络上传输,传输的时候需要这种序列化处理,从服务器硬盘上把序列化的对象取 出,然后通过网络传到客户端,再由客户端把序列化的对象读入内存,执行相应的处理。将二级缓存中的内容持久化保存下来,便于恢复缓存的信息,hibernate的缓存机制通过使用序列化,断定应该是基于序列化的缓存,如没有 serializable接口,在序列化时,使用objectOutputStream的write(object)方法将对象保存到文件时将会出现异常。
Hibernate并不要求持久化类必须实现java.io.Serializable接口,但是对于采用分布式结构的Java应用,当Java对象在不同的进程节点之间传输时,这个对象所属的类必须实现Serializable接口,此外,在Java Web应用中,如果希望对HttpSession中存放的Java对象进行持久化,那么这个Java对象所属的类也必须实现Serializable接口。对于hibernate的缓存机制学习,这里不赘述,推荐两篇博客:https://www.cnblogs.com/xiaoluo501395377/p/3377604.html  

https://blog.csdn.net/weixin_42249629/article/details/81840715

        展示一个【hibernate.cfg.xml】配置文件的基础配置内容示例:

    <!--    sessionFactory代表一个数据库的描述    -->
    <session-factory>
        <!--  mysql账户名  -->
        <property name="connection.username">root</property>
        <!--  mysql密码  -->
        <property name="connection.password">123456</property>
        <!--  mysql连接URL  -->
        <property name="connection.url">jdbc:mysql://localhost:3306/sh05oa</property>
        <!--  mysql驱动  -->
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <!--  数据库方言  -->
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
        <!--
        validate  默认值
        根据持久化类和映射文件检查表的结构
        update
        hibernate容器在启动的时候,会根据持久化类和映射文件检查表的结构
        如果不存在,则创建,如果存在,则更新
        create
        每次启动hibernate容器,不管表是否存在,都会创建
        create-drop
        当启动hibernate容器时创建表,当hibernate容器销毁时,删除表
    -->
        <property name="hbm2ddl.auto">update</property>
        <!--  显示sql语句 引入映射文件 -->
        <property name="show_sql">true</property>
        
    </session-factory>

业务逻辑层(service)

          对于业务逻辑层的操作,重点是对事务的操作,我们应用spring框架作为管理容器,采用面向切面(AOP)编程技术,学习官网:https://spring.io/projects  

          写spring的配置文件,引入sessionFactory,这里给一种简单的引入方式:

    <!--把hibernate的配置文件引入进来-->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="configLocation">
            <value>classpath:hibernate/hibernate.cfg.xml</value>
        </property>
    </bean>

         引入完后最好对sessionFactory进行测试,一步一步的的往后面搭建,避免错误积累太多。

       在spring的配置文件中,这里我写spring的声明式事务管理
       依据的原理是aop:使各个通知和目标方法实现松耦合(目的)

    <!--配置事务管理器:告诉spring容器开启事务用什么技术-->
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory">
            <ref bean="sessionFactory"/>
        </property>
    </bean>
    <!--对哪个方法(目标类里的方法)进行怎么样的事务控制方法,比如这里以save开头的方法-->
    <!-- 
				告诉spring容器什么样的目标方法采用什么样的事务策略
				 name 用来说明目标方法
				   save*  以save开头的目标方法
				   isolation 隔离属性
				   propagation 传播属性
				      	是解决事务嵌套问题的
				   read-only
				      	为true:只读事务  只能读
				      	为false:读写事务
			 -->
    <tx:advice id="tx" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>
        </tx:attributes>
    </tx:advice>
    <aop:config>
        <aop:pointcut expression="execution(* cn.itcast.shoa.service.impl.*.*(..))" id="perform"/>
        <aop:advisor advice-ref="tx" pointcut-ref="perform"/>
    </aop:config>

关于对spring的事务,这里引用部分底层封装源码进行解析,spring容器为事务处理提供了一个平台。

1、spring容器为事务提供了一个总的接口【PlatformTransactionManager】

public interface PlatformTransactionManager {
	//得到一个事务
	TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
	//事务的提交
	void commit(TransactionStatus status) throws TransactionException;
	//事务的回滚
	void rollback(TransactionStatus status) throws TransactionException;
}

2、事务的定义接口【TransactionDefinition】,事务的传播属性  解决事务的嵌套问题;事务的隔离机制   解决多线程并发引起的数据安全问题(脏读、幻读、不可重复读)。

public interface TransactionDefinition {
	//事务的传播属性  解决事务的嵌套问题
	int PROPAGATION_REQUIRED = 0;
	int PROPAGATION_SUPPORTS = 1;
	int PROPAGATION_MANDATORY = 2;
	int PROPAGATION_REQUIRES_NEW = 3;
	int PROPAGATION_NOT_SUPPORTED = 4;
	int PROPAGATION_NEVER = 5;
	int PROPAGATION_NESTED = 6;
	//事务的隔离机制   解决多线程并发引起的数据安全问题(脏读、幻读、不可重复读) 
	int ISOLATION_DEFAULT = -1;
	int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;
	int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;
	int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;
	int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;
	boolean isReadOnly();
}

3、事务的状态解读接口【TransactionStatus】

public interface TransactionStatus extends SavepointManager {
	boolean isNewTransaction();//新的事务

	boolean hasSavepoint();//保存点

	void setRollbackOnly();//设置回滚

	boolean isRollbackOnly();

	void flush();//刷新

	boolean isCompleted();//事务完成
}

4、spring事务提供了一个抽象类【AbstractPlatformTransactionManager】,并且实现了总接口【PlatformTransactionManager】

public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager{
	public final void commit(TransactionStatus status) throws TransactionException {//判断事务状态,是否完成
                               //对该方法进行了实现
	}

	public final void rollback(TransactionStatus status) throws TransactionException {
			       //对该方法进行了实现
	}
	public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
				Object transaction = doGetTransaction();
	}
	protected abstract Object doGetTransaction() throws TransactionException;
}

5、具体的实现类

     如果是jdbc编程

public class DataSourceTransactionManager extends AbstractPlatformTransactionManager{
			//实现以下抽象类的方法
				doGetTransaction(){
				
				}
}

    如果是hibernate编程

public class HibernateTransactionManager extends AbstractPlatformTransactionManager{
		doGetTransaction(){
				
		}
}

    如果是jdo操作数据库

public class JdoTransactionManager extends AbstractPlatformTransactionManager{
		doGetTransaction(){
				
		}
}

     把dao层和service层的类放入到spring的配置文件中

    <!--把dao放到spring管理容器中-->
    <bean id="personDao" class="cn.itcast.shoa.dao.impl.PersonDaoImpl">
        <property name="sessionFactory">
            <ref bean="sessionFactory"/>
        </property>
    </bean>
    <!--把service放到spring管理容器中-->
    <bean id="personService" class="cn.itcast.shoa.service.impl.PersonServiceImpl">
        <property name="personDao">
            <ref bean="personDao"/>
        </property>
    </bean>

   测试personService,看产生的对象是否是代理对象

package cn.itcast.shoa.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringUtils {
    public static ApplicationContext context;
    //注释,公共的测试工具类,后面的测试可以直接调用,静态代码块,在类加载的时候创建对象
    static {
        context = new ClassPathXmlApplicationContext("spring/applicationContext.xml");
    }
}

   测试类:

package cn.itcast.shoa.test;

import cn.itcast.shoa.service.PersonService;
import org.junit.Test;

public class PersonTest extends SpringUtils {
    @Test
    public void TestPersonService(){
        PersonService personService = (PersonService) context.getBean("personService");
        System.out.println(personService);
    }
}

        我们启动后测试,可以用断点调试,只要输出对象地址即表示成功运行。以上内容基本就是整个项目框架搭建过程中的一些对spring事务管理的测试和基本搭建原理。

        就以上内容我们进行一些知识拓展:

为什么会有接口、抽象类、实现类这样的组织结构

         1、接口是用来定义标准的:

  •           得到事务
  •           事务的回滚
  •           事务的提交

        2、抽象类:

  •           实现共同的方法
  •           在不同的实现方法的时候提供抽象方法
  •           具体到事务
  •           得到事务是抽象方法:getTransaction
  •           事务的提交  在该抽象类中得到了实现:commit
  •           事务的回滚  在该抽象类中得到了实现:rollback

         说明:只要得到事务以后,事务的提交和回滚都是一样的操作;但是得到事务的方法是不一样的。

 【spring容器为什么会提供事务接口呢?或者是spring为何要注入接口,而注入实现类就会报错

       为了实现完全的面向接口编程,扩展功能不是修改源代码实现。如果只是单纯注入是可以用实现类接收注入对象的,但是往往开发中会对实现类做增强,如事务,日志等,实现增强的AOP技术是通过动态代理实现的,而spring默认是JDK动态代理,对实现类对象做增强得到的增强类与实现类是兄弟关系,所以不能用实现类接收增强类对象,只能用接口接收。

 【spring怎么知道注入哪个实现?

       如果Spring配置了component scan,并且要注入的接口只有一个实现的话,那么spring框架可以自动将interface与实现组装起来。如果没有配置component scan,那么你必须在application-config.xml(或等同的配置文件)定义这个bean。

 【需要@Qualifier和@Resource注解吗?

       一旦一个接口有多个实现,那么就需要每个特殊化识别并且在自动装载过程中使用@Qualifier和@Autowired一起使用来标明。如果是使用@Resource注解,那么应该使用resource中属性名称来标注@Autowired.

 【为什么@Autowired使用在interface上而不是实现类上?

       首先,一般使用接口是很常用并且有益的编程技术。其次,在spring中,你可以在运行过程中注入各种实现。一个很经典的情况就是在测试阶段,注入模拟的实现类。

 【ssh开发时事务管理为什么交给Spring而不交给hibernate

       事务管理是Hibernate做的,至于为什么在Spring容器中配置是因为SessionFactory或者HibernateTemplate等都交与Spring容器管理了,Spring为Hibernate提供了事务管理的接口。在标准的ssh开发里,spring是各层之间的管理者,说的具体点说,spring实现了数据层,逻辑层和控制层间的松耦合,这样一来,通过spring的aop机制,对sevice层的事务作精确的切面,可以很方便的实现事务管理的业务需求,并且若使用配置文件的方式的的话(也就是声明式,个人感觉用tx标签是最方便的),完全不侵入代码。

 【脏读、不可重复读、幻象读概念说明:

a.脏读:指当一个事务正在访问数据,并且对数据进行了修改,而这个数据还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据还没有提交那么另外一个事务读取到的这个数据我们称之为脏数据。依据脏数据所做的操作肯能是不正确的。

b.不可重复读:指在一个事务内,多次读同一数据。在这个事务还没有执行结束,另外一个事务也访问该同一数据,那么在第一个事务中的两次读取数据之间,由于第二个事务的修改第一个事务两次读到的数据可能是不一样的,这样就发生了在一个事务内两次连续读到的数据是不一样的,这种情况被称为是不可重复读。

c.幻象读:一个事务先后读取一个范围的记录,但两次读取的纪录数不同,我们称之为幻象读(两次执行同一条 select 语句会出现不同的结果,第二次读会增加一数据行,并没有说这两次执行是在同一个事务中)

 【事务几种实现方式

(1)编程式事务管理对基于 POJO 的应用来说是唯一选择。我们需要在代码中调用beginTransaction()、commit()、rollback()等事务管理相关的方法,这就是编程式事务管理。

(2)基于 TransactionProxyFactoryBean的声明式事务管理

(3)基于 @Transactional 的声明式事务管理

(4)基于Aspectj AOP配置事务

 【浅谈说明是spring、什么是IOC、什么是DI

          Spring是一个容器,可以接管各个层次的Bean(action/domain/pojo/javabean),并且可以配置bean与bean之间的关系在java代码里使用bean只需要  用ApplicationContext 的getBean(配置文件里bean的id)方法就可以了,spring是依赖反射机制的。ioc(inverse of control )控制反转:所谓控制反转就是把对象(bean)对象和维护对象(bean)之间的关系的权利转移到Sqring容器中去了(ApplicationContext.xml)而程序本身不再维护了。di(dependency injection)依赖注入:实际上DI和IOC是同一个概念,因为在ApplicationContext.xml配置文件中bean和bean之间通过ref来维护的时候是相互依赖的,所以又叫做依赖注入。也就是控制反转。因为ApplicationContext是非常消耗内存的,所以必须保证一个项目里只有一个ApplicationContext实例;那么如何保证这有一个实例呢,就需要把ApplicationContext对象做成单例形式。

public class PersonServiceImpl{
         private PlatformTransactionManager platformTransactionManager;
	 public void setPlatformTransactionManager(PlatformTransactionManager platformTransactionManager){
		this.platformTransactionManager = platformTransactionManager;
     }

     public void aa(){
		//可以利用PlatformTransactionManager干活了
	 }
}

业务表示层(Action)

          struts2官网:https://struts.apache.org/getting-started/ struts2是一个mvc框架;是一个aop容器。

    struts2的插件机制

        1、在web.xml文件中的拦截器配置

  <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>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>REQUEST</dispatcher>
  </filter-mapping>

  注意,以上配置的版本是struts2低版本(2.5)以下的,如果是2.5及以上的版本,需要将“ng”去掉。

        在该过滤器中:

 说明:
            1、加载了三种struts2的配置文件

  1.               struts-default.xml
  2.               struts-plugin.xml
  3.               struts.xml

            2、配置文件的特点:

  1.               struts-default.xml文件是系统默认的配置文件
  2.               struts-plugin.xml是插件机制的配置文件
  3.               struts.xml是开发中用到的配置文件

      这三种配置文件都是classpath的根目录存放
      这三种配置文件的dtd约束都一样,所以在这三种配置文件中可以出现相同的元素
      如果出现相同的元素,后者覆盖前者
      struts-plugin.xml文件可以有很多个,而且通常情况下是在lib下的jar包中出现

public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter {
	public void init(FilterConfig filterConfig) throws ServletException {
		//1、加载了org/apache/struts/default.properties文件
		//2、加载了三种struts2的配置文件
private static final String DEFAULT_CONFIGURATION_PATHS 
						= 
"struts-default.xml,struts-plugin.xml,struts.xml";
					  
		}
}

        2、对象工厂ObjectFactory

//该方法是struts2创建action的最低层的方法
public Object buildAction(String actionName, String namespace, ActionConfig config, Map<String, Object> extraContext) throws Exception {
	return buildBean(config.getClassName(), extraContext);
}
//该方式是创建结果集的最低层的方法
public Result buildResult(ResultConfig resultConfig, Map<String, Object> extraContext) throws Exception {
			    
}
//该方式是创建拦截器的最低层的方法
public Interceptor buildInterceptor(InterceptorConfig interceptorConfig, Map<String, String> interceptorRefParams) throws ConfigurationException {
}

        3、静态注入

         在struts2的配置文件中有这么一些元素【constant    、include  、   package  、  bean】

        bean:可以配置多个bean

<bean type="com.opensymphony.xwork2.util.ValueStackFactory" name="struts" 
class="com.opensymphony.xwork2.ognl.OgnlValueStackFactory" />

            说明:
               1、值栈的产生工厂是OgnlValueStackFactory
               2、该类在tomcat容器启动的时候就被加载了,所以只加载一次
               3、通过该类就把值栈的产生工厂赋值到struts2容器中了,这个过程称为注入
               4、所以这种行为称为"静态注入"

            package的作用:声明一个action;声明一个拦截器;声明一个结果集

        4、改变action的产生方式

             1、在default.properties文件中有一个配置项:
              struts.objectFactory该配置项指明了对象工厂
             2、写一个类继承ObjectFactory,重写buildAction方法

public class CreateActionBean extends ObjectFactory{
	@Override
	public Object buildAction(String actionName, String namespace,
						ActionConfig config, Map<String, Object> extraContext)
						throws Exception {
	// TODO Auto-generated method stub
	System.out.println("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
	return null;
	}
}

              3、在struts2的配置文件中利用bean元素把CreateActionBean放入到struts2容器中

			<bean type="com.opensymphony.xwork2.ObjectFactory" 
			name="aaa" 
			class="cn.itcast.shoa.struts.action.CreateActionBean" />

               4、在配置文件中引入常量struts.objectFactory

 <constant name="struts.objectFactory" value="aaa"></constant>

        5、分析struts2的插件机制

               1、提供了一个jar包:struts2-spring-plugin-2.3.1.2.jar
               2、在jar包的根目录下存放了一个文件

<bean type="com.opensymphony.xwork2.ObjectFactory" name="spring" 
class="org.apache.struts2.spring.StrutsSpringObjectFactory" />
<constant name="struts.objectFactory" value="spring" />

            重新定义了对象工厂
              3、如果一个框架想要和struts2进行整合,提供一个struts-plugin.xml文件,再提供了一个jar包,该配置文件在jar包的根目录下,然后把该jar包放入到lib下就可以了。

拓展:如果一个单实例bean放到spring容器中,是默认的配置,spring容器在启动的时候就会创建对象;当一个bean的【scope=prototype】的时候,则是在调用getBean方法的时候才创建对象,而struts2默认的是多实例创建对象。Action放到spring容器中,默认情况下,Action生成对象是单实例的;而struts2 Action生成对象是多实例的,所以struts2在spring容器中的配置中应该设置成【scope=prototype】。

    <!--把action放到spring容器中-->
    <bean id="personAction" class="cn.itcast.shoa.struts2.action.PersonAction" scope="prototype">
        <property name="personService">
            <ref bean="personService"/>
        </property>
    </bean>

struts2 六大核心jar包 和作用Struts 2.5.20是目前最新版本

1、Commons-logging.jar ————– 用于通用日志处理 
2、Freemarker.jar ————– 表现层框架,定义了struts2的可视组件主题 
3、Ognl.jar ————– OGNL表达式语言,struts2支持该EL 
4、Struts2-core.jar ————– struts2 的核心库 
5、Xwork.jar ————– webwork的核心库 

6、commons-fileupload ————– struts的上传下载

如要整合其他框架,则需要添加相应的xxx-plugin.jar如: 
整合spring需要将这个jar包导入]。struts2-spring-plugin.jar —struts2的spring插件 
开发struts2项目时,不要一股脑把struts2框架lib下的所有jar复制到自己的项目中,要是在整合其他框架。那样使得项目显得非常之臃肿。而且根本没有什么用的加进去。起不了什么作用。

猜你喜欢

转载自blog.csdn.net/gaofengyan/article/details/86607207
今日推荐