SSH框架总结
一、搭建SSH框架
1.1导入相关Jar包 大约45个
1.2相关配置文件
1.2.1web.xml
1、配置ContextLoaderLinstener,用于加载spring 的applicationContext.xml配置文件
默认加载位置为web-inf下的,需要指明 配置文件位置 contextConfigLocation
如果配置文件找不到,就会报FileNotFound找不到异常
<!--配置Spring加载 监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--配置全局参数 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
clsspath是spring中特有的用于标识类路径下
1、1在配置Struts2可以配置OpenSessionInViewFilter 用于解决nosession问题,非必须,
必须配置在struts2核心控制器之前
<filter>
<filter-name>openSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate5.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>openSessionInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2、配置Struts核心控制器 StrutsPreparedAndExecuteFilter
两个作用:加载struts2配置文件在init方法中加载,配置文件只加载一次,对用户的action请求或空白请求进行拦截
<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>
3、配置Spring编码过滤器 CharacterEncodingFilter 用于解决post请求乱码问题
可以配置参数 encoidng UTF-8
非必须,因为如果使用struts2与spring整合时,struts2本身就解决了post请求乱码问题
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!--配置参数,指定使用UTF-8编码 -->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
1.2.2struts.xml文件配置
1、struts2的配置文件:
default.properties 配置struts运行中的常量
struts-default.xml 配置拦截器以及结果视图,以及bean的配置
struts-xxx-plugin.xml 插件的配置文件,非必须
struts.xml 程序员编写的配置文件,配置常量,action,自定义拦截器等
struts.properties 配置常量,一般不需要配置,在struts.xml文件中配置
2、package配置
name 包名,唯一
namespace名称空间 默认为 "/"
extends 继承的父包 默认为struts-default
3、action配置
name 访问名称 Action_* *表示访问方法
class 类的全限定类名或者spring中对应的id名称
method 方法 ${1}表示第一个*代表的内容
result 逻辑视图
interceptor-ref 明确指明当前使用的是哪个拦截器
可以配置param name
namespace+name 路径为action的完整访问路径
4、interceptor配置
interceptors 表示所有拦截器
interceptor 配置自定义的拦截器 name 拦截器的名称 ,class拦截器的全限定类名
interceptor-stack 配置拦截器栈
interceptor-ref 配置拦截器引用 name拦截器的名称 自定义拦截器放在默认拦截器栈后面
default-interceptor-ref 默认拦截器配置 name=拦截器或着拦截器栈名称
5、拦截器
拦截器是struts2的基石,很多功能都是基于拦截器实现的,在用户访问action方法之前执行,采用责任链的设计模式
,采用递归调用的方式执行。
6、拦截器的编写
实现Interceptor接口
继承AbstractInterceptor类
继承MethodFilterInterceptor类
我们一般使用继承AbstractInterceptor
<struts>
<constant name="struts.enable.DynamicMethodInvocation" value="false" />
<constant name="struts.devMode" value="true" />
<!--配置上传文件大小 -->
<constant name="struts.multipart.maxSize" value="20000000"></constant>
<!-- 加载国际化配置文件 -->
<constant name="struts.custom.i18n.resources" value="uploadError" />
<!--全局包,用于配置全局信息 -->
<package name="public" namespace="/" extends="struts-default">
<interceptors>
<interceptor name="loginInterceptor" class="loginInterceptor"></interceptor>
<interceptor-stack name="myInterceptorStack">
<interceptor-ref name="defaultStack"></interceptor-ref>
<!-- 自定义拦截器放在默认拦截器后面 -->
<interceptor-ref name="loginInterceptor">
<!-- 设置不需要拦截的方法-->
<param name="excludeMethods">customerAction_list,linkmanAction_list,sysUserAction_login,sysUserAction_loginUI</param>
</interceptor-ref>
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="myInterceptorStack"></default-interceptor-ref>
<global-results>
<result name="loginError">/login.jsp</result>
<result name="success">/jsp/success.jsp</result>
<result name="error">/jsp/error.jsp</result>
</global-results>
</package>
<include file="struts/struts-sysUser.xml"></include>
<include file="struts/struts-customer.xml"></include>
<include file="struts/struts-linkman.xml"></include>
<include file="struts/struts-saleVisit.xml"></include>
</struts>
1.2.3applicationContext.xml文件配置
1、配置数据源,用于连接数据库
C3P0数据源 ComboPooledDataSource
driverClass jdbcUrl user password
DBCP数据源 BasicDataSource
JDBC数据源 DriverManagerDataSource
2、配置sessionFactory 是sring与hibernate整合的关键点 类LocalSessionFactoryBean
dataSource configLocations(classpath:hibernate.cfg.xml)
3、配置Hibernate事务管理器
transactionManager
需要注入sessionFactory
4、配置声明式事务通知
transaction-manger 指明使用的事务管理器
tx:method 表示哪些方法使用事务 name为方法名,propagation为事务行为
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
声明式事务的原理:AOP
AOP的原理是什么?动态代理模式
动态代理模式?JDK实现动态代理,cglib实现动态代理
这两种方式的区别:JDK动态代理时目标对象一定实现接口,而cglib可以不实现
JDK动态代理用的类?Proxy.newProxyInstance()
Cglib代理如何实现?
1.创建一个Enhancer eh = new Ehancer();
2.Eh.setSuperClass();
3.设置回调:eh.setCallback();
4.生成代理对象:eh.create();
1. 事务管理器的配置
父接口:PlatformTransactionManager
Hibernate事务管理器的子类:HibernateTransactionManager
Jdbc事务管理器的子类:DataSourceTransactionManager
PlatformTransactionManager它的内部是什么工作的?
TransactionDefinition:事务定义信息
TransactionStatus:事务状态
它需要根据TransactionDefinition这个对象,得到事务的定 义信息,再根据事务的执行状态TransactionStatus,来决定事务是提交还是回滚。
2. 事务通知的配置:<tx:advice>
事务通知advice为什么可以作用到切入点表达式所指定的类中的方法?
因为在配置声明式事务时,它的本质也是去生成代理子类对象,所以此时只要根据你指定的切入点表达式的要求,针对目标对象直接生成代理对象,而同时通过aop来实现横向代码的动态织入,从而实现了事务的控制。它对应的通知类型,可以认为环绕通知。
5、配置声明式事务与aop整合
aop:pointcut 配置切入点表达式 expresssion(需要事务执行的方法)
<aop:config>
<aop:pointcut expression="execution(* cn.lx.crm.service.impl.*.*(..))" id="myPointcut"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut"/>
</aop:config>
6、配置文件分离的思想
公共配置+分层思想
applicationContext.xml
applicationContext-dao.xml
applicationContext-service.xml
applicationContext-action.xml
公共配置+分模块思想
applicationContext.xml
applicationContext-customer.xml
7、bean配置的作用域
request\session\global session\singleton\prototype
signleton 单例
prototype 多实例 配置action时,一定要配置多实例
8、bean生命周期 spring配置文件加载时创建
init destory
9、bean对象的创建三种方式
构造函数 实例工厂 静态工厂
10、bean对象注值的三种方式
setter注值 构造函数注值 p名称空间注值
1.2.4 hibernate.cfg.xml文件配置
1、配置SQL方言
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
2、配置自动建表
<property name="hibernate.hbm2ddl.auto">update</property>
3、配置显示SQL
<property name="hibernate.show_sql">true</property>
4、配置格式化SQL
<property name="hibernate.format_sql">true</property>
5、配置session绑定本地线程
<property name="hibernate.current_session_context_class">org.springframework.orm.hibernate5.SpringSessionContext</property>
6、配置事务隔离级别
<property name="hibernate.connection.isolation">4</property>
7、导入映射文件
<mapping resource="cn/lx/crm/domain/BaseDict.hbm.xml"/>
1.2.5 准备PO类及映射文件
1、PO类的编写规范:
1. 私有属性
2. 提供公有的无参构造
3. 提供 私有属性的公有getter与setter
4. 类名不能用final
5. 实现java.io.Serailizable接口
6. 是一个公有类
7. 如果是基本类型的属性,请使用它的对应包装类
2、映射文件命名 类名.hbm.xml
<class name=”包名.类名” table=””>
<id name=”” column=””>
<generator class=”uuid,assigned,identity,increment,native,sequence”/>
</id>
<property name=”” column=”” not-null=”tru” type=”” length=”” />
<property name=”” column=”” not-null=”tru” type=”” length=”” />
</class>
3、关联关系的配置
分别为一对多关系、多对一关系、多对多关系、一对一关旭
Customer的类中:
一对多
Private Set<Linkman> linkmans = new HashSet<Linkman>(0);
Customer.hbm.xml文件中
<set name=” linkmans” cascade=”” inverse=”” fetch=”join|select” lazy=”true|false|extra”>
<key column=”外键字段名”/>
<one-to-many class=” Linkman” />
</set>
Cascade取值:all,save-update,delete,delete-orphan, delete-orphan-all
Inverse取值:true,false,默认为false
Cascade与inverse的区别?
1. cascade代表父子项操作时的级联关系
2. inverse代表控制权交给谁管理,true代表控制权交给对方管理
3. inverse一般 设置为true,交给多方维护,这样可以提高效率
fetch的特点:fetch=”join”时lazy失效,同时它采用迫切左外连接实现的查询
它可以解决noSession问题
Fetch=select,它将采用多条sql语句的形式进行查询,发出sql的时机由lazy来决定,想立即发出sql就用lazy=”false”,希望在加载这个记录时才出现sql语句,就让lazy=”true|extra”
多对一的配置:
Linkman.java中配置:
Private Customer customer;
Linkman.hbm.xml文件
<many-to-one name=”customer” class=” Customer” column=”外键字段名”></ many-to-one>
多对多的配置:
SysUser与SysRole用户与角色
SysUser.java
Private Set<SysRole> sysRoles = new HashSet<SysRole>(0);
SysUser.hbm.xml文件
<set name=”” table=”” inverse=”true”>
<key column=”关联到自身的来自中间表的外键”/>
<many-to-many class=” SysRole” column=” 关联到SysRole来自中间表的外键”/>
</set>
2 DAO模式
1.编写BaseDao<T>接口
2.编写实现类BaseDaoImpl
Public BaseDaoImpl<T> extends HibernateDaoSupport implements BaseDao<T>{
Class clz;
Public BaseDaoImpl(){
Type type = this.getClass().getGenericSuperClass();
ParameterizedType pt = (ParameterizedType)type;
clz = (Class)Pt.getActualArgumentsType()[0];
}
Public List<Customer> findAll(){
return this.getHibernateTemplate().find(“from ”+clz.getName());
}
}
Class CustomerDaoImpl extends BaseDaoImpl<Customer> implements CustomerDao{
}
getHibernateTemplate().find()内部的本质是什么执行的?
Session session = sessionFactory.openSession();
AOP:横向插入事务开启的代码
Query query = Session.createQuery(“from “+clz.getName());
Query.list();
AOP: 横向插入事务提交或回滚的代码
Hibernate实现数据检索的5种方式
get/load 加载时使用对象的OID(Object Identity Qualified 对象的唯一标识)
QBC Query By Criteria Criteria c = session.createCriteria(Customer.class);
HQL Query query = session.createQuery(“”);
对象导航 前提:先配置好对象之间的关联关系
Set<linkman> linkmans = customer.getLinkmans();
SQL :SQLQuery query = session.createSQLQuery(“sql”).addEntity(Customer.class);
面试:get/load加载数据的区别?
1. get立即加载,load延迟加载
2. load加载的对象为代理对象,get加载的是真实对象
3. load加载失败会出现异常,而get加载失败返回null
4. get加载时就会出现sql语句,而load只会调用属性时才会真正加载(才会有sql)
5. load方法也可以实现与get方法相同的效果:第一种方法只要在相应的映射文件中找到class结点,添加一个lazy=”false” 第二种方法:在PO定义时加上final
结论,加载数据失败时,load总是会抛出异常
3 Hibernate一级缓存
一级缓存:它是与session关联在一起的,如果session不存在了,一级缓存也就不存在了。
一级缓存的内部结构:
Hashtable,就是key-value对。
结于持久态的对象,没有调用update()方法,也会实现更新操作?因为它会根据一级缓存中的快照区与真实对象进行比较,如果有更新,就会在事务提交时,将更新后的结果刷出到数据库中。(tx.commit();默认就会调用session.flush();)
一级缓存中常用的操作方法:
clear();清空一级缓存
evict(Object obj)在一级缓存中清空指定的对象
flush()可以做到一级缓存中数据与数据库中数据的同步更新
refresh()再次将数据库中的数据查询出来,填充一级缓存
4 struts2的封装数据的方式
1.获取参数
ServletActionContext.getRequest.getParameter(“custId”);
2解耦合方式获取参数
ActionContext.getContext().getParameters().get(“custId”)[0];
3.属性驱动来实现参数封装(params拦截器)
Customer/customerAction_find?age=10;
CustomerAction类中
Private int age;
Public void setAge(int age){
This.age =age;
}
Customer/customerAction_find?customer.age=10;
private Customer customer;
//生成它的setter与getter
4.模型 驱动:(modelDriven拦截器)
1.模型驱动要求实现一个接口:ModelDriven<Customer>,同时还要重写getModel(),这个方法要求返回一个已存在的对象。
5 值栈
1.什么是值栈?
它是一个用于保存数据的容器,它本身只是一个叫做ValueStack接口,它的实现类是OgnlValueStack.
2.如何得到ValueStack实例?
ValueStack vs1 = ActionContext.getContext().getValueStack();
ValueStack vs2 = request.getAttribute(“struts.valueStack”);
针对同一次请求,不管用上面的哪个方法,获取的都是同一个对象。每次请求时,valueStack都是一个新的对象,action对象也是一个新的对象
3.ValueStack的内部结构
root :类型 CompoundRoot 而CompoundRoot又继承了ArrayList父类
context:类型 OgnlContext ,而OgnlContext又实现Map接口
默认操作值栈都是在操作root
4.为什么EL表达式也能访问值栈?
用到RequestWrapper对request进行包装,在包装类中可以找到一个getAttribute()而这个方法首先是在request域中找数据,如果没有就会找valueStack.
request = prepare.wrapRequest(request);