加载 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,开发语言,后端)
移动端城市区县二级联动选择功能实现包
good2know
本文还有配套的精品资源,点击获取简介:本项目是一套为移动端设计的jQuery实现方案,用于简化用户在选择城市和区县时的流程。它包括所有必需文件:HTML、JavaScript、CSS及图片资源。通过动态更新下拉菜单选项,实现城市到区县的联动效果,支持数据异步加载。开发者可以轻松集成此功能到移动网站或应用,并可基于需求进行扩展和优化。1.jQuery移动端解决方案概述jQuery技术简介jQuery
深入解析JVM工作原理:从字节码到机器指令的全过程
一、JVM概述Java虚拟机(JVM)是Java平台的核心组件,它实现了Java"一次编写,到处运行"的理念。JVM是一个抽象的计算机器,它有自己的指令集和运行时内存管理机制。JVM的主要职责:加载:读取.class文件并验证其正确性存储:管理内存分配和垃圾回收执行:解释或编译字节码为机器指令安全:提供沙箱环境限制恶意代码二、JVM架构详解JVM由三个主要子系统组成:1.类加载子系统类加载过程分为
JVM 内存模型深度解析:原子性、可见性与有序性的实现
练习时长两年半的程序员小胡
JVM 深度剖析:从面试考点到生产实践 jvm java 内存模型
在了解了JVM的基础架构和类加载机制后,我们需要进一步探索Java程序在多线程环境下的内存交互规则。JVM内存模型(JavaMemoryModel,JMM)定义了线程和主内存之间的抽象关系,它通过规范共享变量的访问方式,解决了多线程并发时的数据一致性问题。本文将从内存模型的核心目标出发,详解原子性、可见性、有序性的实现机制,以及volatile、synchronized等关键字在其中的作用。一、J
Java | 多线程经典问题 - 售票
Ada54
一、售票需求1)同一个票池2)多个窗口卖票,不能出售同一张票二、售票问题代码实现(线程与进程小总结,请戳:Java|线程和进程,创建线程)step1:定义SaleWindow类实现Runnable接口,覆盖run方法step2:实例化SaleWindow对象,创建Thread对象,将SaleWindow作为参数传给Thread类的构造函数,然后通过Thread.start()方法启动线程step3
SpringMVC的执行流程
1、什么是MVCMVC是一种设计模式。MVC的原理图如下所示M-Model模型(完成业务逻辑:有javaBean构成,service+dao+entity)V-View视图(做界面的展示jsp,html……)C-Controller控制器(接收请求—>调用模型—>根据结果派发页面2、SpringMVC是什么SpringMVC是一个MVC的开源框架,SpringMVC=Struts2+Spring,
JAVA接口机结构解析
秃狼
SpringBoot 八股文 Java java 学习
什么是接口机在Java项目中,接口机通常指用于与外部系统进行数据交互的中间层,负责处理请求和响应的转换、协议适配、数据格式转换等任务。接口机的结构我们的接口机的结构分为两个大部分,外部接口机和内部接口机,在业务的调度上也是通过mq来实现的,只要的目的就是为了解耦合和做差异化。在接口机中主要的方法就是定时任务,消息的发送和消费,其他平台调用接口机只能提供外部接口机的方法进行调用,外部接口机可以提供消
最新阿里四面面试真题46道:面试技巧+核心问题+面试心得
风平浪静如码
前言做技术的有一种资历,叫做通过了阿里的面试。这些阿里Java相关问题,都是之前通过不断优秀人才的铺垫总结的,先自己弄懂了再去阿里面试,不然就是去丢脸,被虐。希望对大家帮助,祝面试成功,有个更好的职业规划。一,阿里常见技术面1、微信红包怎么实现。2、海量数据分析。3、测试职位问的线程安全和非线程安全。4、HTTP2.0、thrift。5、面试电话沟通可能先让自我介绍。6、分布式事务一致性。7、ni
图论算法经典题目解析:DFS、BFS与拓扑排序实战
周童學
数据结构与算法 深度优先 算法 图论
图论算法经典题目解析:DFS、BFS与拓扑排序实战图论问题是算法面试中的高频考点,本博客将通过四道LeetCode经典题目(均来自"Top100Liked"题库),深入讲解图论的核心算法思想和实现技巧。涵盖DFS、BFS、拓扑排序和前缀树等知识点,每道题配有Java实现和易错点分析。1.岛屿数量(DFS遍历)问题描述给定一个由'1'(陆地)和'0'(水)组成的二维网格,计算岛屿的数量。岛屿由水平或
【异常】使用 LiteFlow 框架时,提示错误ChainDuplicateException: [chain name duplicate] chainName=categoryChallenge
本本本添哥
002 - 进阶开发能力 java
一、报错内容Causedby:com.yomahub.liteflow.exception.ChainDuplicateException:[chainnameduplicate]chainName=categoryChallengeatcom.yomahub.liteflow.parser.helper.ParserHelper.lambda$null$0(ParserHelper.java:1
Java并发核心:线程池使用技巧与最佳实践! | 多线程篇(五)
bug菌¹
Java实战(进阶版) java Java零基础入门 Java并发 线程池 多线程篇
本文收录于「Java进阶实战」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!环境说明:Windows10+IntelliJIDEA2021.3.2+Jdk1.8本文目录前言摘要正文何为线程池?为什么需要线程池?线程池的好处线程池使用场景如何创建线程池?线程池的常见配置源码解析案例分享案例代码演示案例运行
Java 队列
tryxr
java 开发语言 队列
队列一般用什么哪种结构实现队列的特性数据入队列时一定是从尾部插入吗数据出队列时一定是从头部删除吗队列的基本运算有什么队列支持随机访问吗队列的英文表示什么是队列队列从哪进、从哪出队列的进出顺序队列是用哪种结构实现的Queue和Deque有什么区别Queue接口的方法Queue中的add与offer的区别offer、poll、peek的模拟实现如何利用链表实现队列如何利用顺序表实现队列什么叫做双端队列
JVM 内存分配与回收策略:从对象创建到内存释放的全流程
在JVM的运行机制中,内存分配与回收策略是连接对象生命周期与垃圾收集器的桥梁。它决定了对象在堆内存中的创建位置、存活过程中的区域迁移,以及最终被回收的时机。合理的内存分配策略能减少GC频率、降低停顿时间,是优化Java应用性能的核心环节。本文将系统解析JVM的内存分配规则、对象晋升机制,以及实战中的内存优化技巧。一、对象优先在Eden区分配:新生代的“临时缓冲区”大多数情况下,Java对象在新生代
代码随想录算法训练营第三十五天
01背包问题二维题目链接01背包问题二维题解importjava.util.Scanner;publicclassMain{publicstaticvoidmain(String[]args){Scannersc=newScanner(System.in);intM=sc.nextInt();intN=sc.nextInt();int[]space=newint[M];int[]value=new
微信公众号回调java_处理微信公众号消息回调
weixin_39607620
微信公众号回调java
1、背景在上一节中,咱们知道如何接入微信公众号,可是以后公众号会与咱们进行交互,那么微信公众号如何通知到咱们本身的服务器呢?咱们知道咱们接入的时候提供的url是GET/mp/entry,那么公众号以后产生的事件将会以POST/mp/entry发送到咱们本身的服务器上。html2、代码实现,此处仍是使用weixin-java-mp这个框架实现一、引入weixin-java-mpcom.github.
学C++的五大惊人好处
为什么要学c++学c++有什么用学习c++的好处有1.中考可以加分2.高考可能直接录取3.就业广且工资高4.在未来30--50年c++一定是一个很受欢迎的职业5.c++成功的例子deepsick等AI智能C++语言兼备编程效率和编译运行效率的语言C++语言是C语言功能增强版,在c语言的基础上添加了面向对象编程和泛型编程的支持既继承了C语言高效,简洁,快速和可移植的传统,又具备类似Java、Go等其
Java8 Stream流的sorted()的排序【正序、倒序、多字段排序】
Tony666688888
java windows 开发语言
针对集合排序,java8可以用Stream流的sorted()进行排序。示例Bean以下我们会使用这个Bean来做示例。publicclassOrder{privateStringweight;privateDoubleprice;privateStringdateStr;//忽略getter、setter、构造方法、toString}字段排序首先是比较器Comparator,形式如下:Compa
用代码生成艺术字:设计个性化海报的秘密
本文围绕“用代码生成艺术字:设计个性化海报的秘密”展开,先概述代码生成艺术字在海报设计中的独特价值,接着介绍常用的代码工具(如HTML、CSS、JavaScript等),详细阐述从构思到实现的完整流程,包括字体样式设计、动态效果添加等,还分享了提升艺术字质感的技巧及实际案例。最后总结代码生成艺术字的优势,为设计师提供打造个性化海报的实用指南,助力提升海报设计的独特性与吸引力,符合搜索引擎SEO标准
java实习生40多天有感
别拿爱情当饭吃
从5月15日开始,我开始第一步步入社会,我今年大三,在一家上市互联网公司做一名实习生,主要做java后端开发。开始的时候,觉得公司的环境挺不错的,不过因为公司在CBD,所以隔壁的午饭和晚饭都要20+RMB,而且还吃不饱,这让我感觉挺郁闷的。一到下午,我就会犯困(因为饿)。因此,我又不得不买一些干粮在公司屯着。关于技术,有一个比较大的项目在需求调研当中,我们做实习生,就是辅助项目经理,测试功能,并且
大数据技术笔记—spring入门
卿卿老祖
篇一spring介绍spring.io官网快速开始Aop面向切面编程,可以任何位置,并且可以细致到方法上连接框架与框架Spring就是IOCAOP思想有效的组织中间层对象一般都是切入service层spring组成前后端分离已学方式,前后台未分离:Spring的远程通信:明日更新创建第一个spring项目来源:科多大数据
大学生入门:初识方法及其易踩坑的点
在java学习过程中,我们不难发现有很多重复使用的功能代码块,每次使用如果都要重新写一遍,岂不是很麻烦,就算是“cv”大法,感觉也不是很方便,那么,有什么办法可以解决这个问题呢?方法!java中,一段可重用的,用于执行特定功能的代码块叫做方法,它可以接收参数、返回结果,并且可以被多次使用。一、方法的基本结构[修饰符]返回值类型方法名([参数列表])[throws异常类型]{//方法体}[throw
[Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
这些不会的
解释:这个错误是很常见的错误,错误的提示已经很清楚了就是java的Object数组不能转换成为String[]数组,这就说明你要转换的数组它本身是Object类型的数组,但是你却非要把它转换为String类的数组,这当然是错误的。示例:[java]viewplaincopypackagecom.dada;importjava.util.ArrayList;importjava.util.List;
HikariCP调试日志深度解析:生产环境故障排查完全指南
HikariCP调试日志深度解析:生产环境故障排查完全指南更新时间:2025年7月4日|作者:资深架构师|适用版本:HikariCP5.x+|难度等级:中高级前言在生产环境中,数据库连接池往往是系统性能的关键瓶颈。HikariCP作为当前最流行的Java连接池,其调试日志包含了丰富的运行时信息,能够帮助我们快速定位和解决各种连接池相关问题。本文将深入解析HikariCP的日志体系,提供一套完整的故
大学社团管理系统(11831)
codercode2022
java spring boot spring echarts spring cloud sentinel java-rocketmq
有需要的同学,源代码和配套文档领取,加文章最下方的名片哦一、项目演示项目演示视频二、资料介绍完整源代码(前后端源代码+SQL脚本)配套文档(LW+PPT+开题报告)远程调试控屏包运行三、技术介绍Java语言SSM框架SpringBoot框架Vue框架JSP页面Mysql数据库IDEA/Eclipse开发四、项目截图有需要的同学,源代码和配套文档领取,加文章最下方的名片哦!
今年校招竞争真激烈
12_05
程序员满大街,都要找不到工作了。即使人工智能满大街,我也后悔当初没学机器学习,后悔当初没学Java。C++真难找工作。难道毕了业就失业吗?好担心!
【免费下载】 Aspose for Java:解锁无水印、无限制的文档处理能力
房征劲Kendall
AsposeforJava:解锁无水印、无限制的文档处理能力【下载地址】AsposeforJava-去除水印和数量限制AsposeforJava-去除水印和数量限制Aspose是一个著名的文档处理库,专为Java应用程序设计,支持多种文档格式的操作,如Word、Excel、PDF等项目地址:https://gitcode.com/open-source-toolkit/56c82项目介绍在现代企业
微服务日志追踪,Skywalking接入TraceId功能
Victor刘
微服务 skywalking java
文章目录一、借助skywalking追加traceIdlogbacklog4j2效果二、让skywalking显示日志内容版本差异logback配置文件log4j2配置文件一、借助skywalking追加traceId背景:在微服务或多副本中难以观察一个链路的日志,需要通过唯一traceId标识来查找,下面介绍Skywalking-traceId在Java中的配置方法。介绍两种java日志的配置方
【Java Web实战】从零到一打造企业级网上购书网站系统 | 完整开发实录(三)
笙囧同学
java 前端 状态模式
核心功能设计用户管理系统用户管理是整个系统的基础,我设计了完整的用户生命周期管理:用户注册流程验证失败验证通过验证失败验证通过用户名已存在用户名可用失败成功用户访问注册页面填写注册信息前端表单验证显示错误提示提交到后端后端数据验证返回错误信息用户名唯一性检查提示用户名重复密码加密处理保存用户信息保存成功?显示系统错误注册成功跳转登录页面登录认证机制深度解析我实现了一套企业级的多层次安全认证机制:认
从零到一:基于差分隐私决策树的客户购买预测系统实战开发
笙囧同学
决策树 算法 机器学习
作者简介:笙囧同学,中科院计算机大模型方向硕士,全栈开发爱好者联系方式:
[email protected] 各大平台账号:笙囧同学座右铭:偷懒是人生进步的阶梯文章导航快速导航前言-项目背景与价值项目概览-系统架构与功能技术深度解析-核心算法原理️系统实现详解-工程实践细节性能评估与分析-实验结果分析Web系统开发-前后端开发部署与运维-DevOps实践完整复现指南-手把手教程️实践案例与故障排除-问
从零到一:打造基于GigaChat AI的艺术创作平台 | 笙囧同学的全栈开发实战
作者简介:笙囧同学,中科院计算机大模型方向硕士,全栈开发爱好者联系方式:
[email protected] 各大平台账号:笙囧同学座右铭:偷懒是人生进步的阶梯前言在AI技术飞速发展的今天,如何将前沿的大模型技术与实际应用相结合,一直是我们开发者关注的焦点。今天,笙囧同学将带大家从零开始,构建一个基于GigaChatAI的艺术创作平台,实现React前端+Django后端的完整全栈解决方案。这不仅仅是
基于STM32设计的LCD指针式电子钟与日历项目
鱼弦
单片机系统合集 stm32 嵌入式硬件 单片机
鱼弦:公众号【红尘灯塔】,CSDN博客专家、内容合伙人、新星导师、全栈领域优质创作者、51CTO(Top红人+专家博主)、github开源爱好者(go-zero源码二次开发、游戏后端架构https://github.com/Peakchen)基于STM32设计的LCD指针式电子钟与日历项目1.介绍基于STM32设计的LCD指针式电子钟与日历项目是一款利用STM32微控制器、LCD显示屏和指针机构实
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