0%

SpringMVC

入门案例

  • 导入 maven 依赖
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
    </dependency>

    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.10.RELEASE</version>
    </dependency>
  • 创建 Spring 控制类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    package com.ljguo.controller;

    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;

    @Controller
    @RequestMapping("/user")
    public class UserController {
    @RequestMapping("/save")
    @ResponseBody
    public String save(){
    System.out.println("user save ...");
    return "{'info': 'spring-mvc-user'}";
    }
    }
  1. @Controller 让其加载到 Spring 容器中
  2. @RequestMapping 用于映射访问的 url 路径
  3. @ResponseBody 用于将方法返回值写到响应体中
  • 创建 SpringMVC 配置类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    package com.ljguo.config;

    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;

    @ComponentScan("com.ljguo.controller")
    @EnableWebMvc
    public class SpringMvcConfig {
    }
  1. @ComponentScan
  • 初始化 Servlet 容器, 加载 SpringMVC 环境, 设置 SpringMVC 技术处理的请求
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
    return new Class[]{SpringConfig.class};
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
    return new Class[]{SpringMvcConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
    return new String[]{"/"};
    }

    // 乱码处理
    @Override
    protected Filter[] getServletFilters() {
    CharacterEncodingFilter filter = new CharacterEncodingFilter();
    filter.setEncoding("utf-8");
    return new Filter[]{filter};
    }
    }
  1. 继承 AbstractAnnotationConfigDispatcherServletInitializer, 重写三个方法并配置
  2. getServletFilters 方法用于解决中文乱码

各种参数的传递

  • 普通参数

    1
    2
    3
    4
    5
    @RequestMapping("/save")
    @ResponseBody
    public String save(String username, String password){
    return username + ": " + password;
    }

    前端传输

    1
    2
    username
    password
  • pojo 参数

    1
    2
    3
    4
    5
    @RequestMapping("/pojo")
    @ResponseBody
    public String pojoParam(User user) {
    return user.toString();
    }
  • 嵌套 pojo 参数

    1
    2
    3
    4
    username
    password
    address.province
    address.city
  • 数组

    1
    2
    3
    4
    5
    @RequestMapping("/array")
    @ResponseBody
    public String arrayParam(String[] likes) {
    return Arrays.toString(likes);
    }

    前端传输

    1
    2
    3
    4
    likes
    likes
    likes
    ...
  • 集合

    1
    2
    3
    4
    5
    @RequestMapping("/list")
    @ResponseBody
    public String listParam(@RequestParam List<String> likes) {
    return likes.toString();
    }

    前端传输

    1
    2
    3
    4
    likes
    likes
    likes
    ...

    记得加上 @RequestParam 在参数上

  • Date 时间类型

    1
    2
    3
    4
    5
    @RequestMapping("/date")
    @ResponseBody
    public String dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd") Date date) {
    return date.toString();
    }

    前端传输

    1
    2
    2022/10/20
    2022-10-20

    注意默认时间格式是 yyyy/MM/dd, 如需其它格式, 需要使用 @DateTimeFormat 注解中的 pattern 指定格式

  • json
    这里需要在 SpringMVC 的配置类中加入 @EnableWebMvc 注解, 用于开启由 json 数据转化为我们的对象的功能

    1
    2
    3
    4
    5
    6
    @RequestMapping("/list")
    @ResponseBody
    public String listParam(@RequestBody List<User> users) {
    System.out.println(users);
    return likes.toString();
    }

响应

  • 页面跳转
    1
    2
    3
    4
    @RequestMapping("/jumpPage")
    public String jumpPage() {
    return "/index.jsp";
    }
    注意不要加 @ResponseBody 注解, 因为这个注解是将返回值写入响应体中, 便无法做到页面跳转
  • 纯文字
    1
    2
    3
    4
    5
    @RequestMapping("/toText")
    @ResponseBody
    public String toText() {
    return "hello world!";
    }
  • json 数据
    1
    2
    3
    4
    5
    6
    @RequestMapping("/toJson")
    @ResponseBody
    public User toJson() {
    User user = new User("ljguo","20001020",new Address("sichuan","nanchong"));
    return user;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @RequestMapping("/userList")
    @ResponseBody
    public List<User> userList() {
    User user = new User("ljguo","20001020",new Address("sichuan","nanchong"));
    User user1 = new User("ljguo1","20001020",new Address("sichuan","nanchong"));
    User user2 = new User("ljguo2","20001020",new Address("sichuan","nanchong"));
    List<User> userList = new ArrayList<>();
    userList.add(user);
    userList.add(user1);
    userList.add(user2);
    return userList;
    }
    这里的原理是 SpringMVC 依赖 @ResponseBody 注解并使用 Jackson 类帮我们实现了对象转化为 json 数据并传递到响应体

REST 风格

REST: Representational State Transfer 表现形式状态转换

传统风格

1
2
http://localhost/user/getById?id=1
http://localhost/user/addUser

REST

1
2
http://localhost/user/1
http://localhost/user

优点

  • 隐藏访问行为, 无法通过地址来得知行为
  • 书写简化

需要使用不同的请求方式

1
2
3
4
5
http://localhost/users      查全部信息(GET)
http://localhost/users/1 查指定信息(GET)
http://localhost/users 添加(POST)
http://localhost/users 修改(PUT)
http://localhost/users/1 删除(DELETE)

入门案例

1
2
3
4
5
6
@RequestMapping(value = "/{id}",method = RequestMethod.GET)
@ResponseBody
public String save(@PathVariable int id){
System.out.println("user save ..." + id);
return "{'info': 'spring-mvc-user'}";
}
  1. value = "/{id}" 对应 @PathVariable int id 实现对路径参数的解析
  2. method = RequestMethod.GET 设置请求方式
  • @RequestParam 用于接收表单数据
  • @RequestBody 用于接收 json 数据
  • @PathVariable 用于接收路径参数

简化

1
2
@Controller
@ResponseBody

替换为

1
@RestController
1
@RequestMapping(value = "/{id}", method = RequestMethod.GET)

替换为

1
@GetMapping("/{id}")

SpringMVC 资源放行

  • 配置一个资源放行类, 继承 WebMvcConfigurationSupport 类, 重写 addResourceHandlers() 方法
    1
    2
    3
    4
    5
    6
    7
    8
    @Configuration
    public class SpringSupport extends WebMvcConfigurationSupport {
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
    // 放行, 即访问这些路径时, 不走 SpringMVC, 而是走 tomcat
    registry.addResourceHandler("/**").addResourceLocations("/");
    }
    }

