加载 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,开发语言,后端)
Long类型前后端数据不一致
igotyback
前端
响应给前端的数据浏览器控制台中response中看到的Long类型的数据是正常的到前端数据不一致前后端数据类型不匹配是一个常见问题,尤其是当后端使用Java的Long类型(64位)与前端JavaScript的Number类型(最大安全整数为2^53-1,即16位)进行数据交互时,很容易出现精度丢失的问题。这是因为JavaScript中的Number类型无法安全地表示超过16位的整数。为了解决这个问
LocalDateTime 转 String
igotyback
java 开发语言
importjava.time.LocalDateTime;importjava.time.format.DateTimeFormatter;publicclassMain{publicstaticvoidmain(String[]args){//获取当前时间LocalDateTimenow=LocalDateTime.now();//定义日期格式化器DateTimeFormatterformat
Linux下QT开发的动态库界面弹出操作(SDL2)
13jjyao
QT类 qt 开发语言 sdl2 linux
需求:操作系统为linux,开发框架为qt,做成需带界面的qt动态库,调用方为java等非qt程序难点:调用方为java等非qt程序,也就是说调用方肯定不带QApplication::exec(),缺少了这个,QTimer等事件和QT创建的窗口将不能弹出(包括opencv也是不能弹出);这与qt调用本身qt库是有本质的区别的思路:1.调用方缺QApplication::exec(),那么我们在接口
使用 FinalShell 进行远程连接(ssh 远程连接 Linux 服务器)
编程经验分享
开发工具 服务器 ssh linux
目录前言基本使用教程新建远程连接连接主机自定义命令路由追踪前言后端开发,必然需要和服务器打交道,部署应用,排查问题,查看运行日志等等。一般服务器都是集中部署在机房中,也有一些直接是云服务器,总而言之,程序员不可能直接和服务器直接操作,一般都是通过ssh连接来登录服务器。刚接触远程连接时,使用的是XSHELL来远程连接服务器,连接上就能够操作远程服务器了,但是仅用XSHELL并没有上传下载文件的功能
DIV+CSS+JavaScript技术制作网页(旅游主题网页设计与制作)云南大理
STU学生网页设计
网页设计 期末网页作业 html静态网页 html5期末大作业 网页设计 web大作业
️精彩专栏推荐作者主页:【进入主页—获取更多源码】web前端期末大作业:【HTML5网页期末作业(1000套)】程序员有趣的告白方式:【HTML七夕情人节表白网页制作(110套)】文章目录二、网站介绍三、网站效果▶️1.视频演示2.图片演示四、网站代码HTML结构代码CSS样式代码五、更多源码二、网站介绍网站布局方面:计划采用目前主流的、能兼容各大主流浏览器、显示效果稳定的浮动网页布局结构。网站程
【华为OD机试真题2023B卷 JAVA&JS】We Are A Team
若博豆
java 算法 华为 javascript
华为OD2023(B卷)机试题库全覆盖,刷题指南点这里WeAreATeam时间限制:1秒|内存限制:32768K|语言限制:不限题目描述:总共有n个人在机房,每个人有一个标号(1<=标号<=n),他们分成了多个团队,需要你根据收到的m条消息判定指定的两个人是否在一个团队中,具体的:1、消息构成为:abc,整数a、b分别代
关于城市旅游的HTML网页设计——(旅游风景云南 5页)HTML+CSS+JavaScript
二挡起步
web前端期末大作业 javascript html css 旅游 风景
⛵源码获取文末联系✈Web前端开发技术描述网页设计题材,DIV+CSS布局制作,HTML+CSS网页设计期末课程大作业|游景点介绍|旅游风景区|家乡介绍|等网站的设计与制作|HTML期末大学生网页设计作业,Web大学生网页HTML:结构CSS:样式在操作方面上运用了html5和css3,采用了div+css结构、表单、超链接、浮动、绝对定位、相对定位、字体样式、引用视频等基础知识JavaScrip
HTML网页设计制作大作业(div+css) 云南我的家乡旅游景点 带文字滚动
二挡起步
web前端期末大作业 web设计网页规划与设计 html css javascript dreamweaver 前端
Web前端开发技术描述网页设计题材,DIV+CSS布局制作,HTML+CSS网页设计期末课程大作业游景点介绍|旅游风景区|家乡介绍|等网站的设计与制作HTML期末大学生网页设计作业HTML:结构CSS:样式在操作方面上运用了html5和css3,采用了div+css结构、表单、超链接、浮动、绝对定位、相对定位、字体样式、引用视频等基础知识JavaScript:做与用户的交互行为文章目录前端学习路线
node.js学习
小猿L
node.js node.js 学习 vim
node.js学习实操及笔记温故node.js,node.js学习实操过程及笔记~node.js学习视频node.js官网node.js中文网实操笔记githubcsdn笔记为什么学node.js可以让别人访问我们编写的网页为后续的框架学习打下基础,三大框架vuereactangular离不开node.jsnode.js是什么官网:node.js是一个开源的、跨平台的运行JavaScript的运行
Low Power概念介绍-Voltage Area
飞奔的大虎
随着智能手机,以及物联网的普及,芯片功耗的问题最近几年得到了越来越多的重视。为了实现集成电路的低功耗设计目标,我们需要在系统设计阶段就采用低功耗设计的方案。而且,随着设计流程的逐步推进,到了芯片后端设计阶段,降低芯片功耗的方法已经很少了,节省的功耗百分比也不断下降。芯片的功耗主要由静态功耗(staticleakagepower)和动态功耗(dynamicpower)构成。静态功耗主要是指电路处于等
Java 重写(Override)与重载(Overload)
叨唧唧的
Java重写(Override)与重载(Overload)重写(Override)重写是子类对父类的允许访问的方法的实现过程进行重新编写,返回值和形参都不能改变。即外壳不变,核心重写!重写的好处在于子类可以根据需要,定义特定于自己的行为。也就是说子类能够根据需要实现父类的方法。重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常。例如:父类的一个方法申明了一个检查异常IOExceptio
简单了解 JVM
记得开心一点啊
jvm
目录♫什么是JVM♫JVM的运行流程♫JVM运行时数据区♪虚拟机栈♪本地方法栈♪堆♪程序计数器♪方法区/元数据区♫类加载的过程♫双亲委派模型♫垃圾回收机制♫什么是JVMJVM是JavaVirtualMachine的简称,意为Java虚拟机。虚拟机是指通过软件模拟的具有完整硬件功能的、运行在一个完全隔离的环境中的完整计算机系统(如:JVM、VMwave、VirtualBox)。JVM和其他两个虚拟机
1分钟解决 -bash: mvn: command not found,在Centos 7中安装Maven
Energet!c
开发语言
1分钟解决-bash:mvn:commandnotfound,在Centos7中安装Maven检查Java环境1下载Maven2解压Maven3配置环境变量4验证安装5常见问题与注意事项6总结检查Java环境Maven依赖Java环境,请确保系统已经安装了Java并配置了环境变量。可以通过以下命令检查:java-version如果未安装,请先安装Java。1下载Maven从官网下载:前往Apach
Java企业面试题3
马龙强_
java
1.break和continue的作用(智*图)break:用于完全退出一个循环(如for,while)或一个switch语句。当在循环体内遇到break语句时,程序会立即跳出当前循环体,继续执行循环之后的代码。continue:用于跳过当前循环体中剩余的部分,并开始下一次循环。如果是在for循环中使用continue,则会直接进行条件判断以决定是否执行下一轮循环。2.if分支语句和switch分
JVM、JRE和 JDK:理解Java开发的三大核心组件
Y雨何时停T
Java java
Java是一门跨平台的编程语言,它的成功离不开背后强大的运行环境与开发工具的支持。在Java的生态中,JVM(Java虚拟机)、JRE(Java运行时环境)和JDK(Java开发工具包)是三个至关重要的核心组件。本文将探讨JVM、JDK和JRE的区别,帮助你更好地理解Java的运行机制。1.JVM:Java虚拟机(JavaVirtualMachine)什么是JVM?JVM,即Java虚拟机,是Ja
Java面试题精选:消息队列(二)
芒果不是芒
Java面试题精选 java kafka
一、Kafka的特性1.消息持久化:消息存储在磁盘,所以消息不会丢失2.高吞吐量:可以轻松实现单机百万级别的并发3.扩展性:扩展性强,还是动态扩展4.多客户端支持:支持多种语言(Java、C、C++、GO、)5.KafkaStreams(一个天生的流处理):在双十一或者销售大屏就会用到这种流处理。使用KafkaStreams可以快速的把销售额统计出来6.安全机制:Kafka进行生产或者消费的时候会
白骑士的Java教学基础篇 2.5 控制流语句
白骑士所长
Java 教学 java 开发语言
欢迎继续学习Java编程的基础篇!在前面的章节中,我们了解了Java的变量、数据类型和运算符。接下来,我们将探讨Java中的控制流语句。控制流语句用于控制程序的执行顺序,使我们能够根据特定条件执行不同的代码块,或重复执行某段代码。这是编写复杂程序的基础。通过学习这一节内容,你将掌握如何使用条件语句和循环语句来编写更加灵活和高效的代码。条件语句条件语句用于根据条件的真假来执行不同的代码块。if语句‘
python语法——三目运算符
HappyRocking
python python 三目运算符
在java中,有三目运算符,如:intc=(a>b)?a:b表示c取两者中的较大值。但是在python,不能直接这样使用,估计是因为冒号在python有分行的关键作用。那么在python中,如何实现类似功能呢?可以使用ifelse语句,也是一行可以完成,格式为:aifbelsec表示如果b为True,则表达式等于a,否则等于c。如:c=(aif(a>b)elseb)同样是完成了取最大值的功能。
ArrayList 源码解析
程序猿进阶
Java基础 ArrayList List java 面试 性能优化 架构设计 idea
ArrayList是Java集合框架中的一个动态数组实现,提供了可变大小的数组功能。它继承自AbstractList并实现了List接口,是顺序容器,即元素存放的数据与放进去的顺序相同,允许放入null元素,底层通过数组实现。除该类未实现同步外,其余跟Vector大致相同。每个ArrayList都有一个容量capacity,表示底层数组的实际大小,容器内存储元素的个数不能多于当前容量。当向容器中添
Java爬虫框架(一)--架构设计
狼图腾-狼之传说
java 框架 java 任务 html解析器 存储 电子商务
一、架构图那里搜网络爬虫框架主要针对电子商务网站进行数据爬取,分析,存储,索引。爬虫:爬虫负责爬取,解析,处理电子商务网站的网页的内容数据库:存储商品信息索引:商品的全文搜索索引Task队列:需要爬取的网页列表Visited表:已经爬取过的网页列表爬虫监控平台:web平台可以启动,停止爬虫,管理爬虫,task队列,visited表。二、爬虫1.流程1)Scheduler启动爬虫器,TaskMast
Java:爬虫框架
dingcho
Java java 爬虫
一、ApacheNutch2【参考地址】Nutch是一个开源Java实现的搜索引擎。它提供了我们运行自己的搜索引擎所需的全部工具。包括全文搜索和Web爬虫。Nutch致力于让每个人能很容易,同时花费很少就可以配置世界一流的Web搜索引擎.为了完成这一宏伟的目标,Nutch必须能够做到:每个月取几十亿网页为这些网页维护一个索引对索引文件进行每秒上千次的搜索提供高质量的搜索结果简单来说Nutch支持分
python怎么将png转为tif_png转tif
weixin_39977276
发国外的文章要求图片是tif,cmyk色彩空间的。大小尺寸还有要求。比如网上大神多,找到了一段代码,感谢!https://www.jianshu.com/p/ec2af4311f56https://github.com/KevinZc007/image2Tifimportjava.awt.image.BufferedImage;importjava.io.File;importjava.io.Fi
springboot+vue项目实战一-创建SpringBoot简单项目
苹果酱0567
面试题汇总与解析 spring boot 后端 java 中间件 开发语言
这段时间抽空给女朋友搭建一个个人博客,想着记录一下建站的过程,就当做笔记吧。虽然复制zjblog只要一个小时就可以搞定一个网站,或者用cms系统,三四个小时就可以做出一个前后台都有的网站,而且想做成啥样也都行。但是就是要从新做,自己做的意义不一样,更何况,俺就是专门干这个的,嘿嘿嘿要做一个网站,而且从零开始,首先呢就是技术选型了,经过一番思量决定选择-SpringBoot做后端,前端使用Vue做一
JavaScript 中,深拷贝(Deep Copy)和浅拷贝(Shallow Copy)
跳房子的前端
前端面试 javascript 开发语言 ecmascript
在JavaScript中,深拷贝(DeepCopy)和浅拷贝(ShallowCopy)是用于复制对象或数组的两种不同方法。了解它们的区别和应用场景对于避免潜在的bugs和高效地处理数据非常重要。以下是对深拷贝和浅拷贝的详细解释,包括它们的概念、用途、优缺点以及实现方式。1.浅拷贝(ShallowCopy)概念定义:浅拷贝是指创建一个新的对象或数组,其中包含了原对象或数组的基本数据类型的值和对引用数
uniapp map组件自定义markers标记点
以对_
uni-app学习记录 uni-app javascript 前端
需求是根据后端返回数据在地图上显示标记点,并且根据数据状态控制标记点颜色,标记点背景通过两张图片实现控制{{item.options.labelName}}exportdefault{data(){return{storeIndex:0,locaInfo:{longitude:120.445172,latitude:36.111387},markers:[//标点列表{id:1,//标记点idin
JAVA·一个简单的登录窗口
MortalTom
java 开发语言 学习
文章目录概要整体架构流程技术名词解释技术细节资源概要JavaSwing是Java基础类库的一部分,主要用于开发图形用户界面(GUI)程序整体架构流程新建项目,导入sql.jar包(链接放在了文末),编译项目并运行技术名词解释一、特点丰富的组件提供了多种可视化组件,如按钮(JButton)、文本框(JTextField)、标签(JLabel)、下拉列表(JComboBox)等,可以满足不同的界面设计
笋丁网页自动回复机器人V3.0.0免授权版源码
希希分享
软希网58soho_cn 源码资源 笋丁网页自动回复机器人
笋丁网页机器人一款可设置自动回复,默认消息,调用自定义api接口的网页机器人。此程序后端语言使用Golang,内存占用最高不超过30MB,1H1G服务器流畅运行。仅支持Linux服务器部署,不支持虚拟主机,请悉知!使用自定义api功能需要有一定的建站基础。源码下载:https://download.csdn.net/download/m0_66047725/89754250更多资源下载:关注我。安
WebMagic:强大的Java爬虫框架解析与实战
Aaron_945
Java java 爬虫 开发语言
文章目录引言官网链接WebMagic原理概述基础使用1.添加依赖2.编写PageProcessor高级使用1.自定义Pipeline2.分布式抓取优点结论引言在大数据时代,网络爬虫作为数据收集的重要工具,扮演着不可或缺的角色。Java作为一门广泛使用的编程语言,在爬虫开发领域也有其独特的优势。WebMagic是一个开源的Java爬虫框架,它提供了简单灵活的API,支持多线程、分布式抓取,以及丰富的
博客网站制作教程
2401_85194651
java maven
首先就是技术框架:后端:Java+SpringBoot数据库:MySQL前端:Vue.js数据库连接:JPA(JavaPersistenceAPI)1.项目结构blog-app/├──backend/│├──src/main/java/com/example/blogapp/││├──BlogApplication.java││├──config/│││└──DatabaseConfig.java
00. 这里整理了最全的爬虫框架(Java + Python)
有一只柴犬
爬虫系列 爬虫 java python
目录1、前言2、什么是网络爬虫3、常见的爬虫框架3.1、java框架3.1.1、WebMagic3.1.2、Jsoup3.1.3、HttpClient3.1.4、Crawler4j3.1.5、HtmlUnit3.1.6、Selenium3.2、Python框架3.2.1、Scrapy3.2.2、BeautifulSoup+Requests3.2.3、Selenium3.2.4、PyQuery3.2
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