加载 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,开发语言,后端)
Node.js 的模块作用域和 module 对象详细介绍
还是鼠鼠
node.js node.js javascript 前端 vscode web
目录代码示例1.创建模块文件module-demo.js2.导入模块并使用module-demo.js运行结果总结在Node.js中,每个文件都是一个独立的模块,具有自己的作用域。与浏览器JavaScript代码不同,Node.js采用模块作用域,这意味着一个文件中的变量、函数、类等不会污染全局作用域,而是仅在该模块内部有效。这种设计提高了代码的封装性和安全性。module对象是Node.js提供
python和java的本质区别,python和java有什么关系
2301_81900386
python 开发语言 人工智能
本篇文章给大家谈谈python和java的本质区别,以及python和java有什么关系,希望对各位有所帮助,不要忘了收藏本站喔。一、主要区别:1.Python比Java简单,学习成本低,开发效率高2.Java运行效率高于Python,尤其是纯Python开发的程序,效率极低3.Java相关资料多,尤其是中文资料4.Java版本比较稳定,Python2和3不兼容导致大量类库失效5.Java开发偏向
HarmonyNext深度解析:ArkUI高效渲染与性能优化实战
披光人
harmonyOS ubuntu linux 运维
一、HarmonyNext渲染引擎技术演进(约1200字技术解析)HarmonyOSNext在UI渲染架构层面实现了重大突破,其创新的ArkUI渲染引擎采用分层异步架构设计。核心改进包括:原子化渲染管线采用基于Vulkan的跨平台渲染后端,通过原子化渲染指令拆分技术,实现绘制指令的并行执行能力。在华为Mate60系列实测中,复杂界面渲染延迟降低42%智能脏区检测机制基于机器学习的区域更新预测算法,
HarmonyNext深度解析:ArkUI 3.0声明式开发与高性能渲染实践
披光人
harmonyOS harmonyos
第一章鸿蒙声明式UI架构演进与技术优势1.1从命令式到声明式的范式迁移HarmonyNext的ArkUI3.0标志着鸿蒙开发生态的重大革新,其核心在于采用声明式UI编程范式。相较于传统Android的XML+Java/Kotlin命令式开发模式,声明式UI具有以下技术特征:状态驱动视图:UI呈现完全由数据状态决定,开发者只需描述"UI应该是什么样子",无需手动操作DOM元素单向数据流:采用Stat
数据结构 -- 字符串
_安晓
数据结构 数据结构
字符串串的定义串,即字符串(String)是由零个或多个字符组成的有限序列,一般记为S=‘a1a2a3a4’(n≥0)其中,S是串名,单引号括起来的是字符序列是串的值;ai可以是字母、数字或是其他字符;串中字符的个数n称为串的长度。n=0时的串称为空串(用∅表示)。例:(不同语言可能使用的边界符不同,Java、c等使用双引号(“”)Python等使用单引号(’‘))S="HelloWorld!"T
Java与Python详细比对 -- Java与Python优缺点
知之为
python 开发语言 java
系列文章-Java与PythonPython和Java都是比较流行的编程语言,它们各自有着独特的特性和应用场景。python用途最多的是脚本,java用途最多的是web。文章目录系列文章目录-Java与Python前言一、Java与Python整体区别二、Java与Python详细区别2.1语法结构方面2.2编程特性方面2.3语言执行及内存管理方面2.4多线程及网络编程方面2.5开发工具及相关功能
使用flask快速搭建web应用
alex190824
flask 前端 python echarts
文章目录前言一、Flask是什么?二、使用步骤1.引入包2.简单的服务端应用程序3.添加用于显示折线图代码4.在templates目录下,创建用于渲染的line-simple.html页面5.完整的代码结构前端代码后端代码总结前言在数据处理分析过程中,有快速搭建数据展示的应用场景需求,此时可以使用Flask快速进行web应用环境构建。本示例演示创建web应用及显示渲染echart折线图。一、Fla
webgl threejs 云渲染(服务器渲染、后端渲染)解决方案
allenjiao
Threejs webgl threejs 云渲染 后端渲染 服务器渲染 云流化 三维云渲染
云渲染和流式传输共享三维模型场景1、本地无需高端GPU设备即可提供三维项目渲染云渲染和云流化媒体都可以让3D模型共享变得简单便捷。配备强大GPU的远程服务器早就可以处理密集的处理工作,而专有应用程序,用户也可以从任何个人设备查看全保真模型并与之交互。2、云流媒体实现多终端联动共享价值更高在项目应用场景中,在大屏、电脑、平板、手机和其它移动终端,可以实现多屏联动、远程协助,三维云流化让客户访问时可以
SQLite学习(十一)使用JDBC读写SQLite数据,基于Java实现
Designer 小郑
SQLite从入门到实战 sqlite 数据库 sql java jdbc
1.前言2.基础工作2.1创建Java项目2.2依赖Jar包3.连接SQLite4.查询SQLite数据5.新增SQLite数据6.总结1.前言在上一篇《SQLite学习(十)SQLite的注入问题的防范、数据库文件导入和导出》中,讲解了SQLite的SQL注入问题和应对措施,在本篇博客中,将继续讲解如何使用JDBC读写SQLite数据。同学们将学习到:JDBC是什么使用JDBC读写SQLite请
JVM 调优
百里自来卷
jvm
在生产环境中,JVM调优是确保Java应用程序性能和稳定性的重要步骤。调优的目标通常是减少垃圾回收的时间、降低内存使用和提高应用程序的吞吐量。以下是一些常见的JVM调优策略和方法。选择合适的垃圾收集器-XX:+UseG1GC调整堆内存大小,通过调整堆内存的大小,可以控制应用程序的性能设置初始堆大小:-Xms512m设置最大堆大小:-Xmx2048m设置年轻代大小:-Xmn256m一般推荐将初始堆和
GC 频率和触发条件
百里自来卷
jvm
在Java中,垃圾回收(GC)的频率和触发条件取决于GC算法、堆内存分配、对象生命周期以及JVM参数的配置。下面详细介绍这些影响因素:1.GC触发条件GC主要触发的情况如下:(1)年轻代GC(MinorGC/YoungGC)触发条件:Eden区满了:当新对象分配到Eden区,如果Eden区没有足够的空间分配新对象,就会触发MinorGC。Survivor空间不足:当存活对象从Eden复制到Surv
【测试语言篇四】Python进阶篇之json模块
m0_37135615
编程语言 python php 开发语言
一、json模块介绍JSON(JavaScript对象表示法)是一种轻量级数据格式,用于数据交换。在Python中具有用于编码和解码JSON数据的内置json模块。只需导入它,就可以使用JSON数据了:importjsonJSON的一些优点:JSON作为“字节序列”存在,在我们需要通过网络传输(流)数据的情况下非常有用。与XML相比,JSON小得多,可转化为更快的数据传输和更好的体验。JSON非常
垃圾回收机制是什么 ?JVM 核心结构?
胡图蛋.
jvm
垃圾回收机制是什么jvm的垃圾回收机制是GC(GarbageCollection),也叫垃圾收集器。GC基本原理:将内存中不再被使用的对象进行回收;GC中用于回收的方法称为收集器,由于GC需要消耗一些资源和时间,Java在对对象的生命周期特征进行分析后,按照新生代、老年代的方式来对对象进行收集,以尽可能的缩短GC对应用造成的暂停。不同的对象引用类型,GC会采用不同的方法进行回收,JVM对象的引用分
HarmonyNext实战:基于ArkTS的跨平台3D图形渲染应用开发
harmonyos-next
HarmonyNext实战:基于ArkTS的跨平台3D图形渲染应用开发引言3D图形渲染是现代应用开发中的一个重要领域,尤其在游戏、虚拟现实和增强现实等场景中。HarmonyOSNext作为新一代操作系统,提供了强大的图形渲染能力,而ArkTS作为其核心开发语言,为开发者提供了高效、简洁的开发体验。本文将深入探讨如何在HarmonyNext平台上使用ArkTS开发一个跨平台的3D图形渲染应用,涵盖3
odoo-040 odoo17前端的js方法调用后端py方法action报错
Sapphire~
Odoo 总结 前端 javascript odoo
文章目录问题描述梳理写法xml写法前端方法后端action的写法错误解释问题描述在前端的kanban视图上添加了几个自定义按钮,按钮点击可以跳转到对应的tree视图,在写按钮调用方法的时候报错如下:前端调用后端action报错:action.views=[...action.views.map((v)=>[v[0],v[1]==="tree"?"list":v[1]])];//manipulate
HashMap 中的 key 值类型
百里自来卷
java
在Java中,HashMap的key一般建议使用String而不是自定义对象,主要有以下几个原因:1.String是不可变对象(Immutable)String在Java中是不可变的,一旦创建就不会改变其哈希值(hashCode)。HashMap依赖key的hashCode()计算存储位置,如果key是可变对象,修改key后,它的hashCode()可能会改变,导致HashMap无法正确查找该ke
redis操作zset类型的基本命令
JavaWeb学起来
redis redis 数据结构
zset是有序存储的数据结构,它和set一样,不允许重复的值,下面我们总结一些常用的命令。zaddkey排序的数值值(这里为了zset可以有序的存储,需要设定数值)127.0.0.1:6379>zaddz15java3redis1mysql2nginx4oracle(integer)5zcardkey(返回key中的成员数)127.0.0.1:6379>zcardz1(integer)5zrang
Java 入门指南:Java 8 新特性 —— Stream 流
热带鱼Tech
Java java 后端 个人开发 java-ee
文章目录JavaStream操作类型操作过程创建流操作流遍历forEach过滤filter映射map匹配match归约reduce排序sorted去重distinct限制limit跳过skip转换流流操作的特性JavaStreamJavaStream是Java8引入的一个新的API,它提供了一种函数式编程的方式来处理集合数据。Stream可以看作是一系列支持高效的、函数式操作的元素序列。通过使用S
Java Stream 流从零到一全指南
秋.
JAVA windows java 开发语言 流 strem
1.什么是JavaStream?JavaStream是Java8引入的一种用于处理数据集合的API,提供了声明式的方式进行数据处理。它能够支持函数式编程风格,极大地简化了集合操作,提高了代码的可读性和可维护性。Stream的核心特性链式操作:流操作可以串联在一起,避免了传统迭代方式的冗余代码。惰性求值:只有在终端操作时,流的计算才会执行。内部迭代:相比于for循环的外部迭代,Stream采用内部迭
从前端视角理解消息队列:核心问题与实战指南
秋水为渡
前端
消息队列(MessageQueue)是现代分布式系统的核心组件之一,它在前后端协作、系统解耦、流量削峰等场景中发挥着重要作用。本文从前端开发者视角出发,解析消息队列的关键问题,并结合实际场景给出解决方案。一、为什么要使用消息队列?1.前端常见场景异步任务处理:用户行为日志上报、实时通知推送流量削峰:应对秒杀活动、大文件上传等瞬时高并发场景系统解耦:前端与后端服务、第三方服务之间的松耦合通信2.前端
阿里云服务器使用教程:CentOS 7 安装JDK及Tomcat详细步骤(以jdk1.8、tomcat9.0.37为例)
蓝多多的小仓库
云服务器配置及使用 服务器 阿里云 java
目录1、下载JDK及Tomcat的安装包并上传至服务器2、安装JDK3、安装Tomcat4、Tomcat启动后无法打开Tomcat首页的原因1、下载JDK及Tomcat的安装包并上传至服务器(1)下载JDK1.8版本压缩包官网:JavaDownloads|Oracle(2)下载Tomcat9.0.37的安装包官网:ApacheTomcat®-Welcome!
探索JavaWeb之旅:Tomcat 9.0.62一站式解决方案
富展尤
探索JavaWeb之旅:Tomcat9.0.62一站式解决方案【下载地址】Tomcat9.0.62资源文件下载本仓库提供了一个用于运行JavaWeb项目的资源文件下载,具体为`tocmcat-9.0.62`版本的Tomcat9原始最新版的压缩包。该资源文件是Tomcat9.0.62的完整压缩包,适用于需要使用Tomcat9来部署和运行JavaWeb项目的开发者项目地址:https://gitcod
【2025年饿了么春招-3月14日-第二题(200分)- 小红的排列构造】(题目+思路+Java&C++&Python解析+在线测试)
塔子哥学算法
java c++ python 算法 数据结构 饿了么
题目内容小红希望你构造一个长度为nnn的排列,满足∑i=1n∗i\sum_{i
Apache OFBiz路径遍历漏洞(CVE-2024-36104)
WuY1nSec
漏洞复现 apache
0x01漏洞描述ApacheOFBiz是美国阿帕奇(Apache)基金会的一套企业资源计划(ERP)系统。该系统提供了一整套基于Java的Web应用程序组件和工具。ApacheOFBiz18.12.14之前版本存在命令执行漏洞,该漏洞源于ControlFilter对路径限制不当导致用户能够访问ProgramExport导出功能执行Groovy代码。0x02影响版本ApacheOFBiz<18.12
Spring框架快速入门手册
Uncoverlove
spring mysql mybatis java 后端
说明:本文试图将Spring框架的知识体系进行整合分析,并冠以自己的理解,为初学Spring框架的同学,提供一个快速入门手册。同时呢,也是为了总结一下工作学习中遇到的问题和经验,以免发生遗漏!文末将附上Spring的学习资料,以供大家学习~(申明一下:纯小白一枚,由于工作需要自学的Spring,或许某些理解会出现偏差,烦请各位斧正!不慎感激!!)快速入门推荐阅读书籍(欢迎补充):1、《JavaEE
Java通过Apache POI操作Excel
IT__learning
数据分析 java apache excel
1、添加依赖org.apache.poipoi3.9org.apache.poipoi-ooxml3.9joda-timejoda-time2.10.12、读EXCELpublicstaticvoidread()throwsException{FileInputStreamstream=newFileInputStream("D:\\Test\\file.xlsx");//1.创建工作簿对象,并指
英伟达系列显卡大解析B100、H200、L40S、A100
2301_78234743
java
家里有了变故。。。快手数分秋招一面面经我发现算法岗也不很难进啊(深度学习)算法想转数开…Java零基础校招学习路线突击版(吐血整理)等的花都谢了的华子最后给开了22k,武汉,应该是14a。不过在这几个月里我坚定了搞几年快钱回家和np朋友因骂了hr,boos被封了哈哈哈在央企想被开除需要做什么?2024小米分布式存储研发急招华为2012被毁意向我发现算法岗也不很难进啊(深度学习)在央企想被开除需要做
java24种设计模式目录,为大家整理最全的24种设计模式详解,必收藏
高补
java24种设计模式目录
设计模式六大原则单一职责原则一个方法尽可能做一件事情,一般来说不应该让一个方法承担多个职责。单一职责原则的英文名称是SingleResponsibilityPrinciple,简称是SRP。单一职责原则的定义是:应该有且仅有一个原因引起类的变更。SRP的原话解释是:Thereshouldneverbemorethanonereasonforaclasstochange.单一职责原则提出了一个编写程
结构型模式之适配器模式:让不兼容的接口兼容
菜就多练少说
设计模式 适配器模式
在软件开发中,经常会遇到这样一种情况:系统的不同部分需要进行交互,但由于接口不兼容,导致无法直接使用。这时,适配器模式(AdapterPattern)就能派上用场。适配器模式是设计模式中的结构型模式,它的目的是通过创建一个适配器类来“包装”一个不兼容的接口,使得两个接口能够兼容、协作。简单来说,适配器模式就是“转换接口”模式。本文将深入探讨适配器模式,讲解其概念、应用场景,并展示如何在Java中实
【JS】JS中的jQuery库简介及使用方法
菜就多练少说
javascript javascript jquery 开发语言
jQuery简介及使用方法jQuery简介如何使用jQuery1导入jQuery库2编写自己的jQuery文件3jQuery语法3.1基础语法3.2文档就绪函数3.3选择器3.4事件绑定函数结语jQuery简介jQuery是一个流行的JavaScript库,用于简化JavaScript编程。它提供了许多便捷的方法来处理DOM操作、事件处理、动画效果等,使得JavaScript开发变得更加简单和高效
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