浅谈三层架构与MVC
MVC是一个很早就有的经典的程序设计模式:M-V-C-->Model-View-Control,
它来表示域对象与UI表示层通过control来控制的这样一种架构模式;一般仅仅代表一层
着重处理理清界面;
而web开发中三层架构指的是数据访问层(DAL-DatabassAccessLayer)--业务
逻辑层(BLL-BussinessLoginLayer)--用户界面层(UI-UserInterface),一般只一个
系统的宏观架构;
三层架构优点:
1.一种标准,标准化
2.增加各层逻辑复用
3.结构更明确,方便维护
4.很容易用新的实现来替换原有的实现
缺点:
1.原本很容易访问数据库的,如今必须通过中间层来完成,降低了系统的性能
2.导致级联的修改,修改一层,其他两层可能会需要修改
3.增加了开发成本
struts2是一个基于mvc设计模式的web应用框架,它本质上相当于一个servlet,作为控制器controller来建立模型与视图的数据交互
优点:
1)实现了mvc模式,层次结构清晰,程序员只需关注业务逻辑的实现
2)提供丰富的拦截器,也提供丰富的标签库,大大提高了开发效率
3)可扩展性高,struts-default.xml文件中设置了默认的bean,resulttype。拦截器等等,用户可以更改为自己开发所用
缺点:
1)请求参数封装较为麻烦,由于get/set方法可能比较多,显得比较臃肿
2)校验比较繁琐麻烦细化
3)安全性有待提高,有一些安全漏洞,被黑客攻击
1)浏览器发送一个请求
2)会被StrutsPrepareAndExecuteFilter(前端核心控制器) 拦截,移交控制权给ActionProxy
3)加载struts.xml核心配置文件,会调用struts2框架默认的拦截器inteceptor完成部分功能
4)根据class路径名查找action执行操作
5)根据action方法执行结果来选择跳转result视图,反过来执行拦截器,返回到客户端
1)创建一个pojo类,自己实现功能
2)创建一个类实现Action接口(定义了五个常量一个execute方法)--input,error,login,none,success
3)创建一个类继承ActionSupport类
1)属性驱动;
1)提供与请求参数匹配的属性,提供get/set方法
2)创建一个javaBean,对其提供get/set,这样在页面取值时使用ognl表达式
2)模型驱动:实现ModelDriven接口
1)对象导航图语言,强大的表达式语言,通过简单一致的表达式去存取对象的任意属性
2)OGNL三要素: 表达式 OgnlContext 上下文 Root 根,从非根中获取#{。。。}
3)struts2框架中可以使用ognl+valueStack达到在页面(jsp)上来获取相关的数据。
1)当请求action时,会加载一系列默认的拦截器对象进行拦截,action执行后,产生一个result,再经过一系列的
拦截器,返回到页面;
2)自定义拦截器:实现interceptor接口或者继承MethodFilterInterceptor类,再配置进去,
只是这样默认的拦截器会失效,因此需要手动的引入"defaultStack"
1)满足条件: 表单提交方式method=post
表单中必须有一个组件
表单中必须设置enctype=”multipart/form-data”
2)fileUpload拦截器实现:FileUtils.copyFile(src,dest);
1)对象:{key:value,key:value...}取值方法:对象.key
数组:["","",""..]通过组合可以形成复杂的数据结构
2)fastJson:String json = JsonObject.toJsonString(user);
jackson: ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(ps);
3)异步请求: $.post( url, [data], [callback], [type] )
$.ajax({
type: 'POST',
url: url,
data: data,
success: success,
dataType: dataType
});
hibernate是一个开放源代码的对象关系映射框架,对jdbc进行封装,将pojo与数据库建立映射关系,
可以自动生成sql语句,使程序员随心所欲使用面向对象的思想操作数据库。
1)对象关系映射,Object Relation Mapping
对象和关系数据是业务实体的两种表现形式--》在数据库中是一条数据,在内存中为一个对象
1)核心配置文件:hibernate.cfg.xml--》配置sessionFactory--》datasource等等及映射文件位置
2)映射配置文件:类名.hbm.xml-->配置表与对象的属性对应关系
1)读取解析核心配置文件
2)进而读取解析映射配置文件
3)得到sessionFactory-->不是轻量级的,一般一个项目有一个sessionFactory(单例)
4)得到session(多例)--》因此session对象使非线程安全的;
ⅠsessionFactory.openSession():创建一个新的session,需要手动关闭
ⅡsessionFactory.getCurrectSession():获得一个与线程绑定的session,
这样在各层创建使用的session是同一个,而且使用完后会自动关闭(需要在配置文件中配置)
5)开启事务--》session.beginTransaction()
6)执行持久化操作--》crud
save 保存对象
update 修改操作
delete删除
get/load 根据id进行查询
savenOrUpdate 执行save或update操作
createQuery()获取一个Query对象
createSQLQUery()获取一个可以操作sql的SQLQuery对象
createCriteria() 获取一个Criteria它可以完成条件查询
7)提交事务--》session.getTransaction().commit();
8)关闭session和sessionFactory;
有几点注意:1)PO类中属性尽量用包装类修饰
2)必须有OID与主键对应--hibernate框架就是通过OID来判断对象的
3)不能用fina修饰
主键生成策略:sequence,native,identity,increment,uuid,assigned等等
1)自然主键:具有具体业务含义,如学号,身份证
2)代理主键:不具有业务含义,如mysql自增主键,oracle序列,uuid()生成的唯一序列串
持久化对象三种状态:
1)瞬时态:无oid,刚new出来,不在session管理范围--》save(),saveorupdate()变为持久态
2)持久态:有oid,被session管理,发生改变,hibernate能检测到--》evict(),clear(),close()清空一级缓存变为脱管态
3)游离脱管态:失去session关联,发生改变,hibernate不能检测到
1.一级缓存:session缓存,当我们第一次操作session的save。。等方法时,就会持久化对象到session中,当下一次查询时直接先缓存中查找。
2.持久化对象具有自动更新数据库的能力---》有一个快照,更改提交后,会看快照与实体是否一样,不一样,则向数据库发送update语句。
3.二级缓存:sessionFactory级别缓存
内置缓存:hibernate自带的,不可卸载,hibernate初始化阶段,会将映射数据放置到sessionFactory的缓存中;
外置缓存:通常所说的二级缓存就是外置缓存,一个缓存插件,外部缓存的物理介质可以是内存或者硬盘
适合放入二级缓存的使一些很少被修改,不是很重要的数据(如财务数据就不能放入)
一些较为常见的插件配置:
1.ehcache
2.opensymphony
3.swarmcache
4.jbosscache
1)一对一:
原则有两种:唯一外键对应,在任意一方添加外键来描述对应关系
主键对应,一方的主键作为另一方的主键
2)一对多:
建表原则:在多的一方添加外键来描述关联关系
xml中配置:inverse="true",表示有对方维护外键,一般外键在哪个表就有谁维护外键
级联操作:cascade="delete/save-update"
3)多对多
建表原则:通过一张中间表来描述对应关系,然后中间表至少有两个字段作为外键分别指向多对多双方的主键
@Entity 声明一个实体 @Table来描述类与表对应
@Id来声明一个主键 @GenerateValue 用它来声明一个主键生成策略,默认native
@Column来定义列 @Temporal来声明日期类型
重要:@Transient 表示类的这个属性不需要生成在表中
@OneToMany(mappedBy=""),表示所在一方放弃外键的维护,相当于xml中的inverse="true"
@joinColumn(),表示外键生成列
@joinTable()描述中间表
@Cascade()级联操作
1)导航对象图检索:即根据customer找到order对象
2)OID检索:session.get/load(Customer.class,3)
3)HQL:session.createQuery(hql)--更面向对象
4)QBC:session.createCriteria(Customer.class)--完全面向对象--通过操作api创造查询条件查询
5)SQL:session.createSQLQuery(sql);
本地sql也支持命名查询
注意:多了一个迫切内连接和迫切左外连接
1)显示内连接:inner join with-->from Customer c inner join c.orders with c.id=1;---》返回一个对象数组
2)隐式内连接:.-->from Customer c where c.o.id=1;
3)迫切内连接:直接返回from后面的对象--》select distinct c form Customer c inner join fetch c.orders;
4)(左右)外连接:from Customer c left outer join c.orders
5)迫切左外连接:from Customer c left outer join fetch c.orders where c.id = 1;
很明显,实际开发中对于hql语句查询用的最多的是迫切内连接,直接返回from后的对象
1)尽量少使用not,因为where后面子句中包含了not后,执行时索引就会失效--》全表扫描
2)使用表的别名,首先能提高程序的可阅读性,还能减少解析时间及歧义语法错误
3)。。。
1)延迟加载:hibernate为提高程序执行效率而提供的一种机制,即只有真正使用该对象的数据时才会加载
load:延迟加载,get:采用的立即加载
2)检索策略分两种:
1)类级别检索:
注解:@Proxy(lazy=true/false)即在实体类上方注明是否延迟加载,如果false,load和get一样
2)关联级别检索:查询到某个对象,获得其关联的对象或属性,即为关联级别检索--》c.getOrders().size();
需要研究其抓取策略。
3)主要考虑一个对象关联数据量(集合或者对象)比较大,为了程序的优化,采用延迟加载,manytoOne和oneToOne上使用
xml配置:一些集合set上fetch=""(描述的是sql语句格式,是子查询,还是多表联查等等),lazy=true/false(表示是否立即发送sql语句)
注解配置:在集合set上@fetch(),@lazyColection()
批量抓取(batchFetch的N+1问题即三个对象发送四条sql语句--》采用批量抓取--》底层sql语句用where orders in(...)提升效率)
spring是一个分层的一站式轻量级开源框架,也是一个容器框架,主要特点表现在:整合,即不和现有的解决方案竞争,而是将他们整合融合在一起,高效的完成任务;
2.特点:
1)轻量级:大小和开销都是较小的
2)容器:
3)控制反转:IOC(inversion of controller)
原来由我们自己实例化的对象交给spring容器进行初始化,并由其创建各个实例之间的依赖关系
实现原理:xml配置文件+反射机制+bean工厂--》读取配置文件详细信息,获得类的字节码对象--》反射--》在bean工厂就获得了对象的实例
4)面向切面:
5)MVC
6)框架
IOC:指的是对象实例化的权利交给了spring容器
DI:指的是创建实例化的过程中,通过配置动态的将属性注入到对象中
注入方法:
1)属性注入:
需要提供无参构造方法--》更加灵活,
2)构造器注入:
需要提供有参构造方法--》高内聚,强依赖的体现
1)用途:springBean用途广泛,javaBean一般用来作为值对象传递数据
2)写法:javaBean要求为每个属性都提供对应的set/get方法,而springBean仅需要对其需要注入的属性提供set方法
3)生命周期:JavaBean不接受任何容器的管理,而springBean由容器管理其生命周期及行为
实例化bean,属性注入,BeanPostProcessor显示调用初始化方法的前添加我们自己的逻辑,初始化,BeanPostProcessor显示调用初始化方法的后添加我们自己的逻辑,销毁。
*对于生命周期需要注意BeanPostProcessor,后置处理器,主要用来在实例化属性注入springbean后初始化前后功能增强
提供在程序运行是构造复杂表达式来完成对象属性的存储及方法的调用--》#{表达式}--》#{key.属性}
@Component
@Repository @Service @Controller
@Value("")简单属性注入 @Autowired复杂属性注入--根据类型注入+@Qualifier("")根据名称注入==@Resource(name="")
@Scope("prototype")多例
@RunWith(SpringJUnit4ClassRunner.class) 整合Junit4测试
@ContextConfiguration(locations = "classpath:applicationContext.xml")指明spring配置文件位置
@Lazy(true) 表示延迟初始化
@Before标志一个前置增强方法
@AfterReturning后置增强
@AfterThrowing异常抛出增强
@After--》final增强,必须执行
@Around环绕增强
@DeclareParents引介增强
1)概念:对业务逻辑的各个部分进行隔离,使各部分之间的耦合度降低,提高程序的可复用性,提高开发效率
2)主要功能:日志记录,性能统计,安全控制,事务管理,异常处理--》单独分离出来,不影响主要业务逻辑代码
3)OOP面向对象编程--封装继承多态,高效的开发
AOP面向切面编程--处理过程中某个步骤隔离出来,降低耦合
4)关键词:
1.目标对象:target--》需要被增强的对象,通过动态代理实现
2.连接点:joinpoint--》被拦截到的点或方法
3.切入点:pointcut--》表示对哪些连接点或者方法进行拦截
4.通知(增强):advice--》拦截都连接点之后所进行的具体操作
分为:前置通知,后置通知,异常通知,最终通知,环绕通知
5.引介:introduction
6.切面:aspect--》切入点和通知的结合
7.织入:weaving--》织入是一个过程,将切面应用到目标对象创建出代理对象的过程
spring采用动态织入,aspectj采用静态织入
8.代理:Proxy--》一个类被AOP织入增强后,就产生一个结果代理类
5)底层实现:
1.静态AOP:AspectJ实现的AOP,将切面代码直接编译到Java类文件中
动态AOP:将切面代码动态织入实现AOP
2.SpringAOP为动态AOP,实现技术为:
1)JDK动态代理:JVM内部动态生成class字节码Class对象,只针对于实现了接口的对象进行代理操作
1.Proxy:提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。
2.静态方法创建代理对象:
Proxy.newProxyInstance(ClassLoader loader,Class>[]interfaces,,InvocationHandler)
解释:
ClassLoader:目标类的类加载器对象--》target.getClass().getClassLoader();
Class<>interfaces:目标类实现的接口的class[]对象-->target.getClass().getInterfaces();
InvocationHandler:是代理实例的调用处理程序 实现的接口,每个代理实例都具有一个关联的调用处理程序。
实现InvocationHandler接口后,用this代替即可
3.重写invoke(Object proxy,Method method,Object[]args)方法:具体的代理方法执行者
对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法
即用代理实例调用方法时,都是由invoke方法进行真正的处理调用,
处理并返回结果{return method.invoke(target,args);}--》反射
解释:
proxy:代理对象
method:调用的方法
args:调用方法的参数
*可以在调用方法前做其他操作如校验等等,也可以在方法后做如抛出异常释放资源等操作
2)CGLIB动态字节码增强技术:
CGLIB 包的底层是通过使用一个小而快的字节码处理框架 ASM,来转换字节码并生成
新的类,目标对象不需要实现接口,都能创建代理对象,只不过是目标对象的子类的代理
1.怎么创建:
实现MethodInterceptor接口
Enhancer enhancer = new Enhancer();//创建一个增强对象
enhancer.setSuperclass(target.getClass);
enhancer.setCallback(this);//回掉函数,这个this就是MethodInterceptor
return enhancer.create();//产生代理对象,目标对象的子类代理
2.重写intercept(Object proxy,Method method,Object[]args,MethodProxy methodProxy)方法
解释:
proxy:代理对象
method:调用方法
args:方法参数
methodProxy:自类方法代理对象
返回:{return method.invoke(target,args)或者return methodProxy.invokeSuper(proxy,args)}
同理,也可以在返回前执行校验等操作
3.spring具体采用哪一种动态代理机制:
如果目标对象有接口,优先使用jdk动态代理
如果目标对象没有接口,使用cglib动态代理
前置通知,实现MethodBeforeAdvice
后置通知,实现AfterReturningAdvice
环绕通知,实现MethodInterceptor
异常抛出通知,实现ThrowsAdvice
引介通知,实现IntroductionInterceptor
基于xml配置开发:
1.aspectj框架定义了6种通知:
Before:前置通知 可以完成日志记录,权限控制
AfterReturning:后置通知
Around:环绕通知 可以完成日志操作,权限操作,性能监控,事务管理
AfterThrowing:抛出通知
DeclareParents:引介通知
After:最终通知 可以完成资源释放
2.不需要实现任何接口,定义前置后置等方法后在xml种配置即可
3.
基于注解开发:
1.编写目标,开启注解
2.编写增强 在类上@Component @Aspect-->声明是一个增强
在方法上@Before("execution()")@Around()......
1.平台事务管理器PlatformTransactionManager:
DataSourceTransactionManager 主要针对于 JdbcTemplate 开发 MyBatis 开发
HibernateTransactionManasger 主要针对于 Hibernate 开发
JpaTransactionManager 主要针对于 JPA 开发。
2.事务特性TransactionDefinition接口定义了很多特性:
事务隔离级别:脏读,不可重复读 虚读。
事务传播行为:它解决的是两个被事务管理的方法互相调用问题。它与数据库没关系,是程序内部维护的问题。
事务超时:多长时间这个事务自动回滚
只读状态:true/false
3.对于不现的数据库,它的底层默认事务隔离级别不一样。
Oracle 数据库它默认的是 read_committed--解决脏读,无法解决不可重复读,虚度
Mysql 数据库它默认的是 repeatable_read--解决不可重复读,脏读,解决不了虚度
4.事务管理的方式:
1)编码方案:具有侵入性,将事务管理代码添加到业务代码,不建议使用
2)声明式事务控制:基于aop对目标进行代理,添加around环绕通知,不需要修改业务代码,推荐使用
具体操作:xml中配置事务管理,在service层添加注解@Transactional
区别:声明式事务管理细粒度只能到方法级别,而编程事务管理能到代码块级别,更灵活,但具有侵入性,不方便管理;
1.操作:a.dao层只需继承HibernateDaoSupport--》
b.注入一个sessionFactory,获得HibernateTemplate模板对象,它是对hibernate操作的一个简单封装,使用更方便
c.执行crud操作:this.getHibernateTemplate().save()....
1.web.xml中配置好StrutsPrepareAndExecuteFilter
2.@Scope @Namespace @Controller @ParentPackage() @Action()
3.no session问题解决:
1)不适用延迟加载
2)手动将延迟加载初始化Hibernate.initialize(延迟对象);
3)spring提供一个OpenSessionInViewFilter来解决,在web.xml配置在struts2Filter之前即可
原理是将session的关闭操作不在service完成,而是在视图层才关闭
1) 往上--》sqlSessionFactoryBuilder-->sqlSessionFactory-->sqlSession
2)往下--》executor()/selectOne()/selectList()-->执行sql---》根据核心配置文件sqlMapConfig.xml(配置一些基本环境即数据库连接信息)
--->user.xml(userMapper.xml)(映射文件--》preparestatement配置)--->实现对象与数据库的映射注入操作
1)原生--》user.xml
2)动态代理--》Mapper.xml-->代理对象实现了 InvocationHandler接口,重写invoke方法生成Proxy代理对象
--》session.getMapper(User.class)==userMapper-->userMapper.findUserById();
3)jdk动态代理:
1)实现InvocationHandler--》invoke()-->proxy-->在调用方法前后可以执行复杂的操作
2)JDK中所要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,
这在实际编程中具有一定的局限性,而且使用反射的效率也并不是很高。
cglib动态代理:
1)使用CGLib实现动态代理,完全不受代理类必须实现接口的限制,而且CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高
2)被代理类--》拦截器(实现methodInteceptor接口)--》生成动态代理类(Enhancer字节码增强器)--》在调用方法前后可以执行复杂的操作
1)在SqlMapConfig.xml中配置数据链接池,使用连接池管理数据库链接。
2)将Sql语句配置在XXXXmapper.xml文件中与java代码分离。
3)Mybatis自动将java对象映射至sql语句,通过statement中的parameterType定义输入参数的类型。
4)Mybatis自动将sql执行结果映射至java对象,通过statement中的resultType定义输出结果的类型。
注意:
1)parameterType输入设置
2)resultType和resultMap两种输出封装
不管是哪一种配置,最终的目的都是为了查询对象与映射文件中sql语句匹配起来,即能够一一映射。
mybatis就是面向sql语句的持久层---》重点在于sql语句的掌握,映射文件中一个sql语句就是一个statement对象
hibernate面向查询对象的持久层---》重点在于crud操作的对象api,映射文件中注明了数据库各字段与对象属性的对应关系
1)一对一关联映射:
xml配置:
2)一对多关联映射:
xml配置:
3)多对多关联映射:
配置applicationContext.xml:
1)配置datasource--》如c3p0
2)配置sqlSessionFactory--》负责与数据库连接:sqlSessionFactory交给spring管理--》spring提供SqlSessionFactoryBean(需要datasource及配置文件)
3)传统dao配置:daoImpl--》ref="sqlSessionFactory"-->daoImpl extends SqlSessionDaoSupport
mapper代理dao配置:MapperFactoryBean
mapper代理的开发原则:
1)接口全限定名与namespace名相同
2)方法名与property中的id相同
3)输入参数类型与parameterType一致,输出参数类型与resultType或resultMap中的type属性值相同
包扫描器:MapperScannerConfigurer
1.自动生成映射配置文件和接口文件---》会怎么应用和修改 springwebmvc是spring框架的一部分,是视图层的一个框架 1)HandlerMapping:处理器映射器 1)核心控制器:springmvc在web.xml中配置前端控制器DispatcherServlet,而struts2在web.xml中配置strutsPrepareAndExecutorFilter
2.只能生成单表--》所以得学会在单表基础上修改添加:springmvc
1.概念:
2.架构流程:
1)用户发送请求只前端控制器DispatcherServlet
2)DispatcherServlet收到请求调用HandlerMapping处理器映射器
3)HandlerMapping根据请求的url地址找到具体的处理器,生成处理器对象及拦截器对象,返回给DispatcherServlet
4)DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
5)执行处理器,并返回ModelAndView对象到DispatcherServlet
6)DispatcherServlet将ModelAndView传给视图解析器ViewReslover
7)ViewReslover解析后返回具体的view到DispatcherServlet
8)DispatcherServlet响应回用户
综上所述:前台到controller视图层必须经过DispatcherServlet控制器,都是由他来调用转发组件处理请求的3.三大组件:
2)HandlerAdapter:处理器适配器
3)ViewResolver:视图解析器4.springmvc和struts2的比较:
2)请求路径:springmvc请求路径配置在方法上@RequestMapping,struts2请求连接通过类上的@namespace+@Action配置
3)请求参数封装:springmvc通过形参封装,struts2通过属性驱动或者模型驱动封装
4)返回视图:springmvc通过创建ModelAndView对象,通过request域传输返回,而struts2通过@action配置属性results={@result(location="")}配置,采用值栈存储响应
5)@Controller:一般而言,springmvc基于方法url的@Controller默认单例,struts2基于类设计为多例的
高级参数的绑定:
1.数组:
在形参中添加参数:string[] ids
在pojo添加string[] ids属性,提供set/get方法
2.List:在pojo类中添加List
前台通过value取值
@RequestMapping注解:
方法上必须有
有时为了复杂的需求,可以在类上也添加此注解,组成两级路径--》窄化请求映射
请求方法限定:@RequestMapping(method={RequestMethod.POST})默认post/get都支持
Controller方法返回值:
1.返回ModelAndView--封装数据和视图--return modelAndView;
2.返回字符串--model封装直接返回视图,通过视图解析器解析为物理视图地址
1)逻辑视图名:return "item/editItem";
2)Redirect重定向:return "redirect:queryItem.action"--url地址会变,
相当于一个新的request/response,这时如果还要传递参数,可以在后面拼接?..&...
3)forward转发:return "forword:editItem.action"--url地址不变,还是同一个
request/response,不需要再次添加请求参数
3.返回void--原生态request和response解决跳转和返回值
response.sendRedirect("url"):重定向
request.getRequestDispatcher("").forword(request,response):请求转发
response.getWriter().write();
页面获取值:
${key名称.属性.属性}-->不管是字符串,pojo,vo都可以
异常处理:
客户端---请求》springmvcDispatcherServlet-->Controller-->service-->dao
| | | |
| 抛出 抛出 抛出
HandlerExceptionResolver全局异常处理器进行异常处理
图片上传
json数据交互
@RequestBody-->将请求json转为java对象
@ResponseBody--》将java转为json输出
RESTful--一种资源定位及资源操作的设计风格
/拦截所有请求,不包括jsp
/*拦截所有请求
拦截器:
自定义需要实现HandlerInterceptor