简单介绍Struts2
Struts2概述
Struts2虽然是Struts1的基础上发展起来的,但是实质上是以WebWork框架为核心,为传统的Struts1注入了WebWork的设计理念,统一了Struts1和WebWork两个框架,可以说是一个不同于Struts1的一个全新的框架。
Struts2和WebWork一样,使用拦截器作为处理,以用户的业务逻辑控制器为目标,创建一个控制器代理。
Struts2的入门
Struts2框架的大致处理流程如下:
1.浏览器发送请求
2.浏览器请求被Struts2提供的过滤器StrutsPrepareAndExecuteFilter拦截
3.核心控制器FilterDispatcher根据请求决定调用合适的Action
4.Struts2的拦截器链自动对请求应用通用功能,也包含用户自定义的一些拦截器
5.回调Action的execute方法或自定义的action方法,首先这些方法会获得用户请求的参数,然后执行某种数据库的操作
6.返回result视图,处理的结果会被输出到浏览器中
对于框架来说,一些常用的框架有Struts2 spring hibernate springmvc mybatis sprintboot等。
如何在eclipse等IDE中使用Struts2框架呢?主要包含四个步骤:首先就是导入Struts的jar包。Struts2的核心的jar包主要有以下几个(可以通过Struts压缩文件中官方提供的示例中获得,如果直接导入Struts2压缩包的lib文件夹中的所有jar包就太多了):
第二步导入jar包后就是要在web.xml中配置Struts2为我们提供的过滤器了,也就是我们前面提到过的StrutsPrepareAndExecuteFilter这个过滤器。配置过程如下:
配置过滤器后,就可以保证我们的所有浏览器发出的request请求就会被我们的Struts2过滤器所处理了。
第三步,就是要使用Struts.xml 配置文件了。首先我们先简单介绍一下struts.xml文件。Struts2的配置文件是有两份的:
*配置action的struts.xml文件*配置struts2全局属性的struts.properties文件(我们的struts.xml文件中是继承struts.properties中的内容的,其中配置了struts2的拦截器栈)
其中struts.xml文件内定义了Struts2的一系列的action,当我们在定义action时,指定action的实现类并定义了该action处理结果与视图资源之间的映射关系。下面是=简单看一下struts.xml配置文件:
Struts2的action是必须要配置在package中的,在struts.xml中package标签是可以有多个的,它们是以不同的包的名称来进行区分的。
先看package标签:
name:包的名称 namespace:名称空间,一般配置为 ‘/’ extends:继承,一般为固定值struts-default 表示要引入的struts2的相关信息比如拦截器栈等Struts配置信息
再看action标签:
name : 访问该action的请求路径的一部分和package标签中的namespace属性值一起构成了浏览器中访问该action的请求路径
class:表示该路径请求的真正的类
method:表示要执行的action中的具体的方法,如果不配置默认是执行execute方法的
最后看result标签:
result标签是表示的action处理后的返回视图,比如将处理结果返回到某个页面或者action,name是要返回的具体的页面的一个映射而type指定了返回的方式是转发、重定向或是其他。可以说result指定了execute方法(或method指定的方法)返回值和视图资源之间的映射关系。
除此之外,就是配置struts2全局属性的struts.properties,它是以键值对的形式表示的。
第四步,就是正式编写相关的action类了。对于struts2开发者来说,Action才是应用的核心,因为开发者需要提供大量的Action类,并在struts.xml中配置Action。Action类包含了对用户请求的处理逻辑,也被称为业务控制器。
接下来简单说一下,在struts2中创建action类的三种方式:创建pojo类、实现action接口、继承ActionSupport类。下面具体来说一下:
方式一:直接创建一个普通的java类
//创建action的第一种方式
public class ActionDemo1 { public String execute(){ System.out.println("actiondemo1");
return "hello";
}
}
方式二:实现Action接口
import com.opensymphony.xwork2.Action; //创建action的第二种方法,实现action接口
public class ActionDemo2 implements Action{ @Override
public String execute() throws Exception {
// 创建action的第二种方法
System.out.println("actiondemo2");
return "hello";
} }
方式三:继承ActionSupport类,一般建议采用此种方式创建Action
import com.opensymphony.xwork2.ActionSupport; //创建action的第三种方式继承actionsupport类,最常用的一种方式
public class ActionDemo3 extends ActionSupport{
@Override
public String execute(){ System.out.println("actiondemo3");
return "hello"; }
}
以上就是创建Action的三种方式。综上四个步骤便是在eclipse使用struts2进行开发的一个简单的入门流程。接下来简单了解一下ActionSupport类(实际上它实现了Action函数式接口):ActionSupport为许多Action类提供了一个默认的实现类,用户在创建自己的Action时可以直接继承该类,而无需自己去实现一个Action接口,ActionSupport类为我们提供了许多的方法,比较常用的有两个,一个是用于向action对象中存入表单验证返回的错误信息(addActionError()),一个是用于进行验证操作(validate()子类需要覆写这个方法来进行验证操作)。下面具体针对validate举-个例子:
描述:当用户点击提交按钮的时候,如果用户名为null就显示错误信息,这里将错误信息以属性形式存入action对象中。
action:
import com.opensymphony.xwork2.ActionSupport; public class ActionValidate extends ActionSupport{ private String username ; public String getUsername(){
return username ;
} private String error ;
//提供get方法
public String getError(){
return error ;
} @Override
//重写validate进行非空判断
public void validate(){//特别注意当action的execute执行时默认先执行validate方法,而无需显式调用
if(username == null){//用户提交后验证username文本框是否为空 //如果为空,向变量error添加错误信息
error = "用户名不能为空";
}
} @Override
public String execute(){ if(error != null){
return "validatejsp";
}else{
return NONE;
}
} }
jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="validate" method="post">
username <input type="text" name="username"/><s:property value="error"></s:property>
<br>
<input type="submit" value="提交">
</form>
</body>
</html>
Action访问servlet的API
struts2可以说是对servlet的进行的封装,并解决了servlet产生的一些问题。总体上来说,Struts2的Action并没有和任何的servletAPI进行耦合,这是struts2的一个改良之处,除此之外struts2也提供了一种更加轻松的方式来访问servletAPI,因为web应用中通常需要访问的servletAPI就是HttpServletRequest、HttpSession和ServletContext分别代表着JSP内置对象中的request、session和application。struts2中提供了一个ServletActionContext类,该类是ActionContext类的一个子类(超类、子类分别在不同的包中),提供了直接取得request、response等的对象的方法,具体演示如下:
public class ActionTemp extends ActionSupport{ public String execute(){ HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
HttpSession session = request.getSession();
ServletContext servletContext = ServletActionContext.getServletContext(); return NONE;
} }
Action对数据的封装
主要解决的问题是:在action中如何获得请求参数。主要有两种方式属性驱动和模型驱动。
属性驱动:
第一种方式:直接在action类中提供与请求参数匹配的属性,并提供set和get方法。
public class ActionData extends ActionSupport {
//第一种:直接在action类中提供与请求参数匹配的属性,并提供set和get方法
private String username ;
private String password ;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
//创建login方法
public void login(){
//获得并打印表单数据
System.out.println("username :" + username );
System.out.println("password :" + password );
} }
第二种方式:创建一个javabean(如User),并在action中进行声明和提供set、get方法,请求页面上使用ognl表达式
public class ActionData extends ActionSupport {
//第二种:直接在action类中声明javabean,并提供set和get方法
private User user ; public User getUser() {
return user;
} public void setUser(User user) {
this.user = user;
} //创建login方法
public void login(){
//获得并打印表单数据
System.out.println(user);
} }
注:此种方式要求在页面上必须使用ognl表达式。
模型驱动:
要求:action必须要实现ModelDriven接口;实例化模型对象(即javabean);重写getModel方法将实例化的模型返回(此时页面不使用ognl表达式)
public class ActionData extends ActionSupport implements ModelDriven<User>{
//第三种:使用模型驱动,直接将数据封装
private User user = new User(); @Override
public User getModel() {
// TODO Auto-generated method stub
return user;
} //创建login方法
public void login(){
//获得并打印表单数据
System.out.println(user);
} }
内省
在程序运行期间获得javabean的set和get方法,使用到如下这个类:
java.beans.PropertyDescriptor //描述javabean可以通过一组存储器方法导出一个属性
使用如下一个javabean(先前的User类):
public class User {
private String username ;
private String password ;
public User() {
super();
}
public User(String username, String password) {
super();
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User [username=" + username + ", password=" + password + "]";
} }
下面创建一个方法来动态的为User对象的属性进行赋值和输出
//内省
public class PropertyDescriptorTest {
@Test
public void test() throws Exception{
//构造一个PropertyDescriptor对象
PropertyDescriptor descriptor = new PropertyDescriptor("username", User.class);
//实例化一个User对象
User user = new User();
//获得User的setUsername方法
Method methodRead = descriptor.getWriteMethod();
//执行set方法进行属性设置
methodRead.invoke(user, "韩菱纱");
//获得User的get方法并执行
String username = (String) descriptor.getReadMethod().invoke(user);
//进行信息的打印
System.out.println("username:" + username);
}
}
ognl表达式(表达式、OgnlContext上下文、root根)
即对象图导航语言的缩写,是一种强大的表达式语言,通过表达式的语法可以存取对象的任意属性,调用对
象的任意方法,遍历整个对象的结构图等功能。在struts2框架集成了ognl。下面简单了解一下ognl的作用。
第一:支持对象操作,调用对象的方法
@Test
public void test1() throws Exception{
//获得上下文对象
OgnlContext context = new OgnlContext();
//调用对象的方法(String的length方法)
Object value = Ognl.getValue("'hello'.length()",context,context.getRoot());
//结果输出
System.out.println(value);
}
第二:支持静态成员访问
@Test
public void test2_1() throws Exception {
//获得ognl上下文
OgnlContext context = new OgnlContext();
//操作静态成员访问
Object pi = Ognl.getValue("@java.lang.Math@PI", context, context.getRoot());
//数据输出
System.out.println(pi);
}
注:输出类和成员变量的时候前面别忘了添加@符号并且静态成员变量必须是public权限的。
第三:访问ognl上下文
利用context向上下文中存储数据,并打印
@Test
public void test2() throws Exception{
//获得上下文对象
OgnlContext context = new OgnlContext();
//在上下文中存储数据(保存在了context中),访问必须要加#
context.put("username","tom");
//根据键取得数据
Object value = Ognl.getValue("#username", context, context.getRoot());
//进行数据的输出
System.out.println(value);
}
@Test
public void test2_3() throws Exception{
//获得上下文对象
OgnlContext context = new OgnlContext();
//在上下文中存储数据(保存在了context中),访问必须要加#
context.put("username","tom");
//将数据存入root
context.setRoot(context);
//根据键取得数据
Object value = Ognl.getValue("username", context, context.getRoot());
//进行数据的输出
System.out.println(value);
}
@Test
public void test3() throws Exception{
//获得上下文对象
OgnlContext context = new OgnlContext();
//创建一个map集合,并向其中添加数据
Map<String,String> map = new HashMap<String,String>();
map.put("username", "fox");
//向context中添加数据
context.put("username", "tom");
//将map数据存入root(OgnlContext实现了map接口)
context.setRoot(map);
//从root中取得数据
Object value = Ognl.getValue("username",context,context.getRoot());
System.out.println(value);
//从上下文中取得数据
Object value2 = Ognl.getValue("#username", context, context.getRoot());
System.out.println(value2);
}
注:从根中获得数据,不需要添加#号,从上下文中获得数据,需要添加#
第四:操作集合(支持赋值操作和表达式串联)
@Test
public void test4() throws Exception{
//获得上下文对象
OgnlContext context = new OgnlContext();
//创建一个ArrayList集合
Object value = Ognl.getValue("{'hello','good','well'}", context, context.getRoot());
//将集合存入root
context.setRoot(value);
//通过索引获得结果
Object val = Ognl.getValue("[1]", context,context.getRoot());
//打印数据
System.out.println(val);
System.out.println("***********************");
//创建一个集合LinkedHashMap类型
Object value2 = Ognl.getValue("#{'username':'tom'}", context, context.getRoot());
System.out.println(value2.getClass());
}
@Test
public void test6() throws Exception {
//创建上下文对象
OgnlContext context = new OgnlContext();
//创建一个LinkedHashMap集合
Object val = Ognl.getValue("#{'username':'苏瑶'}", context, context.getRoot());
//将集合存入root
context.setRoot(val);
//获得username从root中
Object value = Ognl.getValue("username", context, context.getRoot());
//打印数据
System.out.println(value);
//使用赋值操作
Object value2 = Ognl.getValue("username='韩菱纱'", context, context.getRoot());
//进行数据打印
System.out.println(value2);
//再次从root中获得username
Object value3 = Ognl.getValue("username", context, context.getRoot());
//打印数据
System.out.println(value3);
}
ognl表达式在struts2中只要是用来取得值栈中的数据。
valueStack值栈
值栈的主要目的是将action中产生的数据携带到页面上即valueStack就是一个容器。在Struts2框架中将valuestack设计
成一个接口。
com.opensymphony.xwork2.util.ValueStack
我们使用的主要是它的实现类:
com.opensymphony.xwork2.ognl.OgnlValueStack
当浏览器向我们发送一个请求,服务器就会创建一个Action来处理请求,struct2中的action是一个多例,每一次请
求都会有一个新的action对应,所以它不存在线程安全问题。特别注意:一个valueStack对应一个action,valuestack
贯穿整个action的生命周期。浏览器将请求(request对象)提交到一个action,action的处理结果会保存到它所对应
的valuestack中,在前台页面,通过ognl表达式,从值栈中获得数据。
struts2框架将valuestack保存在了request中。下面来看一下struts2的源码:
从web.xml文件中查看过滤器:
查看过滤器源码,看doFilter方法
继续进入serviceAction方法:
观察该方法:
试着从request域中取出valuestack,并判断该request中是否存在值栈,如果不存在就获得一个值栈(其实值栈是通过request设置属性存入的)。继续查看该方法的下面的代码:
以上代码中将值栈存入了request域之中。
valueStack内部结构
查看下面的strust2源码:
从以上的代码中进入valuestack(接口):
进而选择进入ValueStack的实现类(OgnlValuestack):
查看OgnlValuestack类:
可见:ValueStack主要由两部分组成,CompoundRoot继承了ArrayList集合,也就说值栈中的root是一个ArrayList集合,主要存储的是action的相关数据。Map<String,Object> context就是一个map集合,也就是说值栈的context是一个map集合,主要存储了一些引用,主要是关于web开发中的相关信息(比如:parameter请求参数、request请求对象中的所有属性、session会话对象中的所有属性、application对象中的所有属性等,这些都是以map形式存储的)。注意,在Struts2框架之中,通过ognl表达式来获取valueStack中的数据,如果不使用“#”即从值栈的CompoundRoot中获取数据,反之,如果使用#,就从context中来获得数据。
获得ValueStack对象
第一种:直接通过request对象来获得
public class ValueStackTest {
@Test
public void test1(){
ValueStack valueStack = (ValueStack) ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
}
}
第二种:使用ActionContext获得
@Test
public void test2(){
ValueStack valueStack = ActionContext.getContext().getValueStack();
}
在获得值栈对象的时候,使用到了ActionContext,下面简单了解一下这个类
ActionContext是action上下文,struts2框架使用actionContext来保存Action在执行过程中所需要的一些对象如是session、application等。ActionContext的获取是通过自身的静态方法getContext获得,Struts2会根据每一次的http请求来创建对应的ActionContext,它是与当前线程绑定的,每一次的请求就是一个线程,对应一个request,
每一次请求,创建一个Action,每一个action对应一个ActionContext,每一次请求也对应一个valuestack。
即:request--Action--ActionContext--ValueStack,它们都是对应着一次请求(一个线程)。
下面来看一下struts2的源代码:
首先,当浏览器发送请求后,servlet引擎为我们创建了对应的request对象(带有http请求信息),这个请求会被struts2的过滤器拦截,此时过滤器会做一些额外的操作如处理乱码问题(post)等,除此之外,还执行了如下方法
createActionContext(request,response)创建一个ActionContext对象。此时要特别注意:
从上面的代码中可以看到ActionContext是一个ThreadLocal的变量,也就是说它是和当前线程绑定的。每一个线程拥有自己的ActionContext。下面进入createActionContext方法:
从以上代码可以看出,struts2过滤器拦截请求后会创建一个ActionContext对象并与当前线程进行绑定。第一次接收请求的时候oldContext一定是空的,所以会执行else中的代码,并创建一个ActionContext对象,该ActionContext持有值栈的context引用。特别注意,在值栈的context中也持有一个root的引用。此时,也就可以
理解为在ActionContext中存有valuestack。
ThreadLocal线程绑定
Java在处理多线程的问题上,为线程安全提供了一些工具类如ThreadLocal类,代表一个线程局部变量,通过
把数据放在ThreadLocal中就可以让每一个线程创建一个该变量的副本,从而避免并发访问的线程安全问题。
ThreadLocal是线程局部变量的意思,将为每一个使用该变量的线程都提供一个变量值的副本,使得每一个线程
都可以独立的改变自己的副本,而不会和其他线程的副本冲突,从线程的角度来看,就好像每一个线程都完全拥有
该变量一样,ThreadLocal提供了三个public方法:
get():返回此线程局部变量中当前线程副本的值
remove():删除此线程局部变量中当前线程的值
set(T value):设置此线程局部变量中当前线程副本中的值
代码示例:
class Account{
/*定义一个ThreadLocal类型的变量,该变量是一个线程局部变量
* 每个线程都会保留该变量的一个副本
* */
private ThreadLocal<String> name = new ThreadLocal<>();
//初始化一个name成员变量
public Account(String str){
this.name.set(str);
//访问当前线程的name副本
System.out.println("---" + this.name.get());
}
public String getName(){
return this.name.get();
}
public void setName(String str){
this.name.set(str);
}
}
class MyTest extends Thread{
private Account account ;
public MyTest(Account account,String name){
super(name);
this.account=account;
}
public void run(){
for(int i=0;i<10;i++){
//当i==6时输出账户名替换成当前线程名
if(i==6){
account.setName(getName());
}
System.out.println(account.getName() + "账户的i值:" + i);
}
}
} public class ThreadLocalTest {
public static void main(String[] args) {
//启动两个线程,两线程共享一个Account
Account account = new Account("初始名");
/*虽然两个线程共享同一个账户,但由于账户名是Threadlocal类的,所以每一个线程
* 都完全拥有各自的账户名副本
* */
new MyTest(account, "A").start();
new MyTest(account, "B").start();
}
}
向valueStack中存储数据
特别注意,使用valueStack来存储数据时,主要是向root中进行数据存储。关于向值栈中存储数据主要介绍两种
方式:手动向值栈存储数据、struts2框架自动向值栈中存储数据。下面具体来看:
手动向值栈存储数据:
@Test
public void test2(){
//获得值栈对象
ValueStack valueStack = ActionContext.getContext().getValueStack();
//手动向值栈中存入数据
valueStack.push("菱纱");
//手动向值栈中存入数据
valueStack.set("username", "lingsha");
}
可见使用set和push向值栈的root中存储数据,说是两个方法其实也可以说是一种方法就是push方法。因为set方法
是将数据封装到一个map集合中,再次调用push方法将map集合存入root中即set方法中底层还是使用push方法向值
栈root中存储。
Struts2框架自动向valuestack中存储数据:
每次访问,这个action对象会被存储到valuestack的root中。还有就是如果自定义的action使用模型驱动来接收数据,那么
这个模型对象也会被存储到值栈的root中。
valueStack操作--获取数据
创建Action类
//用于测试从值栈中获得数据
public class ActionValueStack extends ActionSupport{ public String test(){
//获得值栈对象
ValueStack valueStack = ActionContext.getContext().getValueStack();
//向值栈中存入数据
valueStack.push("韩菱纱");
//向值栈中存入数据
valueStack.set("username", "lingsha");
//跳转页面
return SUCCESS;
}
}
在struts.xml中进行配置
<!-- 获得valuestack数据 -->
<action name="actiontest" class="com.it.action.ActionValueStack" method="test">
<result name="success">/test.jsp</result>
</action>
test.jsp页面取出
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<s:debug></s:debug>
username=<s:property value="username"/>
<hr/>
user=<s:property value="[1].top"/>
</body>
</html>
下面直接获取action中的数据(要求必须提供get方法,才能够取出,不一定要提供对应的成员变量),我们知道action会自动被struts2存入值栈的。
修改之前的action类:
//用于测试从值栈中获得数据
public class ActionValueStack extends ActionSupport{ //添加一个地址属性
private String address = "琼华派";
//提供get方法
public String getAddress(){
return address ;
}
//不添加属性直接提供一个get方法返回一个name
public String getName(){
return "剑林";
} public String test(){
//获得值栈对象
ValueStack valueStack = ActionContext.getContext().getValueStack();
//向值栈中存入数据
valueStack.push("lingsha");
//向值栈中存入数据
valueStack.set("username", "lingsha");
//跳转页面
return SUCCESS;
}
}
修改页面,对于action中的数据,直接通过属性名进行取出即可:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<s:debug></s:debug>
username=<s:property value="username"/>
<hr/>
user=<s:property value="[1].top"/>
<hr/>
<s:property value="address"/>
<hr/>
<s:property value="name"/>
</body>
</html>
接下来通过代码来看一下模型驱动时从值栈中取数据的一个注意点:
修改action代码:
//用于测试从值栈中获得数据
public class ActionValueStack extends ActionSupport implements ModelDriven<User>{ private User user = new User(); @Override
public User getModel() {
// TODO Auto-generated method stub
return user;
} public String test(){ //特别注意此时重新创建一个User对象
user = new User();
//在Action处理业务中,对user对象重新赋值
user.setUsername("张三");
user.setPassword("456"); return SUCCESS;
} }
访问时的路径(拼接数据):
http://localhost:8080/struts2_review/actiontest.action?username=tom&password=123
jsp页面进行修改:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<s:debug></s:debug>
<s:property value="username"/>
<hr/>
<s:property value="password"/>
<hr/>
<s:property value="model.username"/>
<hr/>
<s:property value="model.password"/>
</body>
</html>
注:Action类的配置不变。
查看运行结果:
接下来对以上的运行结果进行分析:
。。。。。。。。
持续更新中。。。。
知道的不多, 如有什么错误的地方敬请指正。。
简单介绍Struts2的更多相关文章
- Freemarker概念简单介绍
Freemarker概念简单介绍 1. Freemarker是什么 模板引擎:一种基于模板的,用来生成输出文本的通过工具. 基于java开发包和类库 2. Freemarker能做什么 MVC ...
- 简单介绍几种Java后台开发常用框架组合
01 前言 Java框架一直以来都是面试必备的知识点,而掌握Java框架,不管在成熟的大公司,快速发展的公司,还是创业阶段的公司,都能对当前正在开发中的系统有整体的认知,从而更好的熟悉和学习技术,这篇 ...
- [原创]关于mybatis中一级缓存和二级缓存的简单介绍
关于mybatis中一级缓存和二级缓存的简单介绍 mybatis的一级缓存: MyBatis会在表示会话的SqlSession对象中建立一个简单的缓存,将每次查询到的结果结果缓存起来,当下次查询的时候 ...
- 利用Python进行数据分析(7) pandas基础: Series和DataFrame的简单介绍
一.pandas 是什么 pandas 是基于 NumPy 的一个 Python 数据分析包,主要目的是为了数据分析.它提供了大量高级的数据结构和对数据处理的方法. pandas 有两个主要的数据结构 ...
- 利用Python进行数据分析(4) NumPy基础: ndarray简单介绍
一.NumPy 是什么 NumPy 是 Python 科学计算的基础包,它专为进行严格的数字处理而产生.在之前的随笔里已有更加详细的介绍,这里不再赘述. 利用 Python 进行数据分析(一)简单介绍 ...
- yii2的权限管理系统RBAC简单介绍
这里有几个概念 权限: 指用户是否可以执行哪些操作,如:编辑.发布.查看回帖 角色 比如:VIP用户组, 高级会员组,中级会员组,初级会员组 VIP用户组:发帖.回帖.删帖.浏览权限 高级会员组:发帖 ...
- angular1.x的简单介绍(二)
首先还是要强调一下DI,DI(Denpendency Injection)伸手获得,主要解决模块间的耦合关系.那么模块是又什么组成的呢?在我看来,模块的最小单位是类,多个类的组合就是模块.关于在根模块 ...
- Linux的简单介绍和常用命令的介绍
Linux的简单介绍和常用命令的介绍 本说明以Ubuntu系统为例 Ubuntu系统的安装自行百度,或者参考http://www.cnblogs.com/CoderJYF/p/6091068.html ...
- iOS-iOS开发简单介绍
概览 终于到了真正接触IOS应用程序的时刻了,之前我们花了很多时间去讨论C语言.ObjC等知识,对于很多朋友而言开发IOS第一天就想直接看到成果,看到可以运行的IOS程序.但是这里我想强调一下,前面的 ...
随机推荐
- typecho for SAE
url:http://cloudbbs.org/forum.php?mod=viewthread&tid=22817 typecho和wordpress差不多,目前使用的用户非常之多.这里分享 ...
- [转]如何使用BackTrack破解WIFI无线网络的WEP密钥
aireplay-ng - -a (bssid) -h ::::: -e (essid) (interface) 你可能已经知道如果你想要加锁自己的WIFI无线网络,你最好选择WPA加密方式,因为WE ...
- 在WIN SERVER 2016上安装DOCKER(带过坑)
目录 1 概要 1 1.1 主要优势 1 2 在Windows Server上部署Docker 2 概要 博客使用Word发博,发布后,排版会出现很多问题,敬请谅解 ...
- js中运动框架的封装
//获取非行间样式的封装 function setStyle(obj,name){ // 考虑兼容性问题 if(obj.currentStyle){//不兼容火狐和谷歌 return obj.curr ...
- 初学vitmio,vitmio的环境配置及遇到的坑
1 到官网或者github下载vitamio 官网地址:https://www.vitamio.org/ github地址:https://github.com/yixia/VitamioBundle ...
- java基础之集合篇
(一) 集合类位于JDK中java.util包中. 常用的集合类型分布在java.util.Collection和java.util.Map接口中. java.util.Collection=> ...
- bootstrap-datetimepicker bootstrap-datepicker bootstrap-timepicker 时间插件
<!DOCTYPE html><head> <title>时间插件测试</title><style type="text/css&quo ...
- C# DataGridView显示日期格式问题
给DataGridView单元格绑定或者赋值DataTime数据后有时会发现不能显示完整的数据格式,怎么办呢?给出解决方案如下:1.指定整列的显示格式:m_dataGridView.Columns[c ...
- CSS3 @keyframes 规则
今天来给大家分享一下CSS3 @keyframes 规则! 在你了解CSS3 @keyframes 规则时我先来给大家说说什么是css3中的动画 动画是使元素从一种样式逐渐变化为另一种样式的效果. 您 ...
- An impassioned circulation of affection
An impassioned circulation of affection time limit per test 2 seconds memory limit per test 256 mega ...