三大框架SSH搭建web项目实例

前言:

      本文主要介绍的是如何使用三大框架来搭建一个简易的web项目。这里的三大框架指的是struts2springhibernate,以为是SpringMVC+Spring+Hibernate的童鞋请自觉绕行。
      由于本文作者水平有限,对SSH的掌握程度较低,作者结合自身的工作经验以及业余时间的摸爬滚打,终于将三大框架搭建好的web项目运行成功。作者希望读者在看完本文后有什么指教,欢迎在本文评论区进行留言,大家一起探讨,共同进步。

一、环境准备

  • Eclipse
  • JDK 1.7
  • struts-2.5.20
  • spring-framework-4.3.18.RELEASE(spring 4)
  • hibernate-release-4.3.11.Final(hibernate 4)
  • tomcat 6.0
  • 其他架包:
    (1)log4j-core-2.11.1.jar
    (2)mysql-connector-java-5.1.26-bin.jar
    (3)aopalliance-1.0.jar
    (4)aspectjrt-1.5.3.jar
    (5)aspectjweaver-1.5.3.jar
    (6)commons-dbcp-1.4.jar
    (7)commons-pool-1.6.jar

三大框架的架包以及其他架包的下载方法可参考作者总结的另一篇文章:三大框架相关架包下载方法

二、搭建步骤

1、创建web项目frameworkdemo,并依次导入三大框架必需架包。

  • 三大框架不同版本所必需的架包可能不同,不过大体必需的架包是一样的。如果想根据本文搭建web项目并正常运行,最好严格按照文章所说的架包进行下载。
  • 架包直接放到/WEB-INF/lib目录下。
序号struts2 - 必需架包
1commons-fileupload-1.4.jar
2commons-lang3-3.8.1.jar
3freemarker-2.3.28.jar
4javassist-3.20.0-GA.jar
5log4j-api-2.11.1.jar
6ognl-3.1.21.jar
7struts2-core-2.5.20.jar
8struts2-spring-plugin-2.5.20.jarspring整合struts2时必需的插件
序号spring 4 - 必需架包(spring4中的所有架包)
1commons-logging-1.2.jar 从struts2中获取该架包
2spring-aop-4.3.18.RELEASE.jar
3spring-aspects-4.3.18.RELEASE.jar
4spring-beans-4.3.18.RELEASE.jar
5spring-context-4.3.18.RELEASE.jar
6spring-context-support-4.3.18.RELEASE.jar
7spring-core-4.3.18.RELEASE.jar
8spring-expression-4.3.18.RELEASE.jar
9spring-instrument-4.3.18.RELEASE.jar
10spring-instrument-tomcat-4.3.18.RELEASE.jar
11spring-jdbc-4.3.18.RELEASE.jar
12spring-jms-4.3.18.RELEASE.jar
13spring-messaging-4.3.18.RELEASE.jar
14spring-orm-4.3.18.RELEASE.jar
15spring-oxm-4.3.18.RELEASE.jar
16spring-test-4.3.18.RELEASE.jar
17spring-tx-4.3.18.RELEASE.jar
18spring-web-4.3.18.RELEASE.jar
19spring-webmvc-4.3.18.RELEASE.jar
20spring-webmvc-portlet-4.3.18.RELEASE.jar
21spring-websocket-4.3.18.RELEASE.jar
序号hibernate 4 - 必需架包+可选 /lib/optional/目录下为可选架包
1antlr-2.7.7.jar
2dom4j-1.6.1.jar
3hibernate-commons-annotations-4.0.5.Final.jar
4hibernate-core-4.3.11.Final.jar
5hibernate-jpa-2.1-api-1.0.0.Final.jar
6jandex-1.1.0.Final.jar
7javassist-3.18.1-GA.jar
8jboss-logging-3.1.3.GA.jar
9jboss-logging-annotations-1.2.0.Beta1.jar
10jboss-transaction-api_1.2_spec-1.0.0.Final.jar
11ehcache-core-2.4.3.jar                   见/lib/optional/ehcache/
12hibernate-ehcache-4.3.11.Final.jar见/lib/optional/ehcache/
13slf4j-api-1.6.1.jar                            见/lib/optional/ehcache/
序号其他架包 到Maven资源库中下载
1log4j-core-2.11.1.jar 日志要用到
2mysql-connector-java-5.1.26-bin.jar mysql驱动包
3aopalliance-1.0.jar        aop配置事务时用
4aspectjrt-1.5.3.jar          aop配置事务时用
5aspectjweaver-1.5.3.jar aop配置事务时用
6commons-dbcp-1.4.jar 使用jdbc.properties配置数据库时用
7commons-pool-1.6.jar  使用jdbc.properties配置数据库时用

