第一章 自定义MVC框架
1.1 MVC模式设计
组成;Model:模型,用于数据和业务的处理
View :视图,用于数据的显示
Controller:控制器,用于进行流程控制
特点:1.一个模型可以对应多个视图
2.显示与逻辑控制的分离
3.分层控制,减低了代码间的耦合
1.2 DTD验证XML文档
声明:<!DOCTYPE 根元素 [定义内容]>
例:<!DOCTYPE poem [
<!ELEMENT poem (author,title,content)>
<!ELEMENT author (#PCDATA)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT content (#PCDATA)>
]>
<peom>
<author>王维</author>
<title>鹿柴</title>
<content>空山不见人,但闻人语声。返景入深林,复照青苔上</content>
</peom>
使用外部DTD验证XML
myStruts.dtd:
<!ELEMENT poems (poem*)>
<!ELEMENT poem (title,author,year,content)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT author (#PCDATA)>
<!ELEMENT year (#PCDATA)>
<!ELEMENT content (#PCDATA)>
myMvc.xml:
<!DOCTYPE poems SYSTEM "myStruts.dtd">
<poems>
<poem>
<title>春晓</title>
<author>孟浩然</author>
<content>春眠不觉晓......</content>
</poem>
</poems>
DTD元素
<!ELEMENT name CONTENT>
ELEMENT:关键字
name:元素名称
CONTENT:元素类型
#PCDATA:任何字符数据,不能在其中包含任何子元素
纯元素类型:只包含子元素,且这些子元素外没有文本
符号:():表示分成几组
| :必须选其一
,:按顺序出现
* :可以出现0到多次
?:可出现也可不出现,出现且只有一次
+ :必须出现,且可以出现多次
DTD属性
<!ATTLIST 元素名称 属性名称 属性类型 属性默认值>
属性类型描述:
CDATA:字符数据
ID:唯一ID
IDREFS:另一个ID
ENTITY:实体
ENTITIES:实体列表
属性值描述:
#REQUIRED:属性值是必须的
#IMPLIED:属性值不是必须的
#FIXED:属性值是固定的
例:<!DOCTYPE mystruts[
<!ELEMENT mystruts (actions)>
<!ELEMENT actions (action*)>
<!ELEMENT action (result*)>
<!ATTLIST auction
name CDATA #REQUIRED
CLASS CDATA #REQUIRED>
<!ATTLIST result
name CDATA #IMPLIED
redirect (true|false) "false">
]>
<mystruts>
<actions>
<action name="register" class="cn.jbit.action.RegisterAction">
<result name="success">page/login.jsp</result>
<result name="input">page/register.jsp</result>
</action>
<action name="login" class="cn.jbit.mvc.LoginAction">
<result name="success">page/guanli.jsp</result>
<result name="input">page/login.jsp</result>
</action>
</actions>
</mystruts>
1.3 XML文档解析
获取root节点 ELement root
1、获取节点名称 root.getName()
2、获取节点属性 root.attribute("dname")
3、获取节点中的text root.getText()
4、获取子节点
使用DOM4J操作XML数据(如:dom4j-1.6.1.jar)
例:student.xml:
<?xml version="1.0" encoding="gb2312"?>
<students>
<student age="31"><!-- 如果没有固定的age 默认为20 -->
<name>崔卫兵</name>
<college>pc学院</college>
<telephone>6234666</telephone>
<notes>男,1985年出生,硕士</notes>
</student>
<student>
<name>张洪泽</name>
<!-- 如果没有固定的leader 默认为leader -->
<college leader="Leader">pc学院</college>
<telephone>6238888</telephone>
<notes>男,1987年出生,硕士</notes>
</student>
</students>
Dom4jReadExmple.java:
public class Dom4jReadExmple {
public void iterateWholeXML(String filename,HashMap<String,String> hm){
SAXReader saxReader=new SAXReader();
try {
Document document=saxReader.read(new File(filename));
Element root=document.getRootElement();
int num=-1;//记录学生编号的变量
//遍历根元素(students)的所有子节点(student)
for(Iterator iter=root.elementIterator();iter.hasNext();){
Element element=(Element) iter.next();
num++;
//获取person节点的age属性
Attribute ageAttribute=element.attribute("age");
if(ageAttribute!=null){
String age=ageAttribute.getValue();
if(age!=null && !age.equals("")){
hm.put(element.getName()+"-"+ageAttribute.getName()+num, age);
}else{
hm.put(element.getName()+"-"+ageAttribute.getName()+num, "20");
}
}else{
hm.put(element.getName()+"-age"+num, "20");
}
//遍历student节点下的所有子节点(name,college,telephone,notes)
for(Iterator iterInner=element.elementIterator();iterInner.hasNext();){
Element elementInner=(Element) iterInner.next();
if(elementInner.getName().equals("college")){
hm.put(elementInner.getName()+num,elementInner.getText());
//获取college节点的leader属性
Attribute leaderAttr=elementInner.attribute("leader");
if(leaderAttr!=null){
String leader=leaderAttr.getValue();
if(leader!=null && !leader.equals("")){
hm.put(elementInner.getName()+"-"+leaderAttr.getName()+num,leader);
}else{
hm.put(elementInner.getName()+"-"+leaderAttr.getName()+num,"leader");
}
}else{
hm.put(elementInner.getName()+"-leader"+num, "leader");
}
}else{
hm.put(elementInner.getName()+num, elementInner.getText());
}
}
}
} catch (DocumentException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
TestDom4jReadExmple.java:
public class TestDom4jReadExmple {
public static void main(String[] args) {
try {
//获取解析后的解析信息
HashMap<String, String> hashMap;
Dom4jReadExmple dre=new Dom4jReadExmple();
//遍历xml文件
hashMap=new HashMap<String, String>();
String n=System.getProperty("user.dir");
//获取xml文件
dre.iterateWholeXML(n+"\\src\\cn\\jbit\\action\\Student.xml", hashMap);
for (int i = 0; i < hashMap.size(); i++) {
int j=i/6;
System.out.print(hashMap.get("name"+j)+"\t");
System.out.print(hashMap.get("student-age"+j)+"\t");
System.out.print(hashMap.get("college"+j)+"\t");
System.out.print(hashMap.get("college-leader"+j)+"\t");
System.out.print(hashMap.get("telephone"+j)+"\t");
System.out.println(hashMap.get("notes"+j)+"\t");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
输出结果:崔卫兵 31 pc学院 leader 6234666 男,1985年出生,硕士
张洪泽 20 pc学院 Leader 6238888 男,1987年出生,硕士
关键操作如下:
1、Document对象相关
SAXReader saxReader=new SAXReader();
Document document=saxReader.read(new File("index.xml"));
2、节点相关
1、获取文档的根节点
Element root=document.getRootElement();
2、获取某节点的单个子节点
Element meberElm=root.element("Member");
3、取得节点的文字
String text=MemberElm.getText();
4、取得某节点下名为:Member的所有子节点并进行遍历
List nodes=rootElm.elements("Member");
5、对某节点下的所有子节点进行遍历
for(Iterator iterInner=element.elementIterator();iterInner.hasNext();){
Element elementInner=(Element) iterInner.next();
//...
}
6、在某节点下添加子节点
Element ageElm=newMemberElm.addElement("age");
7、设置节点文字
ageElm.setText("19");
8、删除某节点
parentElm.remove(childElm);//childElm是待删除的节点,parentElm是父节点
9、添加一个CDATA节点
Element contentElm=infoElm.addElement(content);
contentElm.addCDATA(diary.getContent());
contentElm.getText();
contentElm.clearContent();
3、属性相关
1、取得某节点下的属性
Element root=document.getRootElement();
Attribute ageAttribute=root.attribute("age");
2、取得属性的文字
String text=ageAttribute.getText();
3、遍历某节点的所有属性
Element root=document.getRootElement();
for(Iterator it=root.elementIterator();it.hasNext();){
Attribute attribute=(Attrinute) it.next();
String text=ageAttribute.getText();
System.out.print(text);
}
4、设置某节点的属性和文字
newMemberElm.addAttribute("name","sitinspring');
5、设置属性的文字
Attribute attribute=root.attribute("age");
attribute.setText("sitinspring");
1.4 反射机制
反射的3个动态性质
1.运行时伸出对象实例
2.运行期间调用对象
3.运行时更改对象
反射常用API
Class类:反射核心类
Field类:类的属性
Method类:类的方法
Constructor类:类的构造方法
使用反射的步骤
1、导入jar包
2、获得需要操作的类的java.lang.CLass对象
3、调用Class的方法获取Field、Method等对象
4、使用反射API进行操作
反射的应用
1、获取Class对象
例;
Class clazz=Class.forName("java.lang.String");//正确
Class clazz=Class.forName("String");//错误
2、从Class对象获取信息
访问类信息的常用方法
Constructor[] getConstructors()//返回所表示的类的public构造方法
Constructor[] getDeclaredConstructors()//返回所表示的类的构造方法
Method [] getMethods()//返回所表示的类的public方法
Method [] getDeclaredMethods()//返回所表示的类的全部方法
Field[] getFields()//返回所表示的类的public属性
field[] getDeclaredFields()//返回所表示的类的全部属性
Object get(Object obj)//得到引用类属性值
void set(Object obj,Object val)//将Obj对象的属性设置val值,针对引用类型
Object invoke(Object obj,Object args)//调用类的方法obj是执行方法的对象,args是执行方法时传入的参数
3、创建对象
newInstance():
例:Object retBean=defaultCtor.newInstance();
1.5 构建基于MVC模式的框架(Controller为MVC的核心)
1.5.1、Controller的设计
1、定义Action接口
public interface Action {
public String execute(HttpServletRequest request,HttpServletResponse response)throws Exception;
}
实现接口:
public class LoginAction implements Action {
@Override
public String execute(HttpServletRequest request,
HttpServletResponse response) throws Exception {
String name=request.getParameter("name");
String password=request.getParameter("password");
//业务处理
UserBiz userBiz=new UserBizImpl();
User user=userBiz.login(name,password);
//判断是否登录成功
if(user==null){
request.setAttribute("message","用户名或密码错误!");
return "/page/login.jsp";
}else{
request.getSession().setAttribute("login", user);
return "/page/guanli.jsp";
}
return null;
}
}
2、实现Controller类
public class ActionFilter implements Filter {
private FilterConfig config;
private ActionMappingManager mappingManager;
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
//将请求装换成HttpServletRequest
HttpServletRequest hsr=(HttpServletRequest) request;
HttpServletResponse hsp=(HttpServletResponse) response;
//调用Action的execute方法
Action action=this.getAction(hsr);
String resultView=null;
try {
resultView=action.execute(hsr, hsp);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
//页面跳转
if(resultView!=null){
request.getRequestDispatcher(resultView).forward(request, response);
}
}
@Override
public void init(FilterConfig conf) throws ServletException {
this.config=conf;
String conStr=config.getInitParameter("config");
//可包含多个配置文件
String[] configFiles=null;
if(conStr==null||conStr.isEmpty()){
configFiles=new String[]{"myMvc.xml"};
}else{
//拆分配置文件名称字符串
configFiles=conStr.split(",");
}
this.mappingManager=new ActionMappingManager(configFiles);
}
private Action getAction(HttpServletRequest request) {
//获取请求的uri
String uri = request.getRequestURI();
//获取上下文路径
String contextPath=request.getContextPath();
//截取上下文路劲后面的部分
String actionPath=uri.substring(contextPath.length());
//获取Action名称
String actionName=actionPath.substring(1,actionPath.lastIndexOf(".")).trim();
Action action=null;
//添加新功能时在这里添加
if("login".equals(actionName)){
action=new LoginAction();
}
return null;
}
}
web.xml配置:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name></display-name>
<welcome-file-list>
<welcome-file>login.jsp</welcome-file>
</welcome-file-list>
<filter>
<filter-name>requestFilter</filter-name>
<filter-class>cn.jbit.mvc.ActionFIlter</filter-class>
</filter>
<filter-mapping>
<filter-name>requestFilter</filter-name>
<url-pattern>*.action</url-pattern>
</filter-mapping>
</web-app>
1.6 升级Controller控制器
1、配置文件
myStruts.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mystruts[
<!ELEMENT mystruts (actions)>
<!ELEMENT actions (action*)>
<!ELEMENT action (result*)>
<!ATTLIST auction
name CDATA #REQUIRED
CLASS CDATA #REQUIRED>
<!ATTLIST result
name CDATA #IMPLIED
redirect (true|false) "false">
]>
<mystruts>
<actions>
<action name="register" class="cn.jbit.action.RegisterAction">
<result name="success">page/login.jsp</result>
<result name="input">page/register.jsp</result>
</action>
<action name="login" class="cn.jbit.mvc.LoginAction">
<result name="success">page/guanli.jsp</result>
<result name="input">page/login.jsp</result>
</action>
</actions>
</mystruts>
2、保存Action信息
public class ActionMapping {
//action元素的name属性
private String name;
//action元素的className属性
private String className;
//保存配置爱的result属性信息
private Map<String, String > resultMap=new HashMap<String, String>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public Map<String, String> getResultMap() {
return resultMap;
}
public void setResultMap(Map<String, String> resultMap) {
this.resultMap = resultMap;
}
/*
* 根据result-name返回Result实例
*/
public String getResult(String name,String result){
return resultMap.get(name);
}
public String getResult(String name){
return resultMap.get(name);
}
/*
* 向Map中添加一个View
*/
public void addResult(String name,String result){
this.resultMap.put(name, result);
}
}
3、读取配置文件
public class ActionMappingManager {
//保存所有Action的ActionMapping
private static Map<String,ActionMapping> actionMappings=new HashMap<String, ActionMapping>();
/**
* init方法用来加载Action配置文件
* @param configureFileName 配置文件名
*/
public void init(String configureFileName){
try {
if(configureFileName==null || configureFileName.isEmpty()){
throw new Exception("ConfigureFileName为空");
}
InputStream is=this.getClass().getResourceAsStream("/"+configureFileName);
Document doc=new SAXReader().read(is);
Element root=doc.getRootElement();
Iterator<Element> actionsIt=root.elements("actions").iterator();
Element actions =actionsIt.next();
for(Iterator<Element> actionIt=actions.elementIterator("action");actionIt.hasNext();){
Element action=actionIt.next();
ActionMapping mapping=new ActionMapping();
mapping.setName(action.attributeValue("name"));
mapping.setClassName(action.attributeValue("class"));
for(Iterator<Element> resultIt=action.elementIterator("result");resultIt.hasNext();){
Element resultElement=resultIt.next();
String name=resultElement.attributeValue("name");
String result=resultElement.getText();
if(name==null || "".equals(name)){
name="success";
}
mapping.addResult(name, result);
}
actionMappings.put(mapping.getName(), mapping);
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
/**
* 加载Action配置文件
* @param configureFileNames 配置文件名的数组
*/
public ActionMappingManager(String[] configureFileNames){
for(String configureFileName:configureFileNames){
init(configureFileName);
}
}
/**
* 根据actionName查询对应的ActionMapping实例
* @param actionName
* @return
* @throws Exception
*/
public ActionMapping getActionMappingByName(String actionName)throws Exception{
if(actionName==null || actionName.isEmpty()){
return null;
}
ActionMapping mapping=this.actionMappings.get(actionName);
if(mapping==null){
throw new Exception("mapping为空:["+actionName+"]");
}
return mapping;
}
}
4、反射生成Action
public class ActionManager {
public static Action createAction(String className)throws Exception{
try {
return (Action)loadClass(className).newInstance();
}catch (ClassNotFoundException e) {
e.printStackTrace();
}catch (InstantiationException e) {
e.printStackTrace();
}catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
private static Class loadClass(String className) throws ClassNotFoundException {
Class clazz=null;
clazz=Class.forName(className);
return clazz;
}
}
5、完善Controller
web.xml:
<filter>
<display-name>requestFilter</display-name>
<filter-name>requestFilter</filter-name>
<filter-class>cn.jbit.mvc.ActionFIlter</filter-class>
<init-param>
<param-name>config</param-name>
<param-value>myMvc.xml</param-value>
</init-param>
</filter>
ActionFilter.java修改:
public class ActionFilter implements Filter {
private FilterConfig config;
private ActionMappingManager mappingManager;
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
//将请求装换成HttpServletRequest
HttpServletRequest hsr=(HttpServletRequest) request;
HttpServletResponse hsp=(HttpServletResponse) response;
try {
ActionMapping mapping=this.getActionMapping(hsr);
Action action=ActionManager.createAction(mapping.getName());
//得到结果的逻辑名
String resultName=action.execute(hsr, hsp);
//根据逻辑名返回实际跳转的视图名,也就是跳转的路径
String result=mapping.getResult(resultName);
if(result==null){
return;
}
//页面跳转
hsp.sendRedirect(result);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
@Override
public void init(FilterConfig conf) throws ServletException {
this.config=conf;
String conStr=config.getInitParameter("config");
//可包含多个配置文件
String[] configFiles=null;
if(conStr==null||conStr.isEmpty()){
configFiles=new String[]{"myMvc.xml"};
}else{
//拆分配置文件名称字符串
configFiles=conStr.split(",");
}
this.mappingManager=new ActionMappingManager(configFiles);
}
private ActionMapping getActionMapping(HttpServletRequest request) throws Exception {
//获取请求路径
String uri=((HttpServletRequest)request).getRequestURI();
String contextPath=((HttpServletRequest)request).getContextPath();
//截取上下文路劲后面的部分
String actionPath=uri.substring(contextPath.length());
//获取Action名称
String actionName=actionPath.substring(1,actionPath.lastIndexOf(".")).trim();
ActionMapping mapping=null;
mapping=mappingManager.getActionMappingByName(actionName);
return mapping;
}
}