教你如何使用IDEA + Maven 搭建SSM Web項目(超詳細)

前言

本文將帶你一步一步使用IDEA搭建SSM項目(Spring SpringMVC Mybatis)。最終所實現的效果是通過訪問用戶註冊界面(http://localhost/register.jsp),將新用戶添加進MySQL數據庫。通過post方法訪問http://localhost/emp/getAll,獲取所有用戶信息的json數據。
在這裏插入圖片描述

1. 環境

java version:12.0.2
maven版本:3.6.1
MySQL版本:5.x

2. 前期準備

創建數據庫:

CREATE DATABASE mybatisdb;

創建表:

create table employee(
  eid int(5) primary key,
  ename varchar(20),
  esalary double(8,2),
  esex varchar(2)
);

3. 開始創建工程

1.File->New->Project…

在這裏插入圖片描述

2.選擇Maven項目

在這裏插入圖片描述

3.填寫項目信息

在這裏插入圖片描述

3.設置Maven

我在這裏選擇的是我自己下載的一個maven,maven的源已經被我在settings.xml中設置成了阿里的源,如果不設置的將會導致下載jar包時消耗很長的時間。設置方法就不在這裏講了,網上教程一大堆。
在這裏插入圖片描述

4.選擇項目存放路徑,最後點Finish

此時的項目結構如下:
在這裏插入圖片描述

4. 調整IDEA自動創建的工程目錄

4.1 創建源代碼路徑

右鍵main文件夾---->new—>Directory,取名爲java
在這裏插入圖片描述
將該目錄設置爲源代碼路徑
在這裏插入圖片描述
創建包com.yky,並在包中創建domain、dao、service、controller。創建好的包目錄如下:

在這裏插入圖片描述

4.2 創建test路徑

右鍵src—>new—>Directory,取名爲test。在新創建的test目錄下創建java文件夾,並設置爲test的根目錄

在這裏插入圖片描述
在test/java目錄下創建com.yky.dao包,用於存放dao層的測試代碼

在這裏插入圖片描述

4.3 創建resource路徑

創建resource路徑專門用來存放配置文件
右鍵main文件夾—>new->Directory,取名爲resource。並設置爲resource根路徑

在這裏插入圖片描述
爲了讓配置文件看起來不亂,在resource目錄下創建mapper目錄,專門用來存放Mybatis的mappper文件。

在WEB-INF下創建jsp目錄,用來專門存放jsp。

4.4 整個的目錄結構

至此,目錄就創建完成了。完整的目錄結構如下:
在這裏插入圖片描述

4.5 修改pom.xml文件

想要使用Spring 、SpringMVC、Mybatis,我們需要用Maven導入相關的jar包。配置pom.xml文件如下:
注意這裏的jdk版本設置成了12,如果使用的jdk版本與我的不同,請做出相應的更改。最後記得import一下。

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.yky</groupId>
  <artifactId>ssm-test</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>ssm-test Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <!-- 設置項目編碼編碼 -->
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <!-- spring版本號 -->
    <spring.version>4.3.5.RELEASE</spring.version>
    <!-- mybatis版本號 -->
    <mybatis.version>3.4.1</mybatis.version>
    <java.version>12</java.version>
    <maven.compiler.source>12</maven.compiler.source>
    <maven.compiler.target>12</maven.compiler.target>
  </properties>

  <dependencies>

    <!--springmvc-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <!-- 單元測試 -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
    </dependency>

    <!-- 實現slf4j接口並整合 -->
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.2.2</version>
    </dependency>

    <!-- JSON -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.8.7</version>
    </dependency>
      <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-annotations</artifactId>
          <version>2.10.0.pr3</version>
      </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.10.0.pr3</version>
    </dependency>

    <!-- 數據庫 -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.41</version>
      <scope>runtime</scope>
    </dependency>

    <!-- 數據庫連接池 -->
    <dependency>
      <groupId>com.mchange</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.5.2</version>
    </dependency>

    <!-- MyBatis -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>${mybatis.version}</version>
    </dependency>

    <!-- mybatis/spring整合包 -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.1</version>
    </dependency>

    <!-- Spring -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>${spring.version}</version>
    </dependency>




    <!-- 數據庫 -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.41</version>
      <scope>runtime</scope>
    </dependency>

	<!--servlet API-->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
    </dependency>


  </dependencies>

  <build>
    <finalName>ssm-test</finalName>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.0.0</version>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.7.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.20.1</version>
          <configuration>
            <skipTests>true</skipTests>
          </configuration>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

4.6 增加Mybatis配置文件

在resource目錄下創建mybatis.xml文件,該文件只需要針對mapper文件進行配置,數據庫連接相關操作不在本文件中配置,交由Spring來管理!

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

    <mappers>
        <!--一會兒會在這裏導入Mapper文件-->
    </mappers>
</configuration>

4.7 增加數據庫連接基本參數配置文件

在resource目錄下新建jdbc.properties,並寫入以下內容

database.driver=com.mysql.jdbc.Driver
database.url=jdbc:mysql://localhost:3306/mybatisdb
database.username=root
database.password=123456

4.8 增加Spring配置文件

數據庫的連接、SqlSession的創建、Mybatis事務的管理都交由Spring來管理。可以看到Spring配置文件中有讀取jdbc.properties文件,拿到數據庫的連接參數,從而去鏈接數據庫;Spring配置文件中有配置sqlSessionFactory,並導入了先前創建的mybatis.xml配置文件。
在resource目錄下新建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:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">


    <!-- 配置數據庫相關參數properties的屬性:${url} -->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!-- 配置C3P0連接池,目的:管理數據庫連接 -->
    <bean id="comboPooledDataSourceID" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${database.driver}"/>
        <property name="jdbcUrl" value="${database.url}"/>
        <property name="user" value="${database.username}"/>
        <property name="password" value="${database.password}"/>
    </bean>


    <!-- 配置SqlSessionFactoryBean,目的:加載mybaits配置文件和映射文件,即替代原Mybatis工具類的作用 -->
    <bean id="sqlSessionFactoryBeanID" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="configLocation" value="classpath:mybatis.xml"/>
        <property name="dataSource" ref="comboPooledDataSourceID"/>
    </bean>


    <!-- 配置Mybatis的事務管理器,即因爲Mybatis底層用的是JDBC事務管事器,所以在這裏依然配置JDBC事務管理器 -->
    <bean id="dataSourceTransactionManagerID" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="comboPooledDataSourceID"/>
    </bean>

    <!-- 配置事務通知,即讓哪些方法需要事務支持 -->
    <tx:advice id="tx" transaction-manager="dataSourceTransactionManagerID">
        <tx:attributes>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

    <!-- 配置事務切面,即讓哪些包下的類需要事務 -->
<!--    <aop:config>-->
<!--        <aop:pointcut id="pointcut" expression="execution(* com.yky.service.EmpService.addEmp(..))"/>-->
<!--        <aop:advisor advice-ref="tx" pointcut=""/>-->
<!--    </aop:config>-->

    <!--
        (1)導入jackson-annotations-2.10.0.jar、jackson-core-2.10.0.jar、jackson-databind-2.10.0.jar這三個jar包
        配置JSON適配器
    -->
    <bean id="mappingJackson2HttpMessageConverter"
          class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
        <property name="supportedMediaTypes">
            <list>
                <value>text/html;charset=UTF-8</value>
                <value>text/json;charset=UTF-8</value>
                <value>application/json;charset=UTF-8</value>
            </list>
        </property>
    </bean>
    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
        <property name="messageConverters">
            <list>
                <ref bean="mappingJackson2HttpMessageConverter"/>
            </list>
        </property>
    </bean>
    <!--開啓以註解的方式來管理事務-->
    <tx:annotation-driven transaction-manager="dataSourceTransactionManagerID"/>

    <!--掃描註解-->
    <context:component-scan base-package="com.yky"/>


</beans>

4.9 配置web.xml文件

做好了上面的準備工作後,配置web.xml文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xis="http://www.w3.org/2001/XMLSchema-instance"
         xis:schemaLocation="http://java.sun.com/xml/ns/javaee
         http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <display-name>client</display-name>

  <!--配置前置控制器 -->
  <servlet>
    <servlet-name>ClientDispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:applicationContext.xml</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>ClientDispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
  
  <filter>
    <filter-name>encodingFilter</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>
    <init-param>
      <param-name>forceEncoding</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  
  <!--歡迎文件-->
  <welcome-file-list>
    <welcome-file>/index.jsp</welcome-file>
  </welcome-file-list>
</web-app>

至此,關鍵配置文件添加完畢!再來看一看目錄結構:

在這裏插入圖片描述

5. 配置Tomcat嘗試跑一下

基礎準備工作已經完成,接下來配置Tomcat嘗試跑一下,看歡迎界面能否出來
Run—>Edit Configurations 彈出以下界面
在這裏插入圖片描述
添加Tomcat服務器
在這裏插入圖片描述
按照下面的步驟選擇
在這裏插入圖片描述
在這裏插入圖片描述
改一下Application context,改完點確定
在這裏插入圖片描述
運行服務器,訪問看一下
在這裏插入圖片描述

6. 類的創建

6.1 創建Employee類

package com.yky.domain;

public class Employee {
    private Integer id;
    private String name;
    private Double salary;
    private String sex;
    public Employee(){}
    public Employee(Integer id, String name, Double salary, String sex) {
        this.id = id;
        this.name = name;
        this.salary = salary;
        this.sex = sex;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Double getSalary() {
        return salary;
    }
    public void setSalary(Double salary) {
        this.salary = salary;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Emp{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", sal=" + salary +
                ", sex='" + sex + '\'' +
                '}';
    }
}

6.2 創建EmployeeDao類

package com.yky.dao;

import com.yky.domain.Employee;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public class EmployeeDao
{
    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    /**
     * 增加員工
     */
    public void add(Employee emp) throws Exception
    {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        sqlSession.insert("empNamespace.add",emp);
        sqlSession.commit();
    }

    public List<Employee> getAll() throws Exception
    {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        return sqlSession.selectList("empNamespace.getAll");
    }
}

6.3 增加Mapper文件

在resource/mapper下新建EmployeeMapper.xml文件,並填入以下內容

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="empNamespace">

    <resultMap type="com.yky.domain.Employee" id="empMap">
        <id property="id" column="eid"/>
        <result property="name" column="ename"/>
        <result property="salary" column="esalary"/>
        <result property="sex" column="esex"/>
    </resultMap>

    <!-- 增加員工 -->
    <insert id="add" parameterType="com.yky.domain.Employee">
        insert into employee(eid,ename,esalary,esex) values(#{id},#{name},#{salary},#{sex})
    </insert>

    <select id="getAll" resultMap="empMap">
        select * from employee;
    </select>

</mapper>

mybatis.xml文件導入Mapper文件

<configuration>

    <mappers>
        <mapper resource="mapper/EmployeeMapper.xml"/>
    </mappers>
</configuration>

6.4 創建EmployeeDao的測試類

測試代碼如下:

package com.yky.dao;

import com.yky.domain.Employee;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.List;

import static org.junit.Assert.*;

public class EmployeeDaoTest {

    ApplicationContext applicationContext;
    public void init()
    {
        //加載Spring配置文件
        applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    }
    @Test
    public void add() throws Exception
    {
        Employee employee = new Employee();
        employee.setId(3);
        employee.setName("tom");
        employee.setSalary(11000D);
        employee.setSex("男");

        init();
        EmployeeDao employeeDao = (EmployeeDao) applicationContext.getBean("employeeDao");
        employeeDao.add(employee);
    }

    @Test
    public void getAll() throws Exception {
        init();
        EmployeeDao employeeDao = (EmployeeDao) applicationContext.getBean("employeeDao");
        List<Employee> employees = employeeDao.getAll();
        System.out.println(employees);
    }
}

運行效果:
在這裏插入圖片描述

6.5 創建EmployeeService類,調用Dao層代碼

package com.yky.service;

import com.yky.dao.EmployeeDao;
import com.yky.domain.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
public class EmployeeService
{
    @Autowired
    private EmployeeDao employeeDao;

    @Transactional
    public void addEmp(Employee emp) throws Exception {
        employeeDao.add(emp);
    }

    public List<Employee> getAllEmp() throws Exception{
        return employeeDao.getAll();
    }
}

6.6 創建EmployeeAction類,調用Service層代碼

package com.yky.controller;

import com.yky.domain.Employee;
import com.yky.service.EmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;

@Controller
@RequestMapping("/emp")
//@RestController
public class EmployeeAction
{
    @Autowired
    private EmployeeService employeeService;

    @RequestMapping("/register")
    public String register(Model model, Employee emp) throws Exception
    {
        System.out.println("用戶註冊");
        employeeService.addEmp(emp);
        System.out.println("註冊成功");
        model.addAttribute("message","註冊成功");
        return "/WEB-INF/jsp/message.jsp";
    }

    @RequestMapping("/getAll")
    public @ResponseBody
    List<Employee> getAll() throws Exception
    {
        System.out.println("獲取所有用戶");
        return employeeService.getAllEmp();
    }
}

6.7 創建用戶註冊頁面register.jsp

<%--
  Created by IntelliJ IDEA.
  User: danxi
  Date: 2019/10/31
  Time: 9:34
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>員工註冊</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/emp/register" method="POST">
    <table border="2" align="center">
        <tr>
            <th>編號</th>
            <td><input type="text" name="id"></td>
        </tr>
        <tr>
            <th>姓名</th>
            <td><input type="text" name="name"></td>
        </tr>
        <tr>
            <th>薪水</th>
            <td><input type="text" name="salary"></td>
        </tr>
        <tr>
            <th>性別</th>
            <td>
                <input type="radio" name="sex" value=""/><input type="radio" name="sex" value="" checked/></td>
        </tr>
        <tr>
            <td colspan="2" align="center">
                <input type="submit" value="註冊"/>
            </td>
        </tr>
    </table>
</form>
</body>
</html>

6.8 創建提示頁面message.jsp

<%--
  Created by IntelliJ IDEA.
  User: danxi
  Date: 2019/10/31
  Time: 11:30
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>提示</title>
</head>
<body>
    <h1>${message}</h1>
</body>
</html>

7. 最終效果

瀏覽器訪問http://localhost/register.jsp
在這裏插入圖片描述
成功註冊
在這裏插入圖片描述

通過postman成功拿到json數據:
在這裏插入圖片描述

8. 可能遇到的坑

8.1 JDK版本不匹配導致報錯

解決方法:
步驟1:file—>Progect structure確保以下幾個位置的版本一致
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
步驟2:settings—>Build,Execution,Deployment—>Compiler—>java Compiler
在這裏插入圖片描述
通過以上兩部就解決了JDK版本不一致的問題。

8.2 明明已經在Maven中導入了相關jar包還報錯

諸如以下形式的錯誤:

java.lang.ClassNotFoundException: org.springframework.web.filter.CharacterEncodingFilter
java.lang.ClassNotFoundException: org.springframework.web.servlet.DispatcherServlet

此時查看spring配置文件和web.xml文件中的相關類也可以點進去。
在這裏插入圖片描述
其實問題所在是tomcat的WEB-INF下沒有lib文件所致,既然沒有,那就把它加進去!
file—>Progect structure—>Artifacts
右鍵點擊最右側的工程名,選擇Put into Output Root,點擊後發現左側的WEB-INF下多了lib文件,這樣的話這個問題就解決了!
在這裏插入圖片描述

參考

Mybatis【與Spring整合】
關於IDEA使用maven整合ssm框架

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章