Spring 事务管理

具体步骤

案例描述, 银行两个账户, 一个账户向另一个账户转钱, 需要进行事务管理
Spring 事务管理分为三个步骤

  • 在业务层接口上使用 @Transactional 开启事务管理
    1
    2
    3
    4
    public interface AccountService {
    @Transactional
    void method(String addAccount, String subAccount, Integer money);
    }
  1. 注解加在接口而不是实现类上, 目的是为了降低耦合
  2. 可以加在方法上, 也可以加在类上
  • 设置事务管理器

在配置类中

1
2
3
4
5
6
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
DataSourceTransactionManager dt = new DataSourceTransactionManager();
dt.setDataSource(dataSource);
return dt;
}
  • 开启注解式事务管理驱动

在 SpringConfig 配置文件中

1
@EnableTransactionManagement

入门案例

  • 项目结构
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    .
    ├── pom.xml
    ├── src
    │   └── main
    │   ├── java
    │   │   └── com
    │   │   └── ljguo
    │   │   ├── AppTest.java
    │   │   ├── config
    │   │   │   ├── JdbcConfig.java
    │   │   │   ├── MybatisConfig.java
    │   │   │   └── SpringConfig.java
    │   │   ├── mapper
    │   │   │   └── AccountMapper.java
    │   │   ├── pojo
    │   │   │   └── Account.java
    │   │   └── service
    │   │   ├── AccountService.java
    │   │   └── impl
    │   │   └── AccountServiceImpl.java
    │   └── resources
    │   ├── com
    │   │   └── ljguo
    │   │   └── mapper
    │   │   └── AccountMapper.xml
    │   └── jdbc.properties
    └── transaction.iml

    14 directories, 13 files
    源码仓库地址 transaction

事务角色

  • 事务管理员: 发起事务方, 在 Spring 中通常指代业务层开启事务的方法
  • 事务协调员: 加入事务方, 在 Spring 中通常指代数据层方法, 也可以是业务层方法

每个事务协调员(数据层方法等)的使用就会创建一个事务, 如何加入到事务管理员(发起事务方), 然后变成一个事务, 这样就实现了事务统一管理

事务属性

并不是遇到所有异常事务都会回滚, 只有遇到 Error, 或者运行时异常才会进行事务回滚

这时需要在 @Transactional 加入参数

1
2
3
4
public interface AccountService {
@Transactional(rollbackFor = {IOException.class})
void method(String addAccount, String subAccount, Integer money);
}

通过 rollbackFor 接收参数数组, 然后实现事务回滚

事务传播行为

待补充

AOP

AOP: 面向切面编程

  • 连接点(JoinPoint) 程序执行过程中的任意位置, 粒度为执行方法, 抛出异常, 设置变量等.
    • 在 SpringAOP 中, 理解为方法的执行
  • 切入点(PointCut) 一个切入点可以只描述一个方法, 也可以匹配多个方法
  • 通知(Advice) 在切入点处执行的方法, 也就是共性功能
    • 在 SpringAOP 中, 功能最终以方法的形式呈现
  • 通知类: 定义通知的类
  • 切面(Aspect) 描述通知与切入点的对应关系

