0%

controller层

AdminController

  • public Result<?> login(@RequestBody User user, HttpSession session)

    返回管理员登录结果(如果登录成功记录到 session)

    @PostMapping(“/login”)

  • public Result<?> update(@RequestBody Admin admin)

    返回修改结果

    @PutMapping(“/update”)

StudentController

  • public Result<?> add(@RequestBody Student student)

    返回学生登录结果

    @PostMapping(“/add”)

  • public Result<?> update(@RequestBody Student student)

    返回学生信息更新结果

    @PutMapping(“/update”)

  • public Result<?> delete(@PathVariable String username)

    返回删除学生信息结果

    @DeleteMapping(“/delete/{username}”)

  • 查询学生信息

1
2
3
4
public Result<?> findPage(
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(defaultValue = "") String search)

返回查询结果,使用了 Page 工具类

@GetMapping(“/find”)

  • public Result<?> login(@RequestBody User user, HttpSession session)

    学生登录

    @PostMapping(“/login”)

  • public Result<?> stuNum()

    主页顶部:学生统计

    @GetMapping(“/stuNum”)

  • public Result<?> exist(@PathVariable String value)

    床位信息,查询是否存在该学生

    床位信息,查询床位上的学生信息

    @GetMapping(“/exist/{value}”)

DormManagerController

  • public Result<?> add(@RequestBody DormManager dormManager)

    作用 :添加管理员

    返回值 :添加结果

    @PostMapping(“/add”)

  • public Result<?> update(@RequestBody DormManager dormManager)

    作用 :管理员信息更新

    返回值 :更新结果

    @PutMapping(“/update”)

  • public Result<?> delete(@PathVariable String username)

    作用 :删除宿管信息

    返回值 :删除结果

    @DeleteMapping(“/delete/{username}”)

  • 查询宿管信息

1
2
3
4
 public Result<?> findPage(
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(defaultValue = "") String search)

返回查询信息的结果,使用了 Page 工具类

  • public Result<?> login(@RequestBody User user, HttpSession session)

    作用 :宿管登录

    返回值 :登录结果(如果登录成功,记录 session)

    @PostMapping(“/login”)

MainController

  • public Result<?> loadIdentity(HttpSession session)

    获取身份信息

    @GetMapping(“/loadIdentity”)

  • public Result<?> loadUserInfo(HttpSession session)

    获取个人信息(从 Session 中获得,登录成功会存入对应的实体类)

    @GetMapping(“/loadUserInfo”)

  • public Result<?> signOut(HttpSession session)

    退出登录(销毁 Session 信息)

    @GetMapping(“/signOut”)

DormRoomController

  • 房间的增删改查

  • 空宿舍统计

  • 删除学生床位信息

  • 查询学生是否已经有床位

  • 统计住宿人数

  • 住宿分布人数

  • 学生功能(我的宿舍)

  • 检查房间是否满员

  • 检查床位是否已经有人

  • 检查房间是否存在

DormBuildController

  • 楼宇的增删改查及获取信息

AdjustRoomController

  • 调宿信息的增删改查

VisitorController

  • 访客信息的增删改查

RepairController

  • 报修信息的增删改查

  • 首页报修统计

  • 个人申请报修的查询

NoticeController

  • 公告的增删改查

  • 首页公告展示

FileController

管理头像文件,在前端并没有看到对应功能

DAO层

DAO层使用的是 BaseMapper 接口,已经提供了常用的 CRUD 操作方法。

貌似使用的 mabatis-Plus 生成的实现类

Service层

主要就是一些简单的 sql 逻辑了。(也不一定)

之后如果要用到类似功能再回来看吧。

总结

还是很典型的三层架构开发,通过这个项目学习了很多 Javaweb 的知识,以后有什么不会的,也可以从这个项目的代码中拿来借鉴。

引入依赖

使用相关注解,需要首先在 pom.xml 引入依赖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version> <!-- 具体版本号根据需要选择 -->
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
</dependency>
</dependencies>

使用方法

