加载 properties 文件
在 spring 配置文件中先引入 xmlns:context,在下面添加
如果需要记载多个配置文件逗号分割
< context: property-placeholder
location = " classpath:db.properties" />
在被Spring管理的类中通过@Value(“${key}”)
取出properties中内容
单例设计模式
作用: 在应用程序有保证最多只能有一个实例 实现数据共享. 案例:application 对象
scope 可取值 4.1 singleton 默认值,单例 4.2 prototype 多例,每次获取重新实例化 4.3 request 每次请求重新实例化 4.4 session 每个会话对象内,对象是单例的. 4.5 application 在 application 对象内是单例 4.6 global session spring 推 出 的 一 个 对 象 , 依 赖 于 spring-webmvc-portlet ,类似于 session
声明式事务
1.编程式事务: 1.1 由程序员编程事务控制代码. 1.2 OpenSessionInView 编程式事务
2.声明式事务: 2.1 事务控制代码已经由 spring 写好.程序员只需要声明出哪些方法需要进行事务控制和如何进行事务控制. 3.声明式事务都是针对于 ServiceImpl 类下方法的. 4.事务管理器基于通知(advice)的. 5.在 spring 配置文件中配置声明式事务
< context: property-placeholder
location = " classpath:db.properties,classpath:second.pr
operties" />
< bean id = " dataSource" class = " org.springframework.jdbc.datasource.DriverManagerDataSource" >
< property name = " driverClassName"
value = " ${jdbc.driver}" > property>
< property name = " url"
value = " ${jdbc.url}" > property>
< property name = " username"
value = " ${jdbc.username}" > property>
< property name = " password"
value = " ${jdbc.password}" > property>
bean>
< bean id = " txManager"
class = " org.springframework.jdbc.datasource.DataSour
ceTransactionManager" >
< property name = " dataSource"
ref = " dataSource" > property>
bean>
< tx: advice id = " txAdvice" transaction-manager = " txManager" >
< tx: attributes>
< tx: method name = " ins*" />
< tx: method name = " del*" />
< tx: method name = " upd*" />
< tx: method name = " *" />
tx: attributes>
tx: advice>
< aop: config>
< aop: pointcut expression = " execution(*
com.bjsxt.service.impl.*.*(..))"
id = " mypoint" />
< aop: advisor advice-ref = " txAdvice"
pointcut-ref = " mypoint" />
aop: config>
SpringMVC
简介
重要组件
DispatcherServlet : 前端控制器,接收所有请求(如果配置/不包 含 jsp)
HandlerMapping: 解析请求格式的.判断希望要执行哪个具体 的方法
HandlerAdapter: 负责调用具体的方法
ViewResovler:视图解析器.解析结果,准备跳转到具体的物理视 图
运行原理
如果在 web.xml 中设置 DispatcherServlet 的为/时,当用户发 起 请 求 , 请 求 一 个 控 制 器 , 首 先 会 执 行 DispatcherServlet由DispatcherServlet调用HandlerMapping的DefaultAnnotationHandlerMapping 解 析 URL, 解 析 后 调 用 HandlerAdatper 组 件 的 AnnotationMethodHandlerAdapter 调 用 Controller 中的 HandlerMethod. 当 HandlerMethod 执行完成后会返回 View,会被 ViewResovler 进行视图解析, 解析后调用 jsp 对应的.class 文 件并运行, 最终把运行.class 文件的结果响应给客户端. 以上就是 springmvc 运行原理(给面试官说的
Spring 容器和 SpringMVC 容器是父子容器
字符编码过滤器
在 web.xml 中配置 Filter
tomcat一启动就被实例化,等待回调
< filter>
< filter-name> encoding filter-name>
< filter-class> org.springframework.web.filter.CharacterEncodingFilter filter-class>
< init-param>
< param-name> encoding param-name>
< param-value> utf-8 param-value>
init-param>
filter>
< filter-mapping>
< filter-name> encoding filter-name>
< url-pattern> /* url-pattern>
filter-mapping>
视图解析器
SpringMVC 会提供默认视图解析器
自定义视图解析器
< bean id = " viewResolver"
class = " org.springframework.web.servlet.view.InternalR
esourceViewResolver" >
< property name = " prefix" value = " /" > property>
< property name = " suffix" value = " .jsp" > property>
bean>
如果希望不执行自定义视图解析器,在方法返回值前面添加 forward:或 redirect:
@ResponseBody
在方法上添加@ResponseBody(恒不跳转)
2.1 如果返回值满足 key-value 形式(对象或 map) 2.1.1 把响应头设置为 application/json;charset=utf-8 2.1.2 把转换后的内容输出流的形式响应给客户端.
2.2 如果返回值不满足 key-value,例如返回值为 String 2.2.1 把相应头设置为 text/html 2.2.2 把方法返回值以流的形式直接输出. 2.2.3 如果返回值包含中文,出现中文乱码 2.2.3.1 produces 表示响应头中 Content-Type 取值.
@RequestMapping ( value= "demo12" , produces= "text/ html;
charset= utf- 8 ") @ResponseBody
public String demo12 ( ) throws IOException {
People p = new People ( ) ;
p. setAge ( 12 ) ;
p. setName ( "张三" ) ;
return "中文" ;
}
底层使用 Jackson 进行 json 转换,在项目中一定要导入 jackson 的 jar 3.1 spring4.1.6 对 jackson 不支持较高版本,jackson 2.7 无效
环境搭建
< servlet>
< servlet-name> jqk servlet-name>
< servlet-class> org.springframework.web.servlet.DispatcherServlet<
SpringMVC 容器/servlet-class>
< init-param>
< param-name> contextConfigLocation param-name>
< param-value> classpath:springmvc.xml param-value>
init-param>
< load-on-startup> 1 load-on-startup>
servlet>
< servlet-mapping>
< servlet-name> jqk servlet-name>
< url-pattern> / url-pattern>
servlet-mapping>
在 src 下新建 springmvc.xml
引入 xmlns:mvc 命名空间
< beans xmlns = " http://www.springframework.org/schema/beans"
xmlns: xsi= " http://www.w3.org/2001/XMLSchema-instance"
xmlns: mvc= " http://www.springframework.org/schema/mvc"
xmlns: context= " http://www.springframework.org/schema/context"
xsi: schemaLocation= " http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd" >
< context: component-scan
base-package = " com.bjsxt.controller" > context: component-scan>
< mvc: annotation-driven> mvc: annotation-driven>
< mvc: resources location = " /js/" mapping = " /js/**" > mvc: resources>
< mvc: resources location = " /css/" mapping = " /css/**" > mvc: resources>
< mvc: resources location = " /images/"
mapping = " /images/**" > mvc: resources>
beans>
@Controller
public class DemoController {
@RequestMapping ( "demo" )
public String demo ( ) {
System . out. println ( "执行 demo" ) ;
return "main.jsp" ;
}
@RequestMapping ( "demo2" )
public String demo2 ( ) {
System . out. println ( "demo2" ) ;
return "main1.jsp" ;
}
}
传参
基本数据类型参数
默认保证参数名称和请求中传递的参数名相同
@Controller
public class DemoController {
@RequestMapping ( "demo" )
public String demo ( String name, int age) {
System . out. println ( "执行 demo" + " " + name+ "
"+ age) ;
return "main.jsp" ;
}
}
还可以获取httpservletrequestreq参数 req.setAttribute(“demo123”,“测试”); 前端就可以${demo123}获取到值
如果请求参数名和方法参数名不对应使用@RequestParam()赋 值
@RequestMapping ( "demo" )
public String demo ( @RequestParam ( value= "name1" )
String name, @RequestParam ( value= "age1" ) int age) {
System . out. println ( "执行 demo" + " " + name+ "
"+ age) ;
return "main.jsp" ;
}
如果方法参数是基本数据类型(不是封装类)可以通过 @RequestParam 设置默认值. 2.3.1 防止没有参数时 500
@RequestMapping ( "page" )
public String page ( @RequestParam ( defaultValue= "2" )
int pageSize, @RequestParam ( defaultValue= "1" ) int
pageNumber) {
System . out. println ( pageSize+ " " + pageNumber) ;
return "main.jsp" ;
}
如果强制要求必须有某个参数
@RequestMapping ( "demo2" )
public String demo2 ( @RequestParam ( required= true )
String name) {
System . out. println ( "name 是 SQL 的查询条件, 必须要传
递 name 参数"+ name) ;
return "main.jsp" ;
}
< a href = " demo8/123/abc" > 跳转 a>
7.3 在控制器中 7.3.1 在@RequestMapping 中一定要和请求格式对应 7.3.2 {名称} 中名称自定义名称 7.3.3 @PathVariable 获取@RequestMapping 中内容,默认按照 方法参数名称去寻找.
@RequestMapping ( "demo8/{id1}/{name}" )
public String demo8 ( @PathVariable String
name, @PathVariable ( "id1" ) int age) {
System . out. println ( name + "
"+ age) ;
return "/main.jsp" ;
}
把内容写到方法(HandlerMethod)参数中,SpringMVC 只要有这个内 容,注入内容
HandlerMethod 中参数是对象类型
请求参数名和对象中属性名对应(get/set 方法)
@RequestMapping ( "demo4" )
public String demo4 ( People peo) {
return "main.jsp" ;
}
请求参数中包含多个同名参数的获取方式
复选框传递的参数就是多个同名参数
@RequestMapping ( "demo5" )
public String demo5 ( String name, int
age, @RequestParam ( "hover" ) List < String > abc) {
System . out. println ( name+ " " + age+ " " + abc) ;
return "main.jsp" ;
}
< input type = " text" name = " peo.name" />
< input type = " text" name = " peo.age" />
新建一个类 :对象名和参数中点前面名称对应
public class Demo {
private People peo;
控制器
@RequestMapping ( "demo6" )
public String demo6 ( Demo demo) {
System . out. println ( demo) ;
return "main.jsp" ;
}
< input type = " text" name = " peo[0].name" />
< input type = " text" name = " peo[0].age" />
< input type = " text" name = " peo[1].name" />
< input type = " text" name = " peo[1].age" />
新建类
public class Demo { private List < People > peo;
控制器
@RequestMapping ( "demo6" )
public String demo6 ( Demo demo) {
System . out. println ( demo) ;
return "main.jsp" ;
}
作用域传值
@RequestMapping ( "demo1" )
public String demo1 ( HttpServletRequest
abc, HttpSession sessionParam) {
abc. setAttribute ( "req" , "req 的值" ) ;
HttpSession session = abc. getSession ( ) ;
session. setAttribute ( "session" , "session 的值" ) ;
sessionParam. setAttribute ( "sessionParam" ,
"sessionParam 的值" ) ;
ServletContext application =
abc. getServletContext ( ) ;
application. setAttribute ( "application" , "application 的值" ) ;
return "/index.jsp" ;
}
@RequestMapping ( "demo2" )
public String demo2 ( Map < String , Object > map) {
System . out. println ( map. getClass ( ) ) ;
map. put ( "map" , "map 的值" ) ;
return "/index.jsp" ;
}
使用 SpringMVC 中 Model 接口
把内容最终放入到 request 作用域中.
@RequestMapping ( "demo3" )
public String demo3 ( Model model) {
model. addAttribute ( "model" , "model 的值" ) ;
return "/index.jsp" ;
}
使用 SpringMVC 中 ModelAndView 类
@RequestMapping ( "demo4" ) public ModelAndView demo4 ( ) {
ModelAndView mav = new ModelAndView ( "/index.jsp" ) ;
mav. addObject ( "mav" , "mav 的值" ) ;
return mav;
}
文件下载
访问资源时相应头如果没有设置 Content-Disposition,浏览器默认按照 inline 值进行处理。inline 能显示就显示,不能显示就下载.
只需要修改相应头中 Context-Disposition=”attachment;filename=文件名” 2.1 attachment 下载,以附件形式下载. 2.2 filename=值就是下载时显示的下载文件名
@RequestMapping ( "download" )
public void download ( String
fileName, HttpServletResponse res, HttpServletRequest
req) throws IOException {
res. setHeader ( "Content-Disposition" ,
"attachment;filename=" + fileName) ;
ServletOutputStream os = res. getOutputStream ( ) ;
String path =
req. getServletContext ( ) . getRealPath ( "files" ) ;
System . out. println ( path) ;
File file = new File ( path, fileName) ;
byte [ ] bytes =
FileUtils . readFileToByteArray ( file) ;
os. write ( bytes) ;
os. flush ( ) ;
os. close ( ) ;
}
文件上传
基于 apache 的 commons-fileupload.jar 完成文件上传.
MultipartResovler 作用: (springMVC的组件) 2.1 把客户端上传的文件流转换成 MutipartFile 封装类. 2.2 通过 MutipartFile 封装类获取到文件流
表单数据类型分类 3.1 在的 enctype 属性控制表单类型 3.2 默认值 application/x-www-form-urlencoded,普通表单数据.(少 量文字信息) 3.3 text/plain 大文字量时使用的类型.邮件,论文 3.4 multipart/form-data 表单中包含二进制文件内容(这样表单才能上传文件流)
< form action = " upload" enctype = " multipart/form-data"
method = " post" >
姓名:< input type = " text" name = " name" /> < br/>
文件:< input type = " file" name = " file" /> < br/>
< input type = " submit" value = " 提交" />
form>
< bean id = " multipartResolver"
class = " org.springframework.web.multipart.commons.Comm
onsMultipartResolver" >
< property name = " maxUploadSize"
value = " 50" > property>
bean>
< bean id = " exceptionResolver"
class = " org.springframework.web.servlet.handler.Simple
MappingExceptionResolver" >
< property name = " exceptionMappings" >
< props>
< prop
key = " org.springframework.web.multipart.MaxUploadSizeE
xceededException" > /error.jsp prop>
props>
property>
bean>
@RequestMapping ( "upload" )
public String upload ( MultipartFile file, String name)
throws IOException {
String fileName = file. getOriginalFilename ( ) ;
String suffix =
fileName. substring ( fileName. lastIndexOf ( "." ) ) ;
if ( suffix. equalsIgnoreCase ( ".png" ) ) {
String uuid = UUID. randomUUID ( ) . toString ( ) ;
FileUtils . copyInputStreamToFile ( file. getInputStream
( ) , new File ( "E:/" + uuid+ suffix) ) ;
return "/index.jsp" ;
} else {
return "error.jsp" ;
}
}
自定义拦截器
1.跟过滤器比较像的技术.
2.发送请求时被拦截器拦截,在控制器的前后添加额外功能. 2.1 跟 AOP 区分开.AOP 在特定方法前后扩充(对 ServiceImpl) 2.2 拦截器,请求的拦截.针对点是控制器方法.(对 Controller)
3.SpringMVC 拦截器和 Filter 的区别 3.1 拦截器只能拦截器 Controller 3.2 Filter 可以拦截任何请求.
实现步骤:1. 新建类实现 HandlerInterceptor
public class DemoInterceptor implements
HandlerInterceptor {
@Overridepublic boolean preHandle ( HttpServletRequest arg0,
HttpServletResponse arg1, Object arg2) throws Exception
{
System . out. println ( "arg2:" + arg2) ;
System . out. println ( "preHandle" ) ;
return true ;
}
@Override
public void postHandle ( HttpServletRequest arg0,
HttpServletResponse arg1, Object arg2, ModelAndView
arg3)
throws Exception {
System . out. println ( "往" + arg3. getViewName ( ) + "跳转
") ;
System . out. println ( "model 的值
"+arg3.getModel().get(" model") ) ;
String word =
arg3. getModel ( ) . get ( "model" ) . toString ( ) ; String newWord = word. replace ( "祖国" , "**" ) ;
arg3. getModel ( ) . put ( "model" , newWord) ;
arg3. getModel ( ) . put ( "model" , "修改后的内容" ) ;
System . out. println ( "postHandle" ) ;
}
@Override
public void afterCompletion ( HttpServletRequest arg0,
HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
System . out. println ( "afterCompletion" + arg3. getMessag
e ( ) ) ;
}
}
在 springmvc.xml 配置拦截器需要拦截哪些控制器
拦截所有控制器
< mvc: interceptors>
< bean
class = " com.bjsxt.interceptor.DemoInterceptor" > bean> mvc: interceptors>
拦截特定的的 url
< mvc: interceptors>
< mvc: interceptor>
< mvc: mapping path = " /demo" />
< mvc: mapping path = " /demo1" />
< mvc: mapping path = " /demo2" />
< bean
class = " com.bjsxt.interceptor.DemoInterceptor" > bean>
mvc: interceptor>
mvc: interceptors>
拦截器栈
多个拦截器同时生效时,组成了拦截器栈
顺序:先进后出.
执行顺序和在 springmvc.xml 中配置顺序有关
设置先配置拦截器 A 在配置拦截器 B 执行顺序为 preHandle(A) --> preHandle(B) --> 控制器方法 --> postHandle(B) --> postHanle(A) --> JSP --> afterCompletion(B) --> afterCompletion(A)
SpringMVC 对 Date 类型转换
< mvc: annotation-driven
conversion-service = " conversionService" > mvc: annotati
on-driven > < bean id = " conversionService"
class = " org.springframework.format.support.Formattin
gConversionServiceFactoryBean" >
< property name = " registerDefaultFormatters"
value = " false" />
< property name = " formatters" >
< set>
< bean
class = " org.springframework.format.number.NumberForm
atAnnotationFormatterFactory" />
set>
property>
< property name = " formatterRegistrars" >
< set>
< bean
class = " org.springframework.format.datetime.joda.Jod
aTimeFormatterRegistrar" > < property name = " dateFormatter" >
< bean
class = " org.springframework.format.datetime.joda.Dat
eTimeFormatterFactoryBean" >
< property name = " pattern"
value = " yyyy-MM-dd" />
bean>
property>
bean>
set>
property>
bean>
@RequestMapping ( "demo" )
public String
demo ( @DateTimeFormat ( pattern= "yyyy-MM-dd" ) Date time) {
System . out. println ( time) ;
return "abc.jsp" ;
}
2.2 在实体类中
@RequestMapping ( "demo" )
public String demo ( Demo1 demo) {
System . out. println ( demo) ;
return "abc.jsp" ;
}
public class Demo1 {
@DateTimeFormat ( pattern= "yyyy/MM/dd" )
private Date time;
2.3 注意地方: 2.3.1 不需要导入额外 jar 2.3.2 Date 是 java.util.Date
jFinal
其他
RBAC
基于角色的访问控制
一种思想.根据 RBAC 思想进行数据库设计,根据数据库设计更 好的完成权限控制
权限控制常用分类: 4.1 菜单功能 4.2 url 控制(控制访问不同的控制器.) 4.3 资源可见性控制(页面某些元素对不同用户可见性是不同的 )
JUnit
JUnit来源 测试------- 黑盒 白盒,单元测试需要编码实现《单元测试之道》JUnit JUnit使用-------- 导入jar @Test 断言Assert.assertEquals(期望值,实际值)
shiro
难点在于有很多名词
Shiro 不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过 相应的接口注入给 Shiro 即可。
认证、 授权、加密、会话管理、与 Web 集成、缓存等
shiro的结构体系
Shiro 架构
Subject
需要进行认证和身份授权的用户或第三方程序,用于获取主体信息
Subject 即主体,外部应用与 subject 进行交互,subject 记录了当前操作用户,将用户的 概念理解为当前操作的主体,可能是一个通过浏览器请求的用户,也可能是一个运行的程序 。 Subject 在 shiro 中是一个接口,接口中定义了很多认证授相关的方法,外部程序通过 subject 进行认证授,而 subject 是通过 SecurityManager 安全管理器进行认证授权.
SecurityManager
SecurityManager 即安全管理器,对全部的 subject 进行安全管理,它是 shiro 的核心 , 负责对所有的 subject 进行安全管理。通过 SecurityManager 可以完成 subject 的认证、授权 等,实质上 SecurityManager 是通过 Authenticator 进行认证,通过 Authorizer 进行授权,通 过 SessionManager 进行会话管理等。 SecurityManager 是一个接口,继承了 Authenticator, Authorizer, SessionManager 这三个接 口
Authenticator
Authenticator 即认证器 ,对用户身份进行认证,Authenticator 是一个接口,shiro 提供 ModularRealmAuthenticator 实现类,通过 ModularRealmAuthenticator 基本上可以满足大多数需求,也可以自定义认证器
Authorizer
Authorizer 即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用 户是否有此功能的操作权限
realm
Realm 即领域 ,相当于 datasource 数据源, securityManager 进行安全认证需要通过 Realm 获取用户权限数据,比如:如果用户身份数据在数据库那么 realm 就需要从数据库获取用户 身份信息。
注意:不要把 realm 理解成只是从数据源取数据,在 realm 中还有认证授权校验的相关 的代码。
其他
1.4.6 sessionManager sessionManager 即会话管理,shiro 框架定义了一套会话管理,它不依赖 web 容器的 session, 所以 shiro 可以使用在非 web 应用上,也可以将分布式应用的会话集中在一点管理,此特性 可使它实现 单点登录 。 1.4.7 SessionDAO SessionDAO 即会话 dao,是对 session 会话操作的一套接口,比如要将 session 存储到数据库,可以通过 jdbc 将会话存储到数据库。 1.4.8 CacheManager CacheManager 即缓存管理,将用户权限数据存储在缓存,这样可以提高性能。 1.4.9 Cryptography Cryptography 即密码管理,shiro 提供了一套加密/解密的组件,方便开发。比如提供常 用的散列、加 / 解密等功能。
认证
在应用中谁能证明他就是他本人。一般提供如他们的身份 ID 一些标识信息来 表明他就是他本人,如提供身份证,用户名/密码来证明。 在 shiro 中,用户需要提供 principals (身份)和 credentials(证明)给 shiro,从而应用能 验证用户身份
principals
主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一 即可
credentials
证明/凭证,只有主体知道的安全值 ,如密码/数字证书等
环境搭建
加入相关 jar 包
log4j.properties 日志配置文件
log4j.rootLogger=debug, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n
[users]
zhangsan=1111
lisi=1111
写数据库里也行
@Test
public void testAuthenticator ( ) {
Factory < SecurityManager > factory = new
IniSecurityManagerFactory ( "classpath:shiro.ini" ) ;
SecurityUtils . setSecurityManager ( securityManager) ;
Subject subject = SecurityUtils . getSubject ( ) ;
UsernamePasswordToken token = new
UsernamePasswordToken ( "zhangsan" , "1111" ) ;
try {
subject. login ( token) ;
} catch ( AuthenticationException e) {
e. printStackTrace ( ) ;
}
Assert . assertEquals ( true , subject. isAuthenticated ( ) ) ;
subject. logout ( ) ;
}
自定义 Realm
Shiro 默认使用自带的 IniRealm,IniRealm 从 ini 配置文件中读取用户的信息,大部分情 况下需要从系统的数据库中读取用户信息,所以需要自定义 realm。
jdbcRealm实现从数据库获取用户的验证信息,但不够灵活。 最基础的是 Realm 接口(定义了根据token获取认证信息的方法),CachingRealm 负责缓存处理,AuthenticationRealm 负责认证, AuthorizingRealm 负责授权,通常自定义的 realm 继承 AuthorizingRealm。(既身份认证又授权)
public class UserRealm extends AuthorizingRealm {
@Override
public String getName ( ) {
return "UserRealm" ;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo (
AuthenticationToken token) throws
AuthenticationException {
String username = ( String ) token. getPrincipal ( ) ;
String password = "1111" ;
SimpleAuthenticationInfo simpleAuthenticationInfo = new
SimpleAuthenticationInfo ( username, password, this . getName ( ) ) ;
return simpleAuthenticationInfo;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo (
PrincipalCollection principals) {
return null ;
}
}
[main]
#自定义 realmuserRealm=cn.siggy.realm.UserRealm
#将 realm 设置到 securityManager
securityManager.realms=$userRealm
散列算法
散列算法一般用于生成数据的摘要信息,是一种不可逆的算法,一般适合存储密码之类 的数据,常见的散列算法如 MD5、SHA 等 。一般进行散列时最好提供一个 salt(盐) ,比如 加密密码“admin”,产生的散列值是“21232f297a57a5a743894a0e4a801fc3”,可以到一 些 md5 解密网站很容易的通过散列值得到密码“admin”,即如果直接对密码进行散列相对来说破解更容易,此时我们可以加一些只有系统知道的干扰数据 ,如用户名和 ID(即盐);这样散列的对象是“密码+用户名+ID”,这样生成的散列值相对来说更难破解
public class ShiroTest {
@Test
public void testMd5 ( ) {
String password = new Md5Hash ( "1111" ) . toString ( ) ;
System . out. println ( "加密后:" + password) ;
String password_salt= new Md5Hash ( "1111" ,
"siggy" ) . toString ( ) ;
System . out. println ( "加盐后:" + password_salt) ;
String password_salt_2 = new Md5Hash ( "1111" , "siggy" ,
2 ) . toString ( ) ;
System . out. println ( "散列 2 次:" + password_salt_2) ;
System . out. println ( "simpleHash:" + hash. toString ( ) ) ;
}
}
public class UserRealm extends AuthorizingRealm {
@Override
public String getName ( ) {
return "UserRealm" ;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo (
AuthenticationToken token) throws
AuthenticationException {
String username = ( String ) token. getPrincipal ( ) ;
1111 ,盐是 siggy 2 次散列
String password = "1620d20433da92e2523928e351e90f97" ;
如从数据库中获取密码为 1111
SimpleAuthenticationInfo simpleAuthenticationInfo = new
SimpleAuthenticationInfo ( username,
password,
ByteSource. Util . bytes ( "siggy" ) , this . getName ( ) ) ;
return simpleAuthenticationInfo;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo (
PrincipalCollection principals) {
return null ;
}
}
[main] 配置应用程序的securitymanager实例及任何它的以来组件(如realms)
[ main]
#定义凭证匹配器
credentialsMatcher= org. apache. shiro. authc. credential. HashedCredentialsMatcher
#散列算法
credentialsMatcher. hashAlgorithmName= md5
#散列次数
credentialsMatcher. hashIterations= 2
#将凭证匹配器设置到 realm
userRealm= cn. siggy. realm. UserRealm
userRealm. credentialsMatcher= $credentialsMatcher
securityManager. realms= $userRealm
[users]定义一组静态用户,角色 [roles]定义在[users]中的角色与权限关联起来
授权
也叫访问控制,即在应用中控制谁能访问哪些资源(如访问页面/编辑数据/页面 操作等)。在授权中需了解的几个关键对象:主体(Subject)、资源(Resource)、权限(Permission)、角色(Role)
关键对象
主体:即访问应用的用户,在 Shiro 中使用 Subject 代表该用户。用户只有授权后才允许访 问相应的资源。
资源:在应用中用户可以访问的任何东西,比如访问 JSP 页面、查看/编辑某些数据、访问某个业 务方法、打印文本等等都是资源。用户只要授权后才能访问。 权限安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源,如:访问用户列表页面查看/新增/修 改/删除用户数据(即很多时候都是 CRUD(增查改删)式权限控制)打印文档等等。。。
角色:代表了操作集合,可以理解为权限的集合,一般情况下我们会赋予用户角色而不是权 限,即这样用户可以拥有一组权限,赋予权限时比较方便。典型的如:项目经理、技术总监、CTO、开发工程师等都是角色,不同的角色拥有一组不同的权限。
权限粒度
粗粒度:对表的操纵,如对user的crud 细粒度:对记录的操作,如查询id=1的user的工资。 shiro一般管理的是粗粒度的权限。如菜单,按钮,url 细粒度权限一般是通过业务来控制的
权限表示规则: 资源:操作:实例,可以用通配符 user:add user:delete:100 对user标识为100的记录有删除权限
授权流程
流程如下: 1、首先调用 Subject.isPermitted*/hasRole接口,其会委托给 SecurityManager,而 SecurityManager 接着会委托给 Authorizer; 2、Authorizer 是真正的授权者,如果我们调用如 isPermitted(“user:view”),其首先会通过 PermissionResolver 把字符串转换成相应的 Permission 实例; 3、在进行授权之前,其会调用相应的 Realm 获取 Subject 相应的角色/权限用于匹配传入的 角色/权限; 4、Authorizer 会判断 Realm 的角色/权限是否和传入的匹配,如果有多个 Realm,会委托给 ModularRealmAuthorizer 进行循环判断,如果匹配如 isPermitted /hasRole*会返回 true,否 则返回 false 表示授权失败。
授权方式
Shiro 支持三种方式的授权:
编程式:通过写 if/else 授权代码块完成:
Subject subject = SecurityUtils . getSubject ( ) ; if ( subject. hasRole ( “
admin”) ) {
} else {
}
注解式:通过在执行的 Java 方法上放置相应的注解完成:
@RequiresRoles ( "admin" )
public void hello ( ) {
}
没有权限将抛出相应的异常;
JSP/GSP 标签:在 JSP/GSP 页面通过相应的标签完成:
< shiro: hasRole name = " admin" >
< !— 有权限— >
shiro: hasRole>
授权实现
在 ini 配置文件配置用户拥有的角色及角色-权限关系 (shiro-permission.ini)
[ users]
zhangsan= 1111 , role1, role2
lisi= 1111 , role1
[ roles]
role1= user: create, user: update
role2= user: create, user: delete
public class ShiroTest {
@Test
public void testPermission ( ) {
从 ini 文件中初始化 SecurityManager 环境
Factory < SecurityManager > factory = new
IniSecurityManagerFactory ( "classpath:shiro-permission.ini" ) ;
SecurityManager securityManager = factory. getInstance ( ) ;
SecurityUtils . setSecurityManager ( securityManager) ;
Subject subject = SecurityUtils . getSubject ( ) ;
UsernamePasswordToken token = new
UsernamePasswordToken ( "zhangsan" , "1111" ) ;
try {
subject. login ( token) ;
} catch ( AuthenticationException e) {
e. printStackTrace ( ) ;
}
boolean isAuthenticated = subject. isAuthenticated ( ) ;
System . out. println ( "用户认证状态:" + isAuthenticated) ;
Assert . assertTrue ( subject. hasRole ( "role1" ) ) ;
Assert . assertTrue ( subject. hasAllRoles ( Arrays . asList ( "role1"
, "role2" ) ) ) ;
"role2" , "role3" ) ) ;
Assert . assertEquals ( true , result[ 0 ] ) ;
Assert . assertEquals ( true , result[ 1 ] ) ;
Assert . assertEquals ( false , result[ 2 ] ) ;
Assert . assertTrue ( subject. isPermitted ( "user:create" ) ) ;
Assert . assertTrue ( subject. isPermittedAll ( "user:update" ,
"user:delete" ) ) ;
Assert . assertFalse ( subject. isPermitted ( "user:view" ) ) ;
}
}
与上边认证自定义 realm 一样,大部分情况是要从数据库获取权限数据,这里直接实现 基于资源的授权。
public class UserRealm extends AuthorizingRealm {
@Override
public String getName ( ) {
return "UserRealm" ;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo (
AuthenticationToken token) throws
AuthenticationException {
String password = "1111" ;
SimpleAuthenticationInfo simpleAuthenticationInfo = new
SimpleAuthenticationInfo ( username, password, this . getName ( ) ) ;
return simpleAuthenticationInfo;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo (
PrincipalCollection principals) {
String username =
( String ) principals. getPrimaryPrincipal ( ) ;
List < String > permissions = new ArrayList < String > ( ) ;
permissions. add ( "user:save" ) ;
permissions. add ( "user:delete" ) ;
SimpleAuthorizationInfo simpleAuthorizationInfo = new
SimpleAuthorizationInfo ( ) ;
for ( String permission: permissions) {
simpleAuthorizationInfo. addStringPermission ( permission) ;
}
return simpleAuthorizationInfo;
}
}
main]
#自定义 realm
userRealm= cn. siggy. realm. UserRealm
#将 realm 设置到 securityManager
securityManager. realms= $userRealm
public class UserRealm extends AuthorizingRealm {
@Override
public String getName ( ) {
return "UserRealm" ;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo (
AuthenticationToken token) throws
AuthenticationException {
String username = ( String ) token. getPrincipal ( ) ;
String password = "1111" ;
SimpleAuthenticationInfo simpleAuthenticationInfo = new
SimpleAuthenticationInfo ( username, password, this . getName ( ) ) ;
return simpleAuthenticationInfo;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo (
PrincipalCollection principals) {
String username =
( String ) principals. getPrimaryPrincipal ( ) ;
List < String > permissions = new ArrayList < String > ( ) ;
permissions. add ( "user:save" ) ;
permissions. add ( "user:update" ) ;
permissions. add ( "user:delete" ) ;
SimpleAuthorizationInfo simpleAuthorizationInfo = new
SimpleAuthorizationInfo ( ) ;
for ( String permission: permissions) {
simpleAuthorizationInfo. addStringPermission ( permission) ;
} return simpleAuthorizationInfo;
}
}
shiro 与项目集成开发
完成 springmvc+spring+mybatis 整合 整合 shiro
< filter>
< filter-name> shiroFilter filter-name>
< filter-class> org.springframework.web.filter.DelegatingFilterProxy
filter-class>
< init-param>
< param-name> targetFilterLifecycle param-name>
< param-value> true param-value>
init-param>
< init-param>
< param-name> targetBeanName param-name>
< param-value> shiroFilter param-value>
init-param>
filter>
< filter-mapping>
< filter-name> shiroFilter filter-name>
< url-pattern> /* url-pattern>
filter-mapping>
< bean id = " shiroFilter"
class = " org.apache.shiro.spring.web.ShiroFilterFactoryBean" >
< property name = " securityManager" ref = " securityManager" />
< property name = " loginUrl" value = " /login.do" />
< property name = " successUrl" value = " /index.do" />
< property name = " unauthorizedUrl" value = " /refuse.jsp" />
< property name = " filterChainDefinitions" >
< value>
/login.jsp=anon
/** = authc
value>
property>
bean>
< bean id = " securityManager"
class = " org.apache.shiro.web.mgt.DefaultWebSecurityManager" >
< property name = " realm" ref = " userRealm" />
bean>
< bean id = " userRealm" class = " cn.siggy.realm.UserRealm" >
< property name = " credentialsMatcher"
ref = " credentialsMatcher" />
bean>
< bean id = " credentialsMatcher"
class = " org.apache.shiro.authc.credential.HashedCredentialsMatcher" >
< property name = " hashAlgorithmName" value = " md5" />
< property name = " hashIterations" value = " 1" />
bean>
登录
原理
Shiro 内置了很多默认的过滤器,比如身份验证、授权等相关的。默认过滤器可以参考 org.apache.shiro.web.filter.mgt.DefaultFilter 中的过滤器:
anon:例子/admins/=anon 没有参数,表示可以匿名使用。 authc:例如/admins/user/ =authc 表示需要认证(登录)才能使用,FormAuthenticationFilter 是 表单认证,没有参数 使用 FormAuthenticationFilter 过虑器实现 ,原理如下:将用户没有认证时,请求 loginurl 进行认证,用户身份和用户密码提交数据到 loginurl
FormAuthenticationFilter 拦截住取出 request 中的 username 和 password(两个参数 名称是可以配置的)
FormAuthenticationFilter 调用 realm 传入一个 token( username 和 password)
realm 认证时根据 username 查询用户信息(在 Activeuser 中存储,包括 userid、 usercode、username、menus)。 如果查询不到,realm 返回 null,FormAuthenticationFilter 向 request 域中填充一个 参数(记录了异常信息)
登陆页面
由于 FormAuthenticationFilter 的用户身份和密码的 input 的默认值( username 和 过滤器简称 对应的 java 类 anon org.apache.shiro.web.filter.authc.AnonymousFilter authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter authcBasic org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter perms org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter port org.apache.shiro.web.filter.authz.PortFilter rest org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter roles org.apache.shiro.web.filter.authz.RolesAuthorizationFilter ssl org.apache.shiro.web.filter.authz.SslFilter user org.apache.shiro.web.filter.authc.UserFilter logout org.apache.shiro.web.filter.authc.LogoutFilterpassword),修改页面的账号和密码 的 input 的名称为 username 和 password
代码实现
@Controller
public class LoginController {
@RequestMapping ( "/login.do" )
public String login ( HttpServletRequest req, Model model) {
String exceptionClassName =
( String ) req. getAttribute ( "shiroLoginFailure" ) ;
String error = null ;
if ( UnknownAccountException . class . getName ( ) . equals ( exception
ClassName ) ) {
error = "用户名/密码错误" ;
} else
if ( IncorrectCredentialsException . class . getName ( ) . equals ( except
ionClassName) )
{
error = "用户名/密码错误" ;
} else if ( exceptionClassName != null ) {
error = "其他错误:" + exceptionClassName;
}
model. addAttribute ( "error" , error) ;
return "redirect:login.jsp" ;
}
}
Hibernate
持久化:数据从瞬时状态转化为持久状态
Hibernate:是一个轻量级的持久化框架。没有侵入性。是一个 orm 映射框架。简化了 jdbc操作。极大了提高了开发效率。提供了缓存机制。强大的查询机制。支持多种数据库(数据库移植)
映射规则: 将类名映射数据库的表名 将类的属性名映射为表的字段名 将类的属性类型映射为表的字段的数据类型 将对象的属性映射为表的记录
环境搭建
< hibernate-configuration>
< session-factory>
< property
name = " connection.driver_class" > com.mysql.jdbc.Driver property>
< property
name = " connection.url" > jdbc:mysql://localhost:3306/hibernate4 property>
< property name = " connection.username" > root property>
< property name = " connection.password" > root property>
< property
name = " dialect" > org.hibernate.dialect.MySQLDialect property>
session-factory>
hibernate-configuration>
public class User {
private int id;
private String name; private int age;
public User ( ) {
}
}
< hibernate-mapping
package = " cn.sxt.pojo" >
< class name = " User" table = " t_user" >
< id name = " id" >
< generator class = " native" />
id>
< property name = " name" />
< property name = " age" />
class>
hibernate-mapping>
public class Test {
public static void main ( String [ ] args) {
Configuration cfg = new Configuration ( ) . configure ( ) ;
ServiceRegistry registry = new
StandardServiceRegistryBuilder ( )
. applySettings ( cfg. getProperties ( ) )
. build ( ) ;
SessionFactory factory =
cfg. buildSessionFactory ( registry) ;
Session session = factory. openSession ( ) ;
Transaction tx = session. beginTransaction ( ) ;
User u = new User ( "张三" , 22 ) ;
session. save ( u) ;
tx. commit ( ) ;
session. close ( ) ;
}
}
别的获取数据的方法
User user= ( User ) session. get ( User . class , 1 ) ;
配置文件讲解
< hibernate-configuration>
< session-factory>
< property name = " connection.driver_class" >
com.mysql.jdbc.Driver
property>
< property name = " connection.url" >
jdbc:mysql:///hibernate4
property>
< property name = " connection.username" > root property>
< property name = " connection.password" > root property>
< property name = " dialect" >
org.hibernate.dialect.MySQL5Dialect
property>
< property name = " show_sql" > true property>
< property name = " format_sql" > true property>
< property name = " hbm2ddl.auto" > update property>
< mapping resource = " cn/sxt/pojo/User.hbm.xml" />
session-factory>
hibernate-configuration>
< hibernate-mapping>
< class name = " cn.sxt.pojo.User" table = " t_user" >
< id name = " id" column = " id" >
< generator class = " identity" >
generator>
id>
< property name = " name" length = " 40" />
< property name = " age" />
class>
hibernate-mapping>
hibernate 对象生命周期
对象的 3 种状态: a) 临时状态/瞬时状态 该对象是新创建的;一个持久化状态的对象被删除;一个游离状态的数据被删除 b) 持久化状态 对象从数据库中查询出来时,临时状态的数据被保存时,游离状态的数据被更新/ 锁定 c) 游离状态 持久化状态的数据被(session)清理
总结: 临时状态:内存有,数据库没有 持久状态:内存有,session 有,数据库有 游离状态:内存有,数据有
Struts2
环境搭建
导入所需 jar 包
配置 struts2 的核心控制器 web.xml 文件
< filter>
< filter-name> struts2 filter-name>
< filter-class> org.apache.struts2.dispatcher.ng.filter.Stru
tsPrepareAndExecuteFilter filter-class>
filter>
< filter-mapping>
< filter-name> struts2 filter-name>
< url-pattern> /* url-pattern>
filter-mapping>
public class HelloAction {
public String execute ( ) {
System . out. println ( "hello struts2" ) ;
return "success" ;
}
}
注:在 servlet 中,默认执行 service 方法。在 struts2 中,默认执行 execute 方法。 在 servlet 中,service 方法参数时 HttpServletRequest 和 HttpServletResponse,无返回 值。在 struts2 中,方法都是 public 的,并且返回值都是 String 类型,而且方法都是没 有参数的。
< package name = " hello" extends = " struts-default" >
< action name = " hello" class = " cn.sxt.action.HelloAction" >
< result name = " success" > /index.jsp result>
action>
package>
访问:http://localhost:8080/02struts2_hello/hello
执行流程
当用户提交一个请求,服务器接收,并且交给 struts2 的核心过滤器来进行处理,struts2 的过滤器调用 struts2 的一系列处理器来进行处理(如:解析 struts.xml 配置文件,和用户提 交的请求对比,如果找不到返回 404,如果找到进行下一步处理。)直到调用对应的 Action 类中的 execute 方法执行,执行完后再进行一系列处理到核心过滤器。由核心过滤器返回给 服务器,服务器对浏览器进行相应的响应。
发起请求—》服务器接受请求并交给struts2前端控制器–》根据请求的url查看struts.xml 中的 namespace + actionName —》执行action所对应的类的对应方法—》根据方法的执行结果到action的结果集进行匹配–》响应结果
简介
回顾
在 jsp/servlet 开发中,首先使用的是 Model1
开发模式,在 jsp 内嵌 javaBean 代码。好处 是执行效率比较高。在项目规模比较大的时候,代码非常乱,维护起来非常麻烦。不利于分 工,也不利于代码的重用。
Model2
即MVC 将代码分为了 3 块,视图 View,模型 Model, 控制器 Controller。内容和显示进行分离,开发人员可以专注于某一块,从而提高效率。适 合的项目规模比较大的情况。而且重复利用率得到提高,便于维护。
框架:框架替程序员完成一部分的代码。从而提高开发效率
struts1出现很早,名气很大,局限性(支持前端的技术很窄,只jsp) 然后出现了webwork,支持新的技术,名气不大。 struts2是轻量级的mvc框架 轻量级 :该框架没有侵入性 侵入性 :如果使用一个框架,必须实现框架提供的接口,或继承框架提供的类,就是 有侵入性。
mvc 框架完成的事情: Servlet 做哪些事情: 处理用户提交的数据 调用业务方法 处理业务结果 控制视图显示 用户请求映射到一个 java 类。
Mvc 框架做的事情: 将用户请求映射到一个 java 类。 获取用户提交的数据 渲染数据(将数据封装到前台显示( request)) 控制视图跳转
struts2 线程安全
1、线程安全:在一个进程中有多个线程并发执行,每个线程执行过程中,变量值是相同的, 执行结果也是相同的。
2、Struts2 线程安全吗? 每次请求都会重新创建新的 action 对象,所以线程安全。 由于 action 对象是 struts2,反射生成的,所以要求 Action 类要有一个公共的无参构造 方法。
token令牌
防止用户重复提交,造成冗余数据,使用token默 认拦截器解决。后台生成token令牌,一份存在 session中,一份存在jsp页面展示提交到action ,对比session中的token值, 1.一样的话就是首 次提交。然后清除session中的token值。 2.如果 session中没有这个token值,那就是重复提交。
struts2 的 ajax
< script type = " text/javascript"
src = " js/jquery-1.11.3.js" > script>
< script type = " text/javascript" >
$ ( function ( ) {
$ ( '#btn' ) . click ( function ( ) {
$. post ( "ajax.action" , function ( data ) {
$ ( '#msg' ) . html ( data) ;
} ) ;
} ) ;
} ) ;
script>
head>
< body>
< input type = " button" id = " btn" value = " 获取 ajax 信息" />
< h3 id = " msg" > h3>
body>
public class AjaxAction {
public String execute ( ) throws IOException {
HttpServletResponse resp =
ServletActionContext . getResponse ( ) ;
resp. setCharacterEncoding ( "utf-8" ) ;
resp. getWriter ( ) . print ( "struts ajax" ) ;
return null ;
}
}
< package name = " default" extends = " list-default" namespace = " /" >
< action name = " ajax" class = " cn.sxt.action.AjaxAction" >
action>
package>
public class JsonAction {
private JSONArray root; public String execute ( ) {
List < User > list = new ArrayList < User > ( ) ;
list. add ( new User ( "siggy" , 23 ) ) ;
list. add ( new User ( "zhangsan" , 22 ) ) ;
list. add ( new User ( "老王" , 21 ) ) ;
root = JSONArray . fromObject ( list) ;
System . out. println ( "json=" + root. toString ( ) ) ;
return "success" ;
}
public JSONArray getRoot ( ) {
System . out. println ( "获取 root 数据" ) ;
return root;
}
public void setRoot ( JSONArray root) {
this . root = root;
}
}
< script type = " text/javascript"
src = " js/jquery-1.11.3.js" > script>
< script type = " text/javascript" >
$ ( function ( ) {
$ ( '#btn' ) . click ( function ( ) {
$. post ( "json.action" , function ( data ) {
var html= "" ;
for ( var i= 0 ; i< data. length; i++ ) {
html+= ""+ data[ i] . name+ " "+ data[ i] . age+ "< / td
> < / tr> ";
}
$ ( '#content' ) . html ( html) ;
} , 'json' ) ;
} ) ;
} ) ;
script>
head>
< body>
< input type = " button" id = " btn" value = " 获取 json 数据" />
< table width = " 80%" align = " center" >
< tr>
< td> 姓名 td> < td> 年龄 td>
tr>
< tbody id = " content" >
tbody>
table>
body>
< package name = " default" extends = " json-default" namespace = " /" >
< action name = " json" class = " cn.sxt.action.JsonAction" >
< result type = " json" >
< param name = " root" > root param>
result>
action>
package>
配置文件
< constant name = " struts.i18n.encoding" value = " UTF-8" />
2)自定义扩展名
< constant name = " struts.action.extension"
value = " action,,siggy" />
3)友好的提示信息
< constant name = " struts.devMode" value = " true" />
4)设置配置文件修改后自动加载–推荐在开发中使用
< constant name = " struts.configuration.xml.reload" value = " true" />
常量配置方式二
在 src 下添加 struts.properties 配置文件
团队协作开发配置
通过 include 添加不同人员的配置文件
< include file = " config/sxt/struts/user.xml" />
配置文件加载顺序
struts-default.xml---->struts-plugin.xml---->struts.xml
package 的配置
< package name = " user" namespace = " /user" extends = " struts-default" >
action 的配置
class不写的话就是默认类
< action name = " login" class = " cn.sxt.action.LoginAction" >
减少 action 的配置还可以使用 DMI(动态方法调用),不推荐存在安全隐患
在常量配置中开启DMI 配置不写method 调用处理方法actionName!methodNmae.action
使用注解方法减少action的配置
result 配置
< result type = " redirectAction" > logout result>
- struts2默认提供了五种返回结果,success,none,error,input,login
< global-results>
< result name = " login" > /login.jsp result>
global-results>
在 action 的配置中,如果不去配置 class 属性,将会由默认的 action 来执行,默认的 action 是 ActionSuppot 类。
< default-action-ref name = " default" />
< default-class-ref class = " cn.sxt.action.AddAction" />
< action name = " user*" class = " cn.sxt.action.AddAction"
method = " {1}" >
< result> /index.jsp result>
action>
Action 的实现方式
public class PojoAction {
public String execute ( ) {
System . out. println ( "pojo action" ) ;
return "success" ;
}
}
public class InterfaceAction implements Action {
public String execute ( ) throws Exception {
System . out. println ( "interface action" ) ;
return SUCCESS;
}
}
public class ExtendsAction extends ActionSupport {
@Override
public String execute ( ) throws Exception {
System . out. println ( "extends action" ) ;
return SUCCESS;
}
}
获取表单数据
< form action = " register.action" method = " post" >
用户名:< input type = " text" name = " user.name" /> < br>
密码:< input type = " password" name = " user.pwd" /> < br>
年龄:< input type = " text" name = " user.age" /> < br>
邮箱:< input type = " text" name = " user.email" /> < br>
< input type = " submit" value = " 提交" />
form>
public class UserAction {
private User user;
public String register ( ) {
System . out. println ( user) ;
return Action . SUCCESS;
}
public User getUser ( ) {
return user;
}
public void setUser ( User user) {
this . user = user;
}
}
public class User {
private String name;
private String pwd;
private int age;
private String email;
public String getName ( ) {
return name;
}
public void setName ( String name) {
this . name = name;
}
public String getPwd ( ) {
return pwd;
}
public void setPwd ( String pwd) {
this . pwd = pwd;
}
public int getAge ( ) { return age;
}
public void setAge ( int age) {
this . age = age;
}
public String getEmail ( ) {
return email;
}
public void setEmail ( String email) {
this . email = email;
}
@Override
public String toString ( ) {
return "User [name=" + name + ", pwd=" + pwd + ", age=" +
age
+ ", email=" + email + "]" ;
}
}
< package name = " user" extends = " struts-default" >
< action name = " register"
class = " cn.sxt.action.UserAction" method = " register" >
< result name = " success" > /show.jsp result>
action>
package>
对象驱动
属性多的时候就封装为对象
模型驱动
对象驱动中,页面的表单域名称比较复杂
建议在实体类属性比较多时,采用模型驱动进行开发。
< form action = " regDriven.action" method = " post" >
用户名:< input type = " text" name = " name" /> < br>
密码:< input type = " password" name = " pwd" /> < br>
年龄:< input type = " text" name = " age" /> < br>
邮箱:< input type = " text" name = " email" /> < br>
< input type = " submit" value = " 提交" />
form>
public class UserDrivenAction implements ModelDriven < User > { private User user = new User ( ) ;
public String register ( ) {
System . out. println ( user) ;
return Action . SUCCESS;
}
public User getModel ( ) {
return user;
}
public User getUser ( ) {
return user;
}
public void setUser ( User user) {
this . user = user;
}
}
public class User {
private String name;
private String pwd;
private int age;
private String email;
public String getName ( ) {
return name;
}
public void setName ( String name) {
this . name = name;
}
public String getPwd ( ) {
return pwd;
}
public void setPwd ( String pwd) {
this . pwd = pwd;
}
public int getAge ( ) {
return age;
}
public void setAge ( int age) {
this . age = age;
}
public String getEmail ( ) {
return email;
} public void setEmail ( String email) {
this . email = email;
}
@Override
public String toString ( ) {
return "User [name=" + name + ", pwd=" + pwd + ", age=" +
age
+ ", email=" + email + "]" ;
}
}
< package name = " user" extends = " struts-default" >
< action name = " register" class = " cn.sxt.action.UserAction"
method = " register" >
< result name = " success" > /show.jsp result>
action>
< action name = " regDriven"
class = " cn.sxt.action.UserDrivenAction" method = " register" >
< result name = " success" > /show.jsp result>
action>
package>
String strAge = req. getParameter ( "age" ) ;
int age= 0 ;
if ( strAge!= null ) {
age= Integer . parseInt ( strAge) ;
}
在 struts2 中,常见数据类型 struts2 已经自动的进行了类型转换。在某些情况下,有自定义的类型时,struts2 不能完成类型转换,那么需要手动转换,如 果该自定义类型使用的频率较高时,手动转换重复代码将会增多-------使用 struts2 提供的类 型转换器来进行类型转换。
案例:坐标点(x,y)
不进行类型转换的处理方式
Jsp 页面
< body>
< form action = " point.action" method = " post" >
x:< input type = " text" name = " point.x" /> < br>
y:< input type = " text" name = " point.y" /> < br>
< input type = " submit" value = " 提交" />
form>
body>
public class PointAction {
private Point point;
public String execute ( ) {
System . out. println ( point. getX ( ) + "-----" + point. getY ( ) ) ;
return Action . SUCCESS;
}
public Point getPoint ( ) {
return point;
}
public void setPoint ( Point point) {
this . point = point;
}
}
< package name = " default" namespace = " /" extends = " struts-default" >
< action name = " point" class = " cn.sxt.action.PointAction" >
< result> /index.jsp result>
action>
package>
public class PointConverter extends StrutsTypeConverter {
@Override
public Object convertFromString ( Map context, String [ ] values,
Class toClass) {
String value= values[ 0 ] ;
Point point = new Point ( ) ;
String x = value. substring ( 1 , value. indexOf ( "," ) ) ;
String
y= value. substring ( value. indexOf ( "," ) + 1 , value. length ( ) - 1 ) ;
System . out. println ( "x=" + x) ;
System . out. println ( "y=" + y) ;
point. setX ( Integer . parseInt ( x) ) ;
point. setY ( Integer . parseInt ( y) ) ;
return point;
}
@Override
public String convertToString ( Map context, Object o) {
Point point = ( Point ) o;
return "(" + point. getX ( ) + "," + point. getY ( ) + ")" ;
} }
xwork-conversion.properties 配置文件 cn.sxt.entity.Point=cn.sxt.converter.PointConverter Action 代码不变,struts.xml 配置不变 Jsp 页面
< form action = " point.action" method = " post" >
点:< input type = " text" name = " point" /> < br>
< input type = " submit" value = " 提交" />
form>
编写 xwork-conversion.properties 的配置文件
放于 src 下;内容为 要转换的类型=类型转换器
获取 ServletAPI
struts2 有 2 种方式去获取 servletAPI:一种解耦,一种耦合;
解耦使得使用 struts2 来进行测试的时候 不需要启动服务器。在一定程度上提高开发效 率的。 action—>service—>dao
ActionContext . getContext ( ) . getSession ( ) . put ( "user" , name) ;
用户登录后将信息写入session
public String execute ( ) {
System . out. println ( name+ "---" + pwd) ;
if ( "siggy" . equals ( name) && "1111" . equals ( pwd) ) {
ActionContext . getContext ( ) . getSession ( ) . put ( "user" ,
name) ;
Map < String , Object > request =
( Map ) ActionContext . getContext ( ) . get ( "request" ) ;
Map < String , Object >
application= ActionContext . getContext ( ) . getApplication ( ) ;
Map < String , Object > parameters =
ActionContext . getContext ( ) . getParameters ( ) ;
System . out. println ( "name====" + ( ( String [ ] ) parameters. get ( "na
me") ) [ 0 ] ) ;
return "success" ;
} else {
return "login" ;
}
}
通过 ActionContext 直接获取 HttpServletRequest
public String execute ( ) {
System . out. println ( name+ "---" + pwd) ;
if ( "siggy" . equals ( name) && "1111" . equals ( pwd) ) {
HttpServletRequest
request= ( HttpServletRequest ) ActionContext . getContext ( ) . get ( Str
utsStatics. HTTP_REQUEST) ;
request. getSession ( ) . setAttribute ( "user" , name) ; System . out. println ( "name====" + request. getParameter ( "name" ) )
;
return "success" ;
} else {
return "login" ;
}
}
public class LoginAction2 implements ServletRequestAware {
private String name;
private String pwd;
HttpServletRequest request;
public String execute ( ) {
System . out. println ( name+ "---" + pwd) ;
if ( "siggy" . equals ( name) && "1111" . equals ( pwd) ) {
request. getSession ( ) . setAttribute ( "user" , name) ;
System . out. println ( "name====" + request. getParameter ( "name" ) )
;
return "success" ;
} else {
return "login" ;
}
}
public void setServletRequest ( HttpServletRequest request) {
this . request= request;
}
public String logout ( ) {
ActionContext . getContext ( ) . getSession ( ) . remove ( "user" ) ;
System . out. println ( "退出" ) ;
return "success" ;
}
public String getName ( ) {
return name;
}
public void setName ( String name) {
this . name = name;
}
public String getPwd ( ) {
return pwd;
}
public void setPwd ( String pwd) { this . pwd = pwd;
}
}
通过获取 ServletActionContext 获取 HttpServletRequest 对象
public String execute ( ) {
System . out. println ( name+ "---" + pwd) ;
if ( "siggy" . equals ( name) && "1111" . equals ( pwd) ) {
HttpServletRequest request =
ServletActionContext . getRequest ( ) ;
request. getSession ( ) . setAttribute ( "user" , name) ;
System . out. println ( "name====" + request. getParameter ( "name" ) )
;
return "success" ;
} else {
return "login" ;
}
}
ActionContext
ActionContext 是 map 结构的容器 。ActionContext 是 Action 的上下文(生命周期长) ,存放 Action 执行 过程中数据信息。ActionContext 存放 Action 的数据,ActionInvocation,request 的数据,session 的数据,application 的数据,locale 的数据,conversion errors 等。每次请求时会为当前线程 创 建 一 个 新 的 ActionContext 。 而 ActionContext 采 用 了 ThreadLocal 的 方 式 来 存 放 ActionContext 所以 ActionContext 是线程安全。
将servletAPI中的数据存入actioncontext 实现了struts2和servlet的解耦。使测试可以不依赖 于容器
public static void main ( String [ ] args) {
final ThreadLocal < String > ac = new ThreadLocal < String > ( ) ;
ac. set ( "siggy" ) ;
new Thread ( new Runnable ( ) {
public void run ( ) { System . out. println ( "thread:" + ac. get ( ) ) ;
}
} ) . start ( ) ;
System . out. println ( ac. get ( ) ) ;
}
获取 ActionContext
ActionContext.getContext()获取。由于 ActionContext 是线程安全的,并且是通过静态方 法获取的,所以在本线程中的非 Action 类中 也可以直接访问。
注意:ActionContext 是基于请求创建的,所以在非请求的线程中是不能使用 ActionContext 对象的。如:filter 的 init()方法。
6 大对象
application :servletcontext session request :存放的是httpservletrequest域中的数据 parameters :请求参数 attr(page–>request—>session—>application) 从数据域一个个取 ValueStack(值栈):业务处理类的相关数据
httpservletrequest中有两种数据: 1.setAttribute即域 2.请求
ognl
表达式—el,re,ognl----用简洁的表达式完成比较复杂的功能。 OGNL 全称是 Object-Graph Navigation Language
(对象图形导航语言),相对于 EL 语 言,除了保持 EL 语言的优点外,他的其他优点如下: 能够访问对象的普通方法 能够访问类的静态属性和静态方法 强大的操作集合类对象的能力 支持赋值操作和表达式串联 访问 OGNL 上下文和 ActionContext
public static void main ( String [ ] args) throws OgnlException {
Map < String , Object > map= new HashMap < String , Object > ( ) ;
map. put ( "name" , "张三疯" ) ;
map. put ( "age" , 125 ) ;
User user = new User ( ) ;
user. setName ( "lisi" ) ;
Object obj = Ognl . getValue ( "#name" , map, user) ;
System . out. println ( obj) ;
}
actioncontext做ognl的上下文对象 valuestack做ognl的根对象
<%@ taglib prefix="s" uri="/struts-tags"%>
注意:要使用 struts2 的标签,那么要通过 struts2 过滤器来启用。如果过滤器的配置为 *.action 结尾时,不能直接访问 jsp 页面的。需要通过 action 跳转。如果过滤器配置为/*时, 可以直接访问 jsp 页面。 Struts2 推荐不直接访问 jsp 页面,推荐使用 action 来控制。
<%@ page language="java" import="java.util.*"
pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<%
String path = request.getContextPath();
String basePath =
request.getScheme()+"://"+request.getServerName()+":"+request. getServerPort()+path+"/";
%>
DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" >
< html>
< head>
< base href = " <%=basePath%>" >
< title> My JSP 'index.jsp' starting page title>
< meta http-equiv = " pragma" content = " no-cache" >
< meta http-equiv = " cache-control" content = " no-cache" >
< meta http-equiv = " expires" content = " 0" >
< meta http-equiv = " keywords"
content = " keyword1,keyword2,keyword3" >
< meta http-equiv = " description" content = " This is my page" >
head>
< body>
用户名:< s: property value = " name" /> 【action 中的属性】< br>
用户名:< s: property
value = " #session.user" /> -----------------< a
href = " logout.action" > 退出 a>
body>
html>
结论:使用 ognl 表达式访问 action 的属性时,可以直接访问。访问 actionContext 中的数据时需要加#。
验证机制
数据校验分2类 1.js前端校验 2.后端数据校验
struts2提供两种后端校验方式 1.硬编码实现 2.校验框架实现 action需继承actionsupport类—提供了validate方法,可将验证规则卸载该方法,只由该方法执行通过后,才会执行业务方法。
服务端验证
如果 action 类继承 ActionSupport 类,那么该 action 类将会继承 ActionSupport 的相关功能:如:验证功能
注意:如果执行的是 Action 中的 execute 方法,那么只会执行 validate 方法。如果执行的是自定义的 action ,register 方法,那么将会线执行 validateRegister–validate–register 方法。
案例
一定要加上< s: actionerror/> 或者是< s: fielderror/>
< s: actionerror/>
< form action = " register.action" method = " post" >
用户名:< input type = " text" name = " name" /> < br>
密码:< input type = " password" name = " pwd" /> < br>
年龄:< input type = " text" name = " age" /> < br>
生日:< input type = " text" name = " birthday" /> < br>
< input type = " submit" value = " 登录" />
form>
public class RegisterAction extends ActionSupport {
private String name;
private String pwd;
private int age;
private Date birthday;
@Override
public String execute ( ) throws Exception {
System . out. println ( "execute" ) ;
return Action . SUCCESS;
}
public String register ( ) {
request
validateXxx 方法
validate 方法
xxx 方法
T
T
Result
F : input
reqpsonseSystem. out. println ( "register" ) ;
return Action . SUCCESS;
}
public void validateRegister ( ) {
System . out. println ( "validate age" ) ;
if ( age> 100 || age< 1 ) {
this . addActionError ( "年龄不合法" ) ;
}
}
public void validate ( ) {
System . out. println ( "validate" ) ;
}
public String getName ( ) {
return name;
}
public void setName ( String name) {
this . name = name;
}
public String getPwd ( ) {
return pwd;
}
public void setPwd ( String pwd) {
this . pwd = pwd;
}
public int getAge ( ) {
return age;
}
public void setAge ( int age) {
this . age = age;
}
public Date getBirthday ( ) {
return birthday;
}
public void setBirthday ( Date birthday) {
this . birthday = birthday;
}
}
< package name = " default" extends = " struts-default" namespace = " /" >
< action name = " register"
class = " cn.sxt.action.RegisterAction" method = " register" >
< result> /index.jsp result>
< result name = " input" > /register.jsp result> action>
package>
< s: fielderror> s: fielderror>
< form action = " register.action" method = " post" >
用户名:< input type = " text" name = " name" /> < br>
密码:< input type = " password" name = " pwd" /> < br>
年龄:< input type = " text" name = " age" /> < br>
生日:< input type = " text" name = " birthday" /> < br>
< input type = " submit" value = " 登录" />
form>
public class RegisterValidateAction extends ActionSupport {
private String name;
private String pwd;
private int age;
private Date birthday;
@Override
public String execute ( ) throws Exception {
System . out. println ( "execute" ) ;
return Action . SUCCESS;
}
public String getName ( ) {
return name;
}
public void setName ( String name) {
this . name = name;
}
public String getPwd ( ) {
return pwd;
}
public void setPwd ( String pwd) {
this . pwd = pwd;
}
public int getAge ( ) {
return age;
}
public void setAge ( int age) {
this . age = age; }
public Date getBirthday ( ) {
return birthday;
}
public void setBirthday ( Date birthday) {
this . birthday = birthday;
}
}
< package name = " default" extends = " struts-default" namespace = " /" >
< action name = " register"
class = " cn.sxt.action.RegisterValidateAction" >
< result> /index.jsp result>
< result name = " input" > /register.jsp result>
action>
package>
RegisterValidateAction-validation.xml 验证文件
DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator
1.0.3//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd" >
< validators>
< field name = " name" >
< field-validator type = " requiredstring" >
< param name = " trim" > true param>
< message> 用户名必填 message>
field-validator>
< field-validator type = " stringlength" >
< param name = " trim" > true param>
< param name = " maxLength" > 10 param>
< param name = " minLength" > 4 param>
< message> 用户名去掉 2 端空格后 长度为${minLength}到
${maxLength} message>
field-validator>
field>
< field name = " age" >
< field-validator type = " int" > < param name = " min" > 1 param>
< param name = " max" > 150 param>
< message> 年龄范围为 1~150 message>
field-validator>
field>
validators>
拦截器
拦截器实现是通过代理实现的aop 拦截器是单例的,所有action共享相同的 拦截器,所有拦截器定义常量时需要注意 线程安全问题。
应禁止使用jsp直接访问页面,而应该通过. action方法返回jsp页面,这样就可以进入拦截器 ,也更安全
拦截器和过滤器 很相似。在 action 执行的前后执行。Struts2 的核心功能都是通 过拦截器来实现。
作用:对于 action 的一些公共处理代码可以放到拦截器中来实现。如:权限控制,日志 等等。
多个拦截器之间的执行是采用责任链设计模式来实现。
public class TimeInterceptor extends AbstractInterceptor {
@Override
public String intercept ( ActionInvocation invocation) throws
Exception {
long start= System . currentTimeMillis ( ) ;
String result= invocation. invoke ( ) ;
long end= System . currentTimeMillis ( ) ;
System . out. println ( "执行该 Action 所用时间为:
"+(end-start)+" ms") ;
return result;
} }
< struts>
< package name = " default" extends = " struts-default"
namespace = " /" >
< interceptors>
< interceptor name = " time"
class = " cn.sxt.interceptor.TimeInterceptor" />
interceptors>
< action name = " hello" class = " cn.sxt.action.HelloAction" >
< result> /index.jsp result>
< interceptor-ref name = " time" />
action>
package>
struts>
当请求 hello.action 时将会执行该拦截器。
< default-interceptor-ref name = " defaultStack" />
当引用自定义拦截器后,又想使用 struts2 提供的拦截器功能,那么需要手动引用
< action name = " hello" class = " cn.sxt.action.HelloAction" >
< result> /index.jsp result>
< interceptor-ref name = " time" />
< interceptor-ref name = " defaultStack" />
action>
当 action 引用的拦截器个数比较多时,可以将多个拦截器放入一个拦截器栈中。
< interceptor-stack name = " myStack" >
< interceptor-ref name = " time" />
< interceptor-ref name = " defaultStack" />
interceptor-stack>
< action name = " hello" class = " cn.sxt.action.HelloAction" >
< result> /index.jsp result>
< interceptor-ref name = " myStack" />
action>
当自定义拦截器栈在这个包下的所有 action 都使用的时,可以定义为默认的拦截器 栈,或默认的拦截器
< default-interceptor-ref name = " myStack" />
< action name = " hello" class = " cn.sxt.action.HelloAction" >
< result> /index.jsp result>
action>
public class LoginInterceptor extends AbstractInterceptor {
@Override
public String intercept ( ActionInvocation invocation) throws
Exception {
String actionName= invocation. getProxy ( ) . getActionName ( ) ;
if ( "login" . equals ( actionName) ) {
return invocation. invoke ( ) ;
}
Object obj =
invocation. getInvocationContext ( ) . getSession ( ) . get ( "user" ) ;
if ( obj== null ) {
return Action . LOGIN;
}
return invocation. invoke ( ) ;
}
}
< struts>
< package name = " default" extends = " struts-default"
namespace = " /" >
< interceptors>
< interceptor name = " time"
class = " cn.sxt.interceptor.TimeInterceptor" />
< interceptor name = " loginInterceptor"
class = " cn.sxt.interceptor.LoginInterceptor" />
< interceptor-stack name = " myStack" >
< interceptor-ref name = " loginInterceptor" />
< interceptor-ref name = " time" />
< interceptor-ref name = " defaultStack" />
interceptor-stack>
interceptors>
< default-interceptor-ref name = " myStack" />
< global-results>
< result name = " login" > /login.jsp result>
global-results>
< action name = " hello" class = " cn.sxt.action.HelloAction" >
< result> /WEB-INF/index.jsp result>
action>
< action name = " login" class = " cn.sxt.action.LoginAction" >
< result> /success.jsp result>
action>
package>
struts>
< body>
< form action = " user/login.action" method = " post" >
用户名:< input type = " text" name = " name" /> < br>
密码:< input type = " password" name = " pwd" /> < br>
< input type = " submit" value = " 登录" />
form>
body>
public class LoginAction { private String name;
private String pwd;
public String execute ( ) {
System . out. println ( name+ "---" + pwd) ;
if ( "siggy" . equals ( name) && "1111" . equals ( pwd) ) {
ActionContext . getContext ( ) . getSession ( ) . put ( "user" ,
name) ;
return "success" ;
} else {
return "login" ;
}
}
public String logout ( ) {
System . out. println ( "退出" ) ;
return "success" ;
}
public String getName ( ) {
return name;
}
public void setName ( String name) {
this . name = name;
}
public String getPwd ( ) {
return pwd;
}
public void setPwd ( String pwd) {
this . pwd = pwd;
}
}
< interceptor-ref name = " methodInterceptor" >
< param name = " includeMethods" > list,add param>
< param name = " excludeMethods" > login param>
interceptor-ref>
文件操作
< form action = " upload.action" method = " post"
enctype = " multipart/form-data" >
文件:< input type = " file" name = " file" />
< input type = " submit" value = " 上传" />
form>
action 的代码:在 Action 中需要提供 3 个属性,一个 File 类型,名称是表单域名,其它 两个分别是表单域名+FileName,表单域名+ContentType;并且提供 get/set 方法
public class UploadAction extends ActionSupport {
private File file;
private String fileFileName;
private String fileContentType;
public String upload ( ) throws IOException {
HttpServletRequest request =
ServletActionContext . getRequest ( ) ;
String path= request. getRealPath ( "/upload" ) ;
InputStream is = new FileInputStream ( file) ;
OutputStream os = new FileOutputStream ( new
File ( path, fileFileName) ) ;
byte [ ] buffer = new byte [ 200 ] ;
int len= 0 ;
while ( ( len= is. read ( buffer) ) != - 1 ) {
os. write ( buffer, 0 , len) ;
}
os. close ( ) ;
is. close ( ) ;
return Action . SUCCESS;
}
public File getFile ( ) {
return file;
}
public void setFile ( File file) {
this . file = file;
}
public String getFileFileName ( ) {
return fileFileName; }
public void setFileFileName ( String fileFileName) {
this . fileFileName = fileFileName;
}
public String getFileContentType ( ) {
return fileContentType;
}
public void setFileContentType ( String fileContentType) {
this . fileContentType = fileContentType;
}
}
struts.xml 配 置 文 件 的 编 写 ; 主 要 是 上 传 文 件 大 小 的 控 制 。 需 要 配 置 拦 截 器 的 maximumSize 属性和 struts2 的静态属性:struts.multipart.maxSize;maxSize 要大于等于 maximumSize。
< struts>
< constant name = " struts.multipart.saveDir" value = " c:\" />
< constant name = " struts.multipart.maxSize" value = " 20971520" />
< package name = " default" extends = " struts-default"
namespace = " /" >
< action name = " upload" class = " cn.sxt.action.UploadAction"
method = " upload" >
< result> /index.jsp result>
< interceptor-ref name = " fileUpload" >
< param name = " maximumSize" > 20971520 param>
interceptor-ref>
< interceptor-ref name = " defaultStack" />
action>
package>
struts>
struts2 可以将批量文件处理为数组上传到 action 中
< style type = " text/css" >
p { margin : 5px; }
style>
< script type = " text/javascript"
src = " js/jquery-1.11.3.js" > script>
< script type = " text/javascript" >
$ ( function ( ) {
$ ( '#btn' ) . click ( function ( ) { var field= "< p> < input type= 'file'
name= 'file' / > < input type= 'button' value= '删除'
onclick= 'removed(this);' / > < / p> ";
$ ( '#files' ) . append ( field) ;
} ) ;
} ) ;
function removed ( o ) {
$ ( o) . parent ( ) . remove ( ) ;
}
script>
head>
< body>
< form action = " batch.action" method = " post"
enctype = " multipart/form-data" >
文件:< input type = " file" name = " file" /> < input
type = " button" id = " btn" value = " 添加" />
< div id = " files" > div>
< input type = " submit" value = " 上传" />
form>
body>
public class BatchUploadAction {
private File [ ] file;
private String [ ] fileFileName;
private String [ ] fileContentType;
public String execute ( ) throws IOException {
HttpServletRequest request =
ServletActionContext . getRequest ( ) ;
String path= request. getRealPath ( "/upload" ) ;
for ( int i= 0 ; i< file. length; i++ ) {
InputStream is = new FileInputStream ( file[ i] ) ;
OutputStream os = new FileOutputStream ( new
File ( path, fileFileName[ i] ) ) ;
byte [ ] buffer = new byte [ 200 ] ;
int len= 0 ;
while ( ( len= is. read ( buffer) ) != - 1 ) {
os. write ( buffer, 0 , len) ;
} os. close ( ) ;
is. close ( ) ;
}
return Action . SUCCESS;
}
public File [ ] getFile ( ) {
return file;
}
public void setFile ( File [ ] file) {
this . file = file;
}
public String [ ] getFileFileName ( ) {
return fileFileName;
}
public void setFileFileName ( String [ ] fileFileName) {
this . fileFileName = fileFileName;
}
public String [ ] getFileContentType ( ) {
return fileContentType;
}
public void setFileContentType ( String [ ] fileContentType) {
this . fileContentType = fileContentType;
}
}
< action name = " batch" class = " cn.sxt.action.BatchUploadAction" >
< result> /index.jsp result>
< interceptor-ref name = " fileUpload" >
< param name = " maximumSize" > 20971520 param>
interceptor-ref>
< interceptor-ref name = " defaultStack" />
action>
public class DownloadAction {
public String execute ( ) throws IOException {
HttpServletRequest req= ServletActionContext . getRequest ( ) ;
HttpServletResponse resp =
ServletActionContext . getResponse ( ) ;
String path= req. getRealPath ( "/download" ) ;
File file = new File ( path, "Struts2.chm" ) ;
resp. setContentLength ( ( int ) file. length ( ) ) ;
resp. setCharacterEncoding ( "utf-8" ) ;
resp. setContentType ( "application/octet-stream" ) ;
resp. setHeader ( "Content-Disposition" ,
"attachment;filename=Struts2.chm" ) ;
byte [ ] buffer = new byte [ 400 ] ;
int len= 0 ;
InputStream is = new FileInputStream ( file) ;
OutputStream os = resp. getOutputStream ( ) ;
while ( ( len= is. read ( buffer) ) != - 1 ) {
os. write ( buffer, 0 , len) ;
}
os. close ( ) ;
is. close ( ) ;
return null ;
}
}
< package name = " default" namespace = " /" extends = " struts-default" >
< action name = " download"
class = " cn.sxt.action.DownloadAction" >
action>
package>
< body>
< a href = " download.action" > struts2 的文档 a>
body>
使用 struts2 本来结果集来进行文件下载
Action
public class StreamDownloadAction {
private String fileName;
public String execute ( ) {
return Action . SUCCESS;
}
public InputStream getInputStream ( ) throws
FileNotFoundException {
HttpServletRequest req= ServletActionContext . getRequest ( ) ;
String path= req. getRealPath ( "/download" ) ;
return new FileInputStream ( new File ( path, fileName) ) ;
}
public String getFileName ( ) {
return fileName;
}
public void setFileName ( String fileName) {
this . fileName = fileName;
}
}
< action name = " streamDownload"
class = " cn.sxt.action.StreamDownloadAction" >
< result type = " stream" >
< param name = " inputName" > inputStream param>
< param
name = " contentDisposition" > attachment;filename=${fileName} par
am >
result>
action>
< body>
< a
href = " streamDownload.action?fileName=Struts2.chm" > struts2 的文档
a>
< a
href = " streamDownload.action?fileName=Struts1.3.chm" > struts1 的文档 a>
body>
你可能感兴趣的:(java,代码审计,java,开发语言,后端)
【后端】k8s
骑鱼过海的猫123
kubernetes 容器 云原生
1.命令1.1获取service服务获取集群内所有命名空间的service服务sudokubectlgetservice--all-namespaces获取集群内指定命名空间的service服务sudokubectlgetservice-n命名空间当权限限制到一个命名空间时,只能使用下面这个sudokubectl-n命名空间getservice获取集群内当前命名空间的service服务sudoku
null和undefined的区别
编程星空
JavaScript 前端 javascript 开发语言
null和undefined是JavaScript中两个特殊的值,它们都表示“无”或“空”,但在语义和使用场景上有明显区别。以下是它们的详细对比:1.定义undefined表示变量已声明但未赋值,或函数没有返回值时的默认返回值。是JavaScript引擎默认赋予的初始值。类型为undefined。null表示一个空对象指针,通常用于显式表示“无”或“空”。是开发者主动赋值的值。类型为object(
深入理解Spring Boot中的事件驱动架构
省赚客APP开发者@聚娃科技
spring boot 架构 java
深入理解SpringBoot中的事件驱动架构大家好,我是微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!1.引言事件驱动架构在现代软件开发中越来越受欢迎,它能够提高系统的松耦合性和可扩展性。SpringBoot作为一个流行的Java框架,提供了强大的事件驱动支持。本文将深入探讨SpringBoot中事件驱动架构的实现原理和最佳实践。2.SpringFramework中的事件模型在
dreamweaver html语言,Dreamweaver网页设计与制作(HTML+CSS+JavaScript)
weixin_39979245
dreamweaver html语言
Dreamweaver网页设计与制作(HTML+CSS+JavaScript)编辑锁定讨论上传视频本词条缺少信息栏,补充相关内容使词条更完整,还能快速升级,赶紧来编辑吧!《Dreamweaver网页设计与制作(HTML+CSS+JavaScript)》是2014年清华大学出版社出版的图书。Dreamweaver网页设计与制作(HTML+CSS+JavaScript)图书详细信息编辑ISBN:978
html 5中css的含义,HTML 5+CSS+JavaScript网页设计与制作
律保阁-Michael
html 5中css的含义
HTML5+CSS+JavaScript网页设计与制作编辑锁定讨论上传视频《HTML5+CSS+JavaScript网页设计与制作》是2019年4月清华大学出版社出版的图书,作者是彭进香、张茂红、王玉娟、叶娟、孙秀娟、万幸、刘英。书名HTML5+CSS+JavaScript网页设计与制作作者彭进香张茂红王玉娟叶娟作者孙秀娟展开作者孙秀娟万幸刘英收起出版社清华大学出版社出版时间2019年4月定价48
html+css+javascript实用详解,HTML+CSS+JavaScript 课程标准
vvv666s
②学会运用HTML语言中的标记设置颜色、文本格式和列表;熟练掌握颜色值的配置和背景图案的设置方法,熟练掌握字符、链接颜色的设置方法;③掌握在网页中添加CSS、嵌入图像、声音、多媒体信息的方法;④熟练掌握表格的使用方法,学会利用表格设布局网页;掌握框架制作网页的方法,会使用框架设计网页;掌握制作表单的方法,会利用表单建立交互式页面;⑤掌握JavaScript语言的语法;⑥掌握在HTML语言代码中嵌入
Android Gradle使用总结
Wei_Leng
Android studio android gradle 脚本
其他Groovy入门学习http://blog.csdn.net/zhaoyanjun6/article/details/70313790AndroidGradleAndroid项目使用Gradle作为构建框架,Gradle又是以Groovy为脚本语言。所以学习Gradle之前需要先熟悉Groovy脚本语言。Groovy是基于Java语言的脚本语言,所以它的语法和Java非常相似,但是具有比jav
JavaScript的魔法世界:巧妙之处与实战技巧
skyksksksksks
综合个人杂记 javascript 开发语言 html5 css 前端
一、从浏览器玩具到全栈利器的蜕变之路JavaScript诞生于1995年,原本只是网景公司为浏览器设计的"小脚本"。谁能想到这个曾被戏称为"玩具语言"的家伙,如今已蜕变成支撑现代Web开发的擎天柱?就像一只破茧成蝶的幼虫,JavaScript经历了ECMAScript标准的持续进化,在Node.js的加持下突破了浏览器的桎梏,实现了从客户端到服务端的华丽转身。V8引擎的涡轮增压让它跑得比猎豹还快,
Redis 全方位解析:从入门到实战
kiss strong
redis 数据库 缓存
引言在当今互联网快速发展的时代,高并发、低延迟的应用场景越来越普遍。Redis,作为一款高性能的开源数据库,以其卓越的性能和灵活的功能,成为了许多开发者的首选工具。无论是在缓存、消息队列,还是在实时数据分析等领域,Redis都展现出了强大的能力。本文将从Redis的基本介绍、官网、安装、特性,到具体的存储类型、Java代码实例、SpringBoot整合,以及Redis的主要作用和应用场景,进行全面
前端开发入门指南:HTML、CSS和JavaScript基础知识
方向感超强的
javascript css html 前端
引言:大家好,我是一名简单的前端开发爱好者,对于网页设计和用户体验的追求让我深深着迷。在本篇文章中,我将带领大家探索前端开发的基础知识,涵盖HTML、CSS和JavaScript。如果你对这个领域感兴趣,或者想要了解如何开始学习前端开发,那么这篇文章将为你提供一个良好的起点。1.前端开发概述在我们深入了解前端开发的细节之前,让我们先了解一下前端开发的定义和作用。简而言之,前端开发涉及构建用户直接与
《Java高级-Xml:利用DOM4j解析XML》
大大大钢琴
# Java:经验总结 java xml 开发语言
利用DOM4j解析XMLdom4j是一个简单的开源库,用于处理XML、XPath和XSLT,它基于Java平台,使用Java的集合框架,全面集成了DOM,SAX和JAXP。dom4j是目前在xml解析方面是最优秀的(Hibernate、Sun的JAXM也都使用dom4j来解析XML),它合并了许多超出基本XML文档表示的功能,包括集成的XPath支持、XMLSchema支持以及用于大文档或流化文档
React 渲染 Flash 接口数据
ox0080
# 北漂+滴滴出行 VIP 激励 Web react.js 前端 前端框架
1.后端Python代码使用Flask创建多个接口,每个接口返回不同的数据,并使用自定义装饰器来绑定路由。代码:#app.pyfromflaskimportFlask,jsonifyapp=Flask(__name__)defapi_route(route,methods=['GET']):"""自定义装饰器,用于将函数与HTTP路由绑定"""defdecorator(func):app.rout
前端504错误分析
ox0080
# 北漂+滴滴出行 Web VIP 激励 前端
前端出现504错误(网关超时)通常是由于代理服务器未能及时从上游服务获取响应。以下是详细分析步骤和解决方案:1.确认错误来源504含义:代理服务器(如Nginx、Apache)在等待后端服务响应时超时。常见架构:前端→代理服务器→后端服务,问题通常出在代理与后端之间。2.排查步骤(1)检查后端服务状态确认服务是否运行:通过日志或监控工具(如systemctlstatus,KubernetesPod
LQB---基础练习---十六进制转八进制
「已注销」
# LQB LQB
试题基础练习十六进制转八进制资源限制内存限制:512.0MBC/C++时间限制:1.0sJava时间限制:3.0sPython时间限制:5.0s问题描述给定n个十六进制正整数,输出它们对应的八进制数。输入格式输入的第一行为一个正整数n(1<=n<=10)。接下来n行,每行一个由09、大写字母AF组成的字符串,表示要转换的十六进制正整数,每个十六进制数长度不超过100000。输出格式输出n行,每行为
js如何直接下载文件流
涔溪
js javascript 前端 开发语言
在JavaScript中直接处理文件下载,尤其是在处理文件流的情况下,通常涉及到使用fetchAPI或者XMLHttpRequest来获取文件流,并通过创建一个临时的标签(锚点元素)触发下载。以下是使用fetchAPI的一个示例:fetch('你的文件URL',{method:'GET',headers:{//如果需要的话,可以在这里添加请求头}}).then(response=>response
部署前端项目2
augenstern416
前端
前端项目的部署是将开发完成的前端代码发布到服务器或云平台,使其能够通过互联网访问。以下是前端项目部署的常见步骤和工具:1.准备工作在部署之前,确保项目已经完成以下步骤:代码优化:压缩JavaScript、CSS和图片文件,减少文件体积。环境配置:区分开发环境和生产环境(如API地址、环境变量等)。测试:确保项目在本地测试通过,没有明显Bug。2.部署流程1.构建项目大多数前端项目(如React、V
对象的操作
augenstern416
javascript 开发语言 ecmascript
在前端开发中,JavaScript提供了许多内置对象和方法,用于处理数据、操作DOM、处理事件等。以下是一些常用对象及其方法和扩展技巧:1.Object对象Object是JavaScript中最基础的对象,几乎所有对象都继承自Object。常用方法Object.keys(obj):返回对象的所有可枚举属性的键名数组。constobj={a:1,b:2};console.log(Object.key
前端基础入门:HTML、CSS 和 JavaScript
阿绵
前端 前端 html css js
在现代网页开发中,前端技术扮演着至关重要的角色。无论是个人网站、企业官网,还是复杂的Web应用程序,前端开发的基础技术HTML、CSS和JavaScript都是每个开发者必须掌握的核心技能。本文将详细介绍这三者的基本概念及其应用一、HTML——网页的骨架HTML(HyperTextMarkupLanguage)是构建网页的基础语言。它是网页的结构和内容的标记语言,决定了网页上的文本、图像、表单等元
网页制作03-html,css,javascript初认识のhtml的图像设置
Ama_tor
网页制作专栏 html css 前端
一、图像格式网页中图像的格式有三种,Gif,Jpeg,PngGif:Graphicinterchangeformat图像交换格式,文件最多可使用256种颜色,最适合显示色调不连续或具有大面积单一颜色的图像,例如导航条、按钮、图标、徽标或其他具有统一色彩和色调的图像;还可以制作动态图像Jpeg:Giantphotographicexpectgroup,它是一种图像压缩格式,可包含数百万种颜色,不支持
java进阶篇--生产环境如何排查bug和优化 JVM?
爱分享的淘金达人
Java源码剖析(30讲) jvm优化 jvm调优 优化jvm 生产环境优化jvm 生产环境jvm优化
通过前面几个课时的学习,相信你对JVM的理论及实践等相关知识有了一个大体的印象。而本课时将重点讲解JVM的排查与优化,这样就会对JVM的知识点有一个完整的认识,从而可以更好地应用于实际工作或者面试了。我们本课时的面试题是,生产环境如何排查问题?回答:如果是在生产环境中直接排查JVM的话,最简单的做法就是使用JDK自带的6个非常实用的命令行工具来排查。它们分别是:jps、jstat、jinfo、jm
JavaScript——操作浏览器窗口
yiqi_perss
JavaScript
学习内容:今天学习了alert提示框,提示框中的内容,就是alert后边小括号中的内容例如:alert('我要学JavaScript!');alert('我要学习!');学习总结:日常小总结例如:后面的分号;可以随便去掉,不影响运行效果。不能去掉小括号,否则会报错,不信你可以试试。必须是英文引号,否则会报错。课外扩展:历史渊源例如:ECMAScript是一种语言标准,而JavaScript是网景公
JVM内存模型分区
Lionel·
java基础 java jvm
JVM内存模型划分根据JVM规范,JVM内存共分为Java虚拟机栈,本地方法栈,堆,方法区,程序计数器,五个部分。1.Java堆(线程共享)Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都要在堆上分配。Java堆是垃圾收集器管理的主要区域,因此很多时候也被称做“GC堆”。从内存回收的角度看,由于现在收集器基本都采用分代
MapReduce 读取 Hive ORC ArrayIndexOutOfBoundsException: 1024 异常解决
一张假钞
mapreduce hive 大数据
个人博客地址:MapReduce读取HiveORCArrayIndexOutOfBoundsException:1024异常解决|一张假钞的真实世界在MR处理ORC的时候遇到如下异常:Exceptioninthread"main"java.lang.ArrayIndexOutOfBoundsException:1024atorg.apache.orc.impl.RunLengthIntegerRe
后端重载和重写的区别
kidding723
后端 重载 重写 Overriding Overloading
重载相同的方法名,形参数量不同或者参数顺序不同或者参数类型不同称为方法重载重写方法名和形参列表相同重写方法前提:必须存在继承关系(1)方法重载是:一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载(Overloading)。(2)方法重写是:在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写(Overridi
记录一次进程被操作系统强制终止(OOM Killer)
别告诉我有BUG
jvm java
问题背景场景:在一次系统迁移中,团队将原本运行在16G内存物理机的Java服务迁移到8G内存虚拟机,直接复用了原有的JVM参数(如-Xmx12g)。服务启动后运行正常,但几小时后突然宕机,日志中无明确错误,仅显示进程终止。影响:服务不可用持续30分钟部分业务数据丢失,用户投诉激增分析过程1.初步排查现象确认:进程消失,无Java堆栈或异常日志。系统日志/var/log/messages中发现OOM
Flink CDC报错ArrayIndexOutOfBoundsException解决思路
学亮编程手记
大数据 flink doris
FlinkCDC用两个并行度会报错。一个并行度就不会报错。不知道是什么原因?同步java.lang.ArrayIndexOutOfBoundsException?解决思路看日志,应该是mysql文本字段中有换行符之类的,应该会有一个url的报错提示,然后curl那个url看具体报错。这个问题可能是由于FlinkCDC的并行度设置不正确导致的。当您尝试使用两个并行度时,可能会遇到数组越界异常(jav
【python】懒人福利,通过Python的JIRA库操作JIRA,自动批量提交关闭bug,提高效率
bulabula2022
# CI持续集成 Python jira
简介:Jira是目前比较流行的基于Java架构的管理系统(Atlassian公司支持),有开源代码,方便做二次开发(可扩展性)。Jira是一款功能非常强大的管理工具,广泛的用来缺陷跟踪、用例管理、需求收集、任务跟踪、工时管理、项目计划管理等工作领域。python有支持操作Jira的第三方包,方便自定义一些自动化操作。需要安装jira库:pipinstalljiraJira认证fromjiraimp
百万架构师第四十课:RabbitMq:RabbitMq-工作模型与JAVA编程|JavaGuide
后端
来源:https://javaguide.netRabbitMQ1-工作模型与Java编程课前准备预习资料Windows安装步骤Linux安装步骤官网文章中文翻译系列环境说明操作系统:CentOS7JDK:1.8Erlang:19.0.4或最新版RabbitMQ:3.6.12或最新版版本对应关系典型应用场景跨系统的异步通信。人民银行二代支付系统,使用重量级消息队列IBMMQ,异步,解耦,削峰都有体
Java面试题中高级进阶(JVM篇Java内存)
李老头探索
java jvm 开发语言
前言本来想着给自己放松一下,刷刷博客,突然被几道面试题难倒!说说Java内存结构?说说对象分配规则?描述一下JVM加载class文件的原理机制?似乎有点模糊了,那就大概看一下面试题吧。好记性不如烂键盘***12万字的java面试题整理***Java内存结构方法区和堆是所有线程共享的内存区域;而java栈、本地方法栈和程序员计数器是运行是线程私有的内存区域。Java堆(Heap),是Java虚拟机所
【JavaSE】GUI编程(图形界面)
lil_侯昊
java 开发语言
GUI(GraphicalUserInterface)图形用户界面-àjava提供的图形用户界面UI–用户界面Swing概述Javax.swing包此包中包含了java主要的图形界面的实现类●swing是一个为Java设计的GUI工具包javax.swing,该包中包括了图形用户界面的各种组件支持。●一个Java的图形界面,由各种不同类型的“元素”组成,这些“元素”被称为组件(Component)
Algorithm
香水浓
java Algorithm
冒泡排序
public static void sort(Integer[] param) {
for (int i = param.length - 1; i > 0; i--) {
for (int j = 0; j < i; j++) {
int current = param[j];
int next = param[j + 1];
mongoDB 复杂查询表达式
开窍的石头
mongodb
1:count
Pg: db.user.find().count();
统计多少条数据
2:不等于$ne
Pg: db.user.find({_id:{$ne:3}},{name:1,sex:1,_id:0});
查询id不等于3的数据。
3:大于$gt $gte(大于等于)
&n
Jboss Java heap space异常解决方法, jboss OutOfMemoryError : PermGen space
0624chenhong
jvm jboss
转自
http://blog.csdn.net/zou274/article/details/5552630
解决办法:
window->preferences->java->installed jres->edit jre
把default vm arguments 的参数设为-Xms64m -Xmx512m
----------------
文件上传 下载 解析 相对路径
不懂事的小屁孩
文件上传
有点坑吧,弄这么一个简单的东西弄了一天多,身边还有大神指导着,网上各种百度着。
下面总结一下遇到的问题:
文件上传,在页面上传的时候,不要想着去操作绝对路径,浏览器会对客户端的信息进行保护,避免用户信息收到攻击。
在上传图片,或者文件时,使用form表单来操作。
前台通过form表单传输一个流到后台,而不是ajax传递参数到后台,代码如下:
<form action=&
怎么实现qq空间批量点赞
换个号韩国红果果
qq
纯粹为了好玩!!
逻辑很简单
1 打开浏览器console;输入以下代码。
先上添加赞的代码
var tools={};
//添加所有赞
function init(){
document.body.scrollTop=10000;
setTimeout(function(){document.body.scrollTop=0;},2000);//加
判断是否为中文
灵静志远
中文
方法一:
public class Zhidao {
public static void main(String args[]) {
String s = "sdf灭礌 kjl d{';\fdsjlk是";
int n=0;
for(int i=0; i<s.length(); i++) {
n = (int)s.charAt(i);
if((
一个电话面试后总结
a-john
面试
今天,接了一个电话面试,对于还是初学者的我来说,紧张了半天。
面试的问题分了层次,对于一类问题,由简到难。自己觉得回答不好的地方作了一下总结:
在谈到集合类的时候,举几个常用的集合类,想都没想,直接说了list,map。
然后对list和map分别举几个类型:
list方面:ArrayList,LinkedList。在谈到他们的区别时,愣住了
MSSQL中Escape转义的使用
aijuans
MSSQL
IF OBJECT_ID('tempdb..#ABC') is not null
drop table tempdb..#ABC
create table #ABC
(
PATHNAME NVARCHAR(50)
)
insert into #ABC
SELECT N'/ABCDEFGHI'
UNION ALL SELECT N'/ABCDGAFGASASSDFA'
UNION ALL
一个简单的存储过程
asialee
mysql 存储过程 构造数据 批量插入
今天要批量的生成一批测试数据,其中中间有部分数据是变化的,本来想写个程序来生成的,后来想到存储过程就可以搞定,所以随手写了一个,记录在此:
DELIMITER $$
DROP PROCEDURE IF EXISTS inse
annot convert from HomeFragment_1 to Fragment
百合不是茶
android 导包错误
创建了几个类继承Fragment, 需要将创建的类存储在ArrayList<Fragment>中; 出现不能将new 出来的对象放到队列中,原因很简单;
创建类时引入包是:import android.app.Fragment;
创建队列和对象时使用的包是:import android.support.v4.ap
Weblogic10两种修改端口的方法
bijian1013
weblogic 端口号 配置管理 config.xml
一.进入控制台进行修改 1.进入控制台: http://127.0.0.1:7001/console 2.展开左边树菜单 域结构->环境->服务器-->点击AdminServer(管理) &
mysql 操作指令
征客丶
mysql
一、连接mysql
进入 mysql 的安装目录;
$ bin/mysql -p [host IP 如果是登录本地的mysql 可以不写 -p 直接 -u] -u [userName] -p
输入密码,回车,接连;
二、权限操作[如果你很了解mysql数据库后,你可以直接去修改系统表,然后用 mysql> flush privileges; 指令让权限生效]
1、赋权
mys
【Hive一】Hive入门
bit1129
hive
Hive安装与配置
Hive的运行需要依赖于Hadoop,因此需要首先安装Hadoop2.5.2,并且Hive的启动前需要首先启动Hadoop。
Hive安装和配置的步骤
1. 从如下地址下载Hive0.14.0
http://mirror.bit.edu.cn/apache/hive/
2.解压hive,在系统变
ajax 三种提交请求的方法
BlueSkator
Ajax jqery
1、ajax 提交请求
$.ajax({
type:"post",
url : "${ctx}/front/Hotel/getAllHotelByAjax.do",
dataType : "json",
success : function(result) {
try {
for(v
mongodb开发环境下的搭建入门
braveCS
运维
linux下安装mongodb
1)官网下载mongodb-linux-x86_64-rhel62-3.0.4.gz
2)linux 解压
gzip -d mongodb-linux-x86_64-rhel62-3.0.4.gz;
mv mongodb-linux-x86_64-rhel62-3.0.4 mongodb-linux-x86_64-rhel62-
编程之美-最短摘要的生成
bylijinnan
java 数据结构 算法 编程之美
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
public class ShortestAbstract {
/**
* 编程之美 最短摘要的生成
* 扫描过程始终保持一个[pBegin,pEnd]的range,初始化确保[pBegin,pEnd]的ran
json数据解析及typeof
chengxuyuancsdn
js typeof json解析
// json格式
var people='{"authors": [{"firstName": "AAA","lastName": "BBB"},'
+' {"firstName": "CCC&
流程系统设计的层次和目标
comsci
设计模式 数据结构 sql 框架 脚本
流程系统设计的层次和目标
 
RMAN List和report 命令
daizj
oracle list report rman
LIST 命令
使用RMAN LIST 命令显示有关资料档案库中记录的备份集、代理副本和映像副本的
信息。使用此命令可列出:
• RMAN 资料档案库中状态不是AVAILABLE 的备份和副本
• 可用的且可以用于还原操作的数据文件备份和副本
• 备份集和副本,其中包含指定数据文件列表或指定表空间的备份
• 包含指定名称或范围的所有归档日志备份的备份集和副本
• 由标记、完成时间、可
二叉树:红黑树
dieslrae
二叉树
红黑树是一种自平衡的二叉树,它的查找,插入,删除操作时间复杂度皆为O(logN),不会出现普通二叉搜索树在最差情况时时间复杂度会变为O(N)的问题.
红黑树必须遵循红黑规则,规则如下
1、每个节点不是红就是黑。 2、根总是黑的 &
C语言homework3,7个小题目的代码
dcj3sjt126com
c
1、打印100以内的所有奇数。
# include <stdio.h>
int main(void)
{
int i;
for (i=1; i<=100; i++)
{
if (i%2 != 0)
printf("%d ", i);
}
return 0;
}
2、从键盘上输入10个整数,
自定义按钮, 图片在上, 文字在下, 居中显示
dcj3sjt126com
自定义
#import <UIKit/UIKit.h>
@interface MyButton : UIButton
-(void)setFrame:(CGRect)frame ImageName:(NSString*)imageName Target:(id)target Action:(SEL)action Title:(NSString*)title Font:(CGFloa
MySQL查询语句练习题,测试足够用了
flyvszhb
sql mysql
http://blog.sina.com.cn/s/blog_767d65530101861c.html
1.创建student和score表
CREATE TABLE student (
id INT(10) NOT NULL UNIQUE PRIMARY KEY ,
name VARCHAR
转:MyBatis Generator 详解
happyqing
mybatis
MyBatis Generator 详解
http://blog.csdn.net/isea533/article/details/42102297
MyBatis Generator详解
http://git.oschina.net/free/Mybatis_Utils/blob/master/MybatisGeneator/MybatisGeneator.
让程序员少走弯路的14个忠告
jingjing0907
工作 计划 学习
无论是谁,在刚进入某个领域之时,有再大的雄心壮志也敌不过眼前的迷茫:不知道应该怎么做,不知道应该做什么。下面是一名软件开发人员所学到的经验,希望能对大家有所帮助
1.不要害怕在工作中学习。
只要有电脑,就可以通过电子阅读器阅读报纸和大多数书籍。如果你只是做好自己的本职工作以及分配的任务,那是学不到很多东西的。如果你盲目地要求更多的工作,也是不可能提升自己的。放
nginx和NetScaler区别
流浪鱼
nginx
NetScaler是一个完整的包含操作系统和应用交付功能的产品,Nginx并不包含操作系统,在处理连接方面,需要依赖于操作系统,所以在并发连接数方面和防DoS攻击方面,Nginx不具备优势。
2.易用性方面差别也比较大。Nginx对管理员的水平要求比较高,参数比较多,不确定性给运营带来隐患。在NetScaler常见的配置如健康检查,HA等,在Nginx上的配置的实现相对复杂。
3.策略灵活度方
第11章 动画效果(下)
onestopweb
动画
index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/
FAQ - SAP BW BO roadmap
blueoxygen
BO BW
http://www.sdn.sap.com/irj/boc/business-objects-for-sap-faq
Besides, I care that how to integrate tightly.
By the way, for BW consultants, please just focus on Query Designer which i
关于java堆内存溢出的几种情况
tomcat_oracle
java jvm jdk thread
【情况一】:
java.lang.OutOfMemoryError: Java heap space:这种是java堆内存不够,一个原因是真不够,另一个原因是程序中有死循环; 如果是java堆内存不够的话,可以通过调整JVM下面的配置来解决: <jvm-arg>-Xms3062m</jvm-arg> <jvm-arg>-Xmx
Manifest.permission_group权限组
阿尔萨斯
Permission
结构
继承关系
public static final class Manifest.permission_group extends Object
java.lang.Object
android. Manifest.permission_group 常量
ACCOUNTS 直接通过统计管理器访问管理的统计
COST_MONEY可以用来让用户花钱但不需要通过与他们直接牵涉的权限
D