入门案例

  • 资源目录
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    .
    ├── java
    │   └── com
    │   └── ljguo
    │   ├── App.java
    │   ├── aop
    │   │   └── MyAdvice.java
    │   ├── config
    │   │   └── SpringConfig.java
    │   └── dao
    │   ├── BookDao.java
    │   └── impl
    │   └── BookDaoImpl.java
    └── resources
  • pom.xml 导入依赖
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.10.RELEASE</version>
    </dependency>

    <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
    </dependency>
  • MyAdvice
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    package com.ljguo.aop;

    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Component;

    @Component
    @Aspect
    public class MyAdvice {

    @Pointcut("execution(void com.ljguo.dao.impl.BookDaoImpl.update())")
    private void pointcut(){}

    @Before("pointcut()")
    public void method() {
    System.out.println(System.currentTimeMillis());
    }
    }
  1. @Aspect 注解用于将该类定义为切面
  2. @Pointcu 注解用于定义一个切入点方法, 后面需要跟上一个空方法
  3. execution(void com.ljguo.dao.impl.BookDaoImpl.update()) 叫做切入点表达式, 表示 void(空返回类型), com.ljguo.dao.impl.BookDaoImpl 类中的 update 空参方法
  4. @Before 注解用于定义一个通知方法, 在切入点方法之前执行
  • SpringConfig
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    package com.ljguo.config;

    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;

    @Configuration
    @ComponentScan("com.ljguo")
    @EnableAspectJAutoProxy
    public class SpringConfig {
    }
    @EnableAspectJAutoProxy 注解用于表示这是一个用注解开发的 AOP
  • BookDao
    1
    2
    3
    4
    5
    6
    package com.ljguo.dao;

    public interface BookDao {
    public void save();
    public void update();
    }
  • BookDaoImpl
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    package com.ljguo.dao.impl;

    import com.ljguo.dao.BookDao;
    import org.springframework.stereotype.Repository;

    @Repository
    public class BookDaoImpl implements BookDao {

    public void save() {
    System.out.println("book dao save...");
    }

    public void update() {
    System.out.println("book dao update...");
    }
    }
  • App
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    package com.ljguo;

    import com.ljguo.config.SpringConfig;
    import com.ljguo.dao.BookDao;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;

    public class App {
    public static void main(String[] args) {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
    BookDao bookDao = ac.getBean(BookDao.class);
    bookDao.update();
    }
    }

结果

运行 App 后得到

1
2
1672128872130
book dao update...

深入 AOP

核心本质: 代理

AOP 工作流程

  1. Spring 容器启动
  2. 读取所有切面配置中的切入点(没有配置的切入点不被读取)
  3. 初始化 Bean, 判断 bean 中对应的类中方法是否匹配到任意切入点
    • 匹配失败, 创建对象
    • 匹配成功, 创建原始对象的代理对象
      使用代理对象来实现增强功能, 切片
  4. 获取 bean 执行方法
    • 获取 bean, 调用方法, 完成操作
    • 获取的 bean 是代理对象时, 根据代理对象的运行模式运行原始方法与增强内容, 完成操作

切入点表达式

  • 介绍
    • 切入点: 要增强的方法
    • 切入点表达式: 要进行增强的方法的描述方式
  • 标准格式
    • 动作关键字: 描述切入点的行为动作, 如 execution 表示执行到指定切入点
    • 访问修饰符, 可以省略
    • 返回值
    • 包名
    • 类名/接口
    • 方法名
    • 参数
    • 异常名, 可以省略
  • 通配符
    • * : 单个独立的任意符号,如:
      1
      execution (public * com.ljguo.*.UserService.select* (*))
    • .. : 多个连续任意符号, 如:
      1
      execution (public void com.ljguo.dao.UserDao.add(..))
    • + : 专用于匹配子类类型
      1
      execution (* *..*Service+.*(..))

AOP 通知类型

AOP 的通知类型分为 5 种类型

  • 前置通知: @Before
  • 后置通知: @After
  • 环绕通知: @Around
  • 返回后通知: @AfterReturning
  • 抛出异常后通知: @AfterThrowing

主要掌握环绕通知:

  • 环绕通知需要依赖形参 ProceedingJionPoint 才能实现对原始方法的调用, 如果没使用, 将会跳过原始方法的执行
  • 如果原始方法没有返回值, 那么通知方法可以设置为 void 返回值类型, 如果有, 则必须设置为 Object 类型返回值
  • 可以使用形参 pjp 调用 peoceed 方法, 执行原始方法并返回值
  • 由于无法预知原始方法是否有异常, 所以环绕通知方法必须抛异常

环绕通知详解

1
2
3
4
5
6
7
@Around("pointcut()")
public void around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("before");
Object proceed = pjp.proceed();
Signature signature = pjp.getSignature();
System.out.println("after");
}
  • pjp.getSignature 获取到一个 Signature 对象, 可以通过该对象得到一些原始方法的属性, 例如
    1
    2
    3
    4
    signature.getClass();
    signature.getDeclaringType();
    signature.getDeclaringTypeName();
    signature.getName();

纯注解开发

首先在需要注入 IOC 容器的类上加入 @Component 注解, 这样就向 IOC 容器中注入了一个 Bean 了.

@Component 注解有三个衍生注解

  • @Repository
  • @Service
  • @Controller
    用于标识不同层, 快捷开发

