标签:
配置详解
* spring_mvc_图解
一、Spring2.5之前,我们都是通过实现Controller接口或其实现来定义我们的处理器类。
二、Spring2.5引入注解式处理器支持,通过@Controller 和 @RequestMapping注解定义我们的处理器类。并且提供了一组强大的注解:
需要通过处理器映射DefaultAnnotationHandlerMapping和处理器适配器AnnotationMethodHandlerAdapter来开启支持@Controller 和 @RequestMapping注解的处理器。
@Controller:用于标识是处理器类;
@RequestMapping:请求到处理器功能方法的映射规则;
@RequestParam:请求参数到处理器功能处理方法的方法参数上的绑定;
@ModelAttribute:请求参数到命令对象的绑定;
@SessionAttributes:用于声明session级别存储的属性,放置在处理器类上,通常列出模型属性(如@ModelAttribute)对应的名称,则这些属性会透明的保存到session中;
@InitBinder:自定义数据绑定注册支持,用于将请求参数转换到命令对象属性的对应类型;
三、Spring3.0引入RESTful架构风格支持(通过@PathVariable注解和一些其他特性支持),且又引入了更多的注解支持:
@CookieValue:cookie数据到处理器功能处理方法的方法参数上的绑定;
@RequestHeader:请求头(header)数据到处理器功能处理方法的方法参数上的绑定;
@RequestBody:请求的body体的绑定(通过HttpMessageConverter进行类型转换);
@ResponseBody:处理器功能处理方法的返回值作为响应体(通过HttpMessageConverter进行类型转换);
@ResponseStatus:定义处理器功能处理方法/异常处理器返回的状态码和原因;
@ExceptionHandler:注解式声明异常处理器;
@PathVariable:请求URI中的模板变量部分到处理器功能处理方法的方法参数上的绑定,从而支持RESTful架构风格的URI;
四、还有比如:
JSR-303验证框架的无缝支持(通过@Valid注解定义验证元数据);
使用Spring 3开始的ConversionService进行类型转换(PropertyEditor依然有效),支持使用@NumberFormat 和 @DateTimeFormat来进行数字和日期的格式化;
HttpMessageConverter(Http输入/输出转换器,比如JSON、XML等的数据输出转换器);
ContentNegotiatingViewResolver,内容协商视图解析器,它还是视图解析器,只是它支持根据请求信息将同一模型数据以不同的视图方式展示(如json、xml、html等),RESTful架构风格中很重要的概念(同一资源,多种表现形式);
Spring 3 引入 一个 mvc XML的命名空间用于支持mvc配置,包括如:
<mvc:annotation-driven>:
自动注册基于注解风格的处理器需要的DefaultAnnotationHandlerMapping、AnnotationMethodHandlerAdapter
支持Spring3的ConversionService自动注册
支持JSR-303验证框架的自动探测并注册(只需把JSR-303实现放置到classpath)
自动注册相应的HttpMessageConverter(用于支持@RequestBody 和 @ResponseBody)(如XML输入输出转换器(只需将JAXP实现放置到classpath)、JSON输入输出转换器(只需将Jackson实现放置到classpath))等。
<mvc:interceptors>
:注册自定义的处理器拦截器;
<mvc:view-controller>
:和ParameterizableViewController类似,收到相应请求后直接选择相应的视图;
<mvc:resources>
:逻辑静态资源路径到物理静态资源路径的支持;
<mvc:default-servlet-handler>
:当在web.xml 中DispatcherServlet使用<url-pattern>/</url-pattern>
映射时,能映射静态资源(当Spring Web MVC框架没有处理请求对应的控制器时(如一些静态资源),转交给默认的Servlet来响应静态文件,否则报404找不到资源错误,)。
……等等。
五、Spring3.1新特性:
对Servlet 3.0的全面支持。
@EnableWebMvc:用于在基于Java类定义Bean配置中开启MVC支持,和XML中的<mvc:annotation-driven>
功能一样;
新的@Contoller和@RequestMapping注解支持类:处理器映射RequestMappingHandlerMapping 和 处理器适配器RequestMappingHandlerAdapter组合来代替Spring2.5开始的处理器映射DefaultAnnotationHandlerMapping和处理器适配器AnnotationMethodHandlerAdapter,提供更多的扩展点,它们之间的区别我们在处理器映射一章介绍。
新的@ExceptionHandler 注解支持类:ExceptionHandlerExceptionResolver来代替Spring3.0的AnnotationMethodHandlerExceptionResolver,在异常处理器一章我们再详细讲解它们的区别。
@RequestMapping的”consumes” 和 “produces” 条件支持:用于支持@RequestBody 和 @ResponseBody,
1 consumes指定请求的内容是什么类型的内容,即本处理方法消费什么类型的数据,如consumes=”application/json”表示JSON类型的内容,Spring会根据相应的HttpMessageConverter进行请求内容区数据到@RequestBody注解的命令对象的转换;
2 produces指定生产什么类型的内容,如produces=”application/json”表示JSON类型的内容,Spring的根据相应的HttpMessageConverter进行请求内容区数据到@RequestBody注解的命令对象的转换,Spring会根据相应的HttpMessageConverter进行模型数据(返回值)到JSON响应内容的转换
3 以上内容,本章第×××节详述。
URI模板变量增强:URI模板变量可以直接绑定到@ModelAttribute指定的命令对象、@PathVariable方法参数在视图渲染之前被合并到模型数据中(除JSON序列化、XML混搭场景下)。
@Validated:JSR-303的javax.validation.Valid一种变体(非JSR-303规范定义的,而是Spring自定义的),用于提供对Spring的验证器(org.springframework.validation.Validator)支持,需要Hibernate Validator 4.2及更高版本支持;
@RequestPart:提供对“multipart/form-data”请求的全面支持,支持Servlet 3.0文件上传(javax.servlet.http.Part)、支持内容的HttpMessageConverter(即根据请求头的Content-Type,来判断内容区数据是什么类型,如JSON、XML,能自动转换为命令对象),比@RequestParam更强大(只能对请求参数数据绑定,key-alue格式),而@RequestPart支持如JSON、XML内容区数据的绑定;详见本章的第×××节;
Flash 属性 和 RedirectAttribute:通过FlashMap存储一个请求的输出,当进入另一个请求时作为该请求的输入,典型场景如重定向(POST-REDIRECT-GET模式,1、POST时将下一次需要的数据放在FlashMap;2、重定向;3、通过GET访问重定向的地址,此时FlashMap会把1放到FlashMap的数据取出放到请求中,并从FlashMap中删除;从而支持在两次请求之间保存数据并防止了重复表单提交)。
Spring Web MVC提供FlashMapManager用于管理FlashMap,默认使用SessionFlashMapManager,即数据默认存储在session中。
<context:component-scan base-package="com.dai.base" />
base-package为要自动扫描的包名,包下的类使用注解来声明bean。
@Controller控制器Bean
@Service业务层Bean
@Repository对应数据访问层Bean
@Component所有受Spring 管理组件的通用形式,不推荐使用。
自动扫描的控件默认是单例模式。
可以在注解中指定bean名,如@Service(“auth”)。
当不指定bean名时,将按照最小非大写命名法指定bean名,如AuthService将被命名为authService。
使用Spring的IoC后,不再需要new对象,而是应该使用getBean的方式获取对象,使用getBean就首先要获取上下文,在调用其getBean方法,使用方法:
ApplicationContext ctx =
new ClassPathXmlApplicationContext("spring-mybatis.xml");
Object obj = (Object)ctx.getBean("object");
当然,这样的效率低而且容易出现BUG,正确的姿势应该是创建一个工具类SpringContextsUtil ,通过实现Spring中的ApplicationContextAware接口,在applicationContext.xml中注入bean后Spring会自动调用setApplicationContext方法。此时我们就可以获取到Spring context。
public class SpringContextsUtil implements
ApplicationContextAware{
private static ApplicationContext applicationContext; //Spring应用上下文环境
/**
* 实现ApplicationContextAware接口的回调方法,设置上下文环境
* @param applicationContext
* @throws BeansException
*/
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextsUtil.applicationContext = applicationContext;
}
/**
* @return ApplicationContext
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 获取对象
* @param name
* @return Object 一个以所给名字注册的bean的实例
* @throws BeansException
*/
public static Object getBean(String name) throws BeansException {
return applicationContext.getBean(name);
}
/**
* 获取类型为requiredType的对象
* 如果bean不能被类型转换,相应的异常将会被抛出(BeanNotOfRequiredTypeException)
* @param name bean注册名
* @param requiredType 返回对象类型
* @return Object 返回requiredType类型对象
* @throws BeansException
*/
public static Object getBean(String name, Class requiredType) throws BeansException {
return applicationContext.getBean(name, requiredType);
}
/**
* 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
* @param name
* @return boolean
*/
public static boolean containsBean(String name) {
return applicationContext.containsBean(name);
}
/**
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype。
* 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
* @param name
* @return boolean
* @throws NoSuchBeanDefinitionException
*/
public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
return applicationContext.isSingleton(name);
}
/**
* @param name
* @return Class 注册对象的类型
* @throws NoSuchBeanDefinitionException
*/
public static Class getType(String name) throws NoSuchBeanDefinitionException {
return applicationContext.getType(name);
}
/**
* 如果给定的bean名字在bean定义中有别名,则返回这些别名
* @param name
* @return
* @throws NoSuchBeanDefinitionException
*/
public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
return applicationContext.getAliases(name);
}
}
此时,如果要使用该类加载springXML的配置,需要在该XML中加入以下这句:
<bean id="springContextsUtil" class="com.dai.pojo.SpringContextUtil" lazy-init="false"></bean>
当然,注意将自己的springXML文件加载到webXML的contextConfigLocation属性中:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mybatis.xml,
classpath:spring-service.xml</param-value>
</context-param>
1、Struts2是类级别的拦截, 一个类对应一个request上下文,SpringMVC是方法级别的拦截,一个方法对应一个request上下文,而方法同时又跟一个url对应,所以说从架构本身上SpringMVC就容易实现restful url,而struts2的架构实现起来要费劲,因为Struts2中Action的一个方法可以对应一个url,而其类属性却被所有方法共享,这也就无法用注解或其他方式标识其所属方法了。
2、由上边原因,SpringMVC的方法之间基本上独立的,独享request response数据,请求数据通过参数获取,处理结果通过ModelMap交回给框架,方法之间不共享变量,而Struts2搞的就比较乱,虽然方法之间也是独立的,但其所有Action变量是共享的,这不会影响程序运行,却给我们编码 读程序时带来麻烦,每次来了请求就创建一个Action,一个Action对象对应一个request上下文。
3、由于Struts2需要针对每个request进行封装,把request,session等servlet生命周期的变量封装成一个一个Map,供给每个Action使用,并保证线程安全,所以在原则上,是比较耗费内存的,而SpringMVC的controller则是非线程安全的。
4、 拦截器实现机制上,Struts2有以自己的interceptor机制,SpringMVC用的是独立的AOP方式,这样导致Struts2的配置文件量还是比SpringMVC大。
5、SpringMVC的入口是servlet,而Struts2是filter(这里要指出,filter和servlet是不同的。以前认为filter是servlet的一种特殊),这就导致了二者的机制不同,这里就牵涉到servlet和filter的区别了。
6、SpringMVC集成了Ajax,使用非常方便,只需一个注解@ResponseBody就可以实现,然后直接返回响应文本即可,而Struts2拦截器集成了Ajax,在Action中处理时一般必须安装插件或者自己写代码集成进去,使用起来也相对不方便。
7、SpringMVC验证支持JSR303,处理起来相对更加灵活方便,而Struts2验证比较繁琐,感觉太烦乱。
8、Spring MVC和Spring是无缝的。从这个项目的管理和安全上也比Struts2高(当然Struts2也可以通过不同的目录结构和相关配置做到SpringMVC一样的效果,但是需要xml配置的地方不少)。
9、 设计思想上,Struts2更加符合OOP的编程思想, SpringMVC就比较谨慎,在servlet上扩展。
10、SpringMVC开发效率和性能高于Struts2。
11、SpringMVC可以认为已经100%零配置。
log4j可以通过使用log4j.properties文件来打印日志以及输出常见的调试信息,如输出sql语句:
log4j.rootLogger=DEBUG, Console
#Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
log4j.logger.java.sql.ResultSet=INFO
log4j.logger.org.apache=INFO
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
过滤器(Filter)与拦截器(Interceptor)是SpringMVC常用的两种开发模型,执行顺序一般是先执行filter,只有限制性过滤器之后才可以进入容器执行拦截。
* 过滤器
实现的方式有以下几类:
(1) 直接实现Filter,这一类过滤器只有CompositeFilter;
(2) 继承抽象类GenericFilterBean,该类实现了javax.servlet.Filter,这一类的过滤器只有一个,即DelegatingFilterProxy;
(3) 继承抽象类OncePerRequestFilter,该类为GenericFilterBean的直接子类,这一类过滤器包括CharacterEncodingFilter、HiddenHttpMethodFilter、HttpPutFormContentFilter、RequestContextFilter和ShallowEtagHeaderFilter;
(4) 继承抽象类AbstractRequestLoggingFilter,该类为OncePerRequestFilter的直接子类,这一类过滤器包括CommonsRequestLoggingFilter、Log4jNestedDiagnosticContextFilter和ServletContextRequestLoggingFilter。
\
过滤器放在web资源之前,可以在请求抵达它所应用的web资源(可以是一个Servlet、一个Jsp页面,甚至是一个HTML页面)之前截获进入的请求,并且在它返回到客户之前截获输出请求。Filter:用来拦截请求,处于客户端与被请求资源之间,目的是重用代码。Filter链,在web.xml中哪个先配置,哪个就先调用。在filter中也可以配置一些初始化参数。
Java中的Filter 并不是一个标准的Servlet ,它不能处理用户请求,也不能对客户端生成响应。 主要用于对HttpServletRequest 进行预处理,也可以对HttpServletResponse 进行后处理,是个典型的处理链。
Filter 有如下几个用处 :
1)在HttpServletRequest 到达Servlet 之前,拦截客户的HttpServletRequest 。
2)根据需要检查HttpServletRequest ,也可以修改HttpServletRequest 头和数据。
3)在HttpServletResponse 到达客户端之前,拦截HttpServletResponse 。
4)根据需要检查HttpServletResponse ,可以修改HttpServletResponse 头和数据。
首先定义过滤器:
public class LoginFilter extends OncePerRequestFilter{
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// TODO Auto-generated method stub
// 不过滤的uri
String[] notFilter = new String[] { "login" };
// 请求的uri
String uri = request.getRequestURI();
// 是否过滤
boolean doFilter = true;
for (String s : notFilter) {
if (uri.indexOf(s) != -1) {
// 如果uri中包含不过滤的uri,则不进行过滤
doFilter = false;
break;
}
}
if (doFilter) {
// 执行过滤
// 从session中获取登录者实体
Object obj = request.getSession().getAttribute("user");
if (null == obj) {
response.sendRedirect("login");
} else {
// 如果session中存在登录者实体,则继续
filterChain.doFilter(request, response);
}
} else {
// 如果不执行过滤,则继续
filterChain.doFilter(request, response);
}
}
}
然后在web.xml中声明过滤器:
<!-- login filter -->
<filter>
<filter-name>sessionFilter</filter-name>
<filter-class>com.dai.filter.LoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>sessionFilter<filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
但是注意,因为过滤器匹配的是所有url,故对应在Spring-MVC中声明的静态资源将会出现获取不到的情况,这种情况推荐使用拦截器。
拦截器和过滤器的区别:
①拦截器是基于java的反射机制的,而过滤器是基于函数回调。
②拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
③拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
④拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
⑤在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
⑥拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。
SpringMVC后台获取上传文件有两种方式:
1.通过MultipartFile filename的方式获取,filename与前端input域的name属性相同:
public void Upload(@RequestParam(value = "
filename", required = false)
MultipartFile file)
2.通过HttpServletRequest获取:
public void Upload(HttpServletRequest request,
HttpServletResponse response){
MultipartHttpServletRequest multipartRequest =
(MultipartHttpServletRequest) request;
MultipartFile file = multipartRequest.getFile(" file ");
}
使用request.getSession().getServletContext().getRealPath("/")
即可获取webroot根目录的物理路径。
SpringMVC中使用ajax技术可以方便地像后台POST数据,后台的处理代码如下:
@RequestMapping("/getPerson")
public void getPerson(String args,PrintWriter pw){
//args为前端post过来的参数
pw.write(data);
//data为返回的数据
}
注意:Firefox中默认的情况下把datatype用html来解析了,所以使用ajax或者post获得的返回data会报错object XMLDocument,只要设置一下datatype就可轻松解决了。
有时候服务端响应了中文,而ajax返回前端时变成了乱码,解决这个问题可以设置response如下:
response.setContentType("text/xml;charset=utf-8");
response.setHeader("Cache-Control", "no-cache");
PrintWriter pw=response.getWriter();
然后用pw输出返回值到ajax。
ajax的timeout仅对网络请求耗时生效,也就是说,当服务端接受到ajax请求后,自身的sleep或者别的耗时操作将与timeout无关。
使用Mybatis逆向生成器生成model,DAO,mapping。
使用注解为mapper声明新方法,如:
@Select("select * from user where email=#{email}")
User selectByEmail(String email);
在处理并发时,经常会出现这样的问题:Data source rejected establishment of connection, message from server: “Too many connections” 。数据库连接实在太多了,当然,你可以修改mysql的最大连接数max_connections=3600,默认情况下使用select VARIABLE_VALUE from information_schema.GLOBAL_VARIABLES where VARIABLE_NAME=’MAX_CONNECTIONS’;可以查看连接数应该是151,当然很容易爆掉。
但是,事实上在并发时我们并不需要这么多连接,过多的连接对于服务器也是一种不必要的负担,因为创建一个Connection的时间甚至超过了执行一句sql的时间,代价过于沉重,这样的情况下我们就应该考虑使用连接池来处理数据库连接,如DBCP,C3P0,BONECP,druid等。当然,mybatis自己也有实现连接池,在spring中配置Mybatis如下:
上面destroy-method=”close”的作用是当数据库连接不使用的时候,就把该连接重新放到数据池中,方便下次使用调用。
当然,建议还是使用第三方连接池,如阿里的druid来管理连接池,还有自带的监控界面可以使用。
连接池注意以下几个配置:
defaultAutoCommit:设置从数据源中返回的连接是否采用自动提交机制,默认值为 true;
defaultReadOnly:设置数据源是否仅能执行只读操作, 默认值为 false;
maxActive:最大连接数据库连接数,设置为0时,表示没有限制;
maxIdle:最大等待连接中的数量,设置为0时,表示没有限制;
maxWait:最大等待秒数,单位为毫秒, 超过时间会报出错误信息;
validationQuery:用于验证连接是否成功的查询SQL语句,SQL语句必须至少要返回一行数据, 如你可以简单地设置为:“select count(*) from user”;
removeAbandoned:是否自我中断,默认是 false ;
removeAbandonedTimeout:几秒后数据连接会自动断开,在removeAbandoned为true,提供该值;
logAbandoned:是否记录中断事件, 默认为 false;
Druid是一个阿里开发的JDBC组件,用来替换DBCP和C3P0。Druid提供了一个高效、功能强大、可扩展性好的数据库连接池。它可以监控数据库访问性能,Druid内置提供了一个功能强大的StatFilter插件,能够详细统计SQL的执行性能,这对于线上分析数据库访问性能有帮助。官方文档
mybatis提供了一级缓存和二级缓存支持,当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;如果都查不到,再查询数据库,把结果按照ID放入到缓存搜索;删除、更新、增加数据的时候,同时更新缓存。
MyBatis 的缓存采用了delegate机制 及 装饰器模式设计,当put、get、remove时,其中会经过多层 delegate cache 处理,其Cache类别有:BaseCache(基础缓存)、EvictionCache(排除算法缓存) 、DecoratorCache(装饰器缓存):
(1)BaseCache:为缓存数据最终存储的处理类,默认为 PerpetualCache,基于Map存储;可自定义存储处理,如基于EhCache、Memcached等;
(2)EvictionCache:当缓存数量达到一定大小后,将通过算法对缓存数据进行清除。默认采用 Lru 算法(LruCache),提供有 fifo 算法(FifoCache)等;
(3)DecoratorCache:缓存put/get处理前后的装饰器,如使用 LoggingCache 输出缓存命中日志信息、使用 SerializedCache 对 Cache的数据 put或get 进行序列化及反序列化处理、当设置flushInterval(默认1/h)后,则使用 ScheduledCache 对缓存数据进行定时刷新等。
一般缓存框架的数据结构基本上都是 Key-Value 方式存储,MyBatis 对于其 Key 的生成采取规则为:[hashcode : checksum : mappedStementId : offset : limit : executeSql : queryParams]。
对于并发 Read/Write 时缓存数据的同步问题,MyBatis 默认基于 JDK/concurrent中的ReadWriteLock,使用ReentrantReadWriteLock 的实现,从而通过 Lock 机制防止在并发 Write Cache 过程中线程安全问题。
执行过程:
一级缓存基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该Session中的所有 Cache 就将清空。一级缓存是默认开启的,即是基于同一个sqlsession 的查询语句,在同一个sqlsession下做两次一样的查询并不会执行两句同样的sql。
二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache、Hazelcast、Redis等。
同样二级缓存也是默认打开,全局开关默认是true,如果它配成false,其余各个Mapper XML文件配成支持cache也没用。
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
各个Mapper XML文件,默认是不采用cache。在配置文件加一行就可以支持cache:
<cache />
//type属性可以自定义cache来源
注意,此时对应的Model应该实现Serializable接口。若要不序列化,则需要这样声明:
<cache readOnly="true"></cache>
常用标签有:
eviction=”FIFO” <!--回收策略为先进先出-->
flushInterval=”60000” <!--自动刷新时间60s-->
size=”512” <!--最多缓存512个引用对象-->
此时再执行之前执行过的sql会得到
Cache Hit Ratio [com.dai.DAO.UserMapper]: 0.5
缓存命中的结果,因此将不再重复执行sql。
二级缓存的注意事项:
1. 只能在【只有单表操作】的表上使用缓存
不只是要保证这个表在整个系统中只有单表操作,而且和该表有关的全部操作必须全部在一个namespace下。
2. 在可以保证查询远远大于insert,update,delete操作的情况下使用缓存。
这一点不需要多说,所有人都应该清楚。记住,这一点需要保证在1的前提下才可以
二级缓存相关知识:
1. 缓存是以namespace为单位的,不同namespace下的操作互不影响。
2. insert,update,delete操作会清空所在namespace下的全部缓存。
3. 通常使用MyBatis Generator生成的代码中,都是各个表独立的,每个表都有自己的namespace。
通常情况下要避免使用二级缓存,原因如下:
假如某个Mapper下有另一个Mapper的操作,在另一个Mapper的namespace下刷新了缓存,而在这个Mapper的namespace下缓存依然有效,则数据出现了不一致性。如果此时该Mapper做了insert,update或delete时,将会出现未知的风险。
Java中使用Jedis.jar来连接和管理Redis。整合Redis时有可能报错: nested exception is java.lang.NoClassDefFoundError: org/apache/commons/pool2/impl/GenericObjectPool
这是因为缺少包:commons-pool的缘故。
可以使用一个连接池类来连接redis
/**
* @类描述 redis 工具
* @功能名 POJO
*/
public class RedisUtil {
private static JedisPool pool;
private static int DBIndex=3;
private static String host="127.0.0.1";
private static int port=6379;
private static int timeout=60*1000;
static {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxIdle(20);
config.setMaxWaitMillis((long)1000);
config.setTestOnBorrow(false);
pool = new JedisPool(config, host, port, timeout);//线程数量限制,IP地址,端口,超时时间
}
/**
* 注意:
* 作为一个key value存在,很多开发者自然的使用set/get方式来使用Redis,实际上这并不是最优化的使用方法。
* 尤其在未启用VM情况下,Redis全部数据需要放入内存,节约内存尤其重要。
假如一个key-value单元需要最小占用512字节,即使只存一个字节也占了512字节。
这时候就有一个设计模式,可以把key复用,几个key-value放入一个key中,value再作为一个set存入,
这样同样512字节就会存放10-100倍的容量。
用于存储多个key-value的值,比如可以存储好多的person Object
例子:>redis-cli
存储:redis 127.0.0.1:6379> hset personhash personId personObject
获得:redis 127.0.0.1:6379> hget personhash personId (就可以获得当前personId对应的person对象)
* @param key hashset key
* @param field 相当于personId
* @param value person Object
*/
public static void hsetItem(String key,String field,byte [] value){
Jedis jedis=null;
try {
jedis = pool.getResource();
jedis.connect();
jedis.select(DBIndex);
jedis.hset(key.getBytes(), field.getBytes(), value);
} catch (Exception e) {
e.printStackTrace();
}finally{
if(jedis!=null)
pool.returnResource(jedis);
}
}
public static byte [] hgetItem(String key,String field){
Jedis jedis=null;
byte [] value = null;
try {
jedis = pool.getResource();
jedis.connect();
jedis.select(DBIndex);
value= jedis.hget(key.getBytes(), field.getBytes());
//jedis.hgetAll(key);
} catch (Exception e) {
e.printStackTrace();
}finally{
if(jedis!=null)
pool.returnResource(jedis);
}
return value;
}
/**
* @param key
* @param value
* @param seconds 有效时间 秒为单位 0为永久有效
*/
public static void setItem(String key ,byte [] value,int seconds){
Jedis jedis=null;
try {
jedis = pool.getResource();
jedis.connect();
jedis.select(DBIndex);
if(seconds==0){
jedis.set(key.getBytes(), value);
}else{
jedis.setex(key.getBytes(), seconds, value);
}
} catch (Exception e) {
e.printStackTrace();
}finally{
if(jedis!=null)
pool.returnResource(jedis);
}
}
/**
* 删除
* @param keys
*/
public static void del(String... keys){
Jedis jedis=null;
if(keys!=null){
try {
jedis = pool.getResource();
jedis.connect();
jedis.select(DBIndex);
jedis.del(keys);
} catch (Exception e) {
e.printStackTrace();
}finally{
if(jedis!=null)
pool.returnResource(jedis);
}
}
}
/**
* 头部添加元素
* @param key
* @param value
*/
public static void lpushToList(String key,byte[] value){
Jedis jedis=null;
try {
jedis = pool.getResource();
jedis.connect();
jedis.select(DBIndex);
jedis.lpush(key.getBytes(), value);
} catch (Exception e) {
e.printStackTrace();
}
finally{
if(jedis!=null)
pool.returnResource(jedis);
}
}
/**
* 返回List
* @param key
* @param value
*/
public static List<byte[]> lrangeFromList(String key,int start ,int end){
Jedis jedis=null;
List<byte[]> list = null;
try {
jedis = pool.getResource();
jedis.connect();
jedis.select(DBIndex);
list = jedis.lrange(key.getBytes(), start, end);
} catch (Exception e) {
e.printStackTrace();
}
finally{
if(jedis!=null)
pool.returnResource(jedis);
}
return list;
}
/**
*
* @param key key
* @param member 存储的value
* @param score 排序字段 一般为objecId
*/
public static void addItemToSortSet(String key,byte[] member,double score){
Jedis jedis=null;
try {
jedis = pool.getResource();
jedis.connect();
jedis.select(DBIndex);
jedis.zadd(key.getBytes(), score, member);
} catch (Exception e) {
e.printStackTrace();
}finally{
if(jedis!=null)
pool.returnResource(jedis);
}
}
public static void addListToSortSet(String key,List<byte[]> list,List<Double> scores){
Jedis jedis=null;
try {
jedis = pool.getResource();
jedis.connect();
jedis.select(DBIndex);
if(list!=null&& !list.isEmpty()&& scores!=null&& !scores.isEmpty() && list.size()==scores.size()){
for(int i=0;i<list.size();i++){
jedis.zadd(key.getBytes(), scores.get(i), list.get(i));
}
}
} catch (Exception e) {
e.printStackTrace();
}finally{
if(jedis!=null)
pool.returnResource(jedis);
}
}
public static byte[] getItem(String key)
{
Jedis jedis=null;
byte[] s=null;
try {
jedis = pool.getResource();
jedis.select(DBIndex);
s = jedis.get(key.getBytes());
return s;
} catch (Exception e) {
e.printStackTrace();
return s;
}
finally{
if(jedis!=null)
pool.returnResource(jedis);
}
}
public static void delItem(String key)
{
Jedis jedis=null;
try {
jedis = pool.getResource();
jedis.select(DBIndex);
jedis.del(key.getBytes());
} catch (Exception e) {
e.printStackTrace();
}
finally{
if(jedis!=null)
pool.returnResource(jedis);
}
}
public static long getIncrement(String key)
{
Jedis jedis=null;
try {
jedis = pool.getResource();
jedis.select(DBIndex);
return jedis.incr(key);
} catch (Exception e) {
e.printStackTrace();
return 0L;
}
finally{
if(jedis!=null)
pool.returnResource(jedis);
}
}
public static void getkeys(String pattern){
Jedis jedis=null;
try {
jedis = pool.getResource();
jedis.select(DBIndex);
Set<String> keys = jedis.keys(pattern);
for(String b:keys){
System.out.println("keys==> "+b);
}
} catch (Exception e) {
e.printStackTrace();
}
finally{
if(jedis!=null)
pool.returnResource(jedis);
}
}
public static String getkey(String key){
Jedis jedis=null;
try {
jedis = pool.getResource();
jedis.select(DBIndex);
return jedis.get(key);
} catch (Exception e) {
e.printStackTrace();
}
finally{
if(jedis!=null)
pool.returnResource(jedis);
}
return null;
}
public static void setkey(String key,String value){
Jedis jedis=null;
try {
jedis = pool.getResource();
jedis.select(DBIndex);
jedis.set(key,value);
} catch (Exception e) {
e.printStackTrace();
}
finally{
if(jedis!=null)
pool.returnResource(jedis);
}
}
}
将导出的war包放到tomcat的webapps目录下,运行tomcat就会自动解包并代理运行。
用myeclipse对project使用export,选择Java EE下的WAR file,然后选择导出位置并保存。
* 有时myeclipse 10 导出war包时会出现这样的错误“security alert:integrity check error”
这是破解的问题,需要替换MyEclipse\Common\plugins下的com.genuitec.eclipse.export.wizard_9.0.0.me201211011550.jar包为未破解的原生jar包即可。
在eclipse中,选择项目的pom.xml文件,点击右键菜单中的Run As,然后选择Maven build…选项,在弹出的对话框中的Goals中输入:dependency:copy-dependencies
之后Maven所依赖的jar包会导出到target/dependency中。
由于SpringMVC的controller并非线程安全的,所以使用单纯的controller + DAO处理并发,经测试是无法保证线程安全的。
正确的处理并发的姿势如下:
(1)在内存中,可以使用Collections.synchronized集合
public static List<Integer> list = Collections.synchronizedList(new ArrayList<Integer>());
public void initList() {
list.add(100);
}
public Integer getList(){
return list.get(0);
}
public void subList(){
list.set(0, list.get(0)-1);
}
这样并发访问时,不会出现同步错误,使用同步锁使变量变得进程安全。
(2)在@Controller之前增加@Scope(“prototype”),就可以改变单例模式为原生模式。(经测试,我觉得无效)
(3)使用ThreadLocal
实践证明,因为redis的原子性,使用redis+synchronized关键字可以保证原子性和可见性,使数据达到线程安全的效果。
设计一个接口,然后装饰类实现后,其他被装饰类将装饰类传入构造函数,在实现接口的函数中调用装饰类的方法,即达到了装饰的目的。
标签:
原文地址:http://blog.csdn.net/u012689961/article/details/51131911