总共49个架包导入项目之后,会发现javassist-xx-GA.jar重复,可将javassist-3.18.1-GA.jar移除,最终只留下48个架包。

项目结构预览:
在这里插入图片描述
2、文件配置
作者的思路是:先spring整合hibernate,再spring整合struts2

(1)Spring 整合 Hibernate

  • spring整合hibernate时,spring可以替换hibernate的hibernate.cfg.xmlxxx.hbm.xml配置文件。
  • web.xml
    在web.xml文件中指定spring配置文件的位置
	<!-- 初始化spring容器 -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
        classpath:resources/applicationContext.xml
		</param-value>
	</context-param>
	<listener>
		<!-- 服务器启动时加载spring窗口中配置项 -->
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

classpath:表示的是类路径,即src目录下的包路径,为了方便管理,三大框架的配置文件均放在/resources/包下。

既然已经指定了spring配置文件的路径,那就跟着在/resources/包下创建applicationContext.xml

  • applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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">
	<!-- 分模块,注意jdbc.xml必须在前 -->
	<!-- 数据库源配置 -->
	<import resource="classpath:resources/applicationContext_jdbc.xml" />

	<!-- 事务管理 -->
	<import resource="classpath:resources/applicationContext_transaction.xml" />

	<!-- 控制层相关Bean管理 -->
	<import resource="classpath:resources/applicationContext_action.xml" />
	
	<!-- 业务层相关Bean管理 -->
	<import resource="classpath:resources/applicationContext_service.xml" />
	
	<!-- 持久层相关Bean管理 -->
	<import resource="classpath:resources/applicationContext_dao.xml" />
</beans>

因为spring要配置的事项较多,为了方便管理,最好是通过import引入的方式分模块来管理。

  • applicationContext_jdbc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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">
	
	<!-- 配置数据源,使用jdbc.properties文件的方式,需要引入架包:commons-dbcp-*.jar与commons-pool-*.jar -->
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
		<property name="driverClassName" value="${jdbc.driverClassName}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
	</bean>
	<context:property-placeholder location="classpath:resources/jdbc.properties" />

	<!-- 定义hibernate的 sessionFactory -->
	<bean id="sessionFactory"
		class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
		<property name="dataSource" ref="dataSource"></property>
		<property name="packagesToScan">
			<list>
				<!-- 加载hibernate的jpa注解形式的实体类 -->
				<value>com.demo.entity*</value>
			</list>
		</property>
		
		<!-- 配置一些其他的信息,方言等 -->
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">
					org.hibernate.dialect.MySQLDialect
				</prop>
				<prop key="hibernate.show_sql">true</prop>
				<prop key="hibernate.format_sql">true</prop>
				<prop key="hibernate.hbm2ddl.auto">update</prop>
			</props>
		</property>
	</bean>
</beans>
  • 配置dataSource时需要指定jdbc.properties文件,并且,需要用到 commons-dbcp-.jar与 commons-pool-.jar 这两个架包
  • packagesToScan指定了hibernate映射的实体类中需要加注解,这就替代了映射文件.hbm.xml,加载时会自动扫描指定包中的所有实体类。
  • jdbc.properties
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=root

