javaweb
1.基本概念
1.1前言
web开发:
-
web,网页的意思,www.baidu.com
-
静态web
-
html,css
-
提供给所有人看的数据始终不会发生变化!
-
-
动态web
-
淘宝...等几乎是所有的网站
-
提供给所有人看的数据始终会发生变化,每个人在不同的时间,不同的地点看到的信息各不相同
-
技术栈: Servlet/JSP,ASP,PHP
-
在java中,动态web资源开发的技术统成为javaweb
1.2 web应用程序
程序是什么: 数据结构+算法
-
web应用程序:可以提供浏览器访问的程序
-
a.html、b.html....多个web资源,这些web 资源可以被外界访问,对外界提供服务
-
你们能访问到的任何一个页面或者资源,都存在于这个世界的某一个角落的计算机上
-
URL
-
这个统一的web资源会被放在同一个文件夹下,web应用程序--->Tomcat :服务器
-
一个web应用由多部分组成(静态web,动态web)
-
html,css,js
-
jsp,servlet
-
java程序
-
jar包
-
配置文件(Properties)
-
-
web应用程序编写完毕后,若想提供给外界访问:需要一个服务器来统一管理
1.3静态web
-
.htm,.html,如果服务器上一直存在这些东西,我们就可以直接进行读取
-
web页面存在的缺点
web页面无法动态更新,所有用户看到的都是同一个页面
轮播图,点击特效:伪动态
JavaScript(实际开发中用的最多)
VBScript
它无法和数据库交互(数据无法持久化,用户无法交互)
1.4动态web
缺点:
加入服务器的动态web资源出现了错误,我们需要重新编写我们的后台程序,重新发布,这个时候可以停机维护
优点:
web页面可以动态更新,所有用户看到的都不是一个页面
它可以与数据库交互(数据持久化:注册,商品信息,用户信息....)
2.web服务器
ASP:
-
微软:国内最早流行的就是ASP
-
在HTML中嵌入了VB的脚本,ASP+COM
-
在ASP开发中,基本每一个页面都有几千行的业务代码,页面很乱
-
<h1> <h1> <h1> <h1> <h1> <% System.out.println("hello") %> <h1> <h1>
-
维护成本高
-
C# IIS
php
-
php开发速度很快,功能很强大 ,跨平台,代码很简单(70%,WP)
-
无法承载大访问量的情况(局限性)
JSP/Servlet
B/S:浏览和服务器
C/S:客户端和服务器
-
sun公司主推的B/S架构
-
基于java语言的(所有大公司,或者一些开源的组件,都是用java写的)
-
可以承载三高问题带来的影响(高并发,高可用,高性能)
2.1web的有关服务器
服务器是一种被动的操作,用来处理用户的一些请求和给用户的一些响应信息
IIS
微软的,ASP....,Windows中自带的
Tomcat
面向百度编程
修改信息 在 conf包的server.xml里面
默认端口号:
tomcat的默认端口号为:8080
mysql: 3306
http:80
https:443
默认的主机名为:localhost->127.0.0.1
默认网站应用存放的位置为:webapps
面试题:
请你谈谈网站是如何进行访问的!
1.输入一个域名;回车
2.回车检查本机的 C:\Windows\System32\drivers\etc\hosts配置文件下有没有这个域名映射
有:直接返回对应的ip地址,这个地址中,有我们需要访问的web程序,可以直接访问
没有:就去DNS服务器找,找到的话就返回,找不到就返回找不到
发布一个web网站
不会就先模仿
1.将自己写的网站,放到服务器(tomcat)中指定的web应用的文件夹(webapps)下,就可以访问了
网站应该有的结构
-- webapps: Tomcat服务器的web目录 -root -kuangstudy: 网站的目录名 -WEB -INF -classes: java程序 -lib : web应用所依赖的jar包 - web.xml :网站配置文件 -index.jsp 默认的首页 - static -css -style.css -js -img - .....
3.Http
3.1什么是Http
Http(超文本传输协议)是一个简单的请求-响应协议,它通常运行在TCP之上
-
文本: html,字符串,~...
-
超文本:图片,音乐,视频,定位,地图......
https:443 安全的
3.2两个时代
-
http1.0
-
HTTP/1.0: 客户端可以与web服务器连接后,只能获得一个web资源 短连接
-
-
http2.0
-
HTTP/1.1 客户端可以与web服务器连接后,可以获得多个web资源 长连接
-
3.3Http请求
-
客户端--发请求--服务器
-
请求行
:请求行中的请求方式:GET 能够携带的参数比较少,大小有限制,会在浏览器的URL地址栏显示数据内容,不安全,但高效
请求方式:Get/Post
post:能够携带的参数没有限制,大小有限制,不会在浏览器的URL地址栏显示数据内容,安全,但不高效
3.4Http响应
-
服务器 --响应 --客户端
3.5响应状态码:
200:请求响应成功
3**:请求重定向
重新到我给你的新位置去
404:找不到资源
5**: 服务器代码错误 500 502网关错误
常见面试题: 当你的浏览器中地址栏输入地址并回车的一瞬间页面展示的过程,经历了什么
4.Maven
在javaweb开发中,需要用到大量的jar包,我们需要手动导入
Maven诞生了
4.1Maven项目架构管理工具
Maven核心思想:约定大于配置
Maven会规定好你该如何去编写java代码
###
5.Servelt
Servelt就是sun公司开发动态web的一门技术
Sun在这些API中提供一个接口叫做:Servelt,如果你想开发一个Servelt程序,需要完成两个步骤:
1.编写一个类,实现Servelt接口
2.把开发好的java类部署到web服务器中
把实现了Servelt接口的java程序,叫做Servelt
Servelt接口Sun公司有两个默认的实现类:HTTPServelt,GenericServelt
3.编写Servelt的映射
为什么需要映射:我们写的是ava程序,但是需要通过浏览器访问,浏览器需要连接web服务器,所以我们需要在web服务中注册我们写的Servelt,还需要给他一个浏览器能够访问的路径
Servelt原理
Servelt是由web服务器调用
5.1.Mapping问题
1.一个Servlet可以指定一个映射路径
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
2.一个Servlet可以指定多个映射路径
3.一个Servlet可以指定通用映射路径
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello/*</url-pattern> </servlet-mapping>
4.默认通用路径
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
5.指定一些后缀或者前缀等等.....
<!-- 可以自定义后缀实现请求映射 注意点: *前面不能加项目映射的路径--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>*.qiannan</url-pattern> </servlet-mapping>
5.2ServletContext
web容器在启动的时候,它会为每一个web程序创建一个ServeltContext对象,它代表了当前的web应用:
5.2.1共享数据:
我在这个Servelt中保存的数据,可以在另一个Servelt中拿到
1.放置它的
public class servelt extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//this.getServletConfig() Servelt配置
// this.getInitParameter() 初始化参数
// this.getServletContext() Servelt上下文
ServletContext context = this.getServletContext();
String username = "倩楠";
context.setAttribute("username",username);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
2.读取它的
public class GetServelt extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
String username = (String) context.getAttribute("username");
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/html");
resp.getWriter().print(username);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
3.配置xml
<web-app
xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1">
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.yang.servelt</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>getc</servlet-name>
<servlet-class>com.yang.GetServelt</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>getc</servlet-name>
<url-pattern>/getc</url-pattern>
</servlet-mapping>
</web-app>
5.2.2请求转发
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("进入了Demo03");
ServletContext context = this.getServletContext();
context.getRequestDispatcher("/hello").forward(req,resp);
}
5.2.3读取资源文件
Properties
在java目录下新建Properties 如果打包不了可以参考狂神javaweb11集给web.xml添加build
在resources目录下新建Properties
发现:都被打包到了同一路径下:classes,我们俗称这个路径为classpath
思路:需要一个文件流
username = root password = 123456
public class ServeltDemo05 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");
Properties prop = new Properties();
prop.load(is);
String user = prop.getProperty("username");
resp.getWriter().print(user);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet (req, resp);
}
}
5.3HttpServletResponse
web服务器接收到客户端的http请求,针对这个请求,分别创建一个代表请求的一个HttpServeltRequest对象,代表响应的一个HttpServletResponse
如果要获取客户端请求过来的参数:找HttpServeltRequest
如果要给客户端响应一些信息:找HttpServletResponse
-
-
简单分类: 负责向浏览器发送数据的方法
负责向浏览器发送响应头的方法
-
2.常见应用
-
向浏览器输出消息
-
下载文件
-
要获取下载文件的路径
-
下载的文件名是啥
-
设置想办法让浏览器能够支持下载我们需要的东西
-
获取下载文件的输入流
-
创建缓冲区
-
获取OutputStream对象
-
将FileOutputStream流写入到buffer缓冲区
-
使用OutputStream将缓冲区中的数据输出到客户端
-
public class FileServelt extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // * 要获取下载文件的路径 String realPath = "D:\\diea2\\javaweb-03-Servelt\\Servelt-05\\src\\main\\resources\\1.jpg"; System.out.println("下载文件的路径:"+realPath); // * 下载的文件名是啥 String filename = realPath.substring(realPath.lastIndexOf("\\") + 1); // * 设置想办法让浏览器能够支持下载我们需要的东西 URLEncoder.encode将图片改为中文文件名,否则会乱码 resp.setHeader("Content-disposition","attachment;filename"+ URLEncoder.encode(filename,"UTF-8")); // * 获取下载文件的输入流 FileInputStream in = new FileInputStream(realPath); // * 创建缓冲区 int len =0; byte[] buffer = new byte[1024]; // * 获取OutputStream对象 ServletOutputStream out = resp.getOutputStream(); // * 将FileOutputStream流写入到buffer缓冲区 * 使用OutputStream将缓冲区中的数据输出到客户端 while((len=in.read(buffer))!=-1){ out.write(buffer,0,len); } in.close(); out.close(); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
-
reponse验证码实现
实现重定向
一个web资源收到客户端请求后,他会通知客户端去访问另一个web资源,这个叫做重定向
public class RedirecctServelt extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.sendRedirect("/Servelt_05_war/dp"); // 先/自己tomcat设置的那个路径再/要跳转的路径
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
常见场景: 用户登录
重定向和转发的区别:
相同点:页面都会实现跳转
不同点:请求转发的时候,url不会产生变化
重定向的时候,url地址栏会产生变化
5.4HttpServeltRequest
HttpServeltRequest代表客户端的请求,用户通过Http协议访问服务器,Http请求中的所有信息会被封装到HttpServeltRequest,通过这个HttpServeltRequest的方法,获得客户端的所有信息
public class TestRequest extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("进入这个请求了");
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println(username+password);
//重定向一定要注意路径问题,否则会404
resp.sendRedirect("/Servelt_05_war/aaa.jsp");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
获取参数
,请求转发,转发是服务器内部进行,不需要加项目路径
6.Cookie .Session
6.1会话
会话:用户打开了一个浏览器,点击了很多超链接,访问多个web资源,关闭浏览器,这个过程称之为会话
6.2保存会话的两种技术
cookie 客户端技术 (响应,请求)
session 服务器技术,可以保存用户的会话信息,我们可以把信息或者数据放入session中
常见:网站登录以后,你下次可以不在登录,第二次访问直接就进去了
6.3Cookie
1.从请求中拿到cookie信息
2.服务器响应给客户端cookie
3.cookie一般会保存在本地的用户目录下的appdata
4.一般网站cookie存在上限
一个Cookie只能保存一个信息
一个web站点可以给浏览器发送多个cookie,最多存放20个cookie
Cookie大小有限制比如说4kb
一个浏览器有上限比如说有300个cookie
删除Cookie:
1.不设置有效期,关闭浏览器,自动失效
2.设置有效时间为0
public class CookieDemo01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/html");
PrintWriter out = resp.getWriter();
Cookie[] cookies = req.getCookies();//这里返回数组,说明cookie可能存在多个
//判断cookies是否存在
if(cookies!=null){
//如果存在
out.write("你上一次访问的时间是");
for (int i = 0; i < cookies.length; i++) {
Cookie cookie = cookies[i];
//获取cookie的名字
if(cookie.getName().equals("LastLoginTime")){
//获取cookie中的值
long LastLoginTime = Long.parseLong(cookie.getValue());
Date date = new Date(LastLoginTime);
out.write(date.toLocaleString());
}
}
}else{
out.write("这是你第一次访问本站");
}
//服务器给客户端响应一个cookie
Cookie cookie = new Cookie("LastLoginTime",System.currentTimeMillis()+"");
cookie.setMaxAge(24*60*60);//cookie有效期为一天
resp.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
6.4Session(重点)
1.服务器会给每一个用户(浏览器)创建一个Session对象
2.一个Session独占一个浏览器,只要浏览器没有关闭,这个Session就存在
3.用户登录之后,整个网站它都可以访问 -->保存用户的信息
Session 和Cookie的区别:
1.Cookie是把用户的数据写给用户的浏览器,浏览器保存(可以保存多个)
2.Session是把用户的数据写到用户独占Session中,服务器端保存(保存重要的信息,减少服务器资源的浪费)
3.Session对象由服务器创建
public class SessionDemo01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//解决乱码问题
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
//得到Session
HttpSession session = req.getSession();
//给Session中存东西
session.setAttribute("name","倩楠");
//获取Session的id
String id = session.getId();
//判断Session是不是新创建的
if(session.isNew()){
resp.getWriter().write("session创建成功:"+id);
}else{
resp.getWriter().write("session已经在服务器创建了"+id);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
7.JSP
7.1什么是JSP
Java Server Pages: java服务端页面,也和Servelt异样,用于动态Web技术!
最大的特点:
-
写JSP就像在写HTML
-
区别:
HTML只给用户提供静态的数据
JSP页面中可以嵌入JAVA代码,为用户提供动态数据
7.2JSP原理
JSP到底怎么执行的
-
代码层面没有任何问题
-
服务器内部工作
tomcat中有一个work目录
IDEA中使用Tomcat的会在tomcat中生产一个work目录
浏览器向服务器发送请求,不管访问什么资源,其实都是在访问Servlet
JSP最终也会被转化成java类
JSP本质上就是一个Servlet
在JSP页面中:
只要是JAVA代码就会原封不动的输出;
如果是HTML代码,就会被转换为这样的格式输出到前端
out.write("<html>\r\n");
7.3JSP基础语法
任何语言都有自己的语法,JSP作为java技术的一种应用,它拥有一些自己扩充的语法(了解即可),java所有语法都支持
JSP表达式
<%---JSP表达式 作用:用来将程序输出,输出到客户端 <%=变量或者表达式%> ---%> <%= new java.util.Date() %> </body> </html>
JSP脚本片段
<% int sum = 0; for(int i=1;i<=100;i++){ sum+=i; } out.println("<h1>Sum="+sum+"</h1>"); %>
JSP的注释不会再客户端显示,HTML就会
7.4JSP指令
<%@page errorPage="500.jsp" %> 错误页面的指定 <%@include file=""%>
7.5 9大内置对象
-
PageContext 存东西
-
Request 存东西
-
Response
-
Session 存东西
-
Application {SerlvetContext} 存东西
-
config {ServeltConfig}
-
out
-
page
-
exception
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%-- 内置对象--%>
<%
pageContext.setAttribute("name1","倩楠1号"); //保存的数据只在一个页面中有效
request.setAttribute("name2","倩楠2号");//保存的数据只在一个请求中有效,请求转发会携带这个数据
session.setAttribute("name3","倩楠3号");//保存的数据只在一次会话中有效,从打开浏览器到关闭浏览器
application.setAttribute("name4","倩楠4号");//保存的数据只在服务器中有效,从打开服务器到关闭服务器
%>
<%-- 脚本中的代码会被原封不动的生成到JSP.java
要求:这里面的代码必须保证java语法的正确性--%>
<%
//从pageContext取出,我们通过寻找的方式来
//从底层到高层(作用域)
String name1 = (String) pageContext.getAttribute("name1");
String name2 = (String) pageContext.getAttribute("name2");
String name3 = (String) pageContext.getAttribute("name3");
String name4 = (String) pageContext.getAttribute("name4");
String name5 = (String) pageContext.getAttribute("name5");
%>
<%--使用EL表达式输出 ${}--%>
<h1>取出的值为:</h1>
<h3>${name1}</h3>
<h3>${name2}</h3>
<h3>${name3}</h3>
<h3>${name4}</h3>
<h3><%=name5%></h3>
</body>
</html>
request:客户端向服务器发送请求,产生的数据,用户看完就没用了,比如:新闻
seesion:客户端向服务器发送请求,产生的数据,用户用完一会还有用,比如:购物车
application:客户端向服务器发送请求,产生的数据,一个用户用完了,其他用户还有用,比如:聊天数据
7.6JSP标签 JSTL标签 EL表达式
<!-- jstl 表达式--> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.1.2</version> </dependency> <!-- standard标签库的 --> <dependency> <groupId>taglibs</groupId> <artifactId>standard</artifactId> <version>1.1.2</version> </dependency>
EL表达式: ${}
-
获取数据
-
执行运算
-
获取web开发的常用对象
JSTL表达式
JSTL标签库的使用就是为了弥补HTML标签的不足,它自定义许多标签,可以供我们使用,标签的功能和java代码一样
核心标签
使用步骤:
引入对应的taglib
使用其中的方法
8.JavaBean
实体类
JavaBean有特定的写法:
-
必须要有一个无参构造
-
属性必须私有化
-
必须有对应的get/set方法
-
一般用来和数据库的字段做映射
ORM:对象关系映射
表-->类
字段--->属性
行记录--->对象
9.MVC三层架构
Model view Controller 模型 视图 控制器 可以理解为 数据 页面 执行逻辑
Model:
-
业务处理:业务逻辑(Service)
-
数据持久层: CRUD (Dao)
View:
-
展示数据
-
提供链接发起Servlet请求(a,form,img....)
Controller(Servlet)
-
接受用户的请求:(req:请求参数 Session信息.....)
-
交给业务层处理对应的代码
-
控制视图的跳转
登录---->接受用户的登录请求--->处理用户的请求(获取用户登陆的参数,username,password)---->交给业务层处理登录业务(判断用户名密码是否正确:事务)---->Dao层查询用户名和密码是否正确--->数据库
10.Filter(重点)
Filter:过滤器,用来过滤网站的数据
1.导包实现接口
2.
public class CharacterEncodingFilter implements Filter {
//初始化 web服务器启动,就已经开始初始化了,随时等待过滤对象出现
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("CharacterEncodingFilter初始化");
}
//chain 链
//1.过滤中的所有代码,在过滤特定的请求时都会执行
//2.必须要让过滤器继续通行 filterChain.doFilter(servletRequest,servletResponse)
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
servletRequest.setCharacterEncoding("utf-8");
servletResponse.setCharacterEncoding("utf-8");
servletResponse.setContentType("text/html;charset=UTF-8");
System.out.println("CharacterEncodingFilter执行前....");
filterChain.doFilter(servletRequest,servletResponse);//让我们的请求继续走,如果不写,程序到这里就截止
System.out.println("CharacterEncodingFilter执行后....");
}
//销毁 web服务器关闭的时候会销毁
@Override
public void destroy() {
System.out.println("CharacterEncodingFilter销毁");
}
}
用户登陆后才能进入主页,注销以后就不能进主页!
1.用户登录后,向Session中放入用户的数据
2.进入主页的时候要判断是否已经登陆,要求在过滤器中实现
public class SysFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest req, ServletResponse rep, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) rep;
if(request.getSession().getAttribute("USER_SESSION")==null){
response.sendRedirect("404.jsp");
}
filterChain.doFilter(req,rep);
}
@Override
3.在web.xml里面配置
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>com.yang.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/servelt/*</url-pattern>
</filter-mapping>
11.监听器
GUI编程经常使用 GUI:图形界面编程
12.JDBC
JDBC:java连接数据库
需要jar包的支持:
-
java.sql
-
javax.sql
-
mysql-connector-java... 连接驱动必须的
Junit单元测试
<!-- junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency>
简单使用
@Test注解只有在方法上有效,只要加了这个注解就可以直接运行