SpingMVC流程图
Struts的请求流程
springmvc的流程
0.struts2 MVC框架 Controller
Hibernate 持久化框架 Model
spring 项目管理框架 为javaEE开发提供更好的解决方案
======================================================================================
1.spring:工厂,容器
:创建项目中的所有模块(组件),并持有管理所有的组件.
:spring的所有功能实现,都架设在工厂(容器)的支持下.
======================================================================================
2.spring工厂搭建:
2.1 导包:
2.2 配置文件:告知spring需要生产的组件.
*位置:任意
*名称:任意 applicationContext.xml beans.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
<!-- 告知spring生产如下的组件 -->
<bean id="ud47" class="com.c47.dao.UserDAOImpl"></bean>
<bean id="us47" class="com.c47.serivce.UserServiceImpl"></bean>
</beans>
2.3 api:启动工厂:
ApplicationContext
ClassPathXmlApplicationContext
//启动工厂:指定配置文件位置
ApplicationContext context=new ClassPathXmlApplicationContext("com/c47/config/applicationContext.xml");
//从工厂中获取bean
UserDAO ud=(UserDAO)context.getBean("ud47");
UserService us=(UserService)context.getBean("us47");
=====================================================================================
3.IOC Inverse Of Controll 控制反转
:将属性的赋值权有静态代码中 反转 到配置文件中
:将具有依赖关系的双方的耦合打散.提高项目的可维护性.
:在配置文件中为属性赋值:DI Dependency Injection 依赖注入
=====================================================================================
4.set注入:通过set方法,将属性值存入.
<!-- jdk8中基本类型+String -->
<property name="id" value="1"></property>
<property name="name" value="c47"></property>
<property name="gender" value="true"></property>
<!-- List/数组 -->
<property name="list47">
<list>
<value>1</value>
<value>c47</value>
<value>true</value>
<ref bean="us47"/>
</list>
</property>
<!-- Map -->
<property name="map47">
<map>
<entry key="name" value="lichangpu"></entry>
<entry key="age" value="18"></entry>
<entry key="gender" value="true"></entry>
<entry key="us47" value-ref="us47"></entry>
</map>
</property>
<!-- properties -->
<property name="props">
<props>
<prop key="url">jdbc:oracle:xxx</prop>
<prop key="username">hr</prop>
</props>
</property>
=====================================================================================
5.构造注入:通过构造方法,为属性赋值
<!-- 第一个参数,类型为Integer -->
<constructor-arg index="0" type="java.lang.Integer" value="47"></constructor-arg>
<constructor-arg index="1" type="java.lang.String" value="c47"></constructor-arg>
<constructor-arg index="2" type="java.lang.Boolean" value="true"></constructor-arg>
弊端:不够灵活
:用于构造不提供空参构造的组件.
=====================================================================================
6.自动注入
<!--
autowire:自动装载
byType:将工厂中与目标组件的属性同类型的bean,赋值给对应属性
byName:将工厂中与目标组件的属性同名的bean,赋值给对应属性
-->
<bean id="ioc49" class="com.c47.IOC.test.TestIOC3" autowire="byName"></bean>
=====================================================================================
7.spring对于bean的生产原理:
反射+空参构造:
String path="com.c47.dao.UserDAOImpl";
Class claz=Class.forName(path);
Object obj=claz.newInstance();//调用空参构造
例外:当使用了构造注入时,就不会选取空参构造,而是调用配置中指定的构造方法.
=====================================================================================
8.bean的生命周期:
*bean的构建时刻:spring工厂启动时,所有的单例的bean随之创建
*bean的销毁时刻:spring工厂关闭时,所有bean随之销毁.
*细节:bean的生命历程(了解)
构造-->set-->init-->destroy
=====================================================================================
9.bean的创建模式
*singleton:单例 全局唯一 (默认)
*prototype:非单例 全局不唯一
<bean id="c47" class="xx"/> 单例
<bean id="userAction47" class="xxxx" scope="singleton"> 单例
<bean id="userAction47" class="xxxx" scope="prototype"> 非单例
*单例:无论使用(getBean()/用于注入)多少次,都只有一个对象
*非单例:每使用一次,都要创建一个新的对象.
*细节:
在工厂启动时,非单例的bean不会随之创建.而是在被使用时才创建.
=====================================================================================
1.复杂对象:创建过程复杂.不能直接new.
:FactoryBean==构建复杂对象
=====================================================================================
2.FactoryBean 工厂bean使用流程:
2.1 定制FactoryBean
implements FactoryBean<SessionFactory>{
/**
* 主体逻辑:定制复杂对象的创建流程
*/
public SessionFactory getObject() throws Exception {
....
}
/**
* 返回复杂对象的类对象
*/
public Class getObjectType() {
return SessionFactory.class;
}
/**
* 定制复杂对象的创建模式:
* return true;单例
* return false;非单例
*/
public boolean isSingleton() {
return true;
}
}
2.2 配置
<!-- 声明工厂Bean -->
<bean id="sessionFactory47" class="com.c47.factory_bean.MySessionFactory"></bean>
2.3 使用:
*当从工厂中获取bean时,如果此bean是FactoryBean,则返回给用户的并不是bean本身
的对象.而是去回调此bean中的getObject(),返回此bean中getObject()方法的返回值.
====================================================================================
3.spring 初步的整合hibernate
*将sessionfactory纳入工厂
*将DAO,Service 纳入工厂
*并通过IOC满足DAO和Service的依赖
====================================================================================
4.类型转换器:注入过程,如果双方的类型不匹配,则需要类型转换.
:String-->数字,布尔 自动完成
4.1 定制转换器类:
extends PropertyEditorSupport{
/**
* <bean>
* <property name="birth" value="2015-12-11">
* </bean>
* 转换器的主体逻辑:完成数据格式转换
* param:text 要转换的值(2015-12-11)
* 此方法,会在spring需要类型转换时被spring回调
* setAsText("2015-12-11");
* getValue();
*/
@Override
public void setAsText(String text) throws IllegalArgumentException {
SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd");
try {
Date date=format.parse(text);
//将转换好的数据,交还spring
//将转换好的数据,存入父类的成员变量中,spring会回调父类的getValue(),将值取走.
this.setValue(date);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
4.2 配置:
*声明类型转换器:
<bean id="dateEditor47" class="com.c47.property_editor.MyPropertyEditor"></bean>
*注册:
<!-- 告知spring
CustomEditorConfigurer:统一管理所有转换器的入口
-->
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<!-- 注册自定义的类型转换器
告知spring此转换器可以完成string到Date的转换
-->
<property name="customEditors">
<map>
<entry key="java.util.Date" value-ref="dateEditor47"></entry>
</map>
</property>
</bean>
*使用:
<!-- 转换器测试 -->
<bean id="user47" class="com.c47.entity.User">
<property name="birth" value="2015-12-11 11:51:47"></property>
</bean>
*细节:如果是日期数据的注入,可以不定制转换器,spring默认的日期格式:
:yyyy/MM/dd HH:mm:ss
====================================================================================
*补充:spring对于hiberante更新的支持:
//User{id,name,age,Set<order> orders}
//user{id,name,age,null}
//user2{id,name,age,orders}
public void updateUser(User user) {
Session session=factory.getCurrentSession();
User user2=(User)session.get(User.class, user.getId());
//将user的属性值,赋值给user2中的同名属性.
//技巧:在set方法中添加判断,过滤null
BeanUtils.copyProperties(user, user2);
session.update(user2);
}
0.业务层:冗余:在业务中有大量的重复的逻辑散布在大量的业务方法,导致极大的冗余.
:对于项目的开发,和后期的维护,都是极大的隐患.
:耦合:在业务中有两类逻辑[核心业务],[额外功能],耦合在一起,不利于项目的维护.
public interface UserService{
public void delete(Integer id);
public void update(User user);
}
public class UserServiceImpl implements UserService{
public void delete(Integer id){
//核心业务处理:删除 UserDAO.delete();
}
public void update(User user){
//核心业务处理:更新 UserDAO.update();
}
}
//性能
//事务
静态代理类:组成:[target:核心业务]+[额外功能]
:原则:代理(proxy)和目标(target)要有同样的方法实现(同样的接口实现)
public class UserServiceProxy implements UserService{
//原始的业务类
private UserService us=new UserServiceImpl();
public void delete(Integer id){
//性能
//事务
us.delete(id);
//事务
}
public void update(User user){
//性能
//事务
us.update(id);
}
}
new UserServiceProxy().update(user);
new UserServiceProxy().delete(1);
动态代理类搭建:
1.准备原材料:target中的核心业务
:额外功能
2.通过aop:编织,将原材料,动态的加载,称为一个代理类
3.获取动态生成的代理类,完成相关业务.
======================================================================================
1.AOP :解决业务层中的问题
:面向切面编程 代理模式 代理类
:代理:target+Advice
:切入点:切入了额外功能的 业务方法.
:切面:切入点+额外功能
OOP :面向对象编程 类 对象 接口 继承 封装 多态
=====================================================================================
2.动态代理搭建:*导入第三方的jar
2.1 准备原材料
*target
*额外功能(Advice:建议)
MethodBeforeAdvice 前置额外功能
AfterReturningAdvice 后置额外功能
MethodInterceptor 环绕额外功能
ThrowsAdvice 异常时的额外功能(了解)
implements MethodBeforeAdvice{
/**
* 主体逻辑:
* params:
* m:目标的方法对象
* params:目标方法的参数表,如果方法是空参,则params的长度为0.
* target:代理的目标
*/
public void before(Method m, Object[] params, Object target)
throws Throwable {
//定制在目标的方法之前,执行的逻辑
System.out.println("before: method:"+m.getName()+" params'length:"+params.length+" target:"+target);
}
}
2.2 动态的编织代理类
*声明原材料:target和advice
*Weave:编织
<!-- target -->
<bean id="userService47" class="com.c47.serivce.UserServiceImpl"></bean>
<!-- Advice -->
<bean id="before47" class="com.c47.advice.MyBefore"></bean>
<!-- 编织:基于aop schema完成编织 -->
<aop:config>
<!-- 1.目标,及其中需要附加额外功能的方法.
2.在目标中加入哪些额外功能
-->
<!--
pointcut:切入点=目标中 附加了额外功能的方法
execution()表达式:描述切入点
返回值 包 类 方法名 参数表
* com.c47.service4.UserServiceImpl.*(..)
* com.c47.service4.UserServiceImpl.deleteUser(..)
-->
<aop:pointcut id="pc47" expression="execution(* com.c47.service4.UserServiceImpl.*(..))"/>
<!-- 在位置[pc47]上,附加额外功能[before47] -->
<aop:advisor advice-ref="before47" pointcut-ref="pc47"/>
</aop:config>
2.3 从工厂中获取动态代理对象:通过目标类的beanID即可,用接口类型引用接收.
===================================================================================
3.额外功能:
3.1 后置额外功能:AfterReturningAdvice
implements AfterReturningAdvice{
/**
* 主体逻辑:
* params:
* ret:目标方法的返回值,如果方法是void,则ret为null
* m:目标方法对象
* paras:方法参数表
* target:目标对象
*/
public void afterReturning(Object ret, Method m, Object[] params,
Object target) throws Throwable {
System.out.println("after ret:"+ret+" method:"+m.getName()+" params'length:"+params.length+" target:"+target);
}
}
3.2 环绕额外功能:
implements MethodInterceptor{
/**
* 主体逻辑
* param:
* mi:
*/
public Object invoke(MethodInvocation mi) throws Throwable {
System.out.println("tx begin~~~");
//调用目标的方法.
Object ret=mi.proceed();
System.out.println("tx commit~~~");
//接收目标方法的返回值,并向上返回给用户.
//注意:在环绕额外功能中,目标方法的返回值,务必要向上返回.
return ret;
}
}
3.3 异常时的额外功能:目标抛出异常时触发.(了解)
implements ThrowsAdvice{
/**
* 主体逻辑:目标抛出异常时,执行
* @param ex :抛出的异常对象
*/
public void afterThrowing(Exception ex){
System.out.println("my throws :"+ex.getMessage());
}
}
====================================================================================
4.execution表达式:描述切入点
组成:返回值 包 类 方法名 参数表
1>* com.c47.serivce.UserServiceImpl.deleteUser(..)
返回值:任意
包:com.c47.service
类:UserServiceImpl
方法:deleteUser
参数表:任意
2>* com.c47.serivce.UserServiceImpl.*(..)
返回值:任意
包:com.c47.service
类:UserServiceImpl
方法:任意
参数表:任意
3>* com.c47.serivce.*.*(..)
返回值:任意
包:com.c47.service
类:任意
方法:任意
参数表:任意
4>* *(..)
返回值:任意
包:任意
类:任意
方法:任意
参数表:任意
5>* login(..)
返回值:任意
包:任意
类:任意
方法:login
参数表:任意
6>技巧:* com.c47.serivce.UserServiceImpl.*User(..)
返回值:任意
包:com.c47.service
类:UserServiceImpl
方法:以User结尾的方法(便于批量的切入)
参数表:任意
建议:表达式,定义尽量精确.避免不必要的切入.
===================================================================================
*基于AOP,抽取事务管理:
1>DAO,Service,SessionFactory,DataSource 纳入工厂
2>IOC满足依赖
3>Service不再依赖SessionFactory
4>定制环绕额外功能:事务管理
5>IOC满足事务管理器的依赖
6>AOP将事务管理器切入需要事务的业务方法中.
1.原理:*动态代理类如何创建的.
1>jdk代理 :通过和目标实现同一套接口,保证功能统一
2>cglib代理:通过继承目标,保证功能统一.
spring会动态的选择,如果目标有接口实现,则使用jdk代理,
反之,则使用cglib.
*为什么通过目标的BeanID===>Proxy
1>BeanPostProcessor作用,再次加工.
======================================================================================
2.动态代理类如何创建(jdk代理):
//1.目标 target
final UserService us=new UserServiceImpl();
//2.额外功能
InvocationHandler ih=new InvocationHandler(){
/**
* method:方法对象
* args:参数列表
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("before~~~");
//调用目标(us)的方法
Object ret=method.invoke(us, args);
System.out.println("after~~~");
return ret;
}
};
//3.编织 *类加载器 *目标的所有实现的接口 *额外功能
UserService usProxy=(UserService)Proxy.newProxyInstance(TestAOPYL.class.getClassLoader()
,us.getClass().getInterfaces()
,ih);
====================================================================================
3.为什么通过目标的BeanID===>Proxy
*BeanPostProcessor:后处理bean-->对工厂创建好的bean,进行再次的加工.
被代理的Bean 生产过程:
构造-->set-->
postProcessBeforeInitialization-->
init-->
postProcessAfterInitialization-->
用户
*定制后处理bean:
implements BeanPostProcessor{
/**
* 在init之后执行
* param:bean=postProcessBeforeInitialization加工后的对象
*/
public Object postProcessAfterInitialization(final Object bean, String arg1)
throws BeansException {
}
/**
* 在init之前执行
* param:bean=工厂供创建的目标的bean的对象
*/
public Object postProcessBeforeInitialization(Object bean, String arg1)
throws BeansException {
//加工
return bean;
}
}
*<!-- 声明后处理bean
则在获取当前配置中的任何一个bean时,后处理bean,就会进行再加工
-->
<bean class="com.c47.postprocessor.MyPostProcessor"></bean>
===================================================================================
4.spring事务管理:
4.1 将 HibernateTransactionManager 纳入工厂,并IOC满足依赖:SessionFactory
<!-- 并不是一个完整的事务管理器,其中,管理了事务控制的核心的逻辑
需要对其进一步的包装
-->
<bean id="tx47" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory47"></property>
</bean>
4.2 进一步包装,获取真正的事务管理器
<!-- 基于schema tx 包装真正的事务管理器
事务属性(attributes):
*隔离级别:1>读未提交 事务之间可以读取到彼此未提交的数据,会导致[脏读],并发性极好.
[脏读]:一个事务中读取到了其他事务中未提交的数据
2>读提交 事务之间只可以读取到彼此已提交的数据,可防止[脏读].但不能防止[不可重复读],并发性较好
[不可重复读]:一个事务中,多次读取统一条数据,结果不一致
3>可重复读 一个事务内,多次读取统一条数据,结果一致.可防止[脏读,不可重复读],但不能防止[幻影读],并发性较差
[幻影读]:一个事务中,多次读取统一张表,数据行数不一致.
4>序列化读 多个事务是串行发生.可以防止一切不合法的读取,并发性极差.
oracle:1.读提交(默认) 2.序列化读
*传播性: REQUIRED:如果当前没有事务环境,开启事务;如果已有事务环境,则融入事务.
SUPPORTS:如果当前没有事务环境,则在非事务环境下执行;如果已有事务环境,则融入事务.
*读写性:read-only="true" 只读事务,事务中只能读.
read-only="false" 读写时序,事务中可读 可写.
*回滚时刻:rollback-for="java.lang.Exception" 在出现所有异常时,要回滚.
细节:如果出现了RuntimeException,则会自动回滚.
但是如果出现了Exception,默认不会回滚,依然提交事务.
事务特性:ACID
原子性
一致性
持久性
隔离性
-->
<tx:advice transaction-manager="tx47" id="txManager">
<tx:attributes>
<tx:method name="queryUserByID" propagation="SUPPORTS"/>
<!-- <tx:method name="*User"/> -->
<!-- 其余方法 -->
<tx:method name="*" isolation="READ_COMMITTED" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception"/>
</tx:attributes>
</tx:advice>
*事务管理器,定制完毕,AOP切入业务层中即可
===================================================================================
5.spring对于DAO操作支持:进一步简化DAO操作
HibernateTemplate:模板类,作用在DAO,将session封装到底层,构建了封装后的方法,以完成并
:简化DAO操作.
:集成session几乎所有的方法:get(),save(),delete(),update()
:如何执行HQL:template.find("HQL",Object... param);
:execute();//通用方法.
//execute方法会回调HibernateCallback中的doInHibernate
//且doInHibernate方法的返回值,就是execute方法的返回值.
template.execute(new HibernateCallback<List<User>>(){
//param:session 即当前线程中使用的session
public List<User> doInHibernate(Session session)
throws HibernateException, SQLException {
String hql="from User u order by u.id";
Query query=session.createQuery(hql);
query.setMaxResults(5);
query.setFirstResult((pageNum-1)*5);
return query.list();
}
}
);
:其中将事务的自动提交置为true. connection.setAutoCommit(true);
template.save(xx);
template.delete(xx);
利于测试DAO.
===================================================================================
*spring struts2 hibernate
spring struts2 mybatis
基于注解的ssh
*OpenSessionInViewFilter
1.spring+struts整合
1>搭建struts2
2>将Action纳入工厂(注意:prototype)
3>IOC满足Action依赖
4>导入插件包:struts2-spring-plugin.jar
:在struts.xml配置Action时,<action class="Action_BeanID" ....
:保证前端控制器从工厂中获取需要的Action实例
5>启动工厂:定制监听器=org.springframework.web.context.ContextLoaderListener
=在项目启动时,启动工厂
:定制context-param=contextConfigLocation
=指示spring配置文件位置
======================================================================================
2.spring+mybatis整合:
1>构建连接池
2>通过SqlSessionFactoryBean构建SqlSessionFactory
*依赖点:
*数据源
*别名设置(可选)
*映射文件位置
3>通过MapperScannerConfigurer构建DAO接口的实现类
*依赖点:
*SqlSessionFactory
*DAO接口所在位置
*构建出的DAO接口实现类,会纳入工厂,且ID为对应接口名的首字母小写
4>将构建好的DAO注入给Service
5>搭建DataSourceTransactionManager(注入数据源)
并通过<tx:advice 进一步包装出mybatis的事务管理器,切入service方法中
6>整合struts2....
=====================================================================================
3.基于注解SSH整合:
1>//将当前类纳入工厂,bean的ID默认为类名的首字母小写:userDAOImpl
//也可以明确指定bean的ID
@Component("userDAO") 作用在类上
2> //指示当前bean是非单例/单例(默认)的.
@Scope(value="prototype/singleton") 作用在类上
3>//基于类型自动注入
@Autowired 作用在属性
//基于名称自动注入
@Resource(name="userService") 作用在属性
4>//作用在类上:缺省的为类中的所有方法指定事务属性.
//作用在方法上:为方法单独的指定事务属性
@Transactional(isolation=Isolation.READ_COMMITTED,propagation=Propagation.REQUIRED,readOnly=false,rollbackFor=Exception.class)
*告知spring注解位置:
<context:component-scan base-package="com.c47"></context:component-scan>
*告知spring,事务注解切入的是什么事务:
<bean id="tx47" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<tx:annotation-driven transaction-manager="tx47"/>
=====================================================================================
4.OpenSessionInViewFilter:
:被此过滤器过滤的请求,Session有此过滤器管理:session在事务结束后依然是开启的,直到
视图渲染完毕后,将session关闭,以解决延迟加载异常.
<!-- 配置OpenSessionInViewFilter
注意:要在前端控制器之前定义
此过滤器依赖SessionFactory,且默认向工厂所要id为:"sessionFactory"的bean
如果工厂中的SessionFactory的beanID不是"sessionFactory",通过init-param指定
-->
<filter>
<filter-name>osiv47</filter-name>
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
<init-param>
<param-name>sessionFactoryBeanName</param-name>
<param-value>sessionFactory47</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>osiv47</filter-name>
<!-- 需要在页面中使用延迟属性的请求 -->
<url-pattern>/ssh/user_queryOneUser</url-pattern>
<url-pattern>/user/a</url-pattern>
<url-pattern>/user/b</url-pattern>
<url-pattern>/user/c</url-pattern>
<url-pattern>/user/d</url-pattern>
<url-pattern>/a</url-pattern>
<url-pattern>/b</url-pattern>
</filter-mapping>
====================================================================================