在引入依赖之后,使用方法如下 :

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
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class Main {
@BeforeEach
void setUp() {
System.out.println("Execute before every Test method");
}

@Test
public void addTest() {
int A = 3, B = 2;
int result = A + B;
assertEquals(5, result, "结果应该是5");
}

@Test
public void subTest() {
int A = 3, B = 2;
int result = A - B;
assertEquals(1, result);
}

public static void main(String[] args) {
// Main method can be used to run other logic if needed, but not for tests
}

@AfterEach
public void tearDown() {
System.out.println("Execute after every Test method");
}
}
  • 注意可以运行每个测试函数,控制台会输入 Test passed的相关信息

  • 右键点击文件夹或者包,选择 Run Tests in xxx 即可运行全部测试方法。

相对路径是相对于基准路径来说的。

idea的基准路径就是项目根目录所在位置。

比如说项目结构如下 :

  • Test

    • .idea

    • src

      • main

        • java

          • com

            • qust

              • data.txt

              • Main.class

      • test

    • target

    • .gitignore

    • pom.xml

    • f.txt

假设写 data.txt,访问方式如下 :

1
2
3
4
5
6
7
public class Main {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("src\\main\\java\\com\\qust\\data.txt", true);
fw.append("Hello World\n");
fw.close();
}
}

假设要访问 f.txt,访问方式如下 :

1
2
3
4
5
6
7
public class Main {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("f.txt", true); // 追加内容
fw.append("Hello World");
fw.close();
}
}

HttpSession监听器简介

在Web开发中,Session用于在HTTP协议中维护客户端和服务器之间的状态,解决了HTTP协议无状态的问题。通过Session,服务器可以追踪用户的多次请求,并保持用户的身份信息和相关数据。Session对象通常会在用户首次访问网站时由服务器创建,并与用户的会话持续存在,直到会话结束或Session过期。

Session的生命周期监听

Session的状态管理通常包括以下几个重要的操作和生命周期阶段,可以通过HttpSessionListener来监听和控制:

  1. 创建Session

    当用户首次访问网站时,服务器会自动创建一个Session对象,并与该用户关联。可以通过监听sessionCreated()事件来处理这一时刻的相关操作,例如记录创建时间、初始化数据等。

  2. 销毁Session

    当用户会话超时、主动退出或浏览器关闭时,Session会被销毁。通过监听sessionDestroyed()事件,可以在Session销毁时执行清理操作,如释放资源、记录日志或更新用户状态。

  3. Session超时

    如果用户在一段时间内没有任何操作,服务器会根据设置的超时时间自动销毁Session。监听器中的sessionDestroyed()方法可以用于处理超时销毁的逻辑。

常见的Session监听器接口

  1. HttpSessionListener

    这是最基本的Session监听器,用于监听Session的创建和销毁。

  • sessionCreated(HttpSessionEvent se):当Session创建时调用。
  • sessionDestroyed(HttpSessionEvent se):当Session销毁时调用。
  1. HttpSessionAttributeListener用于监听Session属性的变化,即Session中数据的添加、移除或替换等。
  • attributeAdded(HttpSessionBindingEvent event):当属性添加到Session时调用。
  • attributeRemoved(HttpSessionBindingEvent event):当属性从Session中移除时调用。
  • attributeReplaced(HttpSessionBindingEvent event):当Session中的属性被替换时调用。
    通过这些监听器,开发者可以对用户会话进行细粒度的控制,例如管理用户认证信息、跟踪用户行为、进行会话统计等。

使用Session监听器的实际场景

  1. 用户登录与注销

    在用户登录时,创建Session以保存用户信息;在用户注销时,销毁Session以清除敏感数据。

  2. 购物车管理

    通过Session存储购物车中的商品信息,监听Session的变化来同步更新购物车。

  3. 防止会话劫持

    通过监听Session创建和销毁事件,可以增强对会话的安全性,如重新生成Session ID以防止会话固定攻击。
    总结来说,Session监听是Web开发中的一种重要机制,它使得开发者能够高效且精细地管理用户的会话状态,确保Web应用能正确处理多用户并发访问的需求。

-

Java 泛型概述

Java 泛型(Generics) 是 JDK 1.5 引入的一项功能,允许在类、接口和方法中定义具有参数化类型的代码。泛型通过在编译时进行类型检查,提高了代码的安全性和可读性,减少了类型转换的需求。