注意:不要有空格

  • Student.java
package com.demo.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "student", catalog = "test")
public class Student implements java.io.Serializable {
	private Integer id;
	private String name;

	/** default constructor */
	public Student() {
	}

	/** full constructor */
	public Student(String name) {
		this.name = name;
	}

	// Property accessors
	@Id
	@GeneratedValue
	@Column(name = "id", unique = true, nullable = false)
	public Integer getId() {
		return this.id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	@Column(name = "name")
	public String getName() {
		return this.name;
	}

	public void setName(String name) {
		this.name = name;
	}

}

Student.java是通过hibernateTools工具映射生成的,建议使用MyEclipse自动生成hibernate映射的实体类,如何生成不在本文的考虑范围。

  • applicationContext-transaction.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd 
        http://www.springframework.org/schema/aop 
		http://www.springframework.org/schema/aop/spring-aop.xsd
		http://www.springframework.org/schema/tx 
		http://www.springframework.org/schema/tx/spring-tx.xsd">
	
	<!-- 声明事务管理 -->
	<!-- 配置事务管理器 -->
	<bean id="txManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>

	<!-- 配置事务通知 -->
	<tx:advice id="txAdvice" transaction-manager="txManager">
		<tx:attributes>
			<tx:method name="add*" propagation="REQUIRED" />
			<tx:method name="insert*" propagation="REQUIRED" />
			<tx:method name="update*" propagation="REQUIRED" />
			<tx:method name="delete*" propagation="REQUIRED" />
			<tx:method name="select*" read-only="true" />
			<tx:method name="find*" read-only="true" />
			<tx:method name="get*" read-only="true" />
			<tx:method name="query*" read-only="true" />
		</tx:attributes>
	</tx:advice>
	
	<!-- 开启事务的注解 -->
	<tx:annotation-driven transaction-manager="txManager" />
	
	<!-- 配置aop -->
	<aop:config proxy-target-class="true">
		<aop:pointcut expression=" execution(* com.demo.service..*(..))"
			id="serviceMethod" />
		<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod" />
	</aop:config>
</beans>
  • service开启事务后,service层的实现类上需要加一个注解:@Transactional;
  • 使用到aop配置,需要用到三个架包:aopalliance-1.0.jaraspectjrt-1.5.3.jaraspectjweaver-1.5.3.jar

后面的三个配置文件applicationContext_action.xml、applicationContext_service、applicationContext_dao.xml比较简单了。

  • applicationContext_action.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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">
	<!-- 控制层 -->
	<bean id="studentAction" class="com.demo.action.StudentAction">
		<property name="studentService" ref="studentService"></property>
	</bean>
</beans>
  • applicationContext_service.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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">
	
	<!-- 业务逻辑层 -->
	<bean id="studentService" class="com.demo.service.impl.StudentServiceImpl">
		<property name="studentDao" ref="studentDao"></property>
	</bean>
</beans>
  • applicationContext_dao.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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">
	
	<!-- 持久层 -->
	<bean id="studentDao" class="com.demo.dao.impl.StudentDaoImpl">
		<property name="sessionFactory" ref="sessionFactory"></property>
	</bean>
</beans>

然后就是把各层的类部署好:

  • SysException.java
package com.demo.exception;

/**
 * 自定义异常,全局
 */
public class SysException extends Exception {
	private static final long serialVersionUID = 1L;
	private String message;

	public String getMessage() {
		return message;
	}

	public SysException(String message) {
		this.message = message;
	}
}
  • StudentAction.java
package com.demo.action;

import java.io.PrintWriter;
import java.io.StringWriter;

import com.demo.entity.Student;
import com.demo.exception.SysException;
import com.demo.service.StudentService;
import com.opensymphony.xwork2.Action;

public class StudentAction implements Action {

	private StudentService studentService;

	private String newName;

	@Override
	public String execute() throws Exception {
		return null;
	}