IOC 容器默认是单例的, 可以通过 @Scope 注解, 有 prototype(多例), singleton(单例) 两个参数.
然后需要创建一个 Spring 的配置类

1
2
3
4
5
6
7
8
9
package org.ljguo.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("org.ljguo")
public class BeanConfig {
}
  • @Configuration 注解使得其成为一个注解类
  • @ComponentScan("org.ljguo") 进行包扫描, 扫描刚刚的 @Component 注解

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package org.ljguo.test;

import org.ljguo.config.BeanConfig;
import org.ljguo.dao.UserDao;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class UserTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(BeanConfig.class);
UserDao userDao = ac.getBean(UserDao.class);
// ac.close();
System.out.println(userDao);
}
}
  • 需要使用 AnnotationConfigApplicationContext 类加载配置类, 可以加载多个配置类, 以逗号给出即可.

生命周期

包括 init destroy
定义两个方法,

1
2
3
4
5
6
7
8
9
@PostConstruct
public void init() {
System.out.println("init...");
}

@PreDestroy
public void destroy() {
System.out.println("destroy...");
}

两个注解, @PostConstruct 用于注解 init 方法, 表示在构造器之后执行, @PreDestroy 注解用于 destroy 方法, 表示在销毁之前执行, 但是需要手动关闭 IOC 容器对象.

依赖注入

自动装配

1
2
@Autowired
private User user;

值注入

1
2
@Value("100")
private int num;

也可以从 properties 文件中获得数据, 首先在 Spring 配置文件中引入 properties 文件,

1
@PropertySource("classpath:jdbc.properties")
1
2
3
4
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ljguo
jdbc.username=root
jdbc.password=20001020

然后使用,

1
2
@Value("${jdbc.url}")
private String str;

管理第三方 Bean

建议新建一个配置类, 例如名为 JdbcConfig.class,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package org.ljguo.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.ljguo.dao.impl.UserDaoImpl;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import javax.sql.DataSource;

public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;

@Bean
public DataSource dataSource(UserDaoImpl userDao) {
userDao.show();
DruidDataSource dc = new DruidDataSource();
dc.setDriverClassName(driver);
dc.setUrl(url);
dc.setUsername(username);
dc.setPassword(password);
return dc;
}
}
  • @Value 注解用于注入一些普通类型的值, 注入的是 properties 文件中的数据, 记得需要载入该配置文件
  • @Bean 用于注入一个第三方 Bean
  • 方法名就是要注入 Bean 的 id 值
  • 若要注入引用类型, 需要将类型写在方法的形参里面, 如上的 UserDaoImpl (自动装配)

在主配置文件中, 使用 @Import 注解导入该配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
package org.ljguo.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;

@Configuration
@Import(JdbcConfig.class)
@ComponentScan("org.ljguo")
@PropertySource("classpath:jdbc.properties")
public class BeanConfig {
}

Spring 整合 Mybatis

目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
.
├── pom.xml
└── src
├── main
│   ├── java
│   │   └── com
│   │   └── ljguo
│   │   ├── AppTest.java
│   │   ├── config
│   │   │   ├── JdbcConfig.java
│   │   │   ├── MybatisConfig.java
│   │   │   └── SpringConfig.java
│   │   ├── mapper
│   │   │   └── UserMapper.java
│   │   ├── pojo
│   │   │   └── User.java
│   │   └── service
│   │   └── UserService.java
│   └── resources
│   ├── com
│   │   └── ljguo
│   │   └── mapper
│   │   └── UserMapper.xml
│   ├── jdbc.properties

15 directories, 13 files

源码位置 spring-mybatis

导入依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 <!-- mysql 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.31</version>
</dependency>

<!-- 数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>

<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<!-- spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<!-- spring 整合 mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>