泛型的优势

  • 类型安全:在编译时检查类型,避免运行时的 ClassCastException。

  • 代码重用性:支持定义通用的类、接口和方法,适用于多种数据类型。

  • 可读性和维护性:代码更加清晰,减少了显式类型转换的需要。

泛型的基本语法

  • 泛型类
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.qust;

public class Box<T> {
private T item;

public void setItem(T item) {
this.item = item;
}

public T getItem() {
return item;
}
}
1
2
3
4
5
6
7
8
9
10
11
public class Main {
public static void main(String[] args) {
Box<String> stringBox = new Box<>();
stringBox.setItem("Hello World");
System.out.println(stringBox.getItem());

Box<Integer> intBox = new Box<>();
intBox.setItem(1);
System.out.println(intBox.getItem());
}
}
  • 泛型接口
1
2
3
4
5
// 定义一个泛型接口
public interface Pair {
K getKey();
V getValue();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.qust;

public class KeyValue<K,V> implements Pair<K, V>{
private K key;
private V value;

public KeyValue(K key, V value) {
this.key = key;
this.value = value;
}

@Override
public K getKey() {
return key;
}

@Override
public V getValue() {
return value;
}
}
1
2
3
4
5
6
public class Main {
public static void main(String[] args) {
Pair<String, Integer> pair = new KeyValue<> ("Age", 25);
System.out.println(pair.getKey() + ' ' + pair.getValue());
}
}
  • 泛型方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.qust;

public class GenericMethod {
public static <T> void printArray(T[] array) {
for(T element : array) {
System.out.println(element);
}
System.out.println();
}
public static void main(String[] args) {
Integer[] intArray = {1, 2, 3};
String[] strArray = {"A", "B", "C"};

printArray(intArray);
printArray(strArray);
}
}

泛型的类型限制

  • 通配符 ?

    • 表示未知类型,可用于读取时不确定具体类型。

      1
      2
      3
      4
      5
      public static void printList(List<?> list) {
      for (Object item : list) {
      System.out.println(item);
      }
      }
  • 边界限定

    • 上界通配符:<? extends T> 表示类型是 T 或其子类。

      1
      2
      3
      4
      5
      public static void printNumbers(List<? extends Number> list) {
      for (Number num : list) {
      System.out.println(num);
      }
      }
    • 下界通配符:<? super T> 表示类型是 T 或其父类。

      1
      2
      3
      public static void addNumbers(List<? super Integer> list) {
      list.add(1); // 允许添加 Integer 或其子类
      }
  • 类型参数边界

    • 限制泛型的范围。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      public class Box<T extends Number> {
      private T item;

      public void setItem(T item) {
      this.item = item;
      }

      public T getItem() {
      return item;
      }
      }

泛型的注意事项

  • 类型擦除

    • Java 泛型是通过类型擦除实现的,编译器在编译时会将泛型信息去掉,转换为原始类型(如 Object)。

      • 因此,不能在运行时获取泛型类型参数的信息,例如 List 和 List 在运行时是相同的。
  • 泛型不能用于静态字段或方法

    • 由于泛型类型在编译时确定,而静态成员属于类本身,与实例无关,因此泛型不能应用于静态成员。

      1
      2
      3
      4
      5
      6
      7
      public class GenericClass {
      // 静态变量不能使用泛型
      // private static T staticVar;

      // 静态方法也不能使用泛型
      // public static T getStaticValue() { return null; }
      }
  • 不能创建泛型数组

    1
    2
    // 不允许直接创建泛型数组
    List[] array = new ArrayList[10]; // 编译错误
  • 原始类型

1
2
3
Res res = new Res();
res.a = "Hello"; // 编译通过
res.a = 123; // 仍然编译通过,丢失了类型安全

没有指定泛型的数据类型,导致类型不安全。

常见应用场景

  • 集合框架:Java 的集合框架(如 ArrayList、HashMap 等)广泛使用了泛型。

  • 工具类:如 java.util.Collections 提供了许多泛型方法。

  • 类型安全容器:泛型可以实现自定义的类型安全容器。

@RestController

@RestController 是 Spring 框架中用来标注一个类为 RESTful Web服务控制器 的注解。它是 @Controller@ResponseBody 的组合,主要用来处理 HTTP 请求并返回数据而不是视图。

作用与特点:

