Struts2是类级别的拦截, 一个类对应一个request上下文,SpringMVC是方法级别的拦截,一个方法对应一个request上下文,而方法同时又跟一个url对应,所以说从架构本身上SpringMVC就容易实现restful url,而struts2的架构实现起来要费劲,因为Struts2中Action的一个方法可以对应一个url,而其类属性却被所有方法共享,这也就无法用注解或其他方式标识其所属方法了。
由上边原因,SpringMVC的方法之间基本上独立的,独享request response数据,请求数据通过参数获取,处理结果通过ModelMap交回给框架,方法之间不共享变量,而Struts2搞的就比较乱,虽然方法之间也是独立的,但其所有Action变量是共享的,这不会影响程序运行,却给我们编码读程序时带来麻烦,每次来了请求就创建一个Action,一个Action对象对应一个request上下文。
由于Struts2需要针对每个request进行封装,把request,session等servlet生命周期的变量封装成一个一个Map,供给每个Action使用,并保证线程安全,所以在原则上,是比较耗费内存的。
拦截器实现机制上,Struts2有以自己的interceptor机制,SpringMVC用的是独立的AOP方式,这样导致Struts2的配置文件量还是比SpringMVC大。
SpringMVC的入口是servlet,而Struts2是filter(这里要指出,filter和servlet是不同的。以前认为filter是servlet的一种特殊实现),这就导致了二者的机制不同,这里就牵涉到servlet和filter的区别了。
SpringMVC集成了Ajax,使用非常方便,只需一个注解@ResponseBody就可以实现,然后直接返回响应文本即可(只支持异步调用),而Struts2拦截器集成了Ajax,在Action中处理时一般必须安装插件或者自己写代码集成进去,使用起来也相对不方便。
SpringMVC验证支持JSR303,处理起来相对更加灵活方便,而Struts2验证比较繁琐,感觉太烦乱。
SpringMVC和Spring是无缝的。从这个项目的管理和安全上也比Struts2高(当然Struts2也可以通过不同的目录结构和相关配置做到SpringMVC一样的效果,但是需要xml配置的地方不少)。
Struts2更加符合OOP的编程思想, SpringMVC就比较谨慎,在Servlet上扩展。
SpringMVC开发效率和性能高于Struts2。
HashTable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现,它们都是集合中将数据无序存放的。
HashTable的方法是同步的,是线程安全的,而HashMap不是同步的,是线程不安全的,所以在多线程场合要手动同步HashMap这个区别就像Vector和ArrayList一样。
HashTable不允许 null 值(key 和 value 都不可以),HashMap允许 null 值(key和value都可以)。
两者的遍历方式大同小异,HashTable使用Enumeration,HashMap使用Iterator,HashTable仅仅比HashMap多一个elements方法。
Ajax并不是一门新的技术,而是多种技术的组合(html,js,xml,css)用于快速的创建动态的网页,能够实现无刷新更新数据从而提高了用户体验。
由客户端请求ajax引擎,在由ajax引擎请求服务器,服务器作出一系列的响应之后将结果返回给ajax引擎,由ajax引擎决定将这个结果写入到客户端的什么位置,从而实现了页面无刷新更新数据。
XMLHttpRequest
客户端初始化一个指向Servlet容器(例如Tomcat)的请求
这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin)
接着FilterDispatcher被调用,FilterDispatcher询问ActionMapper来决定这个请是否需要调用某个Action
如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy
ActionProxy通过ConfigurationManager询问框架的配置文件,找到需要调用的Action类
ActionProxy创建一个ActionInvocation的实例。
ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。
一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是(但不总是,也可能是另外的一个Action链)一个需要被表示的JSP或者FreeMarker的模版。在表示的过程中可以使用Struts2 框架中继承的标签。在这个过程中需要涉及到ActionMapper
客户端浏览器发出HTTP请求.
根据web.xml配置,该请求被FilterDispatcher接收
根据struts.xml配置,找到需要调用的Action类和方法, 并通过IoC方式,将值注入给Aciton
Action调用业务逻辑组件处理业务逻辑,这一步包含表单验证。
Action执行完毕,根据struts.xml中的配置找到对应的返回结果result,并跳转到相应页面
返回HTTP响应到客户端浏览器
线程是指程序在执行过程中,能够执行程序代码最小的一个执行单元,在Java语言中,线程有四种状态:运行,就绪,挂起,结束。
进程是一段正在运行的程序,而线程有时也被称为轻量级进程,它是进程的执行单元,一个进程可以拥有多个线程,各个线程之间共享程序的内存空间,但是,各个线程拥有自己的栈空间。
使用多线程可以减少程序的响应时间。单线程如果遇到等待或阻塞,将会导致程序不响应鼠标键盘等操作,使用多线程可以解决此问题,增强程序的交互性。
与进程相比,线程的创建和切换开销更小,因为线程共享代码段、数据段等内存空间。
多核CPU,多核计算机本身就具有执行多线程的能力,如果使用单个线程,将无法重复利用计算资源,造成资源的巨大浪费。
多线程可以简化程序的结构,使程序便于维护,一个非常复杂的进程可以分为多个线程执行。
调用init()方法,在Servlet的生命周期中,仅执行一次。它是在服务器装入Servlet时执行的,负责初始化Servlet对象。
可以配置服务器,以在启动服务器或客户机首次访问Servlet时装入Servlet。无论有多少客户机访问Servlet,都不会重复执行。
调用service()方法,它是Servlet的核心,负责响应客户的请求。
每当一个客户请求一个HttpServlet对象,该对象的Service()方法就要调用,而且传递给这个方法一个“请求”(ServletRequest)对象和一个“响应”(ServletResponse)对象作为参数。在HttpServlet中已存在Service()方法。默认的服务功能是调用与HTTP请求的方法相应的do功能。
调用destroy()方法,仅执行一次,在服务器端停止且卸载Servlet时执行该方法。当Servlet对象退出生命周期时,负责释放占用的资源。
一个Servlet在运行service()方法时可能会产生其他的线程,因此需要确认在调用destroy()方法时,这些线程已经终止或完成。
对于用户到达Servlet的请求,Servlet容器会创建特定于这个请求的ServletRequest对象和ServletResponse对象,然后调用Servlet的service方法。service方法从ServletRequest对象获得客户请求信息,处理该请求,并将请求ServletRequest、ServletResponse 强转为HttpRequest和HttpResponse,然后返回给客户。
Jsp经编译后就变成了Servlet(JSP的本质就是Servlet,JVM只能识别java的类,不能识别JSP的代码,Web容器将JSP的代码编译成JVM能够识别的java类)。
Jsp更擅长表现于页面显示,Servlet更擅长于逻辑控制。
Servlet中没有内置对象,Jsp中的内置对象都是必须通过HttpServletRequest对象,HttpServletResponse对象以及HttpServlet对象得到。
Jsp是Servlet的一种简化,使用Jsp只需要完成程序员需要输出到客户端的内容,Jsp中的Java脚本如何镶嵌到一个类中,由Jsp容器完成。而Servlet则是个完整的Java类,这个类的Service方法用于生成对客户端的响应。
Docker很适合用于测试发布,将Docker封装后可以直接提供给测试人员进行运行,不再需要测试人员与运维、开发进行配合,进行环境搭建与部署。
在测试中,经常由于测试场景变换,需要修改依赖的数据库数据或者清空变动memcache、Redis中的缓存数据。Docker相较于传统的虚拟机,更轻量与方便。可以很容易的将这些数据分离到不同的镜像中,根据不同需要随时进行切换。
开发人员共同使用同一个Docker镜像,同时修改的源代码都被挂载到本地磁盘。不再因为环境的不同而造成的不同程序行为而伤透脑筋,同时新人到岗时也能迅速建立开发、编译环境。
Docker可以支持命令行封装与编程,通过自动加载与服务自发现,可以很方便的将封装于 Docker 镜像中的服务扩展成云服务。类似像 Doc 转换预览这样的服务封装于镜像中,根据业务请求的情况随时增加和减少容器的运行数量,随需应变。
这是Docker初始目的,虚拟机VM最大的好处是基于你的应用配置能够无缝运行在任何平台上。Docker提供同样类似VM的能力,但是没有任何副作用,它能让你将环境和配置放入代码然后部署,同样的Docker配置能够在各种环境中使用,这实际是将应用环境和底层环境实现了解耦。
能够对代码以流式pipeline管道化进行管理,从开发者的机器到生产环境机器这个流程中都能有效管理。因为在这个流程中会有各种不同的环境,每个都可能有微小的区别,Docker提供了跨越这些异构环境以一致性的微环境,从开发到部署实现流畅发布。
在一个开发环境,我们希望我们的开发环境能更加接近于生产环境,我们会让每个服务运行在自己的VM中,这样能模拟生产环境,比如有时我们并不总是需要跨越网络连接,这样我们可以将多个Docker装载一系列服务运行在单机上最大程度模拟生产分布式部署的环境。
有很多理由你需要在一台机器上运行多个应用,这就需要将原来铁板一块monolithic的应用切分为很多微服务。实现应用之间的解耦,将多个应用服务部署在多个Docker中能轻松达到这个目的。
使用Docker也能合并多个服务以降低费用,不多的操作系统内存占用,跨实例共享多个空闲的内存,这些技术Docker能以更加紧密资源提供更有效的服务合并。
Docker能够作为云计算的多租户容器,使用Docker能容易为每个租户创建运行应该多个实例,这得益其灵活的快速环境以及有效diff命令。
Docker通过创建进程的容器,不必重新启动操作系统,几秒内能关闭,你可以在数据中心创建或销毁资源,不用担心额外消耗。典型的数据中心利用率是30%,通过更积极的资源分配,以低成本方式对一个新的实例实现一个更聚合的资源分配,我们很容易超过这个利用率,大大提高数据中心的利用效率。
所谓同步,就是在发出一个 调用 时,在没有得到结果之前,该 调用 就不返回。但是一旦调用返回,就得到返回值了。
换句话说,就是由 调用者 主动等待这个 调用 的结果。
而异步则是相反,调用 在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在调用发出后,被调用者 通过状态、通知来通知调用者,或通过回调函数处理这个调用。
getClass方法是一个final方法,不允许子类重写,并且也是一个native方法。返回当前运行时对象的Class对象。
hashCode方法也是一个native方法。该方法返回对象的哈希码,主要使用在哈希表中,比如JDK中的HashMap。
在java程序执行过程中,在一个对象没有被改变的前提下,无论这个对象被调用多少次,hashCode方法都会返回相同的整数值。对象的哈希码没有必要在不同的程序中保持相同的值。
如果2个对象使用equals方法进行比较并且相同的话,那么这2个对象的hashCode方法的值也必须相等。
如果根据equals方法,得到两个对象不相等,那么这2个对象的hashCode值不需要必须不相同。但是,不相等的对象的hashCode值不同的话可以提高哈希表的性能。
通常情况下,不同的对象产生的哈希码是不同的。默认情况下,对象的哈希码是通过将该对象的内部地址转换成一个整数来实现的。
比较两个对象是否相等。Object类的默认实现,即比较2个对象的内存地址是否相等。
创建并返回当前对象的一份拷贝。一般情况下,对于任何对象 x,表达式 x.clone() != x 为true,x.clone().getClass() == x.getClass() 也为true。
Object对象的默认实现,即输出类的名字@实例
的哈希码的16进制。
notify方法是一个native方法,并且也是final的,不允许子类重写。
唤醒一个在此对象监视器上等待的线程(监视器相当于就是锁的概念)。如果所有的线程都在此对象上等待,那么只会选择一个线程。选择是任意性的,并在对实现做出决定时发生。一个线程在对象监视器上等待可以调用wait方法。
直到当前线程放弃对象上的锁之后,被唤醒的线程才可以继续处理。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争。例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。
跟notify一样,唯一的区别就是会唤醒在此对象监视器上等待的所有线程,而不是一个线程。同样,如果当前线程不是对象监视器的所有者,那么调用notifyAll同样会发生IllegalMonitorStateException异常。
wait(long timeout) throws InterruptedException
wait(long timeout)方法同样是一个native方法,并且也是final的,不允许子类重写。
wait方法会让当前线程等待直到另外一个线程调用对象的notify或notifyAll方法,或者超过参数设置的timeout超时时间。
跟notify和notifyAll方法一样,当前线程必须是此对象的监视器所有者,否则还是会发生IllegalMonitorStateException异常。
wait(long timeout, int nanos) throws InterruptedException
跟wait(long timeout)方法类似,多了一个nanos参数,这个参数表示额外时间(以毫微秒为单位,范围是 0-999999)。 所以超时的时间还需要加上nanos毫秒。
需要注意的是 wait(0, 0)和wait(0)效果是一样的,即一直等待。
wait() throws InterruptedException
跟之前的2个wait方法一样,只不过该方法一直等待,没有超时时间这个概念。
//方法一
getClass().getResource(String name);
//方法二
Object.class.getClassLoader().getResource(String name);
//方法三
Object.class.getResource(String name);
List list1 =new ArrayList();
list1.add("1111");
list1.add("2222");
list1.add("3333");
List list2 =new ArrayList();
list2.add("3333");
list2.add("4444");
list2.add("5555");
//并集
//list1.addAll(list2);
//交集
//list1.retainAll(list2);
//差集
//list1.removeAll(list2);
//无重复并集
list2.removeAll(list1);
list1.addAll(list2);
注解@RequestBody 表示接收请求里的JSON对象。
注解@ResponseBody表示用JSON对象的格式发送响应。
@RequestMapping("/login")
@ResponseBody
public String login(@RequestBody User user){
return "{success: true}";
}
实现ModelDriven接口
implements ModelDriven
实现接口的方法
//这是一个自定义的Bean对象
private Object object;
@Override
public Object getModel() {
return object;
}
Struts2中action是多例的,即一个session产生一个action。
如果是单例的话,若出现两个用户都修改一个对象的属性值,则会因为用户修改时间不同,两个用户访问得到的属性不一样,操作得出的结果不一样。
拦截器是一种可以在让用户在Action执行之前和Result执行之后进行一些功能处理的机制。 拦截器在action执行之前和result执行之后的顺序是相反的,也就是说执行的顺序是:Interceptor1—Interceptor2—Interceptor3—Action—Result—Interceptor3—Interceptor2—Interceptor1。
在struts.xml文件中定义拦截器只需要为拦截器类指定一个拦截器名,就可以完成拦截器的定义。定义拦截器使用
元素,如下:
<interceptor name="拦截器名" class="拦截器实现类" />
如果还需要在配置拦截器时传入拦截器参数。可以在
元素中使用
子元素。如下:
<interceptor name="拦截器名" class="拦截器实现类">
<param name="参数名">参数值param>
interceptor>
除了配置拦截器,还可以把多个拦截器连在一起组成拦截器栈。定义拦截器栈使用
元素,拦截器栈是由多个拦截器组成。因此需要在
元素中使用
元素来定义多个拦截器引用。配置拦截器栈如下:
<interceptor-stack name="拦截器栈名">
<interceptor-ref name="拦截器名" />
<interceptor-ref name="拦截器名" />
interceptor-stack>
从上面的结构中可以看出,一个拦截器栈是由多个拦截器组成的。但是从程序功能上看,拦截器和拦截器栈是统一的,它们包含的方法都会在Action的execute方法执行之前执行。由于拦截器栈和拦截器的功能几乎完全相同,所以我们可以将拦截器栈当成一个更大的拦截器。拦截器栈中也可以包含拦截器栈。配置如下:
<interceptor-stack name="拦截器栈一">
<interceptor-ref name="拦截器一" />
<interceptor-ref name="拦截器二" />
interceptor-stack>
<interceptor-stack name="拦截器栈二">
<interceptor-ref name="拦截器三" />
<interceptor-ref name="拦截器栈一" />
interceptor-stack>
注:如果在两个时机为同一个参数指定不同的参数值,则使用拦截器时指定的参数值将会覆盖默认的参数值。
当配置一个包时,可以为其指定默认拦截器。一旦为某个包指定了默认拦截器,如果该包中的Action没有显示指定拦截器,则默认的拦截器将会起到作用。如果我们为该包中的Action显示应用了某个拦截器,则默认的拦截器不会起作用。
配置默认拦截器使用
元素,该元素作为
元素的子元素使用,为该包下的所有Action配置默认的拦截器。而且每个
元素中只能包含一个
子元素,即没一个包只能指定一个默认拦截器。如果我们需要多个拦截器共同作为默认拦截器,则应该将这些拦截器定义成一个拦截器栈,然后把这个拦截器栈配置成默认拦截器即可。默认拦截器配置如下:
<package name="包名">
<interceptors>
<interceptor .../>
<interceptor-stack .../>
interceptors>
<default-interceptor-ref name="拦截器名或者拦截器栈名" />
<action ../>
package>
需要的jar包
配置文件(注意:所有的配置文件都应该放到web文件中)
配置数据源
配置数据库连接池
配置SqlSessionFactory(MyBatis和Spring整合包中的)
配置mapper文件扫描器。
@Service
注解的类使用jar包:主要是Spring的jar包
配置文件:applicationContext-Service.xml
事务配置
@Controller
注解的类使用jar包:SpringMVC和Spring的jar包
配置文件:springmvc.xml
配置注解驱动
配置Jsp视图解析器
配置web.xml
配置SpringMVC的前端控制器
Spring容器的初始化的listener
Web层:Struts2
Service层:Spring
DAO层:Hibernate
把Struts2的Action对象的创建交给Spring管理
配置Bean:
把Hibernate核心配置文件中的数据库连接配置,直接写在 Spring 核心配置文件中
把Hibernate的SessionFactory对象的创建交给Spring管理
配置在服务器启动时加载Spring核心配置文件,创建出包含 SessionFactory对象在内的一系列对象
Session缓存表示将查询结果放置到Session的临时存储空间(一级缓存中)。Hibernate框架默认支持一级缓存的。一级缓存的范围较小,一旦Session关闭,那么缓存失效。我们使用框架的各种方法,例如:get,load,save,update,delete等都支持一级缓存的。
二级缓存其实就是将查询的数据放置在SessionFactory临时存储空间中,因为一个SessionFactory可以创建多个Session对象,所以范围比Session缓存的要大,多个Session可以共享二级缓存的数据。当然了二级缓存也不能存储大量的数据,这个要根据我们电脑配置进行设置。
在hibernate.cfg.xml配置文件中添加属性标签,启用二级缓存:
<propertyname="hibernate.cache.use_second_level_cache">trueproperty>
二级缓存需要使用额外的第三方组件:ehcache。需要我们拷入对应的jar包,以及将对应的ehcache.xml存放到src目录下。在这个配置文件中,我们可以设置二级缓存的大小等。
让框架识别添加入的ehcache缓存组件,在hibernate.cfg.xml配置文件中添加属性标签:
<propertyname="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProviderproperty>
设置需要缓存的映射类,这里只是将一些查询操作比较频繁的类指定即可,哪些不经常操作的数据,是没有必要利用缓存的。这里例如:
<class-cache usage="read-only" class="com.ljh.hibernate.pojo.Student"/>
查询数据时,会首先从一级缓存中取数据,如果取上,则直接使用,否则到二级缓存中取,如果取到则直接使用,否则,就会发送语句查询数据库。这样利用一级和二级缓存会提高访问效率。
表示查询当前对象或关联对象数据时,不真正访问数据库,当使用对象非主键属性时,才真正发送查询语句,访问数据库。由于在某些情况下,查的数据在后续流程到可能用不上,如果做查询处理就多余了,所以延迟加载功能可以提高性能,合理使用即可。
IOC是一种思想,IOC理论的提出就是为了解决对象之间的“解耦”。DI就是其技术实现,松散耦合。
AOP的实现原理
ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。