配置文件

  • SpringConfig

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    package com.ljguo.config;

    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    import org.springframework.context.annotation.PropertySource;

    @Configuration
    @ComponentScan("com.ljguo")
    @PropertySource("classpath:jdbc.properties")
    @Import({JdbcConfig.class,MybatisConfig.class})
    public class SpringConfig {

    }
  • JdbcConfig

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    import org.springframework.context.annotation.Bean;

    import javax.sql.DataSource;

    public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dataSource() {
    DruidDataSource ds = new DruidDataSource();
    ds.setDriverClassName(driver);
    ds.setUrl(url);
    ds.setUsername(username);
    ds.setPassword(password);
    return ds;
    }
    }
  • MybatisConfig

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    package com.ljguo.config;

    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.mapper.MapperScannerConfigurer;
    import org.springframework.context.annotation.Bean;

    import javax.sql.DataSource;

    public class MybatisConfig {

    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) {
    SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
    ssfb.setTypeAliasesPackage("com.ljguo.pojo");
    ssfb.setDataSource(dataSource);
    return ssfb;
    }

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer() {
    MapperScannerConfigurer msc = new MapperScannerConfigurer();
    msc.setBasePackage("com.ljguo.mapper");
    return msc;
    }
    }
  1. 注入两个对象, SqlSessionFactoryBeanMapperScannerConfigurer
  2. 设置 pojo 和 mapper 包路径进行映射
  • UserMapper

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    package com.ljguo.mapper;

    import com.ljguo.pojo.User;
    import org.springframework.stereotype.Component;

    import java.util.List;

    @Component
    public interface UserMapper {
    List<User> selectAll();

    void add(User user);
    }
  • UserMapper.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <?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="com.ljguo.mapper.UserMapper">
    <resultMap id="userResultMap" type="user">
    <result column="user_id" property="userId"/>
    </resultMap>
    <select id="selectAll" resultType="com.ljguo.pojo.User" resultMap="userResultMap">
    select *
    from tb_user;
    </select>

    <insert id="add" >
    insert into tb_user (username, password, permission)
    values (
    #{username}, #{password}, #{permission}
    );
    </insert>
    </mapper>
  • User 实体类比较简单

  • UserService

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    package com.ljguo.service;

    import com.ljguo.mapper.UserMapper;
    import com.ljguo.pojo.User;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import java.util.List;

    @Service
    public class UserService {

    @Autowired
    private UserMapper userMapper;

    public List<User> selectAll() {
    return userMapper.selectAll();
    }
    }
  1. 自动装配一个 UserMapper 对象来调用对应的方法
  • AppTest
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    package com.ljguo;

    import com.ljguo.config.SpringConfig;
    import com.ljguo.mapper.UserMapper;
    import com.ljguo.service.UserService;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;

    public class AppTest {
    public static void main(String[] args) {
    ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
    UserService userService = ac.getBean(UserService.class);
    System.out.println(userService.selectAll());
    }
    }
  1. 载入 SpringConfig 配置文件来初始化 IOC 容器
  2. 拿到 UserService 对象, 执行方法

Bean构造(使用无参构造器)

1
<bean id="user" class="org.ljguo.pojo.User"/>

测试

1
2
3
4
5
6
7
8
9
10
11
12
// UserTest.java
import org.ljguo.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UserTest {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:beans.xml");
User user = ac.getBean("user", User.class);
System.out.println(user);
}
}

依赖注入

setter 注入
  1. 基本数据类型注入 name value
    1
    2
    3
    4
    <bean id="user" class="org.ljguo.pojo.User">
    <property name="age" value="221"/>
    <property name="username" value="ljguo"/>
    </bean>
  2. 引用类型注入 name ref
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <bean id="user" class="org.ljguo.pojo.User">
    <property name="age" value="221"/>
    <property name="username" value="ljguo"/>
    <property name="classes" ref="class"/>
    </bean>

    <bean id="class" class="org.ljguo.pojo.Class" >
    <property name="className" value="hqyj" />
    <property name="location" value="511" />
    </bean>
  • 构造器注入
  1. 使用 name, name 表示构造器中形参的名字
    1
    2
    3
    4
    5
    <bean id="user" class="org.ljguo.pojo.User">
    <constructor-arg name="username" value="ljguo" />
    <constructor-arg name="age" value="21" />
    <constructor-arg name="classes" ref="class"/>
    </bean>
  2. 使用 type, type 表示构造器中形参的类型
    1
    2
    3
    4
    5
    <bean id="user" class="org.ljguo.pojo.User" >
    <constructor-arg type="java.lang.String" value="ljguo"/>
    <constructor-arg type="int" value="25"/>
    <constructor-arg type="org.ljguo.pojo.Class" ref="class"/>
    </bean>
  3. 使用 index, index 表示构造器形参索引
    1
    2
    3
    4
    5
    <bean id="user" class="org.ljguo.pojo.User">
    <constructor-arg index="0" value="ljguo"/>
    <constructor-arg index="1" value="26"/>
    <constructor-arg index="2" ref="class"/>
    </bean>

自动装配

集合注入

  • pojo 实体类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    package org.ljguo.pojo;

    import java.util.Arrays;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;

    public class Collection {
    private int[] array;
    private List<String> list;
    private Set<String> set;
    private Map<String,String> map;


    public int[] getArray() {
    return array;
    }

    public void setArray(int[] array) {
    this.array = array;
    }

    public List<String> getList() {
    return list;
    }

    public void setList(List<String> list) {
    this.list = list;
    }

    public Set<String> getSet() {
    return set;
    }

    public void setSet(Set<String> set) {
    this.set = set;
    }

    public Map<String, String> getMap() {
    return map;
    }

    public void setMap(Map<String, String> map) {
    this.map = map;
    }

    @Override
    public String toString() {
    return "Collection{" +
    "array=" + Arrays.toString(array) +
    ", list=" + list +
    ", set=" + set +
    ", map=" + map +
    '}';
    }
    }
  • XML 配置依赖注入
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    <bean id="collection" class="org.ljguo.pojo.Collection">
    <property name="array">
    <array>
    <value>1</value>
    <value>2</value>
    <value>3</value>
    </array>
    </property>

    <property name="list">
    <list>
    <value>ljguo</value>
    <value>gxx</value>
    <value>marlin</value>
    </list>
    </property>

    <property name="map">
    <map>
    <entry key="name" value="ljguo"/>
    <entry key="age" value="21"/>
    </map>
    </property>

    <property name="set">
    <set>
    <value>ljguo</value>
    <value>ljguo</value>
    <value>gxx</value>
    </set>
    </property>
    </bean>
  • 测试类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    package org.ljguo.test;

    import org.ljguo.pojo.Collection;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;

    public class CollectionTest {
    public static void main(String[] args) {
    ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:beans.xml");
    Collection collection = ac.getBean("collection", Collection.class);
    System.out.println(collection);
    }
    }