  1. 定义控制器类将类标记为一个控制器,用于接收和处理 HTTP 请求。
  2. 自动将返回值转换为 JSON/XML:@RestController 自动将控制器方法的返回值序列化为 JSON 或 XML 格式,通过 HTTP 响应返回给客户端。
  • 这是因为它隐含了 @ResponseBody 注解。
  1. 简化代码无需在每个方法上显式使用 @ResponseBody,整个类的所有方法默认都将返回数据而不是视图。
    常见用法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class MyRestController {

// GET 请求:返回一个简单字符串
@GetMapping("/hello")
public String sayHello() {
return "Hello, World!";
}

// GET 请求:返回一个对象,自动序列化为 JSON
@GetMapping("/user")
public User getUser() {
return new User(1, "Alice", "alice@example.com");
}
}

示例解析:

  1. /api/hello 返回的是字符串 “Hello, World!”,被序列化为 HTTP 响应体。

  2. /api/user 返回的是 User 对象,会自动被序列化为 JSON,例如:

    1
    2
    3
    4
    5
    {
    "id": 1,
    "name": "Alice",
    "email": "alice@example.com"
    }

    等效写法:
    如果使用 @Controller,需要配合 @ResponseBody,代码会显得冗长:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Controller
@RequestMapping("/api")
public class MyRestController {

@ResponseBody
@GetMapping("/hello")
public String sayHello() {
return "Hello, World!";
}

@ResponseBody
@GetMapping("/user")
public User getUser() {
return new User(1, "Alice", "alice@example.com");
}
}

总结:@RestController 适用于构建 RESTful API,简化开发流程,是 Spring Web 的重要注解之一。如果你的控制器方法大部分是返回数据而不是视图,使用 @RestController 会非常方便。

@Data 注解

  • 作用: 自动生成以下方法:Getter 和 Setter 方法(为所有成员变量)toString() 方法equals() 和 hashCode() 方法canEqual() 方法(用于子类验证)

  • 优势: 减少手写 Getter、Setter 等方法的繁琐代码。

  • 等效代码:

    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
    public class Result {
    private String code;
    private String msg;
    private T data;

    public String getCode() { return code; }
    public void setCode(String code) { this.code = code; }

    public String getMsg() { return msg; }
    public void setMsg(String msg) { this.msg = msg; }

    public T getData() { return data; }
    public void setData(T data) { this.data = data; }

    @Override
    public String toString() {
    return "Result{" +
    "code='" + code + '\'' +
    ", msg='" + msg + '\'' +
    ", data=" + data +
    '}';
    }

    @Override
    public boolean equals(Object o) {
    // equals 方法实现
    }

    @Override
    public int hashCode() {
    // hashCode 方法实现
    }
    }

@AllArgsConstructor注解

  • 作用: 自动生成一个包含所有成员变量的构造方法。

  • 生成的代码:

    1
    2
    3
    4
    5
    public Result(String code, String msg, T data) {
    this.code = code;
    this.msg = msg;
    this.data = data;
    }
  • 用途: 方便在创建对象时直接初始化所有属性。例如:

    1
    Result result = new Result<>("0", "成功", "数据");

@NoArgsConstructor 注解

  • 作用: 自动生成一个无参构造方法。

  • 生成的代码:

    1
    public Result() {}
  • 用途: 提供无参构造器支持,例如需要通过反射或框架(如 Spring)创建对象时。

    1
    Result result = new Result<>();

@Resource注解

@Resource 是 Java 中用于依赖注入的一个注解,它主要用于自动装配资源(如 Bean 或组件),在 Java EE(现在的 Jakarta EE)和 Spring 等框架中得到广泛使用。该注解通常用于标记成员变量、方法或构造函数,指示容器(如 Spring 或应用服务器)需要自动注入指定的资源。

主要特性:

  • 自动注入:@Resource 自动装配资源,可以通过名称或类型来注入。

  • 名称默认匹配:@Resource 默认根据字段名或方法名来进行资源名称匹配。如果没有找到匹配的名称,它会根据类型来注入。

  • 可以用于字段、方法、构造函数:可以用于类的成员变量(字段)、setter 方法或构造函数。

