├── .idea ├── .gitignore ├── JavaSceneConfigState.xml ├── codeStyles │ └── codeStyleConfig.xml ├── compiler.xml ├── encodings.xml ├── inspectionProfiles │ └── Project_Default.xml ├── jarRepositories.xml ├── misc.xml ├── uiDesigner.xml └── vcs.xml ├── LICENSE ├── README-EN.md ├── README.md ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── enndfp │ │ ├── demo │ │ ├── HelloServlet.java │ │ ├── aspect │ │ │ └── ControllerTimeCalculatorAspect.java │ │ ├── controller │ │ │ ├── frontend │ │ │ │ └── MainPageController.java │ │ │ └── superadmin │ │ │ │ ├── HeadLineOperationController.java │ │ │ │ └── ShopCategoryOperationController.java │ │ ├── entity │ │ │ ├── bo │ │ │ │ ├── HeadLine.java │ │ │ │ └── ShopCategory.java │ │ │ └── dto │ │ │ │ ├── MainPageInfoDTO.java │ │ │ │ └── Result.java │ │ └── service │ │ │ ├── combine │ │ │ ├── HeadLineShopCategoryCombineService.java │ │ │ └── impl │ │ │ │ └── HeadLineShopCategoryCombineServiceImpl.java │ │ │ └── solo │ │ │ ├── HeadLineService.java │ │ │ ├── ShopCategoryService.java │ │ │ └── impl │ │ │ ├── HeadLineServiceImpl.java │ │ │ └── ShopCategoryServiceImpl.java │ │ └── simpleframework │ │ ├── aop │ │ ├── AspectListExecutor.java │ │ ├── AspectWeaver.java │ │ ├── ProxyCreator.java │ │ ├── annotation │ │ │ ├── Aspect.java │ │ │ └── Order.java │ │ └── aspect │ │ │ ├── AspectInfo.java │ │ │ ├── DefaultAspect.java │ │ │ └── PointcutLocator.java │ │ ├── core │ │ ├── BeanContainer.java │ │ └── annotation │ │ │ ├── Component.java │ │ │ ├── Controller.java │ │ │ ├── Repository.java │ │ │ └── Service.java │ │ ├── inject │ │ ├── DependencyInjector.java │ │ └── annotation │ │ │ └── Autowired.java │ │ ├── mvc │ │ ├── DispatcherServlet.java │ │ ├── RequestProcessorChain.java │ │ ├── annotation │ │ │ ├── RequestMapping.java │ │ │ ├── RequestParam.java │ │ │ └── ResponseBody.java │ │ ├── processor │ │ │ ├── RequestProcessor.java │ │ │ └── impl │ │ │ │ ├── ControllerRequestProcessor.java │ │ │ │ ├── JspRequestProcessor.java │ │ │ │ ├── PreRequestProcessor.java │ │ │ │ └── StaticResourceRequestProcessor.java │ │ ├── render │ │ │ ├── ResultRender.java │ │ │ └── impl │ │ │ │ ├── DefaultResultRender.java │ │ │ │ ├── InternalErrorResultRender.java │ │ │ │ ├── JsonResultRender.java │ │ │ │ ├── ResourceNotFoundResultRender.java │ │ │ │ └── ViewResultRender.java │ │ └── type │ │ │ ├── ControllerMethod.java │ │ │ ├── ModelAndView.java │ │ │ ├── RequestMethod.java │ │ │ └── RequestPathInfo.java │ │ └── utils │ │ ├── ClassUtil.java │ │ ├── ConverterUtil.java │ │ └── ValidationUtil.java ├── resources │ └── log4j.properties └── webapp │ ├── WEB-INF │ └── jsp │ │ └── hello.jsp │ ├── static │ └── coding.jpg │ └── templates │ └── addheadline.jsp └── test └── java └── com └── enndfp └── simpleframework ├── aop ├── AspectListExecutorTest.java ├── AspectWeaverTest.java └── mock │ ├── Mock1.java │ ├── Mock2.java │ ├── Mock3.java │ ├── Mock4.java │ └── Mock5.java ├── core └── BeanContainerTest.java ├── inject └── DependencyInjectorTest.java └── utils └── ClassUtilTest.java /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/JavaSceneConfigState.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/uiDesigner.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 | 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 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Enndfp 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README-EN.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | [简体中文](README.md) | **English** 4 | 5 |
6 |

🌟 SimpleFramework

7 |
8 | 9 |
10 | 🛠️ Wheel-making project: Implementing the Spring framework from scratch 11 |
12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | ## 📖 Project Description 27 | 28 | > The Spring framework occupies a pivotal position in the Java development world, mainly due to its easy-to-understand and powerful features. It widely applies a variety of design patterns to provide a standardized architecture for the project. More importantly, Spring, as an open source framework, provides developers with opportunities to learn and improve, and brings an innovative spring breeze to Java development. 29 | > 30 | > In view of these advantages of Spring, many Java developers are eager to use basic technologies to implement a framework similar to Spring. This approach is not only an in-depth understanding of Spring architecture and design concepts, but also a demonstration of technical capabilities. Therefore, the "simple-framework" project came into being, aiming to implement a simplified version of the Spring framework so that developers can more easily understand its core concepts and at the same time improve their technical level in the field of Java development. 31 | 32 | **Simple-Framework is a free open source project that provides an easy-to-use and learn Java development framework for all individuals and enterprises, supporting the common progress and innovation of the developer community. ** 33 | 34 | ## 🚀 Technical Highlights 35 | 36 | - **Java 1.8**: Provides optimized performance and stability and is the cornerstone of Java development. 37 | - **CGLIB 3.3.0 & AspectJWeaver 1.9.5**: Powerful libraries that provide a solid foundation for AOP. 38 | - **Java Servlet API & Gson & Lombok**: These technologies together form a powerful web application development environment. 39 | 40 | ## 📚 Project architecture diagram 41 | 42 | ### 🔄 IOC 43 | 44 | ![image-20240118205412786](https://img.enndfp.cn/202401182054883.png) 45 | 46 | ### 🔀 AOP 47 | 48 | ![image-20240119133233090](https://img.enndfp.cn/202401191332233.png) 49 | 50 | ### 🕸️ MVC 51 | 52 | ![image-20240119142136722](https://img.enndfp.cn/202401191421810.png) 53 | 54 | ## ✨ The main function 55 | 56 | This project is a **simple version of the Spring Framework** that implements the three core functions of the Spring Framework: **IOC** (Inversion of Control), **AOP** (Aspect-Oriented Programming) and **MVC* * (Model-View-Controller) and split it into the following core packages: 57 | 58 | #### 📦 Core package 59 | 60 | **Function**: The Core package implements the **core functions** of the framework, including **Bean scanning and loading**, **container maintenance**, **single case mode implementation**, and** Handling of custom beans**. 61 | 62 | **Implementation method**: Use **Java reflection mechanism** to dynamically scan and load classes under specified packages, identify and process different types of annotations (such as `@Component`, `@Controller`, etc.) to manage different types of Beans. At the same time, it implements the **single case mode**, ensuring that each Bean is instantiated only once, and provides basic methods for operating Beans, such as adding, obtaining, and managing Bean instances. 63 | 64 | #### 💉 Inject package 65 | 66 | **Function**: The Inject package is responsible for **dependency injection**, including processing `@Autowired` annotations, implementing **dependency injection in singleton mode**, and injecting implementation classes for **interfaces**. 67 | 68 | **Implementation method**: Scan the fields of the Bean through the **Java reflection mechanism** to find the fields with the `@Autowired` annotation, and use the Bean container to obtain and inject the required dependencies. It supports dependency injection in singleton mode to ensure the consistency and uniqueness of dependencies. At the same time, it can also dynamically inject appropriate implementation classes into the interface, improving the flexibility and maintainability of the code. 69 | 70 | #### 🔍 AOP package 71 | 72 | **Function**: The AOP package follows the **aspect-oriented programming idea**, uses `Aspect` and `Order` annotations to identify and sort aspect classes, and implements horizontal aspects through **CGlib dynamic proxy** and **AspectJWeaver** Weave in all logic and dynamically modify method logic. 73 | 74 | **Implementation method**: Use **CGlib** to create a proxy of the target class, and intercept method calls by implementing the `MethodInterceptor` interface. This allows aspect logic (such as logging, permission checks, etc.) to be performed before and after method execution. At the same time, the expression language of **AspectJ** provides more precise control over the proxy class, allowing the method logic to be modified and enhanced according to different needs. 75 | 76 | #### 🌐 MVC package 77 | 78 | **Function**: MVC package handles **request distribution related functions**, including reconstructing `DispatcherServlet`, implementing `RequestProcessorChain` and `RequestProcessor` matrices, and `ResultRender` matrix to complete various request processing and response rendering . 79 | 80 | **Implementation**: Use `DispatcherServlet` as the **central controller** to process all HTTP requests and distribute them to the corresponding processors. Use `RequestProcessorChain` to manage and execute a series of request processors to handle different types of requests (such as static resources, controller methods, etc.). The `ResultRender` matrix is responsible for selecting an appropriate rendering strategy based on the processing results, such as rendering an HTML page or returning JSON data, ensuring that the response is correctly rendered and returned to the client. 81 | 82 | ## 💡 Quick start guide 83 | 84 | To get started using **SimpleFramework**, you can take the following steps: 85 | 86 | ### 📥 Method 1: Use source code 87 | 88 | 1. Clone the repository: 89 | 90 | ```bash 91 | git clone https://github.com/Enndfp/simple-framework.git 92 | ``` 93 | 94 | 2. Import the project into your IDE (eg IntelliJ IDEA). 95 | 96 | 3. Perform relevant tests in the `demo` directory. This is similar to developing projects with Spring Boot. 97 | 98 | ### 📦 Method 2: War package deployment 99 | 100 | 1. Build the project and generate the War package. 101 | 2. Deploy the War package to your Servlet container, such as Apache Tomcat. 102 | 3. Start the container and the application will be deployed automatically. 103 | 104 | ### 🌟 Sample code 105 | 106 | Here is a simple example showing how to use **SimpleFramework** in your project: 107 | 108 | ```java 109 | import com.simpleframework.core.BeanContainer; 110 | 111 | public class MyApplication { 112 | public static void main(String[] args) { 113 | //Initialize container 114 | BeanContainer container = BeanContainer.getInstance(); 115 | container.loadBeans("com.yourpackage"); 116 | 117 | // Use the container to get the Bean 118 | MyService myService = (MyService) container.getBean(MyService.class); 119 | myService.doSomething(); 120 | } 121 | } 122 | ``` 123 | 124 | In this example, we first obtain an instance of `BeanContainer` and then load all beans under the specified package path. After that, we get an instance of the `MyService` class from the container and call its methods. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | **简体中文** | [English](README-EN.md) 4 | 5 |
6 |

🌟 SimpleFramework