小案例(链接数据库)

  • 导入druid依赖坐标
    1
    2
    3
    4
    5
    6
    <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
    <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.16</version>
    </dependency>
  • 配置xml依赖注入
    1
    2
    3
    4
    5
    6
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/ljguo"/>
    <property name="username" value="root"/>
    <property name="password" value="20001020"/>
    </bean>
  • 测试
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    package org.ljguo.test;

    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;

    import javax.sql.DataSource;

    public class JdbcTest {
    public static void main(String[] args) {
    ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:application.xml");
    DataSource dataSource = ac.getBean("dataSource", DataSource.class);
    System.out.println(dataSource);
    }
    }

使用 properties 配置文件

加载 properties 文件
  • 开启名字空间 context
    1
    2
    3
    4
    5
    6
    7
    8
    9
    <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
    ">
  • 使用 context 空间加载
    1
    <context:property-placeholder location="classpath*:*.properties"/>
    这一行代码加载了所有的properties文件
    properties 文件内容如下
    1
    2
    3
    4
    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/ljguo
    jdbc.username=root
    jdbc.password=20001020
    xml 配置文件可优化为如下内容
    1
    2
    3
    4
    5
    6
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
    </bean>

容器相关

  • BeanFactory 是 IOC 容器的顶层接口, 初始化BeanFactory对象时, 加载的 bean 延迟加载
  • ApplicationContext 接口是 Spring 容器的核心接口, 初始化时 bean 立即加载
  • ApplicationContext 接口的常用初始化类
    • ClassPathXmlApplicationContext
    • FileSystemXmlApplicationContext

DOM(文档对象模型)

当网页被加载时,浏览器会创建页面的文档对象模型(Document Object Model).

通过可编程的对象模型,JavaScript 获得了足够的能力来创建动态的 HTML.

  • JavaScript 能够改变页面中的所有 HTML 元素
  • JavaScript 能够改变页面中的所有 HTML 属性
  • JavaScript 能够改变页面中的所有 CSS 样式
  • JavaScript 能够对页面中的所有事件做出反应

查找HTML元素

  • 通过 id 查找
    1
    var demo = document.getElementById("demo");
  • 通过标签名查找
    1
    var demo = document.getElementsByTagName("p");
  • 通过类名查找
    1
    var demo = document.getElementsByClassName("demo");

修改HTML内容

1
2
3
4
// 获取元素对象(集合)
var demo = document.getElementsByClassName("demo");
// 对元素的 innerHTML 属性赋值, (可读可写)
demo[0].innerHTML = "hello JavaScript!";

修改HTML属性

1
2
3
4
5
6
// 通过TagName获取元素对象集合
var imgobj = document.getElementsByTagName("img");
// 绑定onclick事件, 触发对应函数
imgobj[0].onclick = function () {
imgobj[0].src = "./images/news.jpg";
}

未完待续…

JavaScript 简单介绍

JavaScript 是互联网上最流行的脚本语言, 这门语言可用于 HTML 和 web, 更可广泛用于服务器、PC、笔记本电脑、平板电脑和智能手机等设备

  • JavaScript 是一种轻量级的编程语言
  • JavaScript 是可插入 HTML 页面的编程代码
  • JavaScript 插入 HTML 页面后, 可由所有的现代浏览器执行
  • JavaScript 和 Java 没有关系!!!
  • 解释性脚本语言, 弱类型语言

引入方式

内部引入

不推荐

外部文件引入

1
<script src="./js/test.js"></script>

字符串

1
2
// 创建字符串
var str = "ljguo";

属性方法

1
2
3
4
5
6
7
8
9
10
11
12
var str = "ljguo";
str.length; // 5
str.charAt(0); // l
str.concat("hhh"); // ljguohhh
str.indexOf("g"); // 2, 首次出现该字符串的索引

var str = "ljguo 123 lJguo";
str.lastIndexOf("g"); // 最后一次
str.split(" "); // [ljguo, 123, lJguo]
str.toLowerCase(); // 转小写
str.toUpperCase(); // 转大写
str.trim(); // 去掉首位空白

数组

JavaScript 定义数组有两种方式, 第一种是通过 Array 类来构造

1
var arr = new Array("ljguo", "eng", "old");

第二种是通过字面量直接赋值

1
var arr = ["ljguo", "eng", "old"];
  • length 数组长度属性

对象

创建一个对象, 语法如下