    使用示例:

1. 注入字段:

1
2
3
4
5
6
7
8
9
import javax.annotation.Resource;

public class MyService {

@Resource
private DataSource dataSource;

// 其他代码...
}

在这个例子中,@Resource 注解会根据 dataSource 字段的名称自动注入一个 DataSource 类型的资源。

2. 注入方法:

1
2
3
4
5
6
7
8
9
10
11
import javax.annotation.Resource;

public class MyService {

private DataSource dataSource;

@Resource
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
}

在这个例子中,@Resource 注解被用在了 setter 方法上,容器会自动注入所需的 DataSource

3. 注入构造函数:

1
2
3
4
5
6
7
8
9
10
11
import javax.annotation.Resource;

public class MyService {

private DataSource dataSource;

@Resource
public MyService(DataSource dataSource) {
this.dataSource = dataSource;
}
}

在这个例子中,@Resource 注解应用于构造函数,自动注入 DataSource

注解的属性:

  • name:指定资源的名称,用于自定义资源名称的匹配。例如:@Resource(name=”myDataSource”)

  • type:指定资源的类型,通常不需要显式设置,容器会根据字段的类型自动注入。

    与 @Autowired 比较:

  • @Autowired 是 Spring 框架提供的注解,它主要通过类型来进行依赖注入。与 @Resource 注解相比,@Autowired 主要依据类型进行注入,并且在没有找到唯一匹配的 Bean 时会抛出异常,除非配合 @Qualifier 使用指定 Bean 名称。

  • @Resource 默认通过名称进行注入,只有在没有找到合适的名称匹配时才会通过类型来进行匹配。

在 Java 中,注释的规范化不仅能够提高代码的可读性,还能帮助开发团队更好地维护项目。以下是 Java 注释的规范和建议:

1. 注释的基本类型

Java 支持以下三种类型的注释:

  • 单行注释:使用 // 开头。

  • 多行注释:使用 /* … */。

  • 文档注释:使用 /** … */,通常用于生成 JavaDoc 文档。

    2. 注释的用途

必要时注释,而非注释所有内容

  • 注释的重点:注释应该解释 “为什么”(逻辑、设计意图),而不是 “怎么做”(代码本身已经解释 “怎么做”)。

  • 避免冗余注释:代码本身清晰易懂时不需要注释。

    3. 单行注释规范

  • 用于描述简单的逻辑或特殊处理

  • 位置:通常放在代码行的上方,也可放在代码右侧(不建议太长)。

    1
    2
    3
    4
    // 检查用户是否已登录
    if (user.isLoggedIn()) {
    System.out.println("User is logged in."); // 输出登录信息
    }

4. 多行注释规范

  • 用于描述复杂逻辑屏蔽代码块(屏蔽代码块仅在调试时临时使用,调试完成后应删除)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    /*
    * 以下代码段用于初始化数据库连接,
    * 包括加载驱动程序、创建连接以及设置属性。
    */
    try {
    Class.forName("com.mysql.cj.jdbc.Driver");
    Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
    } catch (Exception e) {
    e.printStackTrace();
    }

5. 文档注释规范

  • 用于类、方法、接口、枚举等 API 元素的说明

  • 工具:可以使用 IDE 生成 JavaDoc 或通过 javadoc 工具生成 HTML 文档。

    文档注释模板

1
2
3
4
5
6
7
8
9
10
/**
* 描述类/方法的功能。
*
* @author 作者
* @version 版本
* @since 起始版本
* @param 参数名 参数说明(仅针对方法)
* @return 返回值说明(仅针对方法)
* @throws 异常类型 异常说明(仅针对方法)
*/

文档注释示例

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
/**
* 用户类,包含用户的基本信息。
*
* @author Wei
* @version 1.0
* @since 2025-01-08
*/
public class User {

/**
* 用户名。
*/
private String username;

/**
* 获取用户名。
*
* @return 用户名。
*/
public String getUsername() {
return username;
}

/**
* 设置用户名。
*
* @param username 新用户名。
* @throws IllegalArgumentException 如果用户名为空。
*/
public void setUsername(String username) {
if (username == null || username.isEmpty()) {
throw new IllegalArgumentException("用户名不能为空!");
}
this.username = username;
}
}

6. 注释规范建议