	public String saveStu() throws Exception {
		Student stu = new Student();
		checkParams();
		stu.setName(newName);
		try {
			studentService.saveOne(stu);
		} catch (Exception e) {
			StringWriter sw = new StringWriter();
			PrintWriter pw = new PrintWriter(sw);
			e.printStackTrace(pw);
			System.out.println(sw.toString());
			throw new SysException("\n" + sw.toString());
		}
		return SUCCESS;
	}

	/**
	 * 校验请求参数
	 */
	private void checkParams() throws SysException {
		if (newName == null) {
			throw new SysException("Name不能为空!");
		}
		newName = newName.trim();
	}

	/******************** get和set方法 ***************************/
	public void setStudentService(StudentService studentService) {
		this.studentService = studentService;
	}

	public String getNewName() {
		return newName;
	}

	public void setNewName(String newName) {
		this.newName = newName;
	}

}
  • StudentService.java
package com.demo.service;

import com.demo.entity.Student;

public interface StudentService {
	
	public void saveOne(Student stu);
	
}
  • StudentServiceImpl.java
package com.demo.service.impl;

import org.springframework.transaction.annotation.Transactional;

import com.demo.dao.StudentDao;
import com.demo.entity.Student;
import com.demo.service.StudentService;

@Transactional
public class StudentServiceImpl implements StudentService {

	private StudentDao studentDao;

	public void saveOne(Student stu) {
		studentDao.saveOne(stu);
	}

	public void setStudentDao(StudentDao studentDao) {
		this.studentDao = studentDao;
	}

}
  • BaseDao.java
package com.demo.dao.impl;

import org.hibernate.Session;
import org.hibernate.SessionFactory;

/**
 * 所有Dao层实现类都要继承该类,统一管理sessionFactory
 */
public class BaseDao {

	private SessionFactory sessionFactory;

	/**
	 * 获取Session对象
	 */
	public Session getSession() {
		return this.sessionFactory.getCurrentSession();
	}

	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

}

  • StudentDao.java
package com.demo.dao;

import com.demo.entity.Student;

public interface StudentDao {

	public void saveOne(Student stu);

}
  • StudentDaoImpl.java
package com.demo.dao;

import com.demo.entity.Student;

public interface StudentDao {

	public void saveOne(Student stu);

}```
- StudentDao.java
```java
package com.demo.dao.impl;

import com.demo.dao.StudentDao;
import com.demo.entity.Student;

public class StudentDaoImpl extends BaseDao implements StudentDao {

	public void saveOne(Student stu) {
		this.getSession().save(stu);
	}
}

至此,Spring与Hibernate已经整合完毕。


(2)Spring 整合 Struts2

spring整合struts2比较简单,它们之间没有多少交互的地方,上面已经使用spring配置好了action中的bean,其他配置项几乎都是struts2一个人的。

  • web.xml
	<!-- Action过滤器配置 -->
	<filter>
		<filter-name>struts2</filter-name>
		<filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
		<init-param>
			<param-name>config</param-name>
			<param-value>resources/struts.xml</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>struts2</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

在web.xml中配置struts2的过滤器,用以处理前台页面发送来的请求,并根据指定的action配置下发到各个action处理请求。以上配置中的init-param指定了struts.xml文件的路径。因为这里的config路径变更,所有默认的struts-default.xml与struts-plugins.xml文件路径也必需在struts.xml中进行配置

  • struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
	"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
	"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
	<include file="struts-default.xml" />
	<include file="struts-plugin.xml" />
	<include file="resources/struts2/struts-*.xml" />
</struts>

为了方便模块管理,这里使用了通配符,只要是在resources/struts2/目录下,且以struts-开头的xml文件,均会被包含在内。