1
2
3
4
5
6
7
8
9
var person = {
name: "ljguo",
age: 18,
sex: "man",
introduction: function () {
var intro = "hello, my name is " + this.name;
console.log(intro);
}
}

访问该对象的方法有两种

1
2
3
4
person["name"];
person.name;
// 访问方法只能用这种方法
person.introduction();

可以使用

1
2
var intr = person.introduction;
console.log(intr);

但是得到的是这个方法的定义

1
2
3
4
ƒ() {
var intro = "hello, my name is " + this.name;
console.log(intro);
}

函数

定义函数

可以使用如下语法定义函数

1
2
3
4
5
6
function fun() {
console.log("hello JavaScript!");
}
var fun = function () {
console.log("hello JavaScript!");
}

也可以定义带参数的函数, 参数可以是任意数量的

1
2
3
4
5
6
7
8
9
10
11
function add(a, b) {
return a + b;
}
// 参数传入函数后会保存到一个 arguments 数组中
function add() {
var sum = 0;
for (var i = 1; i < arguments.length; i++) {
sum += arguments[i];
}
return sum;
}

循环

  • for/in 循环
    1
    2
    3
    4
    5
    var str = [1, 2, 3, 4, 5];
    for (i in str) {
    console.log(str[i]);
    }
    // i 在这里作为 str 数组的索引

CSS 简单介绍

  • CSS 指层叠样式表 (Cascading Style Sheets)
  • 样式通常存储在样式表中
  • 样式定义如何显示 HTML 元素
  • 外部样式表可以极大提高工作效率
  • 多个样式定义可层叠为一个

载入方法

  • 内联载入, 直接在标签中使用 style 属性, 多个键值对之间用空格分隔
    1
    <p style="font-size: 20px;">这是一个段落</p>
  • style 标签导入, 在 head 中使用 style 标签
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <head>
    <style>
    p {
    font-size: 30px;
    color: red;
    }
    </style>
    </head>
    <body>
    <p>这是一个段落</p>
    </body>
  • 外部文件载入, 使用 link 标签载入
    保证当前目录结构如下
    1
    2
    3
    4
    .
    ├── css
    │   └── style.css
    ├── index.html
    然后在 style.css 中写下如下内容

    style.css

    1
    2
    3
    p {
    font-size: 30px;
    }

    index.html

    1
    2
    3
    4
    5
    6
    <head>
    <link rel="stylesheet" href="./css/style.css">
    </head>
    <body>
    <p>这是一个段落</p>
    </body>

选择器(很重要)

标签选择器

1
2
3
4
5
6
7
p {
font-size: 30px;
}
div p {
font-size: 20px;
color: red;
}

第一个直接通过标签名进行选择, 第二个是只选择 div 内的 p 标签

ID 选择器

1
2
3
#sex {
color: blue;
}

以 # 号开头, 名字自己取, 注意需要在 html 中要应用此样式的元素加上对应的 id 属性

1
<div id="sex">this is sex id.</div>

Class 选择器

1
2
3
.ljguo {
background-color: pink;
}

以 . 开头, 名字自己取, 注意需要在 html 中要应用此样式的元素加上对应的 class 属性

1
<div class="ljguo">this is ljguo class.</div>

属性选择器

1
2
3
4
/* 选择带有 class 属性的元素 */
[class] {
font-weight: 700;
}

复合选择器(简单列举)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/* 选择 div 中所有 class 为 ljguo 的元素 */
div.ljguo {
background-color: cyan;
}

/* 选择 div 中所有 id 为 sex 的元素 */
div#sex {
background-color: yellow;
}

/* 选择父元素为 div 的所有 p 元素 */
div>p {
text-align: center;
}

/* 选择紧跟 div 元素的首个 p 元素 */
div+p {
font-weight: 700;
}

/* 选择前面有 div 元素的每个 p 元素 */
div~p {
font-weight: 700;
}

/* 选择 target 属性值为 _blank 的元素 */
[target=_blank] {
font-size: 15px;
}

/* 鼠标点击 a 元素时 */
a:active {
color: cyan;
}

/* 鼠标悬停在 a 元素上时 */
a:hover {
color: red;
}

/* 焦点聚集时 */
input:focus {
background-color: magenta;
}
...

剩下的一些就是各种属性的积累了, 多查官网多使用熟练

HTML 简单介绍

  • HTML 指的是超文本标记语言 (Hyper Text Markup Language)
  • HTML 不是一种编程语言,而是一种标记语言 (markup language)
  • 标记语言是一套标记标签 (markup tag)
  • HTML 使用标记标签来描述网页

标签

基本结构

HTML 页面是由一堆标签组成的.
包括 html,head,body 等.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html>

<head>
<meta charset=utf8 />
<title>this is Title.</title>

</head>
<body>
<h1>这是一级标题</h1>
<p>This is my first paragraph.</p>
<hr />
<a href = "https://www.baidu.com">百度一下你就知道</a> <br />
<p>This is my second paragraph.</p>
</body>
</html>