  1. 保持一致性:确保整个项目的注释风格统一。
  2. 避免过度注释:注释应恰到好处,避免解释每一行代码。
  3. 及时更新注释:修改代码时,确保注释与代码一致。
  4. 语言清晰简洁:使用专业术语,避免冗长复杂的描述。
  5. 注释内容格式化:采用合理的缩进和换行,使注释结构清晰。
    通过遵循上述规范,可以显著提高 Java 代码的可维护性和团队协作效率。

https://www.bilibili.com/video/BV1ke4y1w7yn

什么是反射 ?

反射允许对字段(成员变量),成员方法和构造方法的信息进程编程访问。

获取类对象字节码文件的三种方式

  • Class.forName(“全类名”);

  • 类名.class;

  • 对象.getClass();

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
package com.qust;

import com.qust.reflect.Student;

public class Main {
public static void main(String[] args) {

/**
* 获取 class 对象的三种方式
*/

Class clazz1 = null;
try {
clazz1 = Class.forName("com.qust.reflect.Student");
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}

Class clazz2 = Student.class;

Class clazz3 = (new Student()).getClass();

System.out.println(clazz1);

System.out.println(clazz2);

System.out.println(clazz3);

}
}

点击类名,点击 copy - cpoy as Reference,即可复制全类名(包名 + 类名)

利用反射获取成员变量信息

Class 中用于获取成员变量的方法

  • Field[] getFields() : 返回所有公共成员变量对象的数组

  • Field[] getDeclaredFields(): 返回所有成员变量对象的数组

  • Field getField() : 返回单个公共成员变量对象

  • Field getDeclaredField() : 返回单个成员变量对象

Field 类中用于创建对象的方法

  • void set(Object obj, Object value) : 赋值