  • struts-student.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
	"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
	"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
	<package name="stuManage" namespace="/stuManage" extends="struts-default">
		<!-- 定义全局result -->
		<global-results>
			<!-- 定义名为exception的全局result -->
			<result name="exception">/WEB-INF/pages/error.jsp</result>
		</global-results>
		<!-- 定义全局异常映射 -->
		<global-exception-mappings>
			<exception-mapping result="exception"
				exception="com.demo.exception.SysException"></exception-mapping>
			<exception-mapping result="exception" exception="java.lang.Exception" />
		</global-exception-mappings>
		
		<action name="saveStu" class="studentAction" method="saveStu">
			<result name="success">/default.jsp</result>
		</action>
	</package>
</struts>
  • 这里用到了全局异常处理;
  • action中的studentController必须要与applicationContext_action.xml中的beanid一致。服务器启动时,就会加载studentAction,并依次加载service和dao。
    在这里插入图片描述
  • index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!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>三大框架学习——主页</title>
</head>
<body>
	<form action="stuManage/saveStu" method="post">
		Name:<input type="text" name="newName"> <br> 
		<input type="submit" value="保存学生信息">
	</form>
</body>
</html>
  • success.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!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>测试网页</title>
</head>
<body>
保存成功
</body>
</html>
  • error.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags"%>
<!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>错误页面</title>
</head>
<body>
	<h1><font color="red">出错了!</font></h1>
	<br>
	<h3>错误信息:</h3>
	<hr>
	<font face="Courier New"><s:property value="exception.message" /></font>
</body>
</html>

struts2相关配置结束。
你以为真的结束了吗?是的,但再完善一点,把日志的事解决了。

(3)日志配置

  • web.xml
	<!-- 系统日志配置监听器 -->
	<listener>
		<listener-class>com.demo.listener.Log4j2ConfigListener</listener-class>
	</listener>
	<context-param>
		<description>日志配置文件的路径</description>
		<param-name>log4j.configurationFile</param-name>
		<param-value>resources/log4j2.xml</param-value>
	</context-param>
  • 这里的Log4j2ConfigListener是自定义类;
  • 日志配置文件指定到resources目录。
  • Log4j2ConfigListener
package com.demo.listener;

import java.util.Enumeration;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.apache.logging.log4j.core.config.Configurator;

public class Log4j2ConfigListener implements ServletContextListener {
	private static final String KEY = "log4j.configurationFile";

	@Override
	public void contextDestroyed(ServletContextEvent arg0) {
	}

	@Override
	public void contextInitialized(ServletContextEvent arg0) {
		String fileName = getContextParam(arg0);
		Configurator.initialize("Log4j2", "classpath:" + fileName);
	}

	@SuppressWarnings("unchecked")
	private String getContextParam(ServletContextEvent event) {
		Enumeration<String> names = event.getServletContext()
				.getInitParameterNames();
		while (names.hasMoreElements()) {
			String name = names.nextElement();
			String value = event.getServletContext().getInitParameter(name);
			if (name.trim().equals(KEY)) {
				return value;
			}
		}
		return null;
	}
}
  • log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn">
	<Appenders>
		<Console name="Console" target="SYSTEM_OUT">
			<PatternLayout pattern="[%-5p] %d %c - %m%n" />
		</Console>
		<File name="File" fileName="dist/my.log">
			<PatternLayout pattern="%m%n" />
		</File>
	</Appenders>
	<Loggers>
		<Logger name="com.deml.log.Log4jTest2" level="INFO">
			<AppenderRef ref="File" />
		</Logger>
		<Root level="INFO">
			<AppenderRef ref="Console" />
		</Root>
	</Loggers>
</Configuration>

      内容太多了,对于没有耐心的读者来说这是一种折磨。其实,如果感兴趣的话可以按照本文的步骤先把项目搭建起来,然后再找出其中的知识点去复习。在自己捣鼓如何搭建web项目过程中虽然耗时,但好歹也搭建成功了,虽然不完美,但可以将就使用。
      这个实例只是简单地搭建了一下,在实际应用中有很多业务场景要去实现,可以在这个模板的基础之上,导包,完善下去,相信在这个过程中,会对三大框架更加了解。

全文结束!

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值