7 |
8 | 9 |
10 | 🛠️ 造轮子项目:从头实现Spring框架 11 |
12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | ## 📖 项目简介 26 | 27 | > Spring框架在Java开发界占据了举足轻重的地位,这主要归功于其易于理解和功能强大的特性。它广泛应用了多种设计模式,为项目提供了规范化的架构。更重要的是,Spring作为一个开源框架,为广大开发者提供了学习和提升的机会,为Java开发带来了一种革新的春风。 28 | > 29 | > 鉴于Spring的这些优势,很多Java开发者渴望使用基础技术来实现一个类似于Spring的框架。这种做法不仅是对Spring架构和设计理念的深入理解,也是一种技术能力的展示。因此,“simple-framework”项目应运而生,旨在通过实现一个简化版的Spring框架,使开发者更容易地理解其核心概念,同时也能够提升自己在Java开发领域的技术水平。 30 | 31 | **Simple-Framework是一个免费的开源项目,面向所有个人和企业,提供易于使用和学习的Java开发框架,支持开发者社区的共同进步与创新。** 32 | 33 | ## 🚀 技术亮点 34 | 35 | - **Java 1.8**: 提供优化的性能和稳定性,是Java开发的基石。 36 | - **CGLIB 3.3.0 & AspectJWeaver 1.9.5**: 强大的库,为AOP提供了坚实的基础。 37 | - **Java Servlet API & Gson & Lombok**: 这些技术共同构成了一个强大的Web应用开发环境。 38 | 39 | ## 📚 项目架构图 40 | 41 | ### 🔄 IOC 42 | 43 | ![image-20240118205412786](https://img.enndfp.cn/202401182054883.png) 44 | 45 | ### 🔀 AOP 46 | 47 | ![image-20240119133233090](https://img.enndfp.cn/202401191332233.png) 48 | 49 | ### 🕸️ MVC 50 | 51 | ![image-20240119142136722](https://img.enndfp.cn/202401191421810.png) 52 | 53 | ## ✨ 主要功能 54 | 55 | 本项目是一个**简易版本的Spring框架**,实现了Spring框架的三大核心功能:**IOC**(控制反转)、**AOP**(面向切面编程)和**MVC**(模型-视图-控制器),并将其分为以下核心包: 56 | 57 | #### 📦 Core包 58 | 59 | **功能**: Core包实现了框架的**核心功能**,包括**Bean的扫描加载**、**容器的维护**、**单例模式的实现**,以及**自定义Bean的处理**。 60 | 61 | **实现方式**: 利用**Java反射机制**动态扫描和加载指定包下的类,识别并处理不同类型的注解(如 `@Component`, `@Controller` 等)以管理不同种类的Bean。同时,它实现了**单例模式**,确保每个Bean只被实例化一次,并提供了操作Bean的基本方法,例如添加、获取和管理Bean实例。 62 | 63 | #### 💉 Inject包 64 | 65 | **功能**: Inject包负责**依赖注入**,包括处理 `@Autowired` 注解,实现**单例模式下的依赖注入**,以及为**接口注入实现类**。 66 | 67 | **实现方式**: 通过**Java反射机制**扫描Bean的字段,查找带有 `@Autowired` 注解的字段,并利用Bean容器获取并注入所需依赖。它支持单例模式下的依赖注入,确保依赖的一致性和唯一性。同时,它也能为接口动态地注入适当的实现类,提高了代码的灵活性和可维护性。 68 | 69 | #### 🔍 AOP包 70 | 71 | **功能**: AOP包遵循**面向切面编程思想**,使用 `Aspect` 和 `Order` 注解来标识和排序切面类,通过**CGlib动态代理**和**AspectJWeaver**实现横切逻辑的织入,动态修改方法逻辑。 72 | 73 | **实现方式**: 利用**CGlib**创建目标类的代理,并通过实现 `MethodInterceptor` 接口来拦截方法调用。这允许在方法执行前后执行切面逻辑(如日志记录、权限检查等)。同时,通过**AspectJ**的表达式语言提供对被代理类更精细的控制,使得可以根据不同的需要对方法逻辑进行修改和增强。 74 | 75 | #### 🌐 MVC包 76 | 77 | **功能**: MVC包处理**请求分发相关功能**,包括重构 `DispatcherServlet`,实现 `RequestProcessorChain` 和 `RequestProcessor` 矩阵,以及 `ResultRender` 矩阵,完成多种请求的处理与响应渲染。 78 | 79 | **实现方式**: 通过 `DispatcherServlet` 作为**中心控制器**,处理所有的HTTP请求并将其分发到相应的处理器。利用 `RequestProcessorChain` 管理和执行一系列请求处理器,以处理不同类型的请求(如静态资源、控制器方法等)。`ResultRender` 矩阵负责根据处理结果选择合适的渲染策略,例如渲染HTML页面或返回JSON数据,确保响应正确地渲染和返回给客户端。 80 | 81 | ## 💡 快速上手指南 82 | 83 | 要开始使用 **SimpleFramework**,您可以采取以下步骤: 84 | 85 | ### 📥 方法一:源码使用 86 | 87 | 1. 克隆仓库: 88 | 89 | ```bash 90 | git clone https://github.com/Enndfp/simple-framework.git 91 | ``` 92 | 93 | 2. 导入项目到您的IDE(例如IntelliJ IDEA)。 94 | 95 | 3. 在 `demo` 目录下进行相关测试。这与使用Spring Boot开发项目类似。 96 | 97 | ### 📦 方法二:War包部署 98 | 99 | 1. 构建项目并生成War包。 100 | 2. 将War包部署到您的Servlet容器中,如Apache Tomcat。 101 | 3. 启动容器,应用将自动部署。 102 | 103 | ### 🌟 示例代码 104 | 105 | 以下是一个简单的示例,展示了如何在您的项目中使用 **SimpleFramework**: 106 | 107 | ```java 108 | import com.simpleframework.core.BeanContainer; 109 | 110 | public class MyApplication { 111 | public static void main(String[] args) { 112 | // 初始化容器 113 | BeanContainer container = BeanContainer.getInstance(); 114 | container.loadBeans("com.yourpackage"); 115 | 116 | // 使用容器获取Bean 117 | MyService myService = (MyService) container.getBean(MyService.class); 118 | myService.doSomething(); 119 | } 120 | } 121 | ``` 122 | 123 | 在这个例子中,我们首先获取了 `BeanContainer` 的实例,然后加载了指定包路径下的所有Bean。之后,我们从容器中获取了 `MyService` 类的实例,并调用了其方法。 124 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.enndfp 8 | simple-framework 9 | 1.0-SNAPSHOT 10 | war 11 | 12 | 13 | 8 14 | 8 15 | UTF-8 16 | 17 | 18 | 19 | 20 | cglib 21 | cglib 22 | 3.3.0 23 | 24 | 25 | org.aspectj 26 | aspectjweaver 27 | 1.9.5 28 | 29 | 30 | javax.servlet 31 | javax.servlet-api 32 | 4.0.1 33 | provided 34 | 35 | 36 | javax.servlet.jsp 37 | javax.servlet.jsp-api 38 | 2.3.3 39 | provided 40 | 41 | 42 | com.google.code.gson 43 | gson 44 | 2.8.6 45 | 46 | 47 | org.slf4j 48 | slf4j-log4j12 49 | 1.7.28 50 | 51 | 52 | org.projectlombok 53 | lombok 54 | 1.18.30 55 | 56 | 57 | org.junit.jupiter 58 | junit-jupiter-api 59 | 5.5.2 60 | test 61 | 62 | 63 | 64 | simple-framework 65 | 66 | 67 | 68 | org.apache.maven.plugins 69 | maven-compiler-plugin 70 | 3.8.1 71 | 72 | 8 73 | 8 74 | 75 | 76 | 77 | org.apache.tomcat.maven 78 | tomcat7-maven-plugin 79 | 2.2 80 | 81 | /${project.artifactId} 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/demo/HelloServlet.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.demo; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import javax.servlet.ServletException; 8 | import javax.servlet.annotation.WebServlet; 9 | import javax.servlet.http.HttpServlet; 10 | import javax.servlet.http.HttpServletRequest; 11 | import javax.servlet.http.HttpServletResponse; 12 | import java.io.IOException; 13 | 14 | /** 15 | * @author Enndfp 16 | */ 17 | @Slf4j 18 | @WebServlet("/hello") 19 | public class HelloServlet extends HttpServlet { 20 | @Override 21 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 22 | String name = "我的简易框架"; 23 | log.debug("name is " + name); 24 | req.setAttribute("name", name); 25 | req.getRequestDispatcher("/WEB-INF/jsp/hello.jsp").forward(req, resp); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/demo/aspect/ControllerTimeCalculatorAspect.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.demo.aspect; 2 | 3 | import com.enndfp.simpleframework.aop.annotation.Aspect; 4 | import com.enndfp.simpleframework.aop.annotation.Order; 5 | import com.enndfp.simpleframework.aop.aspect.DefaultAspect; 6 | import lombok.extern.slf4j.Slf4j; 7 | 8 | import java.lang.reflect.Method; 9 | 10 | /** 11 | * @author Enndfp 12 | */ 13 | @Slf4j 14 | @Aspect(pointcut = "execution(* com.enndfp.demo.controller..*.*(..))") 15 | @Order(0) 16 | public class ControllerTimeCalculatorAspect extends DefaultAspect { 17 | private long timestampCache; 18 | 19 | @Override 20 | public void before(Class targetClass, Method method, Object[] args) throws Throwable { 21 | log.info("开始计时,执行的类是[{}],执行的方法是[{}],参数是[{}]", 22 | targetClass.getName(), method.getName(), args); 23 | this.timestampCache = System.currentTimeMillis(); 24 | } 25 | 26 | @Override 27 | public Object afterReturning(Class targetClass, Method method, Object[] args, Object returnValue) { 28 | long endTime = System.currentTimeMillis(); 29 | long costTime = endTime - this.timestampCache; 30 | log.info("结束计时,执行的类是[{}],执行的方法是[{}],参数是[{}],返回值是[{}],时间为[{}]ms", 31 | targetClass.getName(), method.getName(), args, returnValue, costTime); 32 | return returnValue; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/demo/controller/frontend/MainPageController.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.demo.controller.frontend; 2 | 3 | import com.enndfp.demo.entity.bo.HeadLine; 4 | import com.enndfp.demo.entity.dto.MainPageInfoDTO; 5 | import com.enndfp.demo.entity.dto.Result; 6 | import com.enndfp.demo.service.combine.HeadLineShopCategoryCombineService; 7 | import com.enndfp.simpleframework.core.annotation.Controller; 8 | import com.enndfp.simpleframework.inject.annotation.Autowired; 9 | import com.enndfp.simpleframework.mvc.annotation.RequestMapping; 10 | import com.enndfp.simpleframework.mvc.annotation.RequestParam; 11 | import com.enndfp.simpleframework.mvc.annotation.ResponseBody; 12 | import com.enndfp.simpleframework.mvc.type.ModelAndView; 13 | import com.enndfp.simpleframework.mvc.type.RequestMethod; 14 | import lombok.Getter; 15 | 16 | import javax.servlet.http.HttpServletRequest; 17 | import javax.servlet.http.HttpServletResponse; 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | /** 22 | * @author Enndfp 23 | */ 24 | @Controller 25 | @Getter 26 | @RequestMapping("/main") 27 | public class MainPageController { 28 | @Autowired() 29 | private HeadLineShopCategoryCombineService headLineShopCategoryCombineService; 30 | 31 | /** 32 | * 获取主页信息 33 | * 34 | * @param req 35 | * @param resp 36 | * @return 37 | */ 38 | public Result getMainPageInfo(HttpServletRequest req, HttpServletResponse resp) { 39 | return headLineShopCategoryCombineService.getMainPageInfo(); 40 | } 41 | 42 | @RequestMapping(value = "/test1", method = RequestMethod.GET) 43 | public void getTest() { 44 | System.out.println("调用test1方法"); 45 | return; 46 | } 47 | 48 | @RequestMapping(value = "/test2", method = RequestMethod.GET) 49 | public void getTest2() { 50 | System.out.println("调用test2方法"); 51 | throw new RuntimeException("test error"); 52 | } 53 | 54 | @RequestMapping(value = "/test3", method = RequestMethod.GET) 55 | @ResponseBody 56 | public Result queryList() { 57 | List headLineList = new ArrayList<>(); 58 | HeadLine headLine1 = new HeadLine(); 59 | headLine1.setLineId(1L); 60 | headLine1.setLineName("头条1"); 61 | headLine1.setLineLink("www.baidu.com"); 62 | headLine1.setLineImg("头条图片1地址"); 63 | headLineList.add(headLine1); 64 | HeadLine headLine2 = new HeadLine(); 65 | headLine2.setLineId(2L); 66 | headLine2.setLineName("头条2"); 67 | headLine2.setLineLink("www.google.com"); 68 | headLine2.setLineImg("头条图片2地址"); 69 | headLineList.add(headLine2); 70 | return Result.ok(headLineList); 71 | } 72 | 73 | @RequestMapping(value = "/add", method = RequestMethod.POST) 74 | public ModelAndView add(@RequestParam("lineName") String lineName 75 | , @RequestParam("lineLink") String lineLink 76 | , @RequestParam("lineImg") String lineImg 77 | , @RequestParam("priority") Integer priority) { 78 | HeadLine headLine = new HeadLine(); 79 | headLine.setLineLink(lineLink); 80 | headLine.setLineImg(lineImg); 81 | headLine.setLineName(lineName); 82 | headLine.setPriority(priority); 83 | System.out.println(headLine); 84 | Result result = new Result<>(200, "请求成功", true); 85 | ModelAndView modelAndView = new ModelAndView(); 86 | modelAndView.setViewPath("addheadline.jsp").addViewData("result", result); 87 | return modelAndView; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/demo/controller/superadmin/HeadLineOperationController.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.demo.controller.superadmin; 2 | 3 | import com.enndfp.demo.entity.bo.HeadLine; 4 | import com.enndfp.demo.entity.dto.Result; 5 | import com.enndfp.demo.service.solo.HeadLineService; 6 | import com.enndfp.simpleframework.core.annotation.Controller; 7 | import com.enndfp.simpleframework.inject.annotation.Autowired; 8 | 9 | import javax.servlet.http.HttpServletRequest; 10 | import javax.servlet.http.HttpServletResponse; 11 | import java.util.List; 12 | 13 | /** 14 | * @author Enndfp 15 | */ 16 | @Controller 17 | public class HeadLineOperationController { 18 | 19 | @Autowired 20 | private HeadLineService headLineService; 21 | 22 | /** 23 | * 添加头条 24 | * 25 | * @param req 26 | * @param resp 27 | * @return 28 | */ 29 | public Result addHeadLine(HttpServletRequest req, HttpServletResponse resp) { 30 | return headLineService.addHeadLine(new HeadLine()); 31 | } 32 | 33 | /** 34 | * 删除头条 35 | * 36 | * @param req 37 | * @param resp 38 | * @return 39 | */ 40 | public Result removeHeadLine(HttpServletRequest req, HttpServletResponse resp) { 41 | return headLineService.removeHeadLine(1); 42 | } 43 | 44 | /** 45 | * 修改头条 46 | * 47 | * @param req 48 | * @param resp 49 | * @return 50 | */ 51 | public Result modifyHeadLine(HttpServletRequest req, HttpServletResponse resp) { 52 | return headLineService.modifyHeadLine(new HeadLine()); 53 | } 54 | 55 | /** 56 | * 查询头条 57 | * 58 | * @param req 59 | * @param resp 60 | * @return 61 | */ 62 | public Result queryHeadLineById(HttpServletRequest req, HttpServletResponse resp) { 63 | return headLineService.queryHeadLineById(1); 64 | } 65 | 66 | /** 67 | * 查询头条列表 68 | * 69 | * @param req 70 | * @param resp 71 | * @return 72 | */ 73 | public Result> queryHeadLine(HttpServletRequest req, HttpServletResponse resp) { 74 | return headLineService.queryHeadLine(null, 1, 100); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/demo/controller/superadmin/ShopCategoryOperationController.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.demo.controller.superadmin; 2 | 3 | import com.enndfp.demo.entity.bo.ShopCategory; 4 | import com.enndfp.demo.entity.dto.Result; 5 | import com.enndfp.demo.service.solo.ShopCategoryService; 6 | import com.enndfp.simpleframework.core.annotation.Controller; 7 | import com.enndfp.simpleframework.inject.annotation.Autowired; 8 | 9 | import javax.servlet.http.HttpServletRequest; 10 | import javax.servlet.http.HttpServletResponse; 11 | import java.util.List; 12 | 13 | /** 14 | * @author Enndfp 15 | */ 16 | @Controller 17 | public class ShopCategoryOperationController { 18 | 19 | @Autowired 20 | private ShopCategoryService shopCategoryService; 21 | 22 | /** 23 | * 添加店铺类别 24 | * 25 | * @param req 26 | * @param resp 27 | * @return 28 | */ 29 | public Result addShopCategory(HttpServletRequest req, HttpServletResponse resp) { 30 | return shopCategoryService.addShopCategory(new ShopCategory()); 31 | } 32 | 33 | /** 34 | * 删除店铺类别 35 | * 36 | * @param req 37 | * @param resp 38 | * @return 39 | */ 40 | public Result removeShopCategory(HttpServletRequest req, HttpServletResponse resp) { 41 | return shopCategoryService.removeShopCategory(1); 42 | } 43 | 44 | /** 45 | * 修改店铺类别 46 | * 47 | * @param req 48 | * @param resp 49 | * @return 50 | */ 51 | public Result modifyShopCategory(HttpServletRequest req, HttpServletResponse resp) { 52 | return shopCategoryService.modifyShopCategory(new ShopCategory()); 53 | } 54 | 55 | /** 56 | * 查询店铺类别 57 | * 58 | * @param req 59 | * @param resp 60 | * @return 61 | */ 62 | public Result queryShopCategoryById(HttpServletRequest req, HttpServletResponse resp) { 63 | return shopCategoryService.queryShopCategoryById(1); 64 | } 65 | 66 | /** 67 | * 查询店铺类别列表 68 | * 69 | * @param req 70 | * @param resp 71 | * @return 72 | */ 73 | public Result> queryShopCategory(HttpServletRequest req, HttpServletResponse resp) { 74 | return shopCategoryService.queryShopCategory(new ShopCategory(), 1, 100); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/demo/entity/bo/HeadLine.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.demo.entity.bo; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.Date; 6 | 7 | /** 8 | * @author Enndfp 9 | */ 10 | @Data 11 | public class HeadLine { 12 | 13 | private Long lineId; 14 | 15 | private String lineName; 16 | 17 | private String lineLink; 18 | 19 | private String lineImg; 20 | 21 | private Integer priority; 22 | 23 | private Integer enableStatus; 24 | 25 | private Date createTime; 26 | 27 | private Date lastEditTime; 28 | } -------------------------------------------------------------------------------- /src/main/java/com/enndfp/demo/entity/bo/ShopCategory.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.demo.entity.bo; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.Date; 6 | 7 | /** 8 | * @author Enndfp 9 | */ 10 | @Data 11 | public class ShopCategory { 12 | 13 | private Long shopCategoryId; 14 | 15 | private String shopCategoryName; 16 | 17 | private String shopCategoryDesc; 18 | 19 | private String shopCategoryImg; 20 | 21 | private Integer priority; 22 | 23 | private Date createTime; 24 | 25 | private Date lastEditTime; 26 | 27 | private ShopCategory parent; 28 | } -------------------------------------------------------------------------------- /src/main/java/com/enndfp/demo/entity/dto/MainPageInfoDTO.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.demo.entity.dto; 2 | 3 | import com.enndfp.demo.entity.bo.HeadLine; 4 | import com.enndfp.demo.entity.bo.ShopCategory; 5 | import lombok.Data; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * @author Enndfp 11 | */ 12 | @Data 13 | public class MainPageInfoDTO { 14 | 15 | // 头条列表 16 | private List headLineList; 17 | 18 | // 店铺类别列表 19 | private List shopCategoryList; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/demo/entity/dto/Result.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.demo.entity.dto; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author Enndfp 7 | */ 8 | @Data 9 | public class Result { 10 | 11 | // 本次请求结果的状态码,200表示成功 12 | private int code; 13 | 14 | // 本次请求结果的详情 15 | private String message; 16 | 17 | // 本次请求返回的结果集 18 | private T data; 19 | 20 | public Result(int code, String message, T data) { 21 | this.code = code; 22 | this.message = message; 23 | this.data = data; 24 | } 25 | 26 | public static Result ok(E data) { 27 | return new Result<>(200, "成功!", data); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/demo/service/combine/HeadLineShopCategoryCombineService.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.demo.service.combine; 2 | 3 | import com.enndfp.demo.entity.dto.Result; 4 | import com.enndfp.demo.entity.dto.MainPageInfoDTO; 5 | 6 | /** 7 | * @author Enndfp 8 | */ 9 | public interface HeadLineShopCategoryCombineService { 10 | 11 | /** 12 | * 获取主页信息 13 | * 14 | * @return 15 | */ 16 | Result getMainPageInfo(); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/demo/service/combine/impl/HeadLineShopCategoryCombineServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.demo.service.combine.impl; 2 | 3 | import com.enndfp.demo.entity.bo.HeadLine; 4 | import com.enndfp.demo.entity.bo.ShopCategory; 5 | import com.enndfp.demo.entity.dto.Result; 6 | import com.enndfp.demo.service.combine.HeadLineShopCategoryCombineService; 7 | import com.enndfp.demo.service.solo.HeadLineService; 8 | import com.enndfp.demo.service.solo.ShopCategoryService; 9 | import com.enndfp.demo.entity.dto.MainPageInfoDTO; 10 | import com.enndfp.simpleframework.core.annotation.Service; 11 | import com.enndfp.simpleframework.inject.annotation.Autowired; 12 | import lombok.extern.slf4j.Slf4j; 13 | 14 | import java.util.List; 15 | 16 | /** 17 | * @author Enndfp 18 | */ 19 | @Slf4j 20 | @Service 21 | public class HeadLineShopCategoryCombineServiceImpl implements HeadLineShopCategoryCombineService { 22 | 23 | @Autowired 24 | private HeadLineService headLineService; 25 | 26 | @Autowired 27 | private ShopCategoryService shopCategoryService; 28 | 29 | @Override 30 | public Result getMainPageInfo() { 31 | log.info("getMainPageInfo方法被执行!"); 32 | // 1. 获取头条列表 33 | HeadLine headLineCondition = new HeadLine(); 34 | headLineCondition.setEnableStatus(1); 35 | Result> headLineResult = headLineService.queryHeadLine(headLineCondition, 1, 4); 36 | 37 | // 2. 获取店铺类别列表 38 | ShopCategory shopCategoryCondition = new ShopCategory(); 39 | Result> shopCategoryResult = shopCategoryService.queryShopCategory(shopCategoryCondition, 1, 100); 40 | 41 | // 3. 合并两者并返回 42 | Result result = mergeMainPageInfoResult(headLineResult, shopCategoryResult); 43 | return result; 44 | } 45 | 46 | private Result mergeMainPageInfoResult(Result> headLineResult, Result> shopCategoryResult) { 47 | return null; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/demo/service/solo/HeadLineService.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.demo.service.solo; 2 | 3 | import com.enndfp.demo.entity.bo.HeadLine; 4 | import com.enndfp.demo.entity.dto.Result; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * @author Enndfp 10 | */ 11 | public interface HeadLineService { 12 | /** 13 | * 添加头条 14 | * 15 | * @param headLine 16 | * @return 17 | */ 18 | Result addHeadLine(HeadLine headLine); 19 | 20 | /** 21 | * 删除头条 22 | * 23 | * @param headLineId 24 | * @return 25 | */ 26 | Result removeHeadLine(int headLineId); 27 | 28 | /** 29 | * 修改头条 30 | * 31 | * @param headLine 32 | * @return 33 | */ 34 | Result modifyHeadLine(HeadLine headLine); 35 | 36 | /** 37 | * 查询头条 38 | * 39 | * @param headLineId 40 | * @return 41 | */ 42 | Result queryHeadLineById(int headLineId); 43 | 44 | /** 45 | * 查询头条列表 46 | * 47 | * @param headLineCondition 48 | * @param pageIndex 49 | * @param pageSize 50 | * @return 51 | */ 52 | Result> queryHeadLine(HeadLine headLineCondition, int pageIndex, int pageSize); 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/demo/service/solo/ShopCategoryService.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.demo.service.solo; 2 | 3 | import com.enndfp.demo.entity.bo.ShopCategory; 4 | import com.enndfp.demo.entity.dto.Result; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * @author Enndfp 10 | */ 11 | public interface ShopCategoryService { 12 | /** 13 | * 添加店铺类别 14 | * 15 | * @param shopCategory 16 | * @return 17 | */ 18 | Result addShopCategory(ShopCategory shopCategory); 19 | 20 | /** 21 | * 删除店铺类别 22 | * 23 | * @param shopCategoryId 24 | * @return 25 | */ 26 | Result removeShopCategory(int shopCategoryId); 27 | 28 | /** 29 | * 修改店铺类别 30 | * 31 | * @param shopCategory 32 | * @return 33 | */ 34 | Result modifyShopCategory(ShopCategory shopCategory); 35 | 36 | /** 37 | * 查询店铺类别 38 | * 39 | * @param shopCategoryId 40 | * @return 41 | */ 42 | Result queryShopCategoryById(int shopCategoryId); 43 | 44 | /** 45 | * 查询店铺类别列表 46 | * 47 | * @param shopCategoryCondition 48 | * @param pageIndex 49 | * @param pageSize 50 | * @return 51 | */ 52 | Result> queryShopCategory(ShopCategory shopCategoryCondition, int pageIndex, int pageSize); 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/demo/service/solo/impl/HeadLineServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.demo.service.solo.impl; 2 | 3 | import com.enndfp.demo.entity.bo.HeadLine; 4 | import com.enndfp.demo.entity.dto.Result; 5 | import com.enndfp.demo.service.solo.HeadLineService; 6 | import com.enndfp.simpleframework.core.annotation.Service; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * @author Enndfp 12 | */ 13 | @Service 14 | public class HeadLineServiceImpl implements HeadLineService { 15 | @Override 16 | public Result addHeadLine(HeadLine headLine) { 17 | return null; 18 | } 19 | 20 | @Override 21 | public Result removeHeadLine(int headLineId) { 22 | return null; 23 | } 24 | 25 | @Override 26 | public Result modifyHeadLine(HeadLine headLine) { 27 | return null; 28 | } 29 | 30 | @Override 31 | public Result queryHeadLineById(int headLineId) { 32 | return null; 33 | } 34 | 35 | @Override 36 | public Result> queryHeadLine(HeadLine headLineCondition, int pageIndex, int pageSize) { 37 | return null; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/demo/service/solo/impl/ShopCategoryServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.demo.service.solo.impl; 2 | 3 | import com.enndfp.demo.entity.bo.ShopCategory; 4 | import com.enndfp.demo.entity.dto.Result; 5 | import com.enndfp.demo.service.solo.ShopCategoryService; 6 | import com.enndfp.simpleframework.core.annotation.Service; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * @author Enndfp 12 | */ 13 | @Service 14 | public class ShopCategoryServiceImpl implements ShopCategoryService { 15 | @Override 16 | public Result addShopCategory(ShopCategory shopCategory) { 17 | return null; 18 | } 19 | 20 | @Override 21 | public Result removeShopCategory(int shopCategoryId) { 22 | return null; 23 | } 24 | 25 | @Override 26 | public Result modifyShopCategory(ShopCategory shopCategory) { 27 | return null; 28 | } 29 | 30 | @Override 31 | public Result queryShopCategoryById(int shopCategoryId) { 32 | return null; 33 | } 34 | 35 | @Override 36 | public Result> queryShopCategory(ShopCategory shopCategoryCondition, int pageIndex, int pageSize) { 37 | return null; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/aop/AspectListExecutor.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.aop; 2 | 3 | import com.enndfp.simpleframework.aop.aspect.AspectInfo; 4 | import com.enndfp.simpleframework.utils.ValidationUtil; 5 | import lombok.Getter; 6 | import net.sf.cglib.proxy.MethodInterceptor; 7 | import net.sf.cglib.proxy.MethodProxy; 8 | 9 | import java.lang.reflect.Method; 10 | import java.util.Comparator; 11 | import java.util.Iterator; 12 | import java.util.List; 13 | 14 | /** 15 | * @author Enndfp 16 | */ 17 | public class AspectListExecutor implements MethodInterceptor { 18 | // 被代理的类 19 | private Class targetClass; 20 | // 排好序的Aspect列表 21 | @Getter 22 | private List sortedAspectInfoList; 23 | 24 | public AspectListExecutor(Class targetClass, List aspectInfoList) { 25 | this.targetClass = targetClass; 26 | this.sortedAspectInfoList = sortAspectInfoList(aspectInfoList); 27 | } 28 | 29 | /** 30 | * 按照order的值进行升序排序,确保order值小的aspect先被织入 31 | * 32 | * @param aspectInfoList 切面集合 33 | * @return 排序好的切面集合 34 | */ 35 | private List sortAspectInfoList(List aspectInfoList) { 36 | aspectInfoList.sort(new Comparator() { 37 | @Override 38 | public int compare(AspectInfo o1, AspectInfo o2) { 39 | return o1.getOrderIndex() - o2.getOrderIndex(); 40 | } 41 | }); 42 | return aspectInfoList; 43 | } 44 | 45 | /** 46 | * 横切逻辑 47 | * 48 | * @param proxy 49 | * @param method 50 | * @param args 51 | * @param methodProxy 52 | * @return 53 | * @throws Throwable 54 | */ 55 | @Override 56 | public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { 57 | Object returnValue = null; 58 | collectAccurateMatchedAspectList(method); 59 | if (ValidationUtil.isEmpty(sortedAspectInfoList)) { 60 | returnValue = methodProxy.invokeSuper(proxy, args); 61 | return returnValue; 62 | } 63 | // 1. 按照order的顺序升序执行完所有Aspect的before方法 64 | invokeBeforeAdvices(method, args); 65 | try { 66 | // 2. 执行被代理类的方法 67 | returnValue = methodProxy.invokeSuper(proxy, args); 68 | // 3. 如果被代理方法正常返回,则按照order的顺序降序执行完所有Aspect的afterReturning方法 69 | returnValue = invokeAfterReturningAdvices(method, args, returnValue); 70 | } catch (Exception e) { 71 | // 4. 如果被代理方法抛出异常,则按照order的顺序降序执行完所有Aspect的afterThrowing方法 72 | invokeAfterThrowingAdvices(method, args, e); 73 | } 74 | return returnValue; 75 | } 76 | 77 | /** 78 | * 精筛特定的方法 79 | * 80 | * @param method 81 | */ 82 | private void collectAccurateMatchedAspectList(Method method) { 83 | if (ValidationUtil.isEmpty(sortedAspectInfoList)) return; 84 | // for-each不支持动态移除元素,改用迭代器 85 | Iterator iterator = sortedAspectInfoList.iterator(); 86 | while (iterator.hasNext()) { 87 | AspectInfo aspectInfo = iterator.next(); 88 | if (!aspectInfo.getPointcutLocator().accurateMatches(method)) { 89 | iterator.remove(); 90 | } 91 | } 92 | } 93 | 94 | /** 95 | * 如果被代理方法抛出异常,则按照order的顺序降序执行完所有Aspect的afterThrowing方法 96 | * 97 | * @param method 98 | * @param args 99 | * @param e 100 | */ 101 | private void invokeAfterThrowingAdvices(Method method, Object[] args, Exception e) throws Throwable { 102 | for (int i = sortedAspectInfoList.size() - 1; i >= 0; i--) { 103 | sortedAspectInfoList.get(i).getAspectObject().afterThrowing(targetClass, method, args, e); 104 | } 105 | } 106 | 107 | /** 108 | * 如果被代理方法正常返回,则按照order的顺序降序执行完所有Aspect的afterReturning方法 109 | * 110 | * @param method 111 | * @param args 112 | * @param returnValue 113 | * @return 114 | */ 115 | private Object invokeAfterReturningAdvices(Method method, Object[] args, Object returnValue) throws Throwable { 116 | Object result = null; 117 | for (int i = sortedAspectInfoList.size() - 1; i >= 0; i--) { 118 | result = sortedAspectInfoList.get(i).getAspectObject().afterReturning(targetClass, method, args, returnValue); 119 | } 120 | return result; 121 | } 122 | 123 | /** 124 | * 按照order的顺序升序执行完所有Aspect的before方法 125 | * 126 | * @param method 127 | * @param args 128 | * @throws Throwable 129 | */ 130 | private void invokeBeforeAdvices(Method method, Object[] args) throws Throwable { 131 | for (AspectInfo aspectInfo : sortedAspectInfoList) { 132 | aspectInfo.getAspectObject().before(targetClass, method, args); 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/aop/AspectWeaver.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.aop; 2 | 3 | import com.enndfp.simpleframework.aop.annotation.Aspect; 4 | import com.enndfp.simpleframework.aop.annotation.Order; 5 | import com.enndfp.simpleframework.aop.aspect.AspectInfo; 6 | import com.enndfp.simpleframework.aop.aspect.DefaultAspect; 7 | import com.enndfp.simpleframework.aop.aspect.PointcutLocator; 8 | import com.enndfp.simpleframework.core.BeanContainer; 9 | import com.enndfp.simpleframework.utils.ValidationUtil; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.Set; 14 | 15 | /** 16 | * @author Enndfp 17 | */ 18 | public class AspectWeaver { 19 | private final BeanContainer beanContainer; 20 | 21 | public AspectWeaver() { 22 | this.beanContainer = BeanContainer.getInstance(); 23 | } 24 | 25 | /** 26 | * 基于Aspectj实现 27 | */ 28 | public void doAOP() { 29 | // 1. 获取所有的切面类 30 | Set> aspectSet = beanContainer.getClassesByAnnotation(Aspect.class); 31 | if (ValidationUtil.isEmpty(aspectSet)) return; 32 | // 2. 拼装AspectInfoList 33 | List aspectInfoList = packAspectInfoList(aspectSet); 34 | // 3. 遍历容器中的类 35 | Set> classSet = beanContainer.getClasses(); 36 | for (Class targetClass : classSet) { 37 | // 排除AspectClass自身 38 | if (targetClass.isAnnotationPresent(Aspect.class)) continue; 39 | // 4. 粗筛符合条件的Aspect 40 | List roughMatchedAspectList = collectRoughMatchedAspectListForSpecificClass(aspectInfoList, targetClass); 41 | // 5. 尝试进行Aspect织入 42 | wrapIfNecessary(roughMatchedAspectList, targetClass); 43 | } 44 | } 45 | 46 | /** 47 | * 进行Aspect织入 48 | * 49 | * @param roughMatchedAspectList 50 | * @param targetClass 51 | */ 52 | private void wrapIfNecessary(List roughMatchedAspectList, Class targetClass) { 53 | // 1. 校验 54 | if (ValidationUtil.isEmpty(roughMatchedAspectList)) return; 55 | // 2. 创建动态代理对象 56 | AspectListExecutor aspectListExecutor = new AspectListExecutor(targetClass, roughMatchedAspectList); 57 | Object proxyBean = ProxyCreator.createProxy(targetClass, aspectListExecutor); 58 | // 3. 将动态代理对象实例添加到容器中,取代未被代理前的类实例 59 | beanContainer.addBean(targetClass, proxyBean); 60 | } 61 | 62 | /** 63 | * 粗筛符合表达式的特定类 64 | * 65 | * @param aspectInfoList 66 | * @param targetClass 67 | * @return 68 | */ 69 | private List collectRoughMatchedAspectListForSpecificClass(List aspectInfoList, Class targetClass) { 70 | List roughMatchedAspectList = new ArrayList<>(); 71 | for (AspectInfo aspectInfo : aspectInfoList) { 72 | // 将符合表达式的类加入到集合 73 | if (aspectInfo.getPointcutLocator().roughMatches(targetClass)) { 74 | roughMatchedAspectList.add(aspectInfo); 75 | } 76 | } 77 | return roughMatchedAspectList; 78 | } 79 | 80 | /** 81 | * 将所有被@Aspect标记的类拼接成List集合 82 | * 83 | * @param aspectSet 被标记的类集合 84 | * @return 85 | */ 86 | private List packAspectInfoList(Set> aspectSet) { 87 | List aspectInfoList = new ArrayList<>(); 88 | for (Class aspectClass : aspectSet) { 89 | if (!verifyAspect(aspectClass)) { 90 | throw new RuntimeException("@Aspect and @Order must be added to the Aspect class, and Aspect class must extend from DefaultAspect"); 91 | } 92 | // 1. 获取标签值 93 | Order orderTag = aspectClass.getAnnotation(Order.class); 94 | Aspect aspectTag = aspectClass.getAnnotation(Aspect.class); 95 | // 2. 从容器中获取aspect实例 96 | DefaultAspect defaultAspect = (DefaultAspect) beanContainer.getBean(aspectClass); 97 | // 3. 初始化表达式定位器 98 | PointcutLocator pointcutLocator = new PointcutLocator(aspectTag.pointcut()); 99 | AspectInfo aspectInfo = new AspectInfo(orderTag.value(), defaultAspect, pointcutLocator); 100 | aspectInfoList.add(aspectInfo); 101 | } 102 | return aspectInfoList; 103 | } 104 | 105 | /** 106 | * 验证被Aspect标记的类是否满足条件 107 | * 108 | * @param aspectClass Aspect标记的类 109 | * @return 110 | */ 111 | private boolean verifyAspect(Class aspectClass) { 112 | return aspectClass.isAnnotationPresent(Aspect.class) && 113 | aspectClass.isAnnotationPresent(Order.class) && 114 | DefaultAspect.class.isAssignableFrom(aspectClass); 115 | } 116 | 117 | // public void doAOP() { 118 | // // 1. 获取所有的切面类 119 | // Set> aspectSet = beanContainer.getClassesByAnnotation(Aspect.class); 120 | // // 2. 将切面类按照不同的织入目标进行分类 121 | // Map, List> categorizedWeavingTargetMap = new HashMap<>(); 122 | // if (ValidationUtil.isEmpty(aspectSet)) return; 123 | // for (Class aspectClass : aspectSet) { 124 | // if (!verifyAspect(aspectClass)) { 125 | // throw new RuntimeException("@Aspect and @Order have not been added to the Aspect class, " + 126 | // "or Aspect class does not extend from DefaultAspect, or the value in Aspect Tag equals @Aspect"); 127 | // } 128 | // categorizeAspect(categorizedWeavingTargetMap, aspectClass); 129 | // } 130 | // // 3. 按照不同的织入目标分别去按序织入Aspect 131 | // if (ValidationUtil.isEmpty(categorizedWeavingTargetMap)) return; 132 | // for (Class weavingTarget : categorizedWeavingTargetMap.keySet()) { 133 | // weaveByWeavingTarget(weavingTarget, categorizedWeavingTargetMap.get(weavingTarget)); 134 | // } 135 | // } 136 | 137 | // /** 138 | // * 按照不同的织入目标分别去按序织入Aspect 139 | // * 140 | // * @param weavingTarget 织入目标 141 | // * @param aspectInfoList 切面类的集合 142 | // */ 143 | // private void weaveByWeavingTarget(Class weavingTarget, List aspectInfoList) { 144 | // // 1. 获取被代理类的集合 145 | // Set> targetClassSet = beanContainer.getClassesByAnnotation(weavingTarget); 146 | // if (ValidationUtil.isEmpty(targetClassSet)) return; 147 | // // 2. 遍历被代理类,分别为每个被代理类生成动态代理 148 | // for (Class targetClass : targetClassSet) { 149 | // // 创建动态代理对象 150 | // AspectListExecutor aspectListExecutor = new AspectListExecutor(targetClass, aspectInfoList); 151 | // Object proxyBean = ProxyCreator.createProxy(targetClass, aspectListExecutor); 152 | // // 3. 将动态代理对象实例添加到容器中,取代未被代理前的类实例 153 | // beanContainer.addBean(targetClass, proxyBean); 154 | // } 155 | // } 156 | // 157 | // /** 158 | // * 将切面类按照不同的织入目标进行分类 159 | // * 160 | // * @param categorizedWeavingTargetMap 分类完成的织入目标Map 161 | // * @param aspectClass 待分类的切面类 162 | // */ 163 | // private void categorizeAspect(Map, List> categorizedWeavingTargetMap, Class aspectClass) { 164 | // // 1. 获取标签值 165 | // Order orderTag = aspectClass.getAnnotation(Order.class); 166 | // Aspect aspectTag = aspectClass.getAnnotation(Aspect.class); 167 | // // 2. 从容器中获取aspect实例 168 | // DefaultAspect aspect = (DefaultAspect) beanContainer.getBean(aspectClass); 169 | // AspectInfo aspectInfo = new AspectInfo(orderTag.value(), aspect); 170 | // // 3. 将切面类加入到对应的位置的Map 171 | // if (!categorizedWeavingTargetMap.containsKey(aspectTag.value())) { 172 | // categorizedWeavingTargetMap.put(aspectTag.value(), new ArrayList<>()); 173 | // } 174 | // categorizedWeavingTargetMap.get(aspectTag.value()).add(aspectInfo); 175 | // } 176 | // 177 | // /** 178 | // * 验证被Aspect标记的类是否满足条件 179 | // * 180 | // * @param aspectClass Aspect标记的类 181 | // * @return 182 | // */ 183 | // private boolean verifyAspect(Class aspectClass) { 184 | // return aspectClass.isAnnotationPresent(Aspect.class) && 185 | // aspectClass.isAnnotationPresent(Order.class) && 186 | // DefaultAspect.class.isAssignableFrom(aspectClass) && 187 | // aspectClass.getAnnotation(Aspect.class).value() != Aspect.class; 188 | // } 189 | } 190 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/aop/ProxyCreator.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.aop; 2 | 3 | import net.sf.cglib.proxy.Enhancer; 4 | import net.sf.cglib.proxy.MethodInterceptor; 5 | 6 | /** 7 | * @author Enndfp 8 | */ 9 | public class ProxyCreator { 10 | /** 11 | * 创建动态代理对象并返回 12 | * 13 | * @param targetClass 被代理的Class对象 14 | * @param methodInterceptor 方法拦截器 15 | * @return 16 | */ 17 | public static Object createProxy(Class targetClass, MethodInterceptor methodInterceptor) { 18 | return Enhancer.create(targetClass, methodInterceptor); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/aop/annotation/Aspect.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.aop.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * @author Enndfp 7 | */ 8 | @Target(ElementType.TYPE) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface Aspect { 11 | 12 | // 切入点 13 | String pointcut(); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/aop/annotation/Order.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.aop.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * @author Enndfp 7 | */ 8 | @Target(ElementType.TYPE) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface Order { 11 | 12 | // 控制类的执行顺序,值越小优先级越高 13 | int value(); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/aop/aspect/AspectInfo.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.aop.aspect; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | /** 7 | * @author Enndfp 8 | */ 9 | @AllArgsConstructor 10 | @Getter 11 | public class AspectInfo { 12 | 13 | private int orderIndex; 14 | 15 | private DefaultAspect aspectObject; 16 | 17 | private PointcutLocator pointcutLocator; 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/aop/aspect/DefaultAspect.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.aop.aspect; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | /** 6 | * @author Enndfp 7 | */ 8 | public abstract class DefaultAspect { 9 | 10 | /** 11 | * 事前拦截 12 | * 13 | * @param targetClass 被代理的目标类 14 | * @param method 被代理的目标方法 15 | * @param args 被代理的目标方法对应的参数列表 16 | * @throws Throwable 异常 17 | */ 18 | public void before(Class targetClass, Method method, Object[] args) throws Throwable { 19 | 20 | } 21 | 22 | /** 23 | * 正常返回后拦截 24 | * 25 | * @param targetClass 被代理的目标类 26 | * @param method 被代理的目标方法 27 | * @param args 被代理的目标方法对应的参数列表 28 | * @param returnValue 被代理的目标方法执行后的返回值 29 | * @return 返回值 30 | * @throws Throwable 异常 31 | */ 32 | public Object afterReturning(Class targetClass, Method method, Object[] args, Object returnValue) throws Throwable { 33 | return returnValue; 34 | } 35 | 36 | /** 37 | * 发生异常时拦截 38 | * 39 | * @param targetClass 被代理的目标类 40 | * @param method 被代理的目标方法 41 | * @param args 被代理的目标方法对应的参数列表 42 | * @param e 被代理的目标方法抛出的异常 43 | * @return 返回值 44 | * @throws Throwable 异常 45 | */ 46 | public void afterThrowing(Class targetClass, Method method, Object[] args, Throwable e) throws Throwable { 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/aop/aspect/PointcutLocator.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.aop.aspect; 2 | 3 | import org.aspectj.weaver.tools.PointcutExpression; 4 | import org.aspectj.weaver.tools.PointcutParser; 5 | import org.aspectj.weaver.tools.ShadowMatch; 6 | 7 | import java.lang.reflect.Method; 8 | 9 | /** 10 | * 解析Aspect表达式并且定位被织入的目标 11 | * 12 | * @author Enndfp 13 | */ 14 | public class PointcutLocator { 15 | 16 | // Pointcut解析器,使其支持Aspect所有表达式的解析语法 17 | private PointcutParser pointcutParser = PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution( 18 | PointcutParser.getAllSupportedPointcutPrimitives() 19 | ); 20 | 21 | // 表达式的解析 22 | private PointcutExpression pointcutExpression; 23 | 24 | public PointcutLocator(String expression) { 25 | this.pointcutExpression = pointcutParser.parsePointcutExpression(expression); 26 | } 27 | 28 | /** 29 | * 判断传入的Class对象是否是Aspect的目标代理类,即匹配Pointcut表达式(初筛) 30 | * 31 | * @param targetClass 目标类 32 | * @return 是否匹配 33 | */ 34 | public boolean roughMatches(Class targetClass) { 35 | // 只能校验within,不能校验execution、call、get、set,面对无法校验的表达式,直接返回true 36 | return pointcutExpression.couldMatchJoinPointsInType(targetClass); 37 | } 38 | 39 | /** 40 | * 判断传入的Method对象是否是Aspect的目标代理方法,即匹配Pointcut表达式(精筛) 41 | * 42 | * @param method 目标代理方法 43 | * @return 是否匹配 44 | */ 45 | public boolean accurateMatches(Method method) { 46 | ShadowMatch shadowMatch = pointcutExpression.matchesMethodExecution(method); 47 | return shadowMatch.alwaysMatches(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/core/BeanContainer.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.core; 2 | 3 | import com.enndfp.simpleframework.aop.annotation.Aspect; 4 | import com.enndfp.simpleframework.core.annotation.Component; 5 | import com.enndfp.simpleframework.core.annotation.Controller; 6 | import com.enndfp.simpleframework.core.annotation.Repository; 7 | import com.enndfp.simpleframework.core.annotation.Service; 8 | import com.enndfp.simpleframework.utils.ClassUtil; 9 | import com.enndfp.simpleframework.utils.ValidationUtil; 10 | import lombok.AccessLevel; 11 | import lombok.NoArgsConstructor; 12 | import lombok.extern.slf4j.Slf4j; 13 | 14 | import java.lang.annotation.Annotation; 15 | import java.util.*; 16 | import java.util.concurrent.ConcurrentHashMap; 17 | 18 | /** 19 | * @author Enndfp 20 | */ 21 | @Slf4j 22 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 23 | public class BeanContainer { 24 | 25 | // 存放所有被标记的目标对象 26 | private final Map, Object> beanMap = new ConcurrentHashMap<>(); 27 | 28 | // 加载Bean的注解列表 29 | private static final List> BEAN_ANNOTATION 30 | = Arrays.asList(Component.class, Controller.class, Service.class, Repository.class, Aspect.class); 31 | 32 | // 容器是否已经加载过Bean 33 | private boolean loaded = false; 34 | 35 | /** 36 | * 枚举类型的饿汉式单例 37 | */ 38 | private enum ContainerHolder { 39 | HOLDER; 40 | private final BeanContainer instance; 41 | 42 | ContainerHolder() { 43 | instance = new BeanContainer(); 44 | } 45 | } 46 | 47 | /** 48 | * 获取是否已经加载过Bean 49 | * 50 | * @return 51 | */ 52 | public boolean isLoaded() { 53 | return loaded; 54 | } 55 | 56 | /** 57 | * 获取Bean容器实例 58 | * 59 | * @return 60 | */ 61 | public static BeanContainer getInstance() { 62 | return ContainerHolder.HOLDER.instance; 63 | } 64 | 65 | /** 66 | * 获取Bean实例的数量 67 | * 68 | * @return 69 | */ 70 | public int size() { 71 | return beanMap.size(); 72 | } 73 | 74 | /** 75 | * 扫描加载所有Bean 76 | * 77 | * @param packageName 包名 78 | */ 79 | public synchronized void loadBeans(String packageName) { 80 | // 1. 判断容器是否已经加载过 81 | if (isLoaded()) { 82 | log.warn("BeanContainer has been loaded."); 83 | return; 84 | } 85 | 86 | // 2. 获取指定包名的class对象集合 87 | Set> classSet = ClassUtil.extractPackageClass(packageName); 88 | if (ValidationUtil.isEmpty(classSet)) { 89 | log.warn("extract nothing from packageName " + packageName); 90 | return; 91 | } 92 | 93 | // 3. 实例化指定注解的class对象 94 | for (Class clazz : classSet) { 95 | for (Class annotation : BEAN_ANNOTATION) { 96 | // 类上面标记了定义的注解 97 | if (clazz.isAnnotationPresent(annotation)) { 98 | // 将目标类作为键,对应的实例作为值,放入到beanMap中 99 | beanMap.put(clazz, ClassUtil.newInstance(clazz, true)); 100 | } 101 | } 102 | } 103 | 104 | // 4. 修改容器是否被加载的标记 105 | loaded = true; 106 | } 107 | 108 | /** 109 | * 添加Bean实例 110 | * 111 | * @param clazz Class对象 112 | * @param bean Bean实例 113 | * @return 添加的Bean实例,没有则返回null 114 | */ 115 | public Object addBean(Class clazz, Object bean) { 116 | return beanMap.put(clazz, bean); 117 | } 118 | 119 | /** 120 | * 删除Bean实例 121 | * 122 | * @param clazz Class对象 123 | * @return 删除的Bean实例,没有则返回null 124 | */ 125 | public Object removeBean(Class clazz) { 126 | return beanMap.remove(clazz); 127 | } 128 | 129 | /** 130 | * 获取Bean实例 131 | * 132 | * @param clazz Class对象 133 | * @return Bean实例 134 | */ 135 | public Object getBean(Class clazz) { 136 | return beanMap.get(clazz); 137 | } 138 | 139 | /** 140 | * 获取所有Class对象的集合 141 | * 142 | * @return Class集合 143 | */ 144 | public Set> getClasses() { 145 | return beanMap.keySet(); 146 | } 147 | 148 | /** 149 | * 获取所有Bean集合 150 | * 151 | * @return Bean集合 152 | */ 153 | public Set getBeans() { 154 | return new HashSet<>(beanMap.values()); 155 | } 156 | 157 | /** 158 | * 根据注解获取Bean的Class集合 159 | * 160 | * @param annotation 注解 161 | * @return Class集合 162 | */ 163 | public Set> getClassesByAnnotation(Class annotation) { 164 | // 1. 获取beanMap的所有class对象 165 | Set> keySet = getClasses(); 166 | if (ValidationUtil.isEmpty(keySet)) { 167 | log.warn("nothing in beanMap"); 168 | return null; 169 | } 170 | 171 | // 2. 获取指定注解的class对象,并添加到classSet中 172 | Set> classSet = new HashSet<>(); 173 | for (Class clazz : keySet) { 174 | if (clazz.isAnnotationPresent(annotation)) { 175 | classSet.add(clazz); 176 | } 177 | } 178 | return classSet.size() > 0 ? classSet : null; 179 | } 180 | 181 | /** 182 | * 通过接口或者父类获取实现类或者子类的Class集合,不包括其本身 183 | * 184 | * @param interfaceOrClass 接口或者父类 185 | * @return Class集合 186 | */ 187 | public Set> getClassesBySuper(Class interfaceOrClass) { 188 | // 1. 获取beanMap的所有class对象 189 | Set> keySet = getClasses(); 190 | if (ValidationUtil.isEmpty(keySet)) { 191 | log.warn("nothing in beanMap"); 192 | return null; 193 | } 194 | 195 | // 2. 获取指定接口或父类的class对象,并添加到classSet中 196 | Set> classSet = new HashSet<>(); 197 | for (Class clazz : keySet) { 198 | if (interfaceOrClass.isAssignableFrom(clazz) && !clazz.equals(interfaceOrClass)) { 199 | classSet.add(clazz); 200 | } 201 | } 202 | return classSet.size() > 0 ? classSet : null; 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/core/annotation/Component.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.core.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * @author Enndfp 10 | */ 11 | @Target(ElementType.TYPE) // 作用于类 12 | @Retention(RetentionPolicy.RUNTIME) // 运行时有效 13 | public @interface Component { 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/core/annotation/Controller.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.core.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * @author Enndfp 10 | */ 11 | @Target(ElementType.TYPE) // 作用于类 12 | @Retention(RetentionPolicy.RUNTIME) // 运行时有效 13 | public @interface Controller { 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/core/annotation/Repository.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.core.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * @author Enndfp 10 | */ 11 | @Target(ElementType.TYPE) // 作用于类 12 | @Retention(RetentionPolicy.RUNTIME) // 运行时有效 13 | public @interface Repository { 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/core/annotation/Service.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.core.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * @author Enndfp 10 | */ 11 | @Target(ElementType.TYPE) // 作用于类 12 | @Retention(RetentionPolicy.RUNTIME) // 运行时有效 13 | public @interface Service { 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/inject/DependencyInjector.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.inject; 2 | 3 | import com.enndfp.simpleframework.core.BeanContainer; 4 | import com.enndfp.simpleframework.inject.annotation.Autowired; 5 | import com.enndfp.simpleframework.utils.ClassUtil; 6 | import com.enndfp.simpleframework.utils.ValidationUtil; 7 | import lombok.extern.slf4j.Slf4j; 8 | 9 | import java.lang.reflect.Field; 10 | import java.util.Set; 11 | 12 | /** 13 | * @author Enndfp 14 | */ 15 | @Slf4j 16 | public class DependencyInjector { 17 | 18 | // Bean容器 19 | private final BeanContainer beanContainer; 20 | 21 | public DependencyInjector() { 22 | beanContainer = BeanContainer.getInstance(); 23 | } 24 | 25 | /** 26 | * 执行IOC 27 | */ 28 | public void doIOC() { 29 | Set> classSet = beanContainer.getClasses(); 30 | if (ValidationUtil.isEmpty(classSet)) { 31 | log.warn("empty classSet in beanContainer"); 32 | return; 33 | } 34 | // 1. 遍历Bean容器中所有的Class对象 35 | for (Class clazz : classSet) { 36 | // 2. 遍历Class对象的所有成员变量 37 | Field[] fields = clazz.getDeclaredFields(); 38 | if (ValidationUtil.isEmpty(fields)) { 39 | continue; 40 | } 41 | for (Field field : fields) { 42 | // 3. 找出被Autowired标记的成员变量 43 | if (field.isAnnotationPresent(Autowired.class)) { 44 | Autowired autowired = field.getAnnotation(Autowired.class); 45 | String autowiredValue = autowired.value(); 46 | 47 | // 4. 获取这些变量的类型 48 | Class fieldClass = field.getType(); 49 | // 5. 根据类型在容器中找到对应的实例 50 | Object fieldInstance = getFieldInstance(fieldClass, autowiredValue); 51 | if (fieldInstance == null) { 52 | throw new RuntimeException("unable to inject relevant type, target fieldClass is :" 53 | + fieldClass.getName() + "autowiredValue is " + autowiredValue); 54 | } 55 | 56 | // 6. 通过反射将对应的成员变量实例注入到相应位置 57 | Object targetBean = beanContainer.getBean(clazz); 58 | ClassUtil.setField(field, targetBean, fieldInstance, true); 59 | } 60 | } 61 | } 62 | 63 | } 64 | 65 | /** 66 | * 根据Class对象获取beanContainer里的实例 67 | * 68 | * @param fieldClass Class对象 69 | * @param autowiredValue 指定注入的具体类型 70 | * @return Bean实例 71 | */ 72 | private Object getFieldInstance(Class fieldClass, String autowiredValue) { 73 | // 1. 从容器中获取bean 74 | Object fieldInstance = beanContainer.getBean(fieldClass); 75 | if (fieldInstance != null) return fieldInstance; 76 | 77 | // 2. 如果是接口类型则根据实现类获取bean 78 | Class implementedClass = getImplementClass(fieldClass, autowiredValue); 79 | if (implementedClass == null) return null; 80 | return beanContainer.getBean(implementedClass); 81 | } 82 | 83 | /** 84 | * 根据Class对象获取接口的实现类 85 | * 86 | * @param fieldClass Class对象 87 | * @param autowiredValue 指定注入的具体类型 88 | * @return 实现类 89 | */ 90 | private Class getImplementClass(Class fieldClass, String autowiredValue) { 91 | Set> classSet = beanContainer.getClassesBySuper(fieldClass); 92 | if (ValidationUtil.isEmpty(classSet)) return null; 93 | 94 | // 针对有多个实现类,根据类型则无法判断注入哪个实例,则根据指定的类型值 95 | // 1. 用户没有指定类型值 96 | if (ValidationUtil.isEmpty(autowiredValue)) { 97 | // 有多个实现类,报错 98 | if (classSet.size() > 1) { 99 | throw new RuntimeException("multiple implemented classes for " 100 | + fieldClass.getName() + " please set @Autowired's value to pick one: " + classSet); 101 | } 102 | // 只有一个实现类,返回 103 | return classSet.iterator().next(); 104 | } 105 | // 2. 用户指定了类型值 106 | for (Class clazz : classSet) { 107 | if (autowiredValue.equals(clazz.getSimpleName())) { 108 | return clazz; 109 | } 110 | } 111 | return null; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/inject/annotation/Autowired.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.inject.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 仅支持成员变量注入 10 | * 11 | * @author Enndfp 12 | */ 13 | @Target(ElementType.FIELD) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | public @interface Autowired { 16 | String value() default ""; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/mvc/DispatcherServlet.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.mvc; 2 | 3 | import com.enndfp.simpleframework.aop.AspectWeaver; 4 | import com.enndfp.simpleframework.core.BeanContainer; 5 | import com.enndfp.simpleframework.inject.DependencyInjector; 6 | import com.enndfp.simpleframework.mvc.processor.RequestProcessor; 7 | import com.enndfp.simpleframework.mvc.processor.impl.ControllerRequestProcessor; 8 | import com.enndfp.simpleframework.mvc.processor.impl.JspRequestProcessor; 9 | import com.enndfp.simpleframework.mvc.processor.impl.PreRequestProcessor; 10 | import com.enndfp.simpleframework.mvc.processor.impl.StaticResourceRequestProcessor; 11 | 12 | import javax.servlet.ServletException; 13 | import javax.servlet.annotation.WebServlet; 14 | import javax.servlet.http.HttpServlet; 15 | import javax.servlet.http.HttpServletRequest; 16 | import javax.servlet.http.HttpServletResponse; 17 | import java.io.IOException; 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | /** 22 | * @author Enndfp 23 | */ 24 | @WebServlet("/*") 25 | public class DispatcherServlet extends HttpServlet { 26 | 27 | List PROCESSOR = new ArrayList<>(); 28 | 29 | @Override 30 | public void init() throws ServletException { 31 | // 1. 初始化容器 32 | BeanContainer beanContainer = BeanContainer.getInstance(); 33 | beanContainer.loadBeans("com.enndfp.demo"); 34 | new AspectWeaver().doAOP(); 35 | new DependencyInjector().doIOC(); 36 | // 2. 初始化请求处理器责任链 37 | PROCESSOR.add(new PreRequestProcessor()); 38 | PROCESSOR.add(new StaticResourceRequestProcessor(getServletContext())); 39 | PROCESSOR.add(new JspRequestProcessor(getServletContext())); 40 | PROCESSOR.add(new ControllerRequestProcessor()); 41 | } 42 | 43 | @Override 44 | protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 45 | // 1. 创建责任链实例对象 46 | RequestProcessorChain requestProcessorChain = new RequestProcessorChain(PROCESSOR.iterator(), req, resp); 47 | // 2. 通过责任链模式来依次调用请求处理器对请求进行处理 48 | requestProcessorChain.doRequestProcessorChain(); 49 | // 3. 对处理结果进行渲染 50 | requestProcessorChain.doRender(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/mvc/RequestProcessorChain.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.mvc; 2 | 3 | import com.enndfp.simpleframework.mvc.processor.RequestProcessor; 4 | import com.enndfp.simpleframework.mvc.render.ResultRender; 5 | import com.enndfp.simpleframework.mvc.render.impl.DefaultResultRender; 6 | import com.enndfp.simpleframework.mvc.render.impl.InternalErrorResultRender; 7 | import lombok.Data; 8 | import lombok.extern.slf4j.Slf4j; 9 | 10 | import javax.servlet.http.HttpServletRequest; 11 | import javax.servlet.http.HttpServletResponse; 12 | import java.util.Iterator; 13 | 14 | /** 15 | * 1. 以责任链的模式执行注册的请求处理器 16 | * 2. 委派给特定的Render实例对处理后的结果进行渲染 17 | * 18 | * @author Enndfp 19 | */ 20 | @Slf4j 21 | @Data 22 | public class RequestProcessorChain { 23 | 24 | // 请求处理器的迭代器 25 | private Iterator requestProcessorIterator; 26 | 27 | // 请求request 28 | private HttpServletRequest request; 29 | 30 | // 响应response 31 | private HttpServletResponse response; 32 | 33 | // http请求方法 34 | private String requestMethod; 35 | 36 | // http请求路径 37 | private String requestPath; 38 | 39 | // http响应状态码 40 | private int responseCode; 41 | 42 | // 请求结果渲染器 43 | private ResultRender resultRender; 44 | 45 | public RequestProcessorChain(Iterator iterator, HttpServletRequest req, HttpServletResponse resp) { 46 | this.requestProcessorIterator = iterator; 47 | this.request = req; 48 | this.response = resp; 49 | this.requestMethod = req.getMethod(); 50 | this.requestPath = req.getPathInfo(); 51 | this.responseCode = HttpServletResponse.SC_OK; 52 | } 53 | 54 | /** 55 | * 以责任链的模式执行请求链 56 | */ 57 | public void doRequestProcessorChain() { 58 | try { 59 | // 1. 通过迭代器遍历注册的请求处理器实现类列表 60 | while (requestProcessorIterator.hasNext()) { 61 | // 2. 直到某个请求处理器执行后返回false为止 62 | if (!requestProcessorIterator.next().process(this)) { 63 | break; 64 | } 65 | } 66 | } catch (Exception e) { 67 | // 3. 期间如果出现异常,则交由内部异常渲染器处理 68 | this.resultRender = new InternalErrorResultRender(e.getMessage()); 69 | log.error("doRequestProcessorChain error: ", e); 70 | } 71 | } 72 | 73 | /** 74 | * 执行渲染器 75 | */ 76 | public void doRender() { 77 | // 1. 如果请求处理器实现类均未选择合适的渲染器,则使用默认的 78 | if (this.resultRender == null) { 79 | this.resultRender = new DefaultResultRender(); 80 | } 81 | // 2. 调用渲染器的render方法对结果进行渲染 82 | try { 83 | this.resultRender.render(this); 84 | } catch (Exception e) { 85 | log.error("doRender error: ", e); 86 | throw new RuntimeException(e); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/mvc/annotation/RequestMapping.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.mvc.annotation; 2 | 3 | import com.enndfp.simpleframework.mvc.type.RequestMethod; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * 标识Controller的方法与请求路径和请求方法的映射关系 12 | * 13 | * @author Enndfp 14 | */ 15 | @Target({ElementType.TYPE, ElementType.METHOD}) 16 | @Retention(RetentionPolicy.RUNTIME) 17 | public @interface RequestMapping { 18 | // 请求路径 19 | String value() default ""; 20 | 21 | // 请求方法 22 | RequestMethod method() default RequestMethod.GET; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/mvc/annotation/RequestParam.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.mvc.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 请求的方法参数名称 10 | * 11 | * @author Enndfp 12 | */ 13 | @Target(ElementType.PARAMETER) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | public @interface RequestParam { 16 | // 方法参数名称 17 | String value() default ""; 18 | 19 | // 该参数是否必须 20 | boolean required() default true; 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/mvc/annotation/ResponseBody.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.mvc.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 用于标记返回json格式 10 | * 11 | * @author Enndfp 12 | */ 13 | @Target(ElementType.METHOD) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | public @interface ResponseBody { 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/mvc/processor/RequestProcessor.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.mvc.processor; 2 | 3 | import com.enndfp.simpleframework.mvc.RequestProcessorChain; 4 | 5 | /** 6 | * 请求执行器 7 | * @author Enndfp 8 | */ 9 | public interface RequestProcessor { 10 | 11 | boolean process(RequestProcessorChain requestProcessorChain) throws Exception; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/mvc/processor/impl/ControllerRequestProcessor.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.mvc.processor.impl; 2 | 3 | import com.enndfp.simpleframework.core.BeanContainer; 4 | import com.enndfp.simpleframework.mvc.RequestProcessorChain; 5 | import com.enndfp.simpleframework.mvc.annotation.RequestMapping; 6 | import com.enndfp.simpleframework.mvc.annotation.RequestParam; 7 | import com.enndfp.simpleframework.mvc.annotation.ResponseBody; 8 | import com.enndfp.simpleframework.mvc.processor.RequestProcessor; 9 | import com.enndfp.simpleframework.mvc.render.ResultRender; 10 | import com.enndfp.simpleframework.mvc.render.impl.JsonResultRender; 11 | import com.enndfp.simpleframework.mvc.render.impl.ResourceNotFoundResultRender; 12 | import com.enndfp.simpleframework.mvc.render.impl.ViewResultRender; 13 | import com.enndfp.simpleframework.mvc.type.ControllerMethod; 14 | import com.enndfp.simpleframework.mvc.type.RequestPathInfo; 15 | import com.enndfp.simpleframework.utils.ConverterUtil; 16 | import com.enndfp.simpleframework.utils.ValidationUtil; 17 | import lombok.extern.slf4j.Slf4j; 18 | 19 | import javax.servlet.http.HttpServletRequest; 20 | import java.lang.reflect.InvocationTargetException; 21 | import java.lang.reflect.Method; 22 | import java.lang.reflect.Parameter; 23 | import java.util.*; 24 | import java.util.concurrent.ConcurrentHashMap; 25 | 26 | /** 27 | * Controller请求处理器 28 | * 29 | * @author Enndfp 30 | */ 31 | @Slf4j 32 | public class ControllerRequestProcessor implements RequestProcessor { 33 | 34 | // ICO容器 35 | private BeanContainer beanContainer; 36 | 37 | // 请求和Controller方法的映射Map 38 | private Map pathControllerMethodMap = new ConcurrentHashMap<>(); 39 | 40 | /** 41 | * 依靠容器的能力,建立起请求路径、请求方法与Controller方法实例的映射 42 | */ 43 | public ControllerRequestProcessor() { 44 | this.beanContainer = BeanContainer.getInstance(); 45 | Set> requestMappingSet = beanContainer.getClassesByAnnotation(RequestMapping.class); 46 | initPathControllerMethodMap(requestMappingSet); 47 | } 48 | 49 | /** 50 | * 初始化路径到控制器方法的映射 51 | * 52 | * @param requestMappingSet 53 | */ 54 | private void initPathControllerMethodMap(Set> requestMappingSet) { 55 | if (ValidationUtil.isEmpty(requestMappingSet)) return; 56 | 57 | for (Class requestMappingClass : requestMappingSet) { 58 | processClassMapping(requestMappingClass); 59 | } 60 | } 61 | 62 | /** 63 | * 处理类级别的 @RequestMapping 注解 64 | * 65 | * @param requestMappingClass 66 | */ 67 | private void processClassMapping(Class requestMappingClass) { 68 | // 1. 获取类上面的请求地址 69 | RequestMapping classRequestMapping = requestMappingClass.getAnnotation(RequestMapping.class); 70 | String classBasePath = classRequestMapping.value(); 71 | if (!classBasePath.startsWith("/")) { 72 | classBasePath = "/" + classBasePath; 73 | } 74 | 75 | // 2. 获取该类所有申明的方法 76 | Method[] methods = requestMappingClass.getDeclaredMethods(); 77 | if (ValidationUtil.isEmpty(methods)) return; 78 | 79 | for (Method method : methods) { 80 | processMethodMapping(requestMappingClass, classBasePath, method); 81 | } 82 | } 83 | 84 | /** 85 | * 处理方法级别的 @RequestMapping 注解 86 | * 87 | * @param requestMappingClass 88 | * @param classBasePath 89 | * @param method 90 | */ 91 | private void processMethodMapping(Class requestMappingClass, String classBasePath, Method method) { 92 | if (!method.isAnnotationPresent(RequestMapping.class)) return; 93 | 94 | // 1. 获取方法上的请求地址 95 | RequestMapping methodRequestMapping = method.getAnnotation(RequestMapping.class); 96 | String methodPath = methodRequestMapping.value(); 97 | if (!methodPath.startsWith("/")) { 98 | methodPath = "/" + methodPath; 99 | } 100 | // 1.1 拼接完整的请求地址 101 | String url = classBasePath + methodPath; 102 | 103 | // 2. 获取方法的参数列表 104 | Map> methodParamMap = new HashMap<>(); 105 | Parameter[] parameters = method.getParameters(); 106 | 107 | if (!ValidationUtil.isEmpty(parameters)){ 108 | for (Parameter parameter : parameters) { 109 | processParameterAnnotation(parameter, methodParamMap); 110 | } 111 | } 112 | 113 | // 3. 封装 RequestPathInfo 114 | String httpMethod = String.valueOf(methodRequestMapping.method()); 115 | RequestPathInfo requestPathInfo = new RequestPathInfo(httpMethod, url); 116 | // 4. 处理路径信息并将其添加到映射表中 117 | handlePathInfo(requestPathInfo, requestMappingClass, method, methodParamMap); 118 | } 119 | 120 | /** 121 | * 处理路径信息并将其添加到映射表中 122 | * 123 | * @param requestPathInfo 124 | * @param requestMappingClass 125 | * @param method 126 | * @param methodParamMap 127 | */ 128 | private void handlePathInfo(RequestPathInfo requestPathInfo, Class requestMappingClass, Method method, Map> methodParamMap) { 129 | if (pathControllerMethodMap.containsKey(requestPathInfo)) { 130 | log.warn("Duplicate URL:{} registration, current class {} method {} will override the former one", 131 | requestPathInfo.getHttpPath(), requestMappingClass.getName(), method.getName()); 132 | } 133 | ControllerMethod controllerMethod = new ControllerMethod(requestMappingClass, method, methodParamMap); 134 | pathControllerMethodMap.put(requestPathInfo, controllerMethod); 135 | } 136 | 137 | /** 138 | * 处理参数级别的 @RequestParam 注解 139 | * 140 | * @param parameter 141 | * @param methodParamMap 142 | */ 143 | private void processParameterAnnotation(Parameter parameter, Map> methodParamMap) { 144 | RequestParam requestParam = parameter.getAnnotation(RequestParam.class); 145 | // 简化实现,controller里所有的参数都需要标注注解 146 | if (requestParam == null) { 147 | throw new RuntimeException("The parameter must have @RequestParam annotation"); 148 | } 149 | methodParamMap.put(requestParam.value(), parameter.getType()); 150 | } 151 | 152 | @Override 153 | public boolean process(RequestProcessorChain requestProcessorChain) throws Exception { 154 | // 1. 解析HttpServletRequest的请求方法,请求路径,获取对应的ControllerMethod实例 155 | String requestMethod = requestProcessorChain.getRequestMethod(); 156 | String requestPath = requestProcessorChain.getRequestPath(); 157 | ControllerMethod controllerMethod = pathControllerMethodMap.get(new RequestPathInfo(requestMethod, requestPath)); 158 | if (controllerMethod == null) { 159 | requestProcessorChain.setResultRender(new ResourceNotFoundResultRender(requestMethod, requestPath)); 160 | return false; 161 | } 162 | 163 | // 2. 解析请求参数,并传递给获取到的ControllerMethod实例去执行 164 | Object result = invokeControllerMethod(controllerMethod, requestProcessorChain.getRequest()); 165 | 166 | // 3. 根据处理的结果,选择对应的render进行渲染 167 | setResultRender(result, controllerMethod, requestProcessorChain); 168 | return false; 169 | } 170 | 171 | /** 172 | * 根据不同情况设置不同的渲染器 173 | * 174 | * @param result 175 | * @param controllerMethod 176 | * @param requestProcessorChain 177 | */ 178 | private void setResultRender(Object result, ControllerMethod controllerMethod, RequestProcessorChain requestProcessorChain) { 179 | if (result == null) return; 180 | ResultRender resultRender; 181 | boolean isJson = controllerMethod.getInvokeMethod().isAnnotationPresent(ResponseBody.class); 182 | if (isJson) { 183 | resultRender = new JsonResultRender(result); 184 | } else { 185 | resultRender = new ViewResultRender(result); 186 | } 187 | requestProcessorChain.setResultRender(resultRender); 188 | } 189 | 190 | /** 191 | * 执行对应Controller的方法 192 | * 193 | * @param controllerMethod 194 | * @param request 195 | * @return 196 | */ 197 | private Object invokeControllerMethod(ControllerMethod controllerMethod, HttpServletRequest request) { 198 | // 1. 从请求里获取GET或者POST的参数名及其对应的值 199 | Map requestParamMap = new HashMap<>(); 200 | // GET,POST方法的请求参数获取方式 201 | Map parameterMap = request.getParameterMap(); 202 | for (Map.Entry parameter : parameterMap.entrySet()) { 203 | if (!ValidationUtil.isEmpty(parameter.getValue())) { 204 | // 只支持一个参数对应一个值的形式 205 | requestParamMap.put(parameter.getKey(), parameter.getValue()[0]); 206 | } 207 | } 208 | 209 | // 2. 根据获取到的请求参数名及其对应的值,以及controllerMethod里面的参数和类型的映射关系,去实例化出参数列表 210 | List methodParams = new ArrayList<>(); 211 | Map> methodParamMap = controllerMethod.getMethodParameters(); 212 | for (String paramName : methodParamMap.keySet()) { 213 | Class type = methodParamMap.get(paramName); 214 | String requestParamValue = requestParamMap.get(paramName); 215 | Object value; 216 | // 只支持String以及基础类型char,int,short,byte,double,long,float,boolean及它们的包装类型 217 | if (requestParamValue == null) { 218 | // 将请求里的参数值转成适配于参数类型的空值 219 | value = ConverterUtil.primitiveNull(type); 220 | } else { 221 | value = ConverterUtil.convert(type, requestParamValue); 222 | } 223 | methodParams.add(value); 224 | } 225 | 226 | // 3. 执行Controller里面对应的方法并返回结果 227 | Object controller = beanContainer.getBean(controllerMethod.getControllerClass()); 228 | Method invokeMethod = controllerMethod.getInvokeMethod(); 229 | invokeMethod.setAccessible(true); 230 | Object result; 231 | try { 232 | if (methodParams.size() == 0) { 233 | result = invokeMethod.invoke(controller); 234 | } else { 235 | result = invokeMethod.invoke(controller, methodParams.toArray()); 236 | } 237 | } catch (IllegalAccessException e) { 238 | throw new RuntimeException(e); 239 | } catch (InvocationTargetException e) { 240 | throw new RuntimeException(e.getTargetException()); 241 | } 242 | return result; 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/mvc/processor/impl/JspRequestProcessor.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.mvc.processor.impl; 2 | 3 | import com.enndfp.simpleframework.mvc.RequestProcessorChain; 4 | import com.enndfp.simpleframework.mvc.processor.RequestProcessor; 5 | 6 | import javax.servlet.RequestDispatcher; 7 | import javax.servlet.ServletContext; 8 | 9 | /** 10 | * jsp资源请求处理 11 | * 12 | * @author Enndfp 13 | */ 14 | public class JspRequestProcessor implements RequestProcessor { 15 | 16 | private static final String JSP_SERVLET = "jsp"; 17 | 18 | private static final String JSP_RESOURCE_PREFIX = "/templates/"; 19 | 20 | // 处理jsp资源的RequestDispatcher 21 | private final RequestDispatcher jspDispatcher; 22 | 23 | public JspRequestProcessor(ServletContext servletContext) { 24 | jspDispatcher = servletContext.getNamedDispatcher(JSP_SERVLET); 25 | if (null == jspDispatcher) { 26 | throw new RuntimeException("there is no jsp servlet"); 27 | } 28 | } 29 | 30 | @Override 31 | public boolean process(RequestProcessorChain requestProcessorChain) throws Exception { 32 | // 1. 通过请求路径判断请求的是不是jsp资源 webapp/templates 33 | if (isJspResource(requestProcessorChain.getRequestPath())) { 34 | // 2. 如果是jsp资源,则将请求转发给 jspDispatcher 处理 35 | jspDispatcher.forward(requestProcessorChain.getRequest(), requestProcessorChain.getResponse()); 36 | return false; 37 | } 38 | return true; 39 | } 40 | 41 | /** 42 | * 通过请求路径前缀(目录)判断是否是jsp资源 /templates/ 43 | * 44 | * @param requestPath 45 | * @return 46 | */ 47 | private boolean isJspResource(String requestPath) { 48 | return requestPath.startsWith(JSP_RESOURCE_PREFIX); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/mvc/processor/impl/PreRequestProcessor.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.mvc.processor.impl; 2 | 3 | import com.enndfp.simpleframework.mvc.RequestProcessorChain; 4 | import com.enndfp.simpleframework.mvc.processor.RequestProcessor; 5 | 6 | /** 7 | * 请求预处理,包括编码以及路径处理 8 | * 9 | * @author Enndfp 10 | */ 11 | public class PreRequestProcessor implements RequestProcessor { 12 | @Override 13 | public boolean process(RequestProcessorChain requestProcessorChain) throws Exception { 14 | // 1. 设置请求编码,将其统一设置成UTF-8 15 | requestProcessorChain.getRequest().setCharacterEncoding("UTF-8"); 16 | 17 | // 2. 将请求路径末尾的 / 剔除,为后续匹配Controller请求路径做准备 18 | // 一般Controller的处理路径是/aaa/bbb,所以如果传入的路径结尾是/aaa/bbb/,就需要处理成/aaa/bbb 19 | String requestPath = requestProcessorChain.getRequestPath(); 20 | //http://localhost:8080/simple-framework requestPath="/" 21 | if (requestPath.length() > 1 && requestPath.endsWith("/")) { 22 | requestProcessorChain.setRequestPath(requestPath.substring(0, requestPath.length() - 1)); 23 | } 24 | return true; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/mvc/processor/impl/StaticResourceRequestProcessor.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.mvc.processor.impl; 2 | 3 | import com.enndfp.simpleframework.mvc.RequestProcessorChain; 4 | import com.enndfp.simpleframework.mvc.processor.RequestProcessor; 5 | 6 | import javax.servlet.RequestDispatcher; 7 | import javax.servlet.ServletContext; 8 | 9 | /** 10 | * 静态资源请求处理,包括但不限于图片、css以及js文件等 11 | * 12 | * @author Enndfp 13 | */ 14 | public class StaticResourceRequestProcessor implements RequestProcessor { 15 | 16 | private static final String DEFAULT_SERVLET = "default"; 17 | 18 | private static final String STATIC_RESOURCE_PREFIX = "/static/"; 19 | 20 | // tomcat默认请求派发器RequestDispatcher的名称 21 | private final RequestDispatcher defaultDispatcher; 22 | 23 | public StaticResourceRequestProcessor(ServletContext servletContext) { 24 | this.defaultDispatcher = servletContext.getNamedDispatcher(DEFAULT_SERVLET); 25 | if (null == defaultDispatcher) { 26 | throw new RuntimeException("There is no default tomcat servlet"); 27 | } 28 | } 29 | 30 | @Override 31 | public boolean process(RequestProcessorChain requestProcessorChain) throws Exception { 32 | // 1. 通过请求路径判断请求的是不是静态资源 webapp/static 33 | if (isStaticResource(requestProcessorChain.getRequestPath())) { 34 | // 2. 如果是静态资源,则将请求转发给 defaultDispatcher 处理 35 | defaultDispatcher.forward(requestProcessorChain.getRequest(), requestProcessorChain.getResponse()); 36 | return false; 37 | } 38 | return true; 39 | } 40 | 41 | /** 42 | * 通过请求路径前缀(目录)判断是否是静态资源 /static/ 43 | * 44 | * @param requestPath 45 | * @return 46 | */ 47 | private boolean isStaticResource(String requestPath) { 48 | return requestPath.startsWith(STATIC_RESOURCE_PREFIX); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/mvc/render/ResultRender.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.mvc.render; 2 | 3 | import com.enndfp.simpleframework.mvc.RequestProcessorChain; 4 | 5 | /** 6 | * 渲染请求结果 7 | * 8 | * @author Enndfp 9 | */ 10 | public interface ResultRender { 11 | 12 | void render(RequestProcessorChain requestProcessorChain) throws Exception; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/mvc/render/impl/DefaultResultRender.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.mvc.render.impl; 2 | 3 | import com.enndfp.simpleframework.mvc.RequestProcessorChain; 4 | import com.enndfp.simpleframework.mvc.render.ResultRender; 5 | 6 | /** 7 | * 默认渲染器 8 | * 9 | * @author Enndfp 10 | */ 11 | public class DefaultResultRender implements ResultRender { 12 | @Override 13 | public void render(RequestProcessorChain requestProcessorChain) throws Exception { 14 | int responseCode = requestProcessorChain.getResponseCode(); 15 | requestProcessorChain.getResponse().setStatus(responseCode); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/mvc/render/impl/InternalErrorResultRender.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.mvc.render.impl; 2 | 3 | import com.enndfp.simpleframework.mvc.RequestProcessorChain; 4 | import com.enndfp.simpleframework.mvc.render.ResultRender; 5 | 6 | import javax.servlet.http.HttpServletResponse; 7 | 8 | /** 9 | * 内部异常渲染器 10 | * 11 | * @author Enndfp 12 | */ 13 | public class InternalErrorResultRender implements ResultRender { 14 | 15 | private String errorMsg; 16 | 17 | public InternalErrorResultRender(String errorMsg) { 18 | this.errorMsg = errorMsg; 19 | } 20 | 21 | @Override 22 | public void render(RequestProcessorChain requestProcessorChain) throws Exception { 23 | int serverErrorCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR; 24 | requestProcessorChain.getResponse().sendError(serverErrorCode, errorMsg); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/mvc/render/impl/JsonResultRender.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.mvc.render.impl; 2 | 3 | import com.enndfp.simpleframework.mvc.RequestProcessorChain; 4 | import com.enndfp.simpleframework.mvc.render.ResultRender; 5 | import com.google.gson.Gson; 6 | 7 | import javax.servlet.http.HttpServletResponse; 8 | import java.io.PrintWriter; 9 | 10 | /** 11 | * json渲染器 12 | * 13 | * @author Enndfp 14 | */ 15 | public class JsonResultRender implements ResultRender { 16 | 17 | private Object result; 18 | 19 | public JsonResultRender(Object result) { 20 | this.result = result; 21 | } 22 | 23 | @Override 24 | public void render(RequestProcessorChain requestProcessorChain) throws Exception { 25 | // 1. 设置响应头 26 | HttpServletResponse response = requestProcessorChain.getResponse(); 27 | response.setContentType("application/json"); 28 | response.setCharacterEncoding("UTF-8"); 29 | 30 | // 2. 将经过Gson处理后的结果写入响应流,try-with-resources 语句确保在语句结束时自动关闭每个资源 31 | try (PrintWriter writer = response.getWriter()) { 32 | Gson gson = new Gson(); 33 | writer.write(gson.toJson(result)); 34 | // flush()不是必需的,因为close()将被自动调用 35 | writer.flush(); 36 | } catch (Exception e) { 37 | e.printStackTrace(); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/mvc/render/impl/ResourceNotFoundResultRender.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.mvc.render.impl; 2 | 3 | import com.enndfp.simpleframework.mvc.RequestProcessorChain; 4 | import com.enndfp.simpleframework.mvc.render.ResultRender; 5 | 6 | import javax.servlet.http.HttpServletResponse; 7 | 8 | /** 9 | * 资源找不到时使用的渲染器 10 | * 11 | * @author Enndfp 12 | */ 13 | public class ResourceNotFoundResultRender implements ResultRender { 14 | 15 | private String requestMethod; 16 | 17 | private String requestPath; 18 | 19 | public ResourceNotFoundResultRender(String requestMethod, String requestPath) { 20 | this.requestMethod = requestMethod; 21 | this.requestPath = requestPath; 22 | } 23 | 24 | @Override 25 | public void render(RequestProcessorChain requestProcessorChain) throws Exception { 26 | int notFoundCode = HttpServletResponse.SC_NOT_FOUND; 27 | requestProcessorChain.getResponse().sendError(notFoundCode, 28 | "获取不到对应的请求资源:请求路径[" + requestPath + "]," + "请求方法[" + requestMethod + "]"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/mvc/render/impl/ViewResultRender.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.mvc.render.impl; 2 | 3 | import com.enndfp.simpleframework.mvc.RequestProcessorChain; 4 | import com.enndfp.simpleframework.mvc.render.ResultRender; 5 | import com.enndfp.simpleframework.mvc.type.ModelAndView; 6 | 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | import java.util.Map; 10 | 11 | /** 12 | * 页面渲染器 13 | * 14 | * @author Enndfp 15 | */ 16 | public class ViewResultRender implements ResultRender { 17 | 18 | public static final String VIEW_PATH = "/templates/"; 19 | 20 | private ModelAndView modelAndView; 21 | 22 | /** 23 | * 对传入的参数进行处理,并赋值给ModelAndView成员变量 24 | * 25 | * @param mv 26 | */ 27 | public ViewResultRender(Object mv) { 28 | if (mv instanceof ModelAndView) { 29 | // 1. 如果入参类型是ModelAndView,则直接赋值给成员变量 30 | this.modelAndView = (ModelAndView) mv; 31 | } else if (mv instanceof String) { 32 | // 2. 如果入参类型是String,则为视图,需要包装后才赋值给成员变量 33 | this.modelAndView = new ModelAndView().setViewPath((String) mv); 34 | } else { 35 | // 3. 针对其他情况,则直接抛出异常 36 | throw new RuntimeException("illegal request result type"); 37 | } 38 | } 39 | 40 | /** 41 | * 根据请求结果按照视图路径转发至对应视图进行展示 42 | * 43 | * @param requestProcessorChain 44 | * @throws Exception 45 | */ 46 | @Override 47 | public void render(RequestProcessorChain requestProcessorChain) throws Exception { 48 | HttpServletRequest request = requestProcessorChain.getRequest(); 49 | HttpServletResponse response = requestProcessorChain.getResponse(); 50 | 51 | String viewPath = modelAndView.getViewPath(); 52 | Map viewData = modelAndView.getViewData(); 53 | for (Map.Entry entry : viewData.entrySet()) { 54 | request.setAttribute(entry.getKey(), entry.getValue()); 55 | } 56 | request.getRequestDispatcher(VIEW_PATH + viewPath).forward(request, response); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/mvc/type/ControllerMethod.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.mvc.type; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.lang.reflect.Method; 8 | import java.util.Map; 9 | 10 | /** 11 | * 待执行的Controller及其方法实例和参数的映射 12 | * 13 | * @author Enndfp 14 | */ 15 | @Data 16 | @AllArgsConstructor 17 | @NoArgsConstructor 18 | public class ControllerMethod { 19 | // Controller对应的Class对象 20 | private Class controllerClass; 21 | 22 | // 执行的Controller方法实例 23 | private Method invokeMethod; 24 | 25 | // 方法参数名称以及对应的参数类型 26 | private Map> methodParameters; 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/mvc/type/ModelAndView.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.mvc.type; 2 | 3 | import lombok.Getter; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | /** 9 | * 存储处理完后的结果数据以及显示该数据的视图 10 | * 11 | * @author Enndfp 12 | */ 13 | @Getter 14 | public class ModelAndView { 15 | 16 | // 页面所在的路径 17 | private String viewPath; 18 | 19 | // 页面的data数据 20 | private Map viewData = new HashMap<>(); 21 | 22 | public ModelAndView setViewPath(String viewPath) { 23 | this.viewPath = viewPath; 24 | return this; 25 | } 26 | 27 | public ModelAndView addViewData(String attributeName, Object attributeValue) { 28 | viewData.put(attributeName, attributeValue); 29 | return this; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/mvc/type/RequestMethod.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.mvc.type; 2 | 3 | /** 4 | * 目前支持的请求方法 5 | * 6 | * @author Enndfp 7 | */ 8 | public enum RequestMethod { 9 | GET, 10 | POST; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/mvc/type/RequestPathInfo.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.mvc.type; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * 存储http请求路径和请求方法 9 | * 10 | * @author Enndfp 11 | */ 12 | @Data 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | public class RequestPathInfo { 16 | // http请求方法 17 | private String httpMethod; 18 | 19 | // http请求路径 20 | private String httpPath; 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/utils/ClassUtil.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.utils; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | 5 | import java.io.File; 6 | import java.io.FileFilter; 7 | import java.lang.reflect.Constructor; 8 | import java.lang.reflect.Field; 9 | import java.net.URL; 10 | import java.util.HashSet; 11 | import java.util.Set; 12 | 13 | /** 14 | * @author Enndfp 15 | */ 16 | @Slf4j 17 | public class ClassUtil { 18 | 19 | public static final String FILE_PROTOCOL = "file"; 20 | 21 | /** 22 | * 获取包下所有类的集合 23 | * 24 | * @param packageName 包名 25 | * @return 类集合 26 | */ 27 | public static Set> extractPackageClass(String packageName) { 28 | // 1. 获取到类的加载器 29 | ClassLoader classLoader = getClassLoader(); 30 | 31 | // 2. 通过类加载器获取到加载的资源信息 32 | URL url = classLoader.getResource(packageName.replace(".", "/")); 33 | if (url == null) { 34 | log.warn("unable to retrieve anything from package: " + packageName); 35 | return null; 36 | } 37 | 38 | // 3. 依据不同的资源类型,采用不同的方式获取资源的集合 39 | Set> classSet = null; 40 | // 3.1 仅处理文件类型的资源 41 | if (url.getProtocol().equalsIgnoreCase(FILE_PROTOCOL)) { 42 | classSet = new HashSet<>(); 43 | File packageDirectory = new File(url.getPath()); 44 | extractClassFile(classSet, packageDirectory, packageName); 45 | } 46 | return classSet; 47 | } 48 | 49 | /** 50 | * 递归获取目标package及其子目录里面的所有class文件 51 | * 52 | * @param classSet 装载目标类的集合 53 | * @param fileSource 文件或者目录 54 | * @param packageName 包名 55 | */ 56 | private static void extractClassFile(Set> classSet, File fileSource, String packageName) { 57 | if (!fileSource.isDirectory()) return; 58 | // 获取目录下的文件夹(不包括子文件夹) 59 | File[] files = fileSource.listFiles(new FileFilter() { 60 | @Override 61 | public boolean accept(File file) { 62 | if (file.isDirectory()) { 63 | return true; 64 | } else { 65 | // 获取文件的绝对值路径 66 | String absolutePath = file.getAbsolutePath(); 67 | if (absolutePath.endsWith(".class")) { 68 | // 若是class文件,则直接加载 69 | addToClassSet(absolutePath); 70 | } 71 | } 72 | return false; 73 | } 74 | 75 | // 根据class文件的绝对值路径,获取并生成class对象,并放入classSet中 76 | private void addToClassSet(String absolutePath) { 77 | // 1. 从class文件的绝对值路径里提取出包含了package的类名 78 | absolutePath = absolutePath.replace(File.separator, "."); 79 | String className = absolutePath.substring(absolutePath.indexOf(packageName)); 80 | className = className.substring(0, className.lastIndexOf(".")); 81 | 82 | // 2. 通过反射机制获取对应的class对象并加入到classSet中 83 | Class targetClass = loadClass(className); 84 | classSet.add(targetClass); 85 | } 86 | }); 87 | if (files != null) { 88 | for (File f : files) { 89 | // 递归调用 90 | extractClassFile(classSet, f, packageName); 91 | } 92 | } 93 | } 94 | 95 | /** 96 | * 获取ClassLoader 97 | * 98 | * @return 当前ClassLoader 99 | */ 100 | public static ClassLoader getClassLoader() { 101 | return Thread.currentThread().getContextClassLoader(); 102 | } 103 | 104 | /** 105 | * 获取class对象 106 | * 107 | * @param className class全名 = package + 类名 108 | * @return 109 | */ 110 | public static Class loadClass(String className) { 111 | try { 112 | return Class.forName(className); 113 | } catch (ClassNotFoundException e) { 114 | log.error("load class error: ", e); 115 | throw new RuntimeException(e); 116 | } 117 | } 118 | 119 | /** 120 | * 实例化Class对象 121 | * 122 | * @param clazz Class 123 | * @param Class的类型 124 | * @param accessible 是否支持创建出私有class对象的实例 125 | * @return 类的实例化 126 | */ 127 | public static T newInstance(Class clazz, boolean accessible) { 128 | try { 129 | Constructor constructor = clazz.getDeclaredConstructor(); 130 | constructor.setAccessible(accessible); 131 | return (T) constructor.newInstance(); 132 | } catch (Exception e) { 133 | log.error("newInstance error ", e); 134 | throw new RuntimeException(e); 135 | } 136 | } 137 | 138 | /** 139 | * 设置类的属性值 140 | * 141 | * @param field 成员变量 142 | * @param target 类实例 143 | * @param value 成员变量的值 144 | * @param accessible 是否允许设置私有属性 145 | */ 146 | public static void setField(Field field, Object target, Object value, boolean accessible) { 147 | field.setAccessible(accessible); 148 | try { 149 | field.set(target, value); 150 | } catch (IllegalAccessException e) { 151 | log.error("setField error ", e); 152 | throw new RuntimeException(); 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/utils/ConverterUtil.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.utils; 2 | 3 | /** 4 | * @author Enndfp 5 | */ 6 | public class ConverterUtil { 7 | 8 | /** 9 | * 返回基本数据类型的空值 10 | * 需要特殊处理的基本类型即int\double\short\long\byte\float\boolean 11 | * 12 | * @param type 13 | * @return 14 | */ 15 | public static Object primitiveNull(Class type) { 16 | if (type == int.class || type == double.class || 17 | type == short.class || type == long.class || 18 | type == byte.class || type == float.class) { 19 | return 0; 20 | } else if (type == boolean.class) { 21 | return false; 22 | } 23 | return null; 24 | } 25 | 26 | /** 27 | * String类型转换成对应的参数类型 28 | * 29 | * @param type 参数类型 30 | * @param requestParamValue 参数值 31 | * @return 转换后的Object 32 | */ 33 | public static Object convert(Class type, String requestParamValue) { 34 | if (isPrimitive(type)) { 35 | if (ValidationUtil.isEmpty(requestParamValue)) { 36 | return primitiveNull(type); 37 | } 38 | if (type.equals(int.class) || type.equals(Integer.class)) { 39 | return Integer.parseInt(requestParamValue); 40 | } else if (type.equals(String.class)) { 41 | return requestParamValue; 42 | } else if (type.equals(Double.class) || type.equals(double.class)) { 43 | return Double.parseDouble(requestParamValue); 44 | } else if (type.equals(Float.class) || type.equals(float.class)) { 45 | return Float.parseFloat(requestParamValue); 46 | } else if (type.equals(Long.class) || type.equals(long.class)) { 47 | return Long.parseLong(requestParamValue); 48 | } else if (type.equals(Boolean.class) || type.equals(boolean.class)) { 49 | return Boolean.parseBoolean(requestParamValue); 50 | } else if (type.equals(Short.class) || type.equals(short.class)) { 51 | return Short.parseShort(requestParamValue); 52 | } else if (type.equals(Byte.class) || type.equals(byte.class)) { 53 | return Byte.parseByte(requestParamValue); 54 | } 55 | return requestParamValue; 56 | } else { 57 | throw new RuntimeException("count not support non primitive type conversion yet"); 58 | } 59 | } 60 | 61 | /** 62 | * 判断是否是基本数据类型(包括包装类以及String) 63 | * 64 | * @param type 参数类型 65 | * @return 是否为基本数据类型 66 | */ 67 | private static boolean isPrimitive(Class type) { 68 | return type == boolean.class 69 | || type == Boolean.class 70 | || type == double.class 71 | || type == Double.class 72 | || type == float.class 73 | || type == Float.class 74 | || type == short.class 75 | || type == Short.class 76 | || type == int.class 77 | || type == Integer.class 78 | || type == long.class 79 | || type == Long.class 80 | || type == String.class 81 | || type == byte.class 82 | || type == Byte.class 83 | || type == char.class 84 | || type == Character.class; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/com/enndfp/simpleframework/utils/ValidationUtil.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.utils; 2 | 3 | import java.util.Collection; 4 | import java.util.Map; 5 | 6 | /** 7 | * @author Enndfp 8 | */ 9 | public class ValidationUtil { 10 | 11 | /** 12 | * String是否为null或“” 13 | * 14 | * @param obj String 15 | * @return 是否为空 16 | */ 17 | public static boolean isEmpty(String obj) { 18 | return (obj == null || "".equals(obj)); 19 | } 20 | 21 | /** 22 | * Array是否为null或size为0 23 | * 24 | * @param obj Array 25 | * @return 是否为空 26 | */ 27 | public static boolean isEmpty(Object[] obj) { 28 | return obj == null || obj.length == 0; 29 | } 30 | 31 | /** 32 | * Collection是否为null或size为0 33 | * 34 | * @param obj Collection 35 | * @return 是否为空 36 | */ 37 | public static boolean isEmpty(Collection obj) { 38 | return obj == null || obj.isEmpty(); 39 | } 40 | 41 | /** 42 | * Map是否为null或size为0 43 | * 44 | * @param obj Map 45 | * @return 是否为空 46 | */ 47 | public static boolean isEmpty(Map obj) { 48 | return obj == null || obj.isEmpty(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # 设置日志的打印级别以及要输出到的地方 2 | # 优先级从高到低分别是 ERROR、WARN、INFO、DEBUG 3 | # 比如在这里定义了 INFO 级别,则应用程序中所有 DEBUG 级别的日志信息将不被打印 4 | # stdout 指代要输出到的地方,可以是不同的自定义名称,也可以有多个,表示输出到控制台 5 | log4j.rootLogger=debug,stdout 6 | 7 | # 输出信息到控制台 8 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 9 | log4j.appender.stdout.Target=System.out 10 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 11 | 12 | # 输出日志的格式 13 | # %c: 输出日志信息所属的类目,通常就是所在类的全名 14 | # %d: 输出日志时间点的日期或时间 15 | # %p: 输出日志信息优先级,即 DEBUG,INFO,WARN,ERROR,FATAL 16 | # %m: 输出代码中指定的消息,产生的日志具体信息 17 | # %n: 输出一个回车换行符,Windows 平台为"\r\n",Unix 平台为"\n"输出日志信息换行 18 | # 示例:com.enndfp.demo.HelloServlet 15:00:00 -- DEBUG -- test 19 | log4j.appender.stdout.layout.ConversionPattern=%c %d{HH:mm:ss} -- %p -- %m%n -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/jsp/hello.jsp: -------------------------------------------------------------------------------- 1 | <%@ page pageEncoding="UTF-8" isELIgnored="false" %> 2 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> 3 | 4 | 5 | Hello 6 | 7 | 8 |