  • Object get(Object obj) :获取值

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package com.qust;

import com.qust.reflect_.Student;

import java.lang.reflect.Field;

public class Main {
public static void main(String[] args) {
// 获取字节码文件
Class clazz;
try {
clazz = Class.forName("com.qust.reflect_.Student");
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}

// 获取所有公共成员变量
Field[] fields = clazz.getFields();
System.out.println("All public variable:");
for(Field f : fields) {
System.out.println(f.getName());
}
System.out.println();

// 获取所有成员变量
fields = clazz.getDeclaredFields();
System.out.println("All variable:");
for(Field f : fields) {
System.out.println(f.getName());
}
System.out.println();

// 获取单个成员变量
Field gender = null;
try {
gender = clazz.getField("gender");
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
System.out.println(gender);
System.out.println();

Field name = null;
try {
name = clazz.getDeclaredField("name");
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
System.out.println(name);
System.out.println();

// 获取权限修饰符
System.out.println(name.getModifiers()); // 2 : 私有的
// 获取成员变量名
System.out.println(name.getName());
// 获取数据类型
System.out.println(name.getType());
// 获取对象的值
Student student = new Student(11, "wpc", "male");
name.setAccessible(true);
Object value = null;
try {
value = name.get(student);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
System.out.println(value);
// 修改对象值
try {
name.set(student,"wpc_");
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
System.out.println(student);
}
}

利用反射获取成员方法

Class 类中获取成员方法的方法

  • Method[] getMethods() : 返回所有公共成员方法的数组,包括继承的

  • Method[] getDeclaredMethods()

  • Methods[] getMethod(String name, Class<?> … Parameters)

  • Methods[] getDeclaredMethod(String name, Class<?> … Parameters)

Method 类中用于创建对象的方法

  • Object invoke(Object object, Object .. args) : 运行方法

参数一 :用 obj 对象调用该方法

参数二 :调用方法的传递的参数(没有就不写)

返回值 :方法的返回值(没有就不写)

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package com.qust;

import com.qust.reflect__.Student;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test {
public static void main(String[] args) {
Class clazz;
try {
clazz = Class.forName("com.qust.reflect__.Student");
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}

// 获取所有的方法对象(包括父类的所有公共方法)
// Method[] methods = clazz.getMethods();
// for(Method method : methods) {
// System.out.println(method.getName());
// }


// 获取所有方法(不包括父类)
Method[] methods = clazz.getDeclaredMethods();
for(Method method : methods) {
System.out.println(method.getName());
}

// 获取指定的单一方法
Method m = null;
try {
m = clazz.getMethod("eat", String.class);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
System.out.println(m);

// 获取方法的修饰符
int modifiers = m.getModifiers();
// 获取方法名字
String name = m.getName();
// 方法形参
m.getParameters();
m.getParameterCount();
m.getParameterTypes();

// 方法抛出异常
Class[] exps = m.getExceptionTypes();


/**
* 运行方法 invoke
* 参数一 : 方法的调用者
* 参数二 : 调用方法传递的参数
*/
try {
String res = (String) m.invoke(new Student(), "shit");
System.out.println(res);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}

// 获取方法返回值
Class<?> returnType = m.getReturnType();
System.out.println(returnType);
}
}

反射的作用

  • 获取一个类的所有的信息,获取到了之后,在执行其他的业务逻辑

  • 结合配置文件,动态的创建并调用方法

练习一 保存信息

对于任意一个对象,都可以把对象的所有字段名和值,保存到文件中

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
package com.qust.practice1;

import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;

public class MyReflectDemo {
public static void main(String[] args) throws IOException, IllegalAccessException {
Student s = new Student("wpc", 21, "male", "learn", 180.5);
Teacher t = new Teacher("teacher1", -100);
System.out.println(System.getProperty("user.dir"));
saveObjects(s);
}

// 将对象里面所有的成员变量名和值保存到本地文件中
public static void saveObjects(Object obj) throws IOException, IllegalAccessException {
// 1. 获取字节码文件对象
Class clazz = obj.getClass();

// 2. 创建 IO 流
BufferedWriter bw = new BufferedWriter(new FileWriter("C:\\Users\\95432\\Desktop\\Java-Reflect\\demo-reflect\\src\\main\\java\\com\\qust\\practice1\\data.txt"));
// BufferedWriter bw = new BufferedWriter(new FileWriter("data.txt"));

// 3. 获取所有的成员变量
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
String name = field.getName();
Object value = field.get(obj);
System.out.println(name + "=" + value);
bw.write(name + "=" + value);
bw.write("\n");
}
bw.close();
}
}

不知道为什么相对路径写入信息写不进去,也没提示找不到文件报错

练习二

跟配置文件结合,动态创建对象,并调用方法

  • Main.java
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
package com.qust.practice2;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Properties;

public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// 读取配置文件信息
Properties prop = new Properties();
prop.load(new FileInputStream("C:\\Users\\95432\\Desktop\\Java-Reflect\\demo-reflect\\src\\main\\resources\\test .properties"));
System.out.println(prop);

// 获取全类名和方法名
String className = (String) prop.get("classname");
String methodName = (String) prop.get("method");

System.out.println(className);
System.out.println(methodName);

// 利用反射创建对象,并运行方法
Class clazz = Class.forName(className);
// 获取构造方法
Constructor con = clazz.getDeclaredConstructor();
Object o = con.newInstance();
System.out.println(o);

// clazz.newInstance(); @deprecated

clazz.getDeclaredMethod(methodName).invoke(o);
}
}
  • test.properties
1
2
classname=com.qust.practice2.Student
method=learn

如果之后要运行别的类的方法,修改配置文件即可,例如修改为

className

classname=com.qust.practice2.Teacher
method=teach

动态代理

  • 为什么需要代理 ?

无侵入式的给对象增加额外的功能。

调用者 $\rightarrow$ 代理 $\rightarrow$ 对象

(对象功能太多的话,可以通过代理转移部分职责。)

  • 代理长什么样 ?

代理里面就是对象想要被代理的方法

(对象有什么方法想被代理,代理就一定要有对应的方法。)

  • Java通过什么保证代理的样子 ?

通过接口保证,后面的对象和代理需要实现同一个接口,接口中就是被代理的所有方法。

动态代理实例

Interface : Star

1
2
3
4
5
6
7
8
9
package com.qust;

public interface Star {
// 唱歌
public abstract String sing(String name);

// 跳舞
public abstract void dance();
}

Class : BigStar

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
package com.qust;

public class BigStar implements Star{
private String name;

// 唱歌
@Override
public String sing(String name) {
System.out.println(this.name + "正在唱" + name);
return "谢谢";
}

// 跳舞
@Override
public void dance() {
System.out.println(this.name + "正在跳舞");
}

public BigStar() {
}

public BigStar(String name) {
this.name = name;
}

/**
* 获取
* @return name
*/
public String getName() {
return name;
}

/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}

public String toString() {
return "BigStar{name = " + name + "}";
}
}

Class : Proxyutil

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
package com.qust;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
* 类的作用 :
* 创建一个代理
*/


public class ProxyUtil {

/**
* 方法的作用 : 创建一个代理
* @param bigStar
* 返回值 :
* 给 BigStar 创建的代理
*/
public static Star createProxy(BigStar bigStar) {
/**
* java.lang.reflect.Proxy类 :提供了为对象产生代理对象的方法
* public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
* 参数一 : 用于指定哪个类加载器
* 参数二 : 指定接口,这些接口用于指定生成的代理长什么样,也就是有哪些方法
* 参数三 :指定生成的代理对象要干什么事情
*/
Star star = (Star) Proxy.newProxyInstance(
BigStar.class.getClassLoader(),
new Class[]{Star.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("sing".equals(method.getName())) {
System.out.println("准备话筒,收钱");
}
else if("dance".equals(method.getName())) {
System.out.println("准备场地,收钱");
}

return method.invoke(bigStar, args);

}
}
);

return star;
}

}

Class : Main

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

//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
public class Main {
public static void main(String[] args) {

// 获取代理对象
BigStar bigStar = new BigStar("鸡哥");
Star proxy = ProxyUtil.createProxy(bigStar);

// 调用对应方法
String result = proxy.sing("只因你太美");
System.out.println(result);

proxy.dance();
}
}

Usage

usage就是这个东西在哪里被使用了

Javabean

JavaBean 是一种符合特定规范的 Java 类,用于封装数据并通过 getter/setter 方法访问。

Ptg 可以一键生成 Javabean

class<?> []

class<?>[] 是 Java 中的一种数组类型,表示一个元素类型为 任意类型的 Class 对象数组

1
2
3
4
5
6
7
8
9
10
11
public class Example {
public static void main(String[] args) {
// 创建一个 Class<?> 数组
Class<?>[] classes = {String.class, Integer.class, Double.class};

// 输出每个元素的类名
for (Class<?> clazz : classes) {
System.out.println(clazz.getName());
}
}
}
1
2
3
java.lang.String
java.lang.Integer
java.lang.Double

打包成 jar 包会去除所有注释吗?

不会,打包成 JAR 包时注释不会被自动去除

具体原因是:

