简单介绍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程序.但是这里我想强调一下,前面的 ...
随机推荐
- Heartbeat高可用解决方案
Heartbeat高可用 Heartbeat作用: 通过heartbeat,可以将资源(ip以及程序服务等资源)从一台已经故障的计算机快速转移到另一台正常运转的机器上继续提供服务,一般称之为高可用服务 ...
- OVS 中的 upcall 线程
总体概览如下: 假设upcall handler线程有两个,vport有四个,那么每个vport下都将持有两个NetLink连接的信息,这两个NetLink连接将被用来上送upcall消息. 每个Ne ...
- 预约会议sql
CREATE proc sp_MeetingCheck_Test @serialno varchar(max)='', ---- 主档serialno @title ...
- 线程-join();
一.join()方法,官方描述 waits for this thread to die 等待当前线程死亡: 源码: //无参,默认调用join(0) public final void join ...
- ReactJS入门基础
渲染这俩字可能在很多地方都见过.但可能不太理解是啥意思. 那么首先我们来理解一下渲染. 渲染 我觉得这样理解比较通俗. 我们做一个汽车,开始是没有喷漆的(没有css) 只是些框框架架(HTML标签). ...
- 提高java编程质量 - (三)三目运算符的两个操作数类型尽量一致
先看一个例子: package com.test; public class TernaryOperator { public static void main(String[] args) { in ...
- servlet与Javabean之间的区别
在JSP中调用JAVA类和使用JavaBean有什么区别? 可以像使用一般的类一样使用JavaBean,Bean只是一种特殊的类.特殊在可以通过<jsp:useBean/>调用JavaBe ...
- c# networkcomms 3.0实现模拟登陆总结
最近项目需要做一个客户查询状态系统,当前上位机缺少服务功能,于是找到了networkcomms 开源框架,作为项目使用. 最新版networkcomms 下载地址:https://github.com ...
- mac 安装mysqldb组件包及mac中安装mysql-python遇到的问题
错误1:mysql_config not found 问题描述:在执行sudo pip install mysql-python安装时报错误:EnvironmentError: mysql_confi ...
- Swing系列之控件一
Swing系列之控件 JTextArea JTextArea是一个实现多行文本的控件 构造函数 JTextArea() 构造新的TextArea. JTextArea(Document doc) 构造 ...