解释

  • title 标签是用来设置页面标题, 写在 head 标签中
  • meta 标签用来设置一些页面初始化信息, 没有结束标签, 称为单标签
  • h{number} 标签, 标题标签, number 取值 1-6, 对应 1-6 级标签
  • p 标签, 段落标签
  • hr 水平线标签
  • a 标签, 超链接(锚)标签, 用于设置超链接
  • href = "xxx", 属性, 这里第一次接触到属性, 在后面会遇到很多
  • br 换行标签

图片

1
<img src = "url" alt = "text"  title = "some text" />
  • src 图片源属性, 值为图片路径, 可以是在线 url, 亦可以是本地图片(注意文件路径)
  • alt 在图片地址失效时显示的文本
  • title 当鼠标悬停在图片上面显示的文本

表格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<table border="1">
<tr>
<th>Heading</th>
<th>Another Heading</th>
</tr>
<tr>
<td>row 1, cell 1</td>
<td>row 1, cell 2</td>
</tr>
<tr>
<td>row 2, cell 1</td>
<td>row 2, cell 2</td>
</tr>
</table>
  • tatle 表格标签, 所有表格应该以它为开始
  • th 表头标签, 定义表头
  • tr 行标签, 定义表格行
  • td 列标签, 定义表格列
  • border 表格线宽度, 更建议使用后面的 CSS 来设置
  • caption 标题标签

横跨两列的单元格

1
2
3
4
5
6
7
8
9
10
11
<table border="1">
<tr>
<th>姓名</th>
<th colspan="2">电话</th>
</tr>
<tr>
<td>Bill Gates</td>
<td>555 77 854</td>
<td>555 77 855</td>
</tr>
</table>
姓名 电话
Bill Gates 555 77 854 555 77 855

横跨两行的单元格

1
2
3
4
5
6
7
8
9
10
11
12
13
<table border="1">
<tr>
<th>姓名</th>
<td>Bill Gates</td>
</tr>
<tr>
<th rowspan="2">电话</th>
<td>555 77 854</td>
</tr>
<tr>
<td>555 77 855</td>
</tr>
</table>
姓名 Bill Gates
电话 555 77 854
555 77 855

列表

无序列表

1
2
3
4
<ul>
<li>Coffee</li>
<li>Milk</li>
</ul>

有序列表

1
2
3
4
<ol>
<li>Coffee</li>
<li>Milk</li>
</ol>

定义列表

1
2
3
4
5
6
<dl>
<dt>Coffee</dt>
<dd>Black hot drink</dd>
<dt>Milk</dt>
<dd>White cold drink</dd>
</dl>

列表嵌套

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<ul>
<li>咖啡</li>
<li>
<ul>
<li>红茶</li>
<li>绿茶
<ul>
<li>中国茶</li>
<li>非洲茶</li>
</ul>
</li>
</ul>
</li>
<li>牛奶</li>
</ul>

div & span

这两个标签无实际意义, div 是块级元素, 其特点是独占一行, span 是行内联元素, 配合 CSS 用的很多

进阶一点点

不知道什么标题(为 CSS 做铺垫吧)

  • id 元素的 id 属性, 指定 HTML 元素的唯一 ID, id 属性的值在 HTML 文档中必须是唯一的.
    id 属性用于指向样式表中的特定样式声明. JavaScript 也可使用它来访问和操作拥有特定 ID 的元素
  • class 元素的 class 属性, 可以有多个

表单

form 元素

用于接收提交用户的提交信息

  • action 规定当提交表单时向何处发送表单数据
  • method post|get 规定用于发送 form-data 的 HTTP 方法
  • name 规定表单的名称

input

  • text & password 文本域和密码域

    1
    2
    3
    4
    5
    <form action="./index.html" method="get" name="xxx"> 
    用户名:<input type="text" name="username">
    <br />
    密码:<input type="password" name="password">
    </form>
  • checkbox 复选框

    1
    2
    3
    4
    <form action="./index.html" method="get" name="xxx">
    爱好:<input type="checkbox" name="hobby" />篮球
    <input type="checkbox" name="hobby" /> 游戏
    </form>
  • radio 单选框
    注意:需要为每个选项设置同一 name 属性值

    1
    性别:<input type="radio" name="sex" checked /><input type="radio" name="sex" />

    这里的 checked 表示默认选中状态

  • select 下拉列表

    1
    2
    3
    4
    5
    6
    城市:
    <select name="address">
    <option value="SiChan">四川</option>
    <option value="GuangDong" selected>广东</option>
    <option value="ShangHai">上海</option>
    </select>
  • textarea 文本域

    1
    2
    3
    <textarea rows="10" cols="30"> 
    The cat was playing in the garden.
    </textarea>

    rows & cols 默认行列宽度

  • submit 提交

    1
    <input type="submit" value="提交">

    用于将表单数据提交到 action 指定的位置

  • label 用于创建链接, 提交用户体验

    1
    <input type="checkbox" name="hobby" value="0" id="bass" /> <label for="bass">篮球</label>

    此时点击“篮球”也会识别


开始 CSS 了, HTML 真是太无趣了….