加载 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,开发语言,后端)
js代码后续
翻滚吧键盘
vue javascript 开发语言 ecmascript
这是一个非常棒的问题,也是每个学完一个系统课程的人都会问的问题。答案是:不,你没有学完“所有”的JavaScript知识,但你已经出色地完成了成为一名合格JavaScript开发者的所有“必修课”。让我用一个比喻来解释:你已经学完了建造一栋坚固房屋所需的所有核心蓝图和关键技能。你知道如何打地基(基础语法)、如何搭建承重墙(函数与数据结构)、如何布线通电(异步编程)、如何装修得更漂亮高效(ES6+语
js代码08
翻滚吧键盘
vue javascript 开发语言 ecmascript
题目好的,我们正式进入JavaScript的另一个深水区,这也是面向对象编程的基石:this关键字。this是JavaScript中最强大、最灵活,也最容易引起困惑的概念之一。但别担心,它的行为遵循一套清晰的规则。一旦你理解了这些规则,就能完全驾驭它。练习08:this的指向-解开JS中最微妙的谜题核心法则:在学习this之前,请先记住这条黄金法则:this的值取决于函数被调用时的“执行上下文”(
Java分布式存储炼金术:故障检测与自愈的魔法阵
墨夶
Java学习资料1 java 分布式 开发语言
一、环境搭建:魔法阵的基础1.1依赖库与工具“准备炼金材料:框架、锁、断路器!”org.ap
开源 java android app 开发(十三)绘图定义控件、摇杆控件的制作
ajassi2000
linux C 到 Android App开发 开源 java android linux python
文章的目的为了记录使用java进行androidapp开发学习的经历。本职为嵌入式软件开发,公司安排开发app,临时学习,完成app的开发。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。相关链接:开源javaandroidapp开发(一)开发环境的搭建-CSDN博客开源javaandroidapp开发(二)工程文件结构-CSDN博客开源javaandroidapp开发(三)GUI界面布局和常用组
Java线程池源码分析,深度解读
努力的橙子go
java 开发语言
前言本文将深入分析Java线程池的源码,包括线程池的创建、任务提交、工作线程的执行和线程池的关闭等过程。通过对线程池源码的解析,我们能够更好地理解线程池的原理和机制,为我们在实际开发中合理使用线程池提供指导。文章内容较长,建议找个安静的环境慢慢细读。线程池简介概念在传统的多线程编程中,每次需要执行任务时都会创建一个新的线程,任务执行完毕后再销毁该线程。这种方式存在一些问题,例如频繁创建和销毁线程会
Java List Iterator ConcurrentModificationException异常原因
二十六画生的博客
Java SSM Java List Iterator ConcurrentMod
异常原因packagecom.company;importjava.util.ArrayList;importjava.util.Iterator;importjava.util.List;/***@Authoryouguess*@Date2021/1/712:33*@Version1.0*@Desc*/publicclassMain26{publicstaticvoidmain(String[]
Java中ThreadPoolExecutor源码深度解析
振华少爷
java 开发语言 前端
Java中ThreadPoolExecutor源码深度解析目录引言ThreadPoolExecutor的数据结构核心方法分析构造方法execute方法shutdown方法shutdownNow方法性能分析使用注意事项总结引言ThreadPoolExecutor是Java并发包中的一个线程池实现类,它提供了灵活的线程池管理功能,可以根据需要创建、管理和销毁线程。ThreadPoolExecutor通
【Java实战】高并发场景下账户金额操作的解决方案
.猫的树
【Java实战】系列 Java并发编程 分布式锁 高并发解决方案 原子操作 数据库事务
文章目录前言:金融系统中的并发危机一、并发问题现场还原1.1问题代码示例1.2并发测试暴露问题1.3问题根源分析二、五大解决方案深度剖析2.1synchronized同步锁2.2ReentrantLock显式锁2.3CAS无锁编程(Atomic原子类)2.4数据库乐观锁2.5分布式锁(Redis实现)三、方案选型指南四、防踩坑指南总结前言:金融系统中的并发危机在支付系统、电商平台等金融场景中,账户
Java线程池原理深度解析:从设计思想到源码实现
北辰alk
java java python 开发语言
文章目录一、线程池概述1.1为什么需要线程池1.2Java线程池框架二、线程池核心参数2.1关键参数详解2.2工作队列类型2.3拒绝策略三、线程池工作流程3.1流程图解3.2流程说明四、源码深度解析4.1核心数据结构4.2状态控制机制4.3Worker线程实现4.4任务执行核心方法4.5任务获取逻辑五、线程池使用实践5.1创建线程池的正确方式5.2线程池监控5.3合理配置参数六、常见问题与解决方案
物联网实战:多语言(Java、Go、Rust、C++、C#、Rust)设备接入与数据处理
KENYCHEN奉孝
Rust C++ go spring java vue.js rust c++
SpringBoot物联网设备接入与数据处理实例物联网(IoT)设备接入与数据处理是SpringBoot的常见应用场景之一。以下是一个完整的实例,涵盖设备接入、数据传输、数据处理和存储等关键环节。设备接入物联网设备通常通过MQTT、HTTP或WebSocket等协议接入系统。MQTT是物联网领域最常用的轻量级协议。//MQTT配置类@ConfigurationpublicclassMqttConf
【Java源码阅读系列28】深度解读Java CompletableFuture 源码
·云扬·
源码阅读系列之Java java 开发语言
Java8引入的CompletableFuture是并发编程中处理异步任务的核心工具,它通过实现Future和CompletionStage接口,提供了链式调用、任务组合、异常处理等强大功能。本文将结合源码,深入解析其核心机制、设计模式,并给出常见使用场景与代码示例。一、核心架构:状态管理与依赖任务CompletableFuture的核心目标是将异步任务的完成状态与依赖操作解耦,允许通过链式调用定
ASP.NET Core + Vue.js前后端分离黄金法则:从零到部署的深度实战
墨夶
C#学习资料 asp.net vue.js 后端
——跨域、热更新、容器化部署一网打尽为什么选择前后端分离?在微服务与敏捷开发盛行的今天,前后端分离架构已成为企业级应用的标配。本文将通过12个实战代码示例、跨域问题终极解决方案和Docker部署全流程,手把手教你实现:零配置跨域通信Vue热重载+WebAPI实时调试JWT身份验证与权限控制生产环境优化与容器化部署一、环境准备与项目搭建1.1开发环境配置工具版本要求官网链接.NET8SDK8.0.1
Webpack中的Loader详解
Loader是Webpack中用于处理模块的转换器。它们可以将文件从一种格式转换为另一种格式,比如将TypeScript转换为JavaScript,将SCSS转换为CSS等。使用Loader,可以使Webpack处理各种类型的文件,而不仅仅是JavaScript。1.Loader基础1.1.基础使用Loader就像是一个翻译员,能把源文件经过转化后输出新的结果,并且一个文件还可以链式地经过多个翻译
【Java源码阅读系列27】深度解读Java ThreadPoolExecutor 源码
·云扬·
源码阅读系列之Java java 开发语言
Java的ThreadPoolExecutor是并发编程中处理任务执行的核心类,广泛应用于异步任务调度、批量数据处理等场景。本文将从源码层面解析其核心机制,提炼设计模式,并结合实际场景给出使用示例。一、线程池核心架构:状态管理与核心参数1.1状态压缩与原子控制:ctl变量ThreadPoolExecutor通过一个原子整数ctl(类型为AtomicInteger)同时管理线程池状态(runStat
JAI Core 1.1.3:Java 高级图像处理的利器
顾润治
JAICore1.1.3:Java高级图像处理的利器javax.mediajai_core1.1.3如何下载项目地址:https://gitcode.com/Resource-Bundle-Collection/bda8b项目介绍JAICore1.1.3是JavaAdvancedImaging(JAI)库的核心组件,专为处理多媒体数据,特别是图像处理操作而设计。由SunMicrosystems(现
Java分布式任务调度交响乐:用代码指挥千台服务器跳起精准的华尔兹
墨夶
Java学习资料1 java 分布式 服务器
一、架构设计:分布式任务调度的指挥系统1.1架构图(用文字构建你的想象)[调度中心]→[任务路由]→[执行器集群]↑↓││├─数据库存储─┤││└─监控告警─┘关键组件:调度中心:任务的"总指挥",负责任务注册、调度、状态监控执行器集群:任务的"舞团",每个节点都是潜在的表演者任务路由:动态分配任务的"交通调度系统"数据库存储:任务元数据的"记分牌"二、核心技术实现:分布式调度的魔法阵2.1XXL
Java与Kotlin中的泛型之:擦除、不变、协变、逆变
Σ冰咖啡
java kotlin
Java与Kotlin中的泛型之:擦除、不变、协变、逆变前言对于Java中泛型的使用方法和应用场景等,不在本文章中作讨论,在阅读此篇文章时,我已经默认你对Java泛型有了一个较为清楚的认识和较为熟悉的应用熟练度。代码中的部分声明因篇幅原因没办法完全展示,只展示关键代码,但是别担心,你一定能看懂。本文章的内容均参考《Kotlin核心编程》中对该知识点的讲述,以及结合本人的实际开发经验。概述Java中
kotlin入门之泛型
易帜¤
kotlin android
【码上开学】Kotlin的泛型在学习kotlin泛型之前我们先来回顾一下关于Java的泛型基础吧。说道泛型,我们可能最常用的就是在三大集合中去使用。泛型将具体的类型泛化,编码的时候用符号来值代类型,在使用时再确定他的类型。因为泛型的存在,我们可以省去强制类型转化。泛型是跟类型相关的,那么是不是也能使用与类型的多态呢?场景一://多态,因为Button是TextView的子类,向上转型TextVie
java中对象可达性分析 + 自动回收算法
盒子6910
运维专栏 算法 java jvm
“对象可达性分析+自动回收算法”是JavaGC(垃圾回收)核心的两个环节,下面详细解释:1.对象可达性分析(ReachabilityAnalysis)目的:判定哪些对象“活着”,哪些对象已经变成“垃圾”可以回收。原理:JVM会用一组叫“GCRoots(垃圾收集根节点)”的基础对象为起点,从这些根出发,沿着对象之间的引用关系去递归搜索。如果某个对象能通过这条引用链与GCRoot相连,那么它就是“可达
Kotlin 细节三:集合与泛型
假笑骑士
Kotlin 基础
Kotlin的Collections与Java的关系Kotlin通过typealias关键字将Kotlin的集合映射为java的集合。目的是:为了以后在更多的平台上运行。只提供接口,具体实现依赖于平台。@SinceKotlin("1.1")publicactualtypealiasRandomAccess=java.util.RandomAccess@SinceKotlin("1.1")publi
jvm 锁升级机制
@ chen
Java基础 jvm java 多线程
Java虚拟机(JVM)中的锁升级机制(也称为锁膨胀)是HotSpot虚拟机为了优化synchronized关键字的性能而引入的一项重要技术。它的核心思想是:根据实际遇到的竞争激烈程度,动态地将锁从开销最小的状态逐步升级到开销更大的状态,从而在无竞争或低竞争时减少锁操作的开销,而在高竞争时保证必要的互斥性和线程调度能力。锁的状态主要有四种,升级路径如下:无锁->偏向锁->轻量级锁->重量级锁锁
创建对象的步骤和方法
ashui811
Java
创建对象的步骤①为对象分配内存空间,将对象的实例变量初始化为其变量类型的默认值②如果实例变量在声明时被显式的初始化则将初始化值赋给实例变量③调用构造方法④返回对象的引用创建对象的方式①类名对象名=new类名();最常用方式,步骤①②③④②运用反射手段,调用java.lang.Class或者java.lang.reflect.Constructor类的newInstance()实例方法步骤①②③④③
spring注解整合
多大的心灵伤害吖
spring java
使用注解的优势:1.采用纯java代码,不在需要配置繁杂的xml文件2.在配置中也可享受面向对象带来的好处3.类型安全对重构可以提供良好的支持4.减少复杂配置文件的同时亦能享受到springIoC容器提供的功能一、注解详解(配备了完善的释义)------(可采用ctrl+F来进行搜索哦~~~~)@SpringBootApplication:申明让springboot自动给程序进行必要的配置,这个配
java数据保存到文件_Java把数据存储到本地txt文件
永不放弃yes
java数据保存到文件
码农公社210.net.cn210=102410月24日一个重要的节日--码农(程序员)节Java把数据存储到本地txt文件java存储数据,方便打印日志等1、覆盖以前的数据try{FilewriteName=newFile("D:\\data.txt");//相对路径,如果没有则要建立一个新的output.txt文件if(!writeName.exists()){writeName.create
纯前端本地文件管理器(VSCode风格)(浏览器对本地文件增删改查)
与鱼有约
前端 vscode ide
纯前端本地文件管理器(VSCode风格)(浏览器对本地文件增删改查)简介本项目为一个纯前端实现的本地文件管理器网页(index.html),可在Chrome/Edge浏览器中直接打开,具备类似VSCode的本地文件夹操作体验。无需后端,所有功能均在浏览器端实现。主要功能选择本地文件夹用户点击左上角文件夹按钮,授权后可浏览和操作本地文件夹内容。文件树展示以树形结构展示所选文件夹下的所有文件和子文件夹
Java 正则表达式
你都会上树?
Java java 正则表达式
基本语法元字符转义号\\限定符符号含义*指定前面内容可以出现0次及以上+指定前面内容可以出现1次及以上。至少会出现一次?指定前面内容可以出现0次或一次{n}指定前面的内容只能出现n次{n,}指定前面内容至少出现n次{n,m}指定前面内容至少出现n次但不多于m次选择匹配符符号解释|逻辑或的含义,符号两边内容有一个成立即可|分组组合和反向引用符分组常用分组构造形式说明(pattern)非命名捕获。捕获
Java 大顶堆、小顶堆
你都会上树?
数据结构 java 开发语言 数据结构
堆堆结构实际上是一个完全二叉树,不过它又在完全二叉树的基础上做了升级。小顶堆:其每个节点的父节点都小于当前节点,那么根节点就是其最小的节点。大顶堆:其正好与小顶堆相反,每个节点的父节点都大于当前节点,所以根节点就是最大的节点。结构在Java中,没有实际意义上的堆数据结构。不过,通常都使用数组来存储。接下来边简单概述为什么要使用数组以及数组存储的好处。对于完全二叉树结构,它当前所在层数用n表示,那么
数据结构:链表和二叉树的应用和算法设计
鱼弦
数据结构 链表
鱼弦:CSDN内容合伙人、CSDN新星导师、全栈领域创作新星创作者、51CTO(Top红人+专家博主)、github开源爱好者(go-zero源码二次开发、游戏后端架构https://github.com/Peakchen)链表:链表是一种常见的线性数据结构,由一系列节点组成,每个节点包含数据和指向下一个节点的指针。链表的优势在于可以动态添加和删除元素,不需要预先分配固定大小的内存空间。链表常用于
Java SE知识点五:面向对象之:继承
时来天地皆同力.
Java SE知识点 java 开发语言 改行学it idea intellij idea 后端
1.什么是继承1.1继承的概念Java中的类是对事物的抽象描述,但有时候一个类与其他类有许多的共性,比如猫和狗都是动物,那么猫和狗这两个类就难免会有许多相似之处:classDog{publicStringname;publicintage;publicStringcolor;publicvoideat(){}publicvoidsleep(){}}classCat{publicStringname
Java基础 集合框架 之Set框架之TreeSet
骑牛小道士
集合框架之Set java 开发语言
TreeSetTreeSet数据结构及实现原理TreeSet的构造方法TreeSet核心特性有序性(`排序大小输出`)自然排序定制排序唯一性底层数据结构:红黑树导航方法(特色核心优势)基础导航方法范围视图(不修改原集合)提取和删除元素逆序视图不允许null元素TreeSet线程不安全TreeSet线程不安全体现解决方案TreeSet优缺点TreeSet应用场景类结构传承去区别于HashSet实现了
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