  1. 注释属于源码的一部分,它们不会被编译到 .class 文件中,因此在编译阶段就被丢弃了。JAR 包中存储的是编译后的字节码(.class 文件),而不是源码(.java 文件)。

  2. 如果 JAR 包中需要包含源码(比如 -sources JAR),源码中的注释会被保留,但这些注释只是存储在 .java 文件中,供开发者阅读使用,不影响运行时。

    总结

  • 普通 JAR 包(包含 .class 文件):没有注释,因为注释早在编译时被去掉了。

  • 源码 JAR 包(包含 .java 文件):注释会被保留。
    如果你的目标是生成一个干净的 JAR 包,避免泄露源码或敏感注释,可以通过排除 .java 文件的方式只打包字节码。

term

  • invoke v. 调用

  • utility /juːˈtɪləti/ n. 实用工具

  • AOP,Aspect-Oriented Programming,面向切面编程

  • proxy /ˈprɒksi/ n. 代理权,代理人

从0到1开发一个SpringBoot入门Demo

项目打包方法

  • 终端输入 mvn clean install 命令,等待控制台输出 BUILD SUCCESS,在 target 目录生成一个 .jar

  • 复制 .jar 包的路径,例如 /Users/evan/IdeaProjects/boot-demo/target/1.jar ,控制台输入 cd /Users/evan/IdeaProjects/boot-demo/target/ ,输入 ll 可以查看内容。

  • 使用 java -jar 1.jar 即可运行启动应用,直接访问项目 ;也可以修改端口,java -jar 1.jar --server.port=8081