Hello!

9 |

太牛逼了,${name}

10 | 11 | -------------------------------------------------------------------------------- /src/main/webapp/static/coding.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Enndfp/simple-framework/98bb7e2d70c68bd5f4e932da1fcc95e9a62c6b18/src/main/webapp/static/coding.jpg -------------------------------------------------------------------------------- /src/main/webapp/templates/addheadline.jsp: -------------------------------------------------------------------------------- 1 | <%@ page pageEncoding="UTF-8" isELIgnored="false" %> 2 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> 3 | 4 | 5 | addheadline 6 | 7 | 8 | 9 |

表单提交:


10 | 11 | 头条说明:
12 | 头条链接:
13 | 头条图片地址:
14 | 优先级:
15 | 结果:

状态码:${result.code} 信息:${result.message}


16 | 17 | 18 |
19 | 20 | -------------------------------------------------------------------------------- /src/test/java/com/enndfp/simpleframework/aop/AspectListExecutorTest.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.aop; 2 | 3 | import com.enndfp.simpleframework.aop.aspect.AspectInfo; 4 | import com.enndfp.simpleframework.aop.mock.*; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | /** 11 | * @author Enndfp 12 | */ 13 | public class AspectListExecutorTest { 14 | 15 | @Test 16 | public void sortTest() { 17 | List aspectInfoList = new ArrayList<>(); 18 | aspectInfoList.add(new AspectInfo(3, new Mock1(),null)); 19 | aspectInfoList.add(new AspectInfo(5, new Mock2(),null)); 20 | aspectInfoList.add(new AspectInfo(2, new Mock3(),null)); 21 | aspectInfoList.add(new AspectInfo(4, new Mock4(),null)); 22 | aspectInfoList.add(new AspectInfo(1, new Mock5(),null)); 23 | AspectListExecutor aspectListExecutor = new AspectListExecutor(AspectListExecutorTest.class, aspectInfoList); 24 | List sortedAspectInfoList = aspectListExecutor.getSortedAspectInfoList(); 25 | for (AspectInfo aspectInfo : sortedAspectInfoList) { 26 | System.out.println(aspectInfo.getAspectObject().getClass().getName()); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/test/java/com/enndfp/simpleframework/aop/AspectWeaverTest.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.aop; 2 | 3 | import com.enndfp.demo.controller.frontend.MainPageController; 4 | import com.enndfp.simpleframework.core.BeanContainer; 5 | import com.enndfp.simpleframework.inject.DependencyInjector; 6 | import org.junit.jupiter.api.Test; 7 | 8 | /** 9 | * @author Enndfp 10 | */ 11 | public class AspectWeaverTest { 12 | 13 | @Test 14 | void doAOPTest() { 15 | BeanContainer beanContainer = BeanContainer.getInstance(); 16 | beanContainer.loadBeans("com.enndfp.demo"); 17 | new AspectWeaver().doAOP(); 18 | new DependencyInjector().doIOC(); 19 | MainPageController mainPageController = (MainPageController) beanContainer.getBean(MainPageController.class); 20 | mainPageController.getMainPageInfo(null, null); 21 | } 22 | } -------------------------------------------------------------------------------- /src/test/java/com/enndfp/simpleframework/aop/mock/Mock1.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.aop.mock; 2 | 3 | import com.enndfp.simpleframework.aop.aspect.DefaultAspect; 4 | 5 | /** 6 | * @author Enndfp 7 | */ 8 | public class Mock1 extends DefaultAspect { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/com/enndfp/simpleframework/aop/mock/Mock2.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.aop.mock; 2 | 3 | import com.enndfp.simpleframework.aop.aspect.DefaultAspect; 4 | 5 | /** 6 | * @author Enndfp 7 | */ 8 | public class Mock2 extends DefaultAspect { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/com/enndfp/simpleframework/aop/mock/Mock3.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.aop.mock; 2 | 3 | import com.enndfp.simpleframework.aop.aspect.DefaultAspect; 4 | 5 | /** 6 | * @author Enndfp 7 | */ 8 | public class Mock3 extends DefaultAspect { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/com/enndfp/simpleframework/aop/mock/Mock4.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.aop.mock; 2 | 3 | import com.enndfp.simpleframework.aop.aspect.DefaultAspect; 4 | 5 | /** 6 | * @author Enndfp 7 | */ 8 | public class Mock4 extends DefaultAspect { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/com/enndfp/simpleframework/aop/mock/Mock5.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.aop.mock; 2 | 3 | import com.enndfp.simpleframework.aop.aspect.DefaultAspect; 4 | 5 | /** 6 | * @author Enndfp 7 | */ 8 | public class Mock5 extends DefaultAspect { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/com/enndfp/simpleframework/core/BeanContainerTest.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.core; 2 | 3 | import com.enndfp.simpleframework.mvc.DispatcherServlet; 4 | import com.enndfp.demo.controller.frontend.MainPageController; 5 | import com.enndfp.demo.service.solo.HeadLineService; 6 | import com.enndfp.demo.service.solo.impl.HeadLineServiceImpl; 7 | import com.enndfp.simpleframework.core.annotation.Controller; 8 | import org.junit.jupiter.api.*; 9 | 10 | /** 11 | * @author Enndfp 12 | */ 13 | @TestMethodOrder(MethodOrderer.OrderAnnotation.class) 14 | public class BeanContainerTest { 15 | 16 | private static BeanContainer beanContainer; 17 | 18 | @BeforeAll 19 | static void init() { 20 | beanContainer = BeanContainer.getInstance(); 21 | } 22 | 23 | @Order(1) 24 | @Test 25 | public void loadBeansTest() { 26 | Assertions.assertEquals(false, beanContainer.isLoaded()); 27 | beanContainer.loadBeans("com.enndfp.demo"); 28 | Assertions.assertEquals(7, beanContainer.size()); 29 | Assertions.assertEquals(true, beanContainer.isLoaded()); 30 | } 31 | 32 | @Order(2) 33 | @Test 34 | public void getBeanTest() { 35 | MainPageController controller = (MainPageController) beanContainer.getBean(MainPageController.class); 36 | Assertions.assertEquals(true, controller instanceof MainPageController); 37 | DispatcherServlet dispatcherServlet = (DispatcherServlet) beanContainer.getBean(DispatcherServlet.class); 38 | Assertions.assertEquals(null, dispatcherServlet); 39 | } 40 | 41 | @Order(3) 42 | @Test 43 | public void getClassesByAnnotationTest() { 44 | Assertions.assertEquals(true, beanContainer.isLoaded()); 45 | Assertions.assertEquals(3, beanContainer.getClassesByAnnotation(Controller.class).size()); 46 | } 47 | 48 | @Order(4) 49 | @Test 50 | public void getClassesBySuperTest() { 51 | Assertions.assertEquals(true, beanContainer.isLoaded()); 52 | Assertions.assertEquals(true, beanContainer.getClassesBySuper(HeadLineService.class).contains(HeadLineServiceImpl.class)); 53 | } 54 | } -------------------------------------------------------------------------------- /src/test/java/com/enndfp/simpleframework/inject/DependencyInjectorTest.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.inject; 2 | 3 | import com.enndfp.demo.controller.frontend.MainPageController; 4 | import com.enndfp.demo.service.combine.impl.HeadLineShopCategoryCombineServiceImpl; 5 | import com.enndfp.simpleframework.core.BeanContainer; 6 | import org.junit.jupiter.api.Assertions; 7 | import org.junit.jupiter.api.Test; 8 | 9 | /** 10 | * @author Enndfp 11 | */ 12 | public class DependencyInjectorTest { 13 | 14 | @Test 15 | public void doIOCTest() { 16 | BeanContainer beanContainer = BeanContainer.getInstance(); 17 | beanContainer.loadBeans("com.enndfp.demo"); 18 | Assertions.assertEquals(true,beanContainer.isLoaded()); 19 | MainPageController mainPageController = (MainPageController) beanContainer.getBean(MainPageController.class); 20 | Assertions.assertEquals(true, mainPageController instanceof MainPageController); 21 | Assertions.assertEquals(null,mainPageController.getHeadLineShopCategoryCombineService()); 22 | new DependencyInjector().doIOC(); 23 | Assertions.assertNotEquals(null,mainPageController.getHeadLineShopCategoryCombineService()); 24 | Assertions.assertEquals(true,mainPageController.getHeadLineShopCategoryCombineService() instanceof HeadLineShopCategoryCombineServiceImpl); 25 | } 26 | } -------------------------------------------------------------------------------- /src/test/java/com/enndfp/simpleframework/utils/ClassUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.enndfp.simpleframework.utils; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.util.Set; 7 | 8 | /** 9 | * @author Enndfp 10 | */ 11 | public class ClassUtilTest { 12 | 13 | @Test 14 | public void extractPackageClassTest() { 15 | Set> classSet = ClassUtil.extractPackageClass("com.enndfp.demo.entity"); 16 | System.out.println(classSet); 17 | Assertions.assertEquals(4, classSet.size()); 18 | } 19 | } 20 | --------------------------------------------------------------------------------