├── .gitattributes ├── README.md ├── managementsystem ├── managementsystem.iml ├── pom.xml ├── src │ └── main │ │ ├── java │ │ └── org │ │ │ └── simple4j │ │ │ └── managementsystem │ │ │ ├── Service │ │ │ └── CustomerService.java │ │ │ ├── aspect │ │ │ └── ControllerAspect.java │ │ │ ├── controller │ │ │ └── CustomerController.java │ │ │ └── model │ │ │ └── Customer.java │ │ ├── resources │ │ ├── log4j.properties │ │ └── simple.properties │ │ └── webapp │ │ ├── WEB-INF │ │ └── view │ │ │ ├── customer.jsp │ │ │ ├── customer_create.jsp │ │ │ ├── customer_edit.jsp │ │ │ └── customer_show.jsp │ │ └── asset │ │ └── lib │ │ ├── jquery-form │ │ ├── jquery.form.js │ │ └── jquery.form.min.js │ │ └── jquery │ │ ├── jquery.js │ │ ├── jquery.min.js │ │ └── jquery.min.map └── target │ ├── classes │ ├── AppSecurity.class │ ├── log4j.properties │ ├── org │ │ └── simple4j │ │ │ └── managementsystem │ │ │ ├── Service │ │ │ └── CustomerService.class │ │ │ ├── aspect │ │ │ └── ControllerAspect.class │ │ │ ├── controller │ │ │ └── CustomerController.class │ │ │ └── model │ │ │ └── Customer.class │ └── simple.properties │ └── management-system-1.0-SNAPSHOT │ ├── META-INF │ └── MANIFEST.MF │ ├── WEB-INF │ ├── classes │ │ ├── AppSecurity.class │ │ ├── log4j.properties │ │ ├── org │ │ │ └── simple4j │ │ │ │ └── managementsystem │ │ │ │ ├── Service │ │ │ │ └── CustomerService.class │ │ │ │ ├── aspect │ │ │ │ └── ControllerAspect.class │ │ │ │ ├── controller │ │ │ │ └── CustomerController.class │ │ │ │ └── model │ │ │ │ └── Customer.class │ │ └── simple.properties │ ├── lib │ │ ├── asm-3.3.1.jar │ │ ├── cglib-2.2.2.jar │ │ ├── commons-codec-1.10.jar │ │ ├── commons-collections4-4.0.jar │ │ ├── commons-dbcp2-2.0.1.jar │ │ ├── commons-dbutils-1.6.jar │ │ ├── commons-fileupload-1.3.1.jar │ │ ├── commons-io-2.2.jar │ │ ├── commons-lang3-3.3.2.jar │ │ ├── commons-logging-1.1.3.jar │ │ ├── commons-pool2-2.2.jar │ │ ├── jackson-annotations-2.5.0.jar │ │ ├── jackson-core-2.5.1.jar │ │ ├── jackson-databind-2.5.2.jar │ │ ├── jstl-1.2.jar │ │ ├── log4j-1.2.17.jar │ │ ├── mysql-connector-java-5.1.33.jar │ │ ├── simple-framework-1.0-SNAPSHOT.jar │ │ ├── slf4j-api-1.7.7.jar │ │ └── slf4j-log4j12-1.7.7.jar │ └── view │ │ ├── customer.jsp │ │ ├── customer_create.jsp │ │ ├── customer_edit.jsp │ │ └── customer_show.jsp │ └── asset │ └── lib │ ├── jquery-form │ ├── jquery.form.js │ └── jquery.form.min.js │ └── jquery │ ├── jquery.js │ ├── jquery.min.js │ └── jquery.min.map ├── note ├── img │ └── 1.png └── note.md ├── pom.xml └── src └── main └── java └── com └── xctian └── framework ├── DispatcherServlet.java ├── HelperLoader.java ├── PropertiesConfigConstant.java ├── annotation ├── Action.java ├── Aspect.java ├── Controller.java ├── Inject.java ├── Service.java └── Transaction.java ├── bean ├── Data.java ├── Handler.java ├── Param.java ├── Request.java └── View.java ├── helper ├── AopHelper.java ├── BeanHelper.java ├── ClassHelper.java ├── ControllerHelper.java ├── DatabaseHelper.java ├── IocHelper.java ├── PropertiesConfigHelper.java └── ServletHelper.java ├── proxy ├── AspectProxy.java ├── Proxy.java ├── ProxyChain.java ├── ProxyManager.java └── TransactionProxy.java └── utils ├── ArrayUtil.java ├── CastUtil.java ├── ClassUtil.java ├── CodecUtil.java ├── CollectionUtil.java ├── JsonUtil.java ├── PropsUtil.java ├── ReflectionUtil.java ├── StreamUtil.java └── StringUtil.java /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js linguist-language=java 2 | *.css linguist-language=java 3 | *.html linguist-language=java 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simple-Framework使用文档 2 | 3 | ​ Simple-Framework是一种轻量级Java Web框架,可基于此框架进行简单的web开发。 4 | 5 | ​ 本项目主要供学习交流使用,Email: xctian@zju.edu.cn 6 | 7 | ## 简介 8 | 9 | ​ Simple-Framework是基于原生Servlet实现的一种轻量级Java Web框架,它具有目前市面上流行的Java Web框架(如Spring、SpringMVC)所具备的核心功能:**Bean容器,依赖注入,请求转发控制器,IOC和AOP功能,事务管理控制**等。Simple-Framework的实现过程并未引入或使用其他web框架,它是一种轻量级MVC框架的自行开发和探索,同时也是本人对Java Web后台开发技术知识的学习及整合。 10 | 11 | ​ 实现过程中使用到的技术有:**Tomcat、Servlet 、JDBC、数据库、反射、CGLIB动态代理、设计模式**(模板模式、责任链模式)、**ThreadLocal**等。虽然Simple-Framework功能没有流行框架那样齐全,但其实现和使用非常轻量级,适合作为学习Java Web框架实现的参考项目。以下介绍Simple-Framework的功能组成和特性: 12 | 13 | ### 配置文件 14 | 15 | - 配置文件少,只需在对应web项目resources目录下创建并配置simple.properties的文件 16 | - 配置项简单,只需配置数据库连接相关参数、项目基础包名,以及JSP和静态资源文件基础路径 17 | - 部分配置项支持缺省配置 18 | 19 | ### **类加载和操作工具** 20 | 21 | - 本项目提供一个自定义类加载工具类ClassUtil,提供类操作相关的方法,如获取类加载器、加载指定类、获取指定包名下的所有类等 22 | - 提供ClassHelper助手类封装ClassUtil,包含以下功能:获取应用包名下所有类,应用包名下所有Service类,应用包名下所有Controller类,获取应用包名下所有Bean类 23 | 24 | ### 注解 25 | 26 | ​ 本项目提供以下自定义注解 27 | 28 | - Controller:控制器类标记注解,在控制器类上使用,用以标注控制器类 29 | - Service:服务类标记注解,在服务类上使用,用以标注服务类 30 | - Inject:依赖注入标记注解,在成员变量上使用,用以表示将服务类依赖注入进来 31 | - Action:单成员注解,在控制器类中的方法上使用,用以表明该方法是处理对应请求的Action方法,含义String类型的内部成员value,用以接收请求类型与路径 32 | - Aspect:单成员注解,用于标记并配置切面类。内部有一个注解类型的成员,用于标明被AOP框架拦截的类 33 | - Transaction:事务标记注解,在Service类中的方法上使用 34 | 35 | ### Bean容器 36 | 37 | - 提供反射工具类ReflectionUtil,封装Java反射相关API,用以提供获取实例化对象的相关方法 38 | - Bean容器对象BEAN_MAP的类型:Map,Object>,key:类的Class对象,value:Class对象对应的Bean实例 39 | - 提供BeanHelper助手类,封装BEAN_MAP,BeanHelper加载时将BEAN_MAP初始化,项目运行过程中可通过BeanHelper.getBean(Class cls)方法直接获取对应Bean实例 40 | 41 | ### 依赖注入和IOC 42 | 43 | - 提供IocHelper助手类,IocHelper初始化时会扫描所有Inject注解,并从Bean容器中找到对应的Service实例通过ReflectionUtil对成员变量进行注入(详见文末提供的开发文档) 44 | - 本项目通过Bean容器及依赖注入功能实现IOC(控制反转) 45 | 46 | ### Controller加载 47 | 48 | - 对于Action方法的请求参数,封装请求对象Request,包含请求方法requestMethod和请求路径requestPath 49 | - 为每一个Request封装对应Handler,Handler包含处理请求的Controller类controllerClass和对应方法actionMethod 50 | 51 | - 提供一个ControllerHelper控制器助手类,封装一个用于存放Request和对应Handler的Map容器Map ACTION_MAP,ControllerHelper初始化时会自动初始化ACTION_MAP(实现参考文末开发文档) 52 | 53 | ### 请求分发器DispatcerServlet 54 | 55 | - 添加请求参数封装对象Param,封装一个paramMap用于接收请求参数 56 | 57 | - DispatcerServlet继承HttpServlet,在重写的init方法进行初始化时会将以上Helper进行初始化, 58 | - DispatcerServlet重写的Service中主要是获取HttpServletRequst中的内容,将必要的参数封装Param对象,通过ControllerHelper拿到对应请求处理的Handler,传入Param利用反射对Action方法发起调用 59 | - 对应Action方法返回值提供两种处理情况:(1)返回值是View类型视图对象,则返回一个JSP; (2)返回值是Data类型数据对象,则返回JSON数据 60 | 61 | ### AOP和事务管理 62 | 63 | ​ 本项目提供的AOP框架基于注解实现 64 | 65 | - 提供切面注解Aspect,它是单成员注解,内部有一个注解类型的成员,内部注解用于标明被拦截的类 66 | - 提供一个抽象切面类AspectProxy,只需根据业务需求定义自己的切面类,扩展AspectProxy并定义Aspect注解,然后完成特定的钩子方法,即可将横切逻辑与业务逻辑分离 67 | - 通过责任链模式,实现对一个切点织入多个增强 68 | - 框架启动时,会自动将Class对象及其经过AOP处理后的实例对象以k,v的形式存入IOC容器BEAN_MAP 69 | - 提供方法级别的事务注解Transaction,用于对Service类中的方法进行事务管理 70 | 71 | ## 使用说明 72 | 73 | ​ 在本项目中,提供了一个简单的web项目managementsystem,里面整合并使用了Simple-Framework进行开发,该项目用于对Simple-Framework进行简单的测试。也可以自行选取其他web项目集成本框架。本项目作为web框架并未提供ORM框架的功能,与数据库交互工作需要自行完成。以下是Simple-Framework使用说明: 74 | 75 | ### 项目导入 76 | 77 | - clone项目到本地,使用IDEA打开,配置maven和本地仓库,等待依赖自动导入完成。 78 | - 该项目是一个Web框架,它可以作为jar包被其他项目所依赖使用。使用maven的install命令,将项目打包到本地仓库。 79 | - 打开本地的web项目,pom文件中引入以下的dependency: 80 | 81 | ```xml 82 | 83 | com.xctian 84 | simple-framework 85 | 1.0-SNAPSHOT 86 | 87 | ``` 88 | 89 | ### 配置项说明 90 | 91 | 在web项目resources目录下创建并配置simple.properties的文件,配置示例如下: 92 | 93 | ```properties 94 | simple.framework.jdbc.driver=com.mysql.jdbc.Driver 95 | simple.framework.jdbc.url=jdbc:mysql://localhost:3306/demo 96 | simple.framework.jdbc.username=root 97 | simple.framework.jdbc.password=admin 98 | 99 | simple.framework.app.base_package=org.simple4j.managementsystem 100 | simple.framework.app.jsp_path=/WEB-INF/view/ 101 | simple.framework.app.asset_path=/asset/ 102 | ``` 103 | 104 | 其中前4项是jdbc相关配置,simple.framework.app.base_package是web应用基础包名,必须进行配置。simple.framework.app.jsp_path是JSP文件的基础路径,缺省值为/WEB-INF/view/,simple.framework.app.asset_path为静态资源文件的基础路径,缺省值为/asset/ 105 | 106 | ### 注解使用说明 107 | 108 | - Controller和Service是类级别的标记注解,只能使用在类上 109 | 110 | - Inject是依赖注入注解,在Controller类中的成员变量上使用 111 | 112 | - Transaction是方法级别的注解,只能用于Service类中的方法上 113 | 114 | - Action是方法级别的注解,在Controller类中的方法上使用,用以表明该方法是处理对应请求的Action方法,在Action需要配置请求方法和路径: 115 | 116 | ```java 117 | @Controller 118 | public class XXXController { 119 | @Inject 120 | private CustomerService customerService; 121 | ... 122 | @Action("get:/customer") 123 | public View index() { 124 | List customerList = customerService.getCustomerList(); 125 | LOGGER.info("列表展示"); 126 | return new View("customer.jsp").addModel("customerList", customerList); 127 | } 128 | ... 129 | } 130 | ``` 131 | 132 | - Aspect是类级别的注解,用于自定义切面类: 133 | 134 | ```java 135 | @Aspect(Controller.class) 136 | public class ControllerAspect extends AspectProxy { 137 | private long begin; 138 | 139 | @Override 140 | public void before(Class cls, Method method, Object[] params) throws Throwable { 141 | ... 142 | } 143 | 144 | @Override 145 | public void after(Class cls, Method method, Object[] params, Object result) throws Throwable { 146 | ... 147 | } 148 | } 149 | ``` 150 | 151 | 如在切面类ControllerAspect类上使用注解@Aspect(Controller.class)则表明所有被Controller注解的类都会被拦截,其所有方法会被织入增强 152 | 153 | ### AOP说明 154 | 155 | 根据业务需求定义自己的切面类,扩展AspectProxy并定义Aspect注解,然后完成特定的钩子方法,示例: 156 | 157 | ```java 158 | @Aspect(Controller.class) 159 | public class ControllerAspect extends AspectProxy { 160 | 161 | private static final Logger LOGGER = LoggerFactory.getLogger(ControllerAspect.class); 162 | 163 | private long begin; 164 | 165 | @Override 166 | public void before(Class cls, Method method, Object[] params) throws Throwable { 167 | LOGGER.debug("---------- begin ----------"); 168 | LOGGER.debug(String.format("class: %s", cls.getName())); 169 | LOGGER.debug(String.format("method: %s", method.getName())); 170 | begin = System.currentTimeMillis(); 171 | } 172 | 173 | @Override 174 | public void after(Class cls, Method method, Object[] params, Object result) throws Throwable { 175 | LOGGER.debug(String.format("time: %dms", System.currentTimeMillis() - begin)); 176 | LOGGER.debug("----------- end -----------"); 177 | } 178 | } 179 | ``` 180 | 181 | AspectProxy抽象类中提供了一系列横切逻辑方法以供重写,具体可以直接进入源码查看 182 | 183 | ## Simple-Framework开发说明 184 | 185 | [这里](note/note.md)教你如何从零开发Simple-Framework框架,介绍本项目各个功能模块的实现要点,开发思路,以及一些学习笔记。 -------------------------------------------------------------------------------- /managementsystem/managementsystem.iml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /managementsystem/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.xctian 8 | management-system 9 | 1.0-SNAPSHOT 10 | war 11 | 12 | 13 | 14 | 15 | com.xctian 16 | simple-framework 17 | 1.0-SNAPSHOT 18 | 19 | 20 | 21 | org.slf4j 22 | slf4j-log4j12 23 | 1.7.7 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | org.apache.maven.plugins 32 | maven-compiler-plugin 33 | 3.3 34 | 35 | 1.6 36 | 1.6 37 | 38 | 39 | 40 | 41 | org.apache.maven.plugins 42 | maven-surefire-plugin 43 | 2.18.1 44 | 45 | true 46 | 47 | 48 | 49 | 50 | org.apache.tomcat.maven 51 | tomcat7-maven-plugin 52 | 2.2 53 | 54 | /${project.artifactId} 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /managementsystem/src/main/java/org/simple4j/managementsystem/Service/CustomerService.java: -------------------------------------------------------------------------------- 1 | package org.simple4j.managementsystem.Service; 2 | 3 | import com.xctian.framework.annotation.Service; 4 | import com.xctian.framework.annotation.Transaction; 5 | import com.xctian.framework.helper.DatabaseHelper; 6 | import org.simple4j.managementsystem.model.Customer; 7 | 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | /** 12 | * @author xctian 13 | * @date 2020/1/29 14 | */ 15 | @Service 16 | public class CustomerService { 17 | 18 | /** 19 | * 获取客户列表 20 | */ 21 | public List getCustomerList() { 22 | String sql = "SELECT * FROM customer"; 23 | return DatabaseHelper.queryEntityList(Customer.class, sql); 24 | } 25 | 26 | /** 27 | * 获取客户 28 | */ 29 | public Customer getCustomer(long id) { 30 | String sql = "SELECT * FROM customer WHERE id = ?"; 31 | return DatabaseHelper.queryEntity(Customer.class, sql, id); 32 | } 33 | 34 | /** 35 | * 创建客户 36 | */ 37 | @Transaction 38 | public boolean createCustomer(Map fieldMap) { 39 | return DatabaseHelper.insertEntity(Customer.class, fieldMap); 40 | } 41 | 42 | /** 43 | * 更新客户 44 | */ 45 | @Transaction 46 | public boolean updateCustomer(long id, Map fieldMap) { 47 | return DatabaseHelper.updateEntity(Customer.class, id, fieldMap); 48 | } 49 | 50 | /** 51 | * 删除客户 52 | */ 53 | @Transaction 54 | public boolean deleteCustomer(long id) { 55 | return DatabaseHelper.deleteEntity(Customer.class, id); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /managementsystem/src/main/java/org/simple4j/managementsystem/aspect/ControllerAspect.java: -------------------------------------------------------------------------------- 1 | package org.simple4j.managementsystem.aspect; 2 | 3 | import com.xctian.framework.annotation.Aspect; 4 | import com.xctian.framework.annotation.Controller; 5 | import com.xctian.framework.proxy.AspectProxy; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.lang.reflect.Method; 10 | 11 | /** 12 | * 拦截Controller所有方法 13 | * 14 | * @author xctian 15 | * @date 2020/1/28 16 | */ 17 | @Aspect(Controller.class) 18 | public class ControllerAspect extends AspectProxy { 19 | 20 | private static final Logger LOGGER = LoggerFactory.getLogger(ControllerAspect.class); 21 | 22 | private long begin; 23 | 24 | @Override 25 | public void before(Class cls, Method method, Object[] params) throws Throwable { 26 | LOGGER.debug("---------- begin ----------"); 27 | LOGGER.debug(String.format("class: %s", cls.getName())); 28 | LOGGER.debug(String.format("method: %s", method.getName())); 29 | begin = System.currentTimeMillis(); 30 | } 31 | 32 | @Override 33 | public void after(Class cls, Method method, Object[] params, Object result) throws Throwable { 34 | LOGGER.debug(String.format("time: %dms", System.currentTimeMillis() - begin)); 35 | LOGGER.debug("----------- end -----------"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /managementsystem/src/main/java/org/simple4j/managementsystem/controller/CustomerController.java: -------------------------------------------------------------------------------- 1 | package org.simple4j.managementsystem.controller; 2 | 3 | import com.xctian.framework.annotation.Action; 4 | import com.xctian.framework.annotation.Controller; 5 | import com.xctian.framework.annotation.Inject; 6 | import com.xctian.framework.bean.Data; 7 | import com.xctian.framework.bean.Param; 8 | import com.xctian.framework.bean.View; 9 | import org.simple4j.managementsystem.Service.CustomerService; 10 | import org.simple4j.managementsystem.model.Customer; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | /** 18 | * 处理客户管理相关请求 19 | * 20 | * @author xctian 21 | * @date 2020/1/29 22 | */ 23 | @Controller 24 | public class CustomerController { 25 | 26 | private static final Logger LOGGER = LoggerFactory.getLogger(CustomerController.class); 27 | 28 | @Inject 29 | private CustomerService customerService; 30 | 31 | /** 32 | * 进入 客户列表 界面 33 | */ 34 | @Action("get:/customer") 35 | public View index() { 36 | List customerList = customerService.getCustomerList(); 37 | LOGGER.info("列表展示"); 38 | return new View("customer.jsp").addModel("customerList", customerList); 39 | } 40 | 41 | /** 42 | * 显示客户基本信息 43 | */ 44 | @Action("get:/customer_show") 45 | public View show(Param param) { 46 | long id = param.getLong("id"); 47 | Customer customer = customerService.getCustomer(id); 48 | return new View("customer_show.jsp").addModel("customer", customer); 49 | } 50 | 51 | /** 52 | * 进入 创建客户 界面 53 | */ 54 | @Action("get:/customer_create") 55 | public View create() { 56 | return new View("customer_create.jsp"); 57 | } 58 | 59 | /** 60 | * 进入 编辑客户 界面 61 | */ 62 | @Action("get:/customer_edit") 63 | public View edit(Param param) { 64 | long id = param.getLong("id"); 65 | Customer customer = customerService.getCustomer(id); 66 | LOGGER.info("进入编辑界面"); 67 | return new View("customer_edit.jsp").addModel("customer", customer); 68 | } 69 | 70 | /** 71 | * 处理 编辑客户 请求 72 | */ 73 | @Action("put:/customer_edit") 74 | public Data editSubmit(Param param) { 75 | long id = param.getLong("id"); 76 | Map fieldMap = param.getParamMap(); 77 | boolean result = customerService.updateCustomer(id, fieldMap); 78 | return new Data(result); 79 | } 80 | 81 | /** 82 | * 处理 删除客户 请求 83 | */ 84 | @Action("delete:/customer_edit") 85 | public Data delete(Param param) { 86 | long id = param.getLong("id"); 87 | boolean result = customerService.deleteCustomer(id); 88 | return new Data(result); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /managementsystem/src/main/java/org/simple4j/managementsystem/model/Customer.java: -------------------------------------------------------------------------------- 1 | package org.simple4j.managementsystem.model; 2 | 3 | /** 4 | * 客户 5 | * 6 | * @author xctian 7 | * @date 2020/1/29 8 | */ 9 | public class Customer { 10 | /** 11 | * ID 12 | */ 13 | private long id; 14 | 15 | /** 16 | * 客户名称 17 | */ 18 | private String name; 19 | 20 | /** 21 | * 联系人 22 | */ 23 | private String contact; 24 | 25 | /** 26 | * 电话号码 27 | */ 28 | private String telephone; 29 | 30 | /** 31 | * 邮箱地址 32 | */ 33 | private String email; 34 | 35 | /** 36 | * 备注 37 | */ 38 | private String remark; 39 | 40 | public long getId() { 41 | return id; 42 | } 43 | 44 | public void setId(long id) { 45 | this.id = id; 46 | } 47 | 48 | public String getName() { 49 | return name; 50 | } 51 | 52 | public void setName(String name) { 53 | this.name = name; 54 | } 55 | 56 | public String getContact() { 57 | return contact; 58 | } 59 | 60 | public void setContact(String contact) { 61 | this.contact = contact; 62 | } 63 | 64 | public String getTelephone() { 65 | return telephone; 66 | } 67 | 68 | public void setTelephone(String telephone) { 69 | this.telephone = telephone; 70 | } 71 | 72 | public String getEmail() { 73 | return email; 74 | } 75 | 76 | public void setEmail(String email) { 77 | this.email = email; 78 | } 79 | 80 | public String getRemark() { 81 | return remark; 82 | } 83 | 84 | public void setRemark(String remark) { 85 | this.remark = remark; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /managementsystem/src/main/resources/log4j.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/src/main/resources/log4j.properties -------------------------------------------------------------------------------- /managementsystem/src/main/resources/simple.properties: -------------------------------------------------------------------------------- 1 | simple.framework.jdbc.driver=com.mysql.jdbc.Driver 2 | simple.framework.jdbc.url=jdbc:mysql://localhost:3306/demo 3 | simple.framework.jdbc.username=root 4 | simple.framework.jdbc.password=admin 5 | 6 | simple.framework.app.base_package=org.simple4j.managementsystem 7 | simple.framework.app.jsp_path=/WEB-INF/view/ 8 | simple.framework.app.asset_path=/asset/ 9 | -------------------------------------------------------------------------------- /managementsystem/src/main/webapp/WEB-INF/view/customer.jsp: -------------------------------------------------------------------------------- 1 | <%@ page pageEncoding="UTF-8" %> 2 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 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 | 32 | 33 | 34 |
客户名称联系人电话号码邮箱地址操作
${customer.name}${customer.contact}${customer.telephone}${customer.email} 29 | 编辑 30 | 删除 31 |
35 | 36 | 37 | -------------------------------------------------------------------------------- /managementsystem/src/main/webapp/WEB-INF/view/customer_create.jsp: -------------------------------------------------------------------------------- 1 | <%@ page pageEncoding="UTF-8" %> 2 | 3 | 4 | 客户管理 - 创建客户 5 | 6 | 7 | 8 |

创建客户界面

9 | 10 | <%-- TODO --%> 11 | 12 | 13 | -------------------------------------------------------------------------------- /managementsystem/src/main/webapp/WEB-INF/view/customer_edit.jsp: -------------------------------------------------------------------------------- 1 | <%@ page pageEncoding="UTF-8" %> 2 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 3 | 4 | 5 | 6 | 7 | 8 | 客户管理 - 编辑客户 9 | 10 | 11 | 12 |

编辑客户界面

13 | 14 |
15 | 16 | 17 | 18 | 19 | 22 | 23 | 24 | 25 | 28 | 29 | 30 | 31 | 34 | 35 | 36 | 37 | 40 | 41 |
客户名称: 20 | 21 |
联系人: 26 | 27 |
电话号码: 32 | 33 |
邮箱地址: 38 | 39 |
42 | 43 |
44 | 45 | 46 | 47 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /managementsystem/src/main/webapp/WEB-INF/view/customer_show.jsp: -------------------------------------------------------------------------------- 1 | <%@ page pageEncoding="UTF-8" %> 2 | 3 | 4 | 客户管理 - 查看客户 5 | 6 | 7 | 8 |

查看客户界面

9 | 10 | <%-- TODO --%> 11 | 12 | 13 | -------------------------------------------------------------------------------- /managementsystem/src/main/webapp/asset/lib/jquery-form/jquery.form.min.js: -------------------------------------------------------------------------------- 1 | !function(e){"use strict";"function"==typeof define&&define.amd?define(["jquery"],e):e("undefined"!=typeof jQuery?jQuery:window.Zepto)}(function(e){"use strict";function t(t){var r=t.data;t.isDefaultPrevented()||(t.preventDefault(),e(t.target).ajaxSubmit(r))}function r(t){var r=t.target,a=e(r);if(!a.is("[type=submit],[type=image]")){var n=a.closest("[type=submit]");if(0===n.length)return;r=n[0]}var i=this;if(i.clk=r,"image"==r.type)if(void 0!==t.offsetX)i.clk_x=t.offsetX,i.clk_y=t.offsetY;else if("function"==typeof e.fn.offset){var o=a.offset();i.clk_x=t.pageX-o.left,i.clk_y=t.pageY-o.top}else i.clk_x=t.pageX-r.offsetLeft,i.clk_y=t.pageY-r.offsetTop;setTimeout(function(){i.clk=i.clk_x=i.clk_y=null},100)}function a(){if(e.fn.ajaxSubmit.debug){var t="[jquery.form] "+Array.prototype.join.call(arguments,"");window.console&&window.console.log?window.console.log(t):window.opera&&window.opera.postError&&window.opera.postError(t)}}var n={};n.fileapi=void 0!==e("").get(0).files,n.formdata=void 0!==window.FormData;var i=!!e.fn.prop;e.fn.attr2=function(){if(!i)return this.attr.apply(this,arguments);var e=this.prop.apply(this,arguments);return e&&e.jquery||"string"==typeof e?e:this.attr.apply(this,arguments)},e.fn.ajaxSubmit=function(t){function r(r){var a,n,i=e.param(r,t.traditional).split("&"),o=i.length,s=[];for(a=0;o>a;a++)i[a]=i[a].replace(/\+/g," "),n=i[a].split("="),s.push([decodeURIComponent(n[0]),decodeURIComponent(n[1])]);return s}function o(a){for(var n=new FormData,i=0;i').val(m.extraData[d].value).appendTo(w)[0]:e('').val(m.extraData[d]).appendTo(w)[0]);m.iframeTarget||v.appendTo("body"),g.attachEvent?g.attachEvent("onload",s):g.addEventListener("load",s,!1),setTimeout(t,15);try{w.submit()}catch(h){var x=document.createElement("form").submit;x.apply(w)}}finally{w.setAttribute("action",i),w.setAttribute("enctype",c),r?w.setAttribute("target",r):f.removeAttr("target"),e(l).remove()}}function s(t){if(!x.aborted&&!F){if(M=n(g),M||(a("cannot access response document"),t=k),t===D&&x)return x.abort("timeout"),void S.reject(x,"timeout");if(t==k&&x)return x.abort("server abort"),void S.reject(x,"error","server abort");if(M&&M.location.href!=m.iframeSrc||T){g.detachEvent?g.detachEvent("onload",s):g.removeEventListener("load",s,!1);var r,i="success";try{if(T)throw"timeout";var o="xml"==m.dataType||M.XMLDocument||e.isXMLDoc(M);if(a("isXml="+o),!o&&window.opera&&(null===M.body||!M.body.innerHTML)&&--O)return a("requeing onLoad callback, DOM not available"),void setTimeout(s,250);var u=M.body?M.body:M.documentElement;x.responseText=u?u.innerHTML:null,x.responseXML=M.XMLDocument?M.XMLDocument:M,o&&(m.dataType="xml"),x.getResponseHeader=function(e){var t={"content-type":m.dataType};return t[e.toLowerCase()]},u&&(x.status=Number(u.getAttribute("status"))||x.status,x.statusText=u.getAttribute("statusText")||x.statusText);var c=(m.dataType||"").toLowerCase(),l=/(json|script|text)/.test(c);if(l||m.textarea){var f=M.getElementsByTagName("textarea")[0];if(f)x.responseText=f.value,x.status=Number(f.getAttribute("status"))||x.status,x.statusText=f.getAttribute("statusText")||x.statusText;else if(l){var p=M.getElementsByTagName("pre")[0],h=M.getElementsByTagName("body")[0];p?x.responseText=p.textContent?p.textContent:p.innerText:h&&(x.responseText=h.textContent?h.textContent:h.innerText)}}else"xml"==c&&!x.responseXML&&x.responseText&&(x.responseXML=X(x.responseText));try{E=_(x,c,m)}catch(y){i="parsererror",x.error=r=y||i}}catch(y){a("error caught: ",y),i="error",x.error=r=y||i}x.aborted&&(a("upload aborted"),i=null),x.status&&(i=x.status>=200&&x.status<300||304===x.status?"success":"error"),"success"===i?(m.success&&m.success.call(m.context,E,"success",x),S.resolve(x.responseText,"success",x),d&&e.event.trigger("ajaxSuccess",[x,m])):i&&(void 0===r&&(r=x.statusText),m.error&&m.error.call(m.context,x,i,r),S.reject(x,"error",r),d&&e.event.trigger("ajaxError",[x,m,r])),d&&e.event.trigger("ajaxComplete",[x,m]),d&&!--e.active&&e.event.trigger("ajaxStop"),m.complete&&m.complete.call(m.context,x,i),F=!0,m.timeout&&clearTimeout(j),setTimeout(function(){m.iframeTarget?v.attr("src",m.iframeSrc):v.remove(),x.responseXML=null},100)}}}var c,l,m,d,p,v,g,x,y,b,T,j,w=f[0],S=e.Deferred();if(S.abort=function(e){x.abort(e)},r)for(l=0;l'),v.css({position:"absolute",top:"-1000px",left:"-1000px"})),g=v[0],x={aborted:0,responseText:null,responseXML:null,status:0,statusText:"n/a",getAllResponseHeaders:function(){},getResponseHeader:function(){},setRequestHeader:function(){},abort:function(t){var r="timeout"===t?"timeout":"aborted";a("aborting upload... "+r),this.aborted=1;try{g.contentWindow.document.execCommand&&g.contentWindow.document.execCommand("Stop")}catch(n){}v.attr("src",m.iframeSrc),x.error=r,m.error&&m.error.call(m.context,x,r,t),d&&e.event.trigger("ajaxError",[x,m,r]),m.complete&&m.complete.call(m.context,x,r)}},d=m.global,d&&0===e.active++&&e.event.trigger("ajaxStart"),d&&e.event.trigger("ajaxSend",[x,m]),m.beforeSend&&m.beforeSend.call(m.context,x,m)===!1)return m.global&&e.active--,S.reject(),S;if(x.aborted)return S.reject(),S;y=w.clk,y&&(b=y.name,b&&!y.disabled&&(m.extraData=m.extraData||{},m.extraData[b]=y.value,"image"==y.type&&(m.extraData[b+".x"]=w.clk_x,m.extraData[b+".y"]=w.clk_y)));var D=1,k=2,A=e("meta[name=csrf-token]").attr("content"),L=e("meta[name=csrf-param]").attr("content");L&&A&&(m.extraData=m.extraData||{},m.extraData[L]=A),m.forceSync?o():setTimeout(o,10);var E,M,F,O=50,X=e.parseXML||function(e,t){return window.ActiveXObject?(t=new ActiveXObject("Microsoft.XMLDOM"),t.async="false",t.loadXML(e)):t=(new DOMParser).parseFromString(e,"text/xml"),t&&t.documentElement&&"parsererror"!=t.documentElement.nodeName?t:null},C=e.parseJSON||function(e){return window.eval("("+e+")")},_=function(t,r,a){var n=t.getResponseHeader("content-type")||"",i="xml"===r||!r&&n.indexOf("xml")>=0,o=i?t.responseXML:t.responseText;return i&&"parsererror"===o.documentElement.nodeName&&e.error&&e.error("parsererror"),a&&a.dataFilter&&(o=a.dataFilter(o,r)),"string"==typeof o&&("json"===r||!r&&n.indexOf("json")>=0?o=C(o):("script"===r||!r&&n.indexOf("javascript")>=0)&&e.globalEval(o)),o};return S}if(!this.length)return a("ajaxSubmit: skipping submit process - no element selected"),this;var u,c,l,f=this;"function"==typeof t?t={success:t}:void 0===t&&(t={}),u=t.type||this.attr2("method"),c=t.url||this.attr2("action"),l="string"==typeof c?e.trim(c):"",l=l||window.location.href||"",l&&(l=(l.match(/^([^#]+)/)||[])[1]),t=e.extend(!0,{url:l,success:e.ajaxSettings.success,type:u||e.ajaxSettings.type,iframeSrc:/^https/i.test(window.location.href||"")?"javascript:false":"about:blank"},t);var m={};if(this.trigger("form-pre-serialize",[this,t,m]),m.veto)return a("ajaxSubmit: submit vetoed via form-pre-serialize trigger"),this;if(t.beforeSerialize&&t.beforeSerialize(this,t)===!1)return a("ajaxSubmit: submit aborted via beforeSerialize callback"),this;var d=t.traditional;void 0===d&&(d=e.ajaxSettings.traditional);var p,h=[],v=this.formToArray(t.semantic,h);if(t.data&&(t.extraData=t.data,p=e.param(t.data,d)),t.beforeSubmit&&t.beforeSubmit(v,this,t)===!1)return a("ajaxSubmit: submit aborted via beforeSubmit callback"),this;if(this.trigger("form-submit-validate",[v,this,t,m]),m.veto)return a("ajaxSubmit: submit vetoed via form-submit-validate trigger"),this;var g=e.param(v,d);p&&(g=g?g+"&"+p:p),"GET"==t.type.toUpperCase()?(t.url+=(t.url.indexOf("?")>=0?"&":"?")+g,t.data=null):t.data=g;var x=[];if(t.resetForm&&x.push(function(){f.resetForm()}),t.clearForm&&x.push(function(){f.clearForm(t.includeHidden)}),!t.dataType&&t.target){var y=t.success||function(){};x.push(function(r){var a=t.replaceTarget?"replaceWith":"html";e(t.target)[a](r).each(y,arguments)})}else t.success&&x.push(t.success);if(t.success=function(e,r,a){for(var n=t.context||this,i=0,o=x.length;o>i;i++)x[i].apply(n,[e,r,a||f,f])},t.error){var b=t.error;t.error=function(e,r,a){var n=t.context||this;b.apply(n,[e,r,a,f])}}if(t.complete){var T=t.complete;t.complete=function(e,r){var a=t.context||this;T.apply(a,[e,r,f])}}var j=e("input[type=file]:enabled",this).filter(function(){return""!==e(this).val()}),w=j.length>0,S="multipart/form-data",D=f.attr("enctype")==S||f.attr("encoding")==S,k=n.fileapi&&n.formdata;a("fileAPI :"+k);var A,L=(w||D)&&!k;t.iframe!==!1&&(t.iframe||L)?t.closeKeepAlive?e.get(t.closeKeepAlive,function(){A=s(v)}):A=s(v):A=(w||D)&&k?o(v):e.ajax(t),f.removeData("jqxhr").data("jqxhr",A);for(var E=0;Ec;c++)if(d=u[c],f=d.name,f&&!d.disabled)if(t&&o.clk&&"image"==d.type)o.clk==d&&(a.push({name:f,value:e(d).val(),type:d.type}),a.push({name:f+".x",value:o.clk_x},{name:f+".y",value:o.clk_y}));else if(m=e.fieldValue(d,!0),m&&m.constructor==Array)for(r&&r.push(d),l=0,h=m.length;h>l;l++)a.push({name:f,value:m[l]});else if(n.fileapi&&"file"==d.type){r&&r.push(d);var v=d.files;if(v.length)for(l=0;li;i++)r.push({name:a,value:n[i]});else null!==n&&"undefined"!=typeof n&&r.push({name:this.name,value:n})}}),e.param(r)},e.fn.fieldValue=function(t){for(var r=[],a=0,n=this.length;n>a;a++){var i=this[a],o=e.fieldValue(i,t);null===o||"undefined"==typeof o||o.constructor==Array&&!o.length||(o.constructor==Array?e.merge(r,o):r.push(o))}return r},e.fieldValue=function(t,r){var a=t.name,n=t.type,i=t.tagName.toLowerCase();if(void 0===r&&(r=!0),r&&(!a||t.disabled||"reset"==n||"button"==n||("checkbox"==n||"radio"==n)&&!t.checked||("submit"==n||"image"==n)&&t.form&&t.form.clk!=t||"select"==i&&-1==t.selectedIndex))return null;if("select"==i){var o=t.selectedIndex;if(0>o)return null;for(var s=[],u=t.options,c="select-one"==n,l=c?o+1:u.length,f=c?o:0;l>f;f++){var m=u[f];if(m.selected){var d=m.value;if(d||(d=m.attributes&&m.attributes.value&&!m.attributes.value.specified?m.text:m.value),c)return d;s.push(d)}}return s}return e(t).val()},e.fn.clearForm=function(t){return this.each(function(){e("input,select,textarea",this).clearFields(t)})},e.fn.clearFields=e.fn.clearInputs=function(t){var r=/^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i;return this.each(function(){var a=this.type,n=this.tagName.toLowerCase();r.test(a)||"textarea"==n?this.value="":"checkbox"==a||"radio"==a?this.checked=!1:"select"==n?this.selectedIndex=-1:"file"==a?/MSIE/.test(navigator.userAgent)?e(this).replaceWith(e(this).clone(!0)):e(this).val(""):t&&(t===!0&&/hidden/.test(a)||"string"==typeof t&&e(this).is(t))&&(this.value="")})},e.fn.resetForm=function(){return this.each(function(){("function"==typeof this.reset||"object"==typeof this.reset&&!this.reset.nodeType)&&this.reset()})},e.fn.enable=function(e){return void 0===e&&(e=!0),this.each(function(){this.disabled=!e})},e.fn.selected=function(t){return void 0===t&&(t=!0),this.each(function(){var r=this.type;if("checkbox"==r||"radio"==r)this.checked=t;else if("option"==this.tagName.toLowerCase()){var a=e(this).parent("select");t&&a[0]&&"select-one"==a[0].type&&a.find("option").selected(!1),this.selected=t}})},e.fn.ajaxSubmit.debug=!1}); -------------------------------------------------------------------------------- /managementsystem/target/classes/AppSecurity.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/classes/AppSecurity.class -------------------------------------------------------------------------------- /managementsystem/target/classes/log4j.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/classes/log4j.properties -------------------------------------------------------------------------------- /managementsystem/target/classes/org/simple4j/managementsystem/Service/CustomerService.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/classes/org/simple4j/managementsystem/Service/CustomerService.class -------------------------------------------------------------------------------- /managementsystem/target/classes/org/simple4j/managementsystem/aspect/ControllerAspect.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/classes/org/simple4j/managementsystem/aspect/ControllerAspect.class -------------------------------------------------------------------------------- /managementsystem/target/classes/org/simple4j/managementsystem/controller/CustomerController.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/classes/org/simple4j/managementsystem/controller/CustomerController.class -------------------------------------------------------------------------------- /managementsystem/target/classes/org/simple4j/managementsystem/model/Customer.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/classes/org/simple4j/managementsystem/model/Customer.class -------------------------------------------------------------------------------- /managementsystem/target/classes/simple.properties: -------------------------------------------------------------------------------- 1 | simple.framework.jdbc.driver=com.mysql.jdbc.Driver 2 | simple.framework.jdbc.url=jdbc:mysql://localhost:3306/demo 3 | simple.framework.jdbc.username=root 4 | simple.framework.jdbc.password=admin 5 | 6 | simple.framework.app.base_package=org.simple4j.managementsystem 7 | simple.framework.app.jsp_path=/WEB-INF/view/ 8 | simple.framework.app.asset_path=/asset/ 9 | -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Built-By: xctian 3 | Created-By: IntelliJ IDEA 4 | Build-Jdk: 1.8.0_191 5 | 6 | -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/classes/AppSecurity.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/classes/AppSecurity.class -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/classes/log4j.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/classes/log4j.properties -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/classes/org/simple4j/managementsystem/Service/CustomerService.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/classes/org/simple4j/managementsystem/Service/CustomerService.class -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/classes/org/simple4j/managementsystem/aspect/ControllerAspect.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/classes/org/simple4j/managementsystem/aspect/ControllerAspect.class -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/classes/org/simple4j/managementsystem/controller/CustomerController.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/classes/org/simple4j/managementsystem/controller/CustomerController.class -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/classes/org/simple4j/managementsystem/model/Customer.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/classes/org/simple4j/managementsystem/model/Customer.class -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/classes/simple.properties: -------------------------------------------------------------------------------- 1 | simple.framework.jdbc.driver=com.mysql.jdbc.Driver 2 | simple.framework.jdbc.url=jdbc:mysql://localhost:3306/demo 3 | simple.framework.jdbc.username=root 4 | simple.framework.jdbc.password=admin 5 | 6 | simple.framework.app.base_package=org.simple4j.managementsystem 7 | simple.framework.app.jsp_path=/WEB-INF/view/ 8 | simple.framework.app.asset_path=/asset/ 9 | -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/asm-3.3.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/asm-3.3.1.jar -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/cglib-2.2.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/cglib-2.2.2.jar -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/commons-codec-1.10.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/commons-codec-1.10.jar -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/commons-collections4-4.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/commons-collections4-4.0.jar -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/commons-dbcp2-2.0.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/commons-dbcp2-2.0.1.jar -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/commons-dbutils-1.6.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/commons-dbutils-1.6.jar -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/commons-fileupload-1.3.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/commons-fileupload-1.3.1.jar -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/commons-io-2.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/commons-io-2.2.jar -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/commons-lang3-3.3.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/commons-lang3-3.3.2.jar -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/commons-logging-1.1.3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/commons-logging-1.1.3.jar -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/commons-pool2-2.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/commons-pool2-2.2.jar -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/jackson-annotations-2.5.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/jackson-annotations-2.5.0.jar -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/jackson-core-2.5.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/jackson-core-2.5.1.jar -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/jackson-databind-2.5.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/jackson-databind-2.5.2.jar -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/jstl-1.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/jstl-1.2.jar -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/log4j-1.2.17.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/log4j-1.2.17.jar -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/mysql-connector-java-5.1.33.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/mysql-connector-java-5.1.33.jar -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/simple-framework-1.0-SNAPSHOT.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/simple-framework-1.0-SNAPSHOT.jar -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/slf4j-api-1.7.7.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/slf4j-api-1.7.7.jar -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/slf4j-log4j12-1.7.7.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/lib/slf4j-log4j12-1.7.7.jar -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/view/customer.jsp: -------------------------------------------------------------------------------- 1 | <%@ page pageEncoding="UTF-8" %> 2 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 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 | 32 | 33 | 34 |
客户名称联系人电话号码邮箱地址操作
${customer.name}${customer.contact}${customer.telephone}${customer.email} 29 | 编辑 30 | 删除 31 |
35 | 36 | 37 | -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/view/customer_create.jsp: -------------------------------------------------------------------------------- 1 | <%@ page pageEncoding="UTF-8" %> 2 | 3 | 4 | 客户管理 - 创建客户 5 | 6 | 7 | 8 |

创建客户界面

9 | 10 | <%-- TODO --%> 11 | 12 | 13 | -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/view/customer_edit.jsp: -------------------------------------------------------------------------------- 1 | <%@ page pageEncoding="UTF-8" %> 2 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 3 | 4 | 5 | 6 | 7 | 8 | 客户管理 - 编辑客户 9 | 10 | 11 | 12 |

编辑客户界面

13 | 14 |
15 | 16 | 17 | 18 | 19 | 22 | 23 | 24 | 25 | 28 | 29 | 30 | 31 | 34 | 35 | 36 | 37 | 40 | 41 |
客户名称: 20 | 21 |
联系人: 26 | 27 |
电话号码: 32 | 33 |
邮箱地址: 38 | 39 |
42 | 43 |
44 | 45 | 46 | 47 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/WEB-INF/view/customer_show.jsp: -------------------------------------------------------------------------------- 1 | <%@ page pageEncoding="UTF-8" %> 2 | 3 | 4 | 客户管理 - 查看客户 5 | 6 | 7 | 8 |

查看客户界面

9 | 10 | <%-- TODO --%> 11 | 12 | 13 | -------------------------------------------------------------------------------- /managementsystem/target/management-system-1.0-SNAPSHOT/asset/lib/jquery-form/jquery.form.min.js: -------------------------------------------------------------------------------- 1 | !function(e){"use strict";"function"==typeof define&&define.amd?define(["jquery"],e):e("undefined"!=typeof jQuery?jQuery:window.Zepto)}(function(e){"use strict";function t(t){var r=t.data;t.isDefaultPrevented()||(t.preventDefault(),e(t.target).ajaxSubmit(r))}function r(t){var r=t.target,a=e(r);if(!a.is("[type=submit],[type=image]")){var n=a.closest("[type=submit]");if(0===n.length)return;r=n[0]}var i=this;if(i.clk=r,"image"==r.type)if(void 0!==t.offsetX)i.clk_x=t.offsetX,i.clk_y=t.offsetY;else if("function"==typeof e.fn.offset){var o=a.offset();i.clk_x=t.pageX-o.left,i.clk_y=t.pageY-o.top}else i.clk_x=t.pageX-r.offsetLeft,i.clk_y=t.pageY-r.offsetTop;setTimeout(function(){i.clk=i.clk_x=i.clk_y=null},100)}function a(){if(e.fn.ajaxSubmit.debug){var t="[jquery.form] "+Array.prototype.join.call(arguments,"");window.console&&window.console.log?window.console.log(t):window.opera&&window.opera.postError&&window.opera.postError(t)}}var n={};n.fileapi=void 0!==e("").get(0).files,n.formdata=void 0!==window.FormData;var i=!!e.fn.prop;e.fn.attr2=function(){if(!i)return this.attr.apply(this,arguments);var e=this.prop.apply(this,arguments);return e&&e.jquery||"string"==typeof e?e:this.attr.apply(this,arguments)},e.fn.ajaxSubmit=function(t){function r(r){var a,n,i=e.param(r,t.traditional).split("&"),o=i.length,s=[];for(a=0;o>a;a++)i[a]=i[a].replace(/\+/g," "),n=i[a].split("="),s.push([decodeURIComponent(n[0]),decodeURIComponent(n[1])]);return s}function o(a){for(var n=new FormData,i=0;i').val(m.extraData[d].value).appendTo(w)[0]:e('').val(m.extraData[d]).appendTo(w)[0]);m.iframeTarget||v.appendTo("body"),g.attachEvent?g.attachEvent("onload",s):g.addEventListener("load",s,!1),setTimeout(t,15);try{w.submit()}catch(h){var x=document.createElement("form").submit;x.apply(w)}}finally{w.setAttribute("action",i),w.setAttribute("enctype",c),r?w.setAttribute("target",r):f.removeAttr("target"),e(l).remove()}}function s(t){if(!x.aborted&&!F){if(M=n(g),M||(a("cannot access response document"),t=k),t===D&&x)return x.abort("timeout"),void S.reject(x,"timeout");if(t==k&&x)return x.abort("server abort"),void S.reject(x,"error","server abort");if(M&&M.location.href!=m.iframeSrc||T){g.detachEvent?g.detachEvent("onload",s):g.removeEventListener("load",s,!1);var r,i="success";try{if(T)throw"timeout";var o="xml"==m.dataType||M.XMLDocument||e.isXMLDoc(M);if(a("isXml="+o),!o&&window.opera&&(null===M.body||!M.body.innerHTML)&&--O)return a("requeing onLoad callback, DOM not available"),void setTimeout(s,250);var u=M.body?M.body:M.documentElement;x.responseText=u?u.innerHTML:null,x.responseXML=M.XMLDocument?M.XMLDocument:M,o&&(m.dataType="xml"),x.getResponseHeader=function(e){var t={"content-type":m.dataType};return t[e.toLowerCase()]},u&&(x.status=Number(u.getAttribute("status"))||x.status,x.statusText=u.getAttribute("statusText")||x.statusText);var c=(m.dataType||"").toLowerCase(),l=/(json|script|text)/.test(c);if(l||m.textarea){var f=M.getElementsByTagName("textarea")[0];if(f)x.responseText=f.value,x.status=Number(f.getAttribute("status"))||x.status,x.statusText=f.getAttribute("statusText")||x.statusText;else if(l){var p=M.getElementsByTagName("pre")[0],h=M.getElementsByTagName("body")[0];p?x.responseText=p.textContent?p.textContent:p.innerText:h&&(x.responseText=h.textContent?h.textContent:h.innerText)}}else"xml"==c&&!x.responseXML&&x.responseText&&(x.responseXML=X(x.responseText));try{E=_(x,c,m)}catch(y){i="parsererror",x.error=r=y||i}}catch(y){a("error caught: ",y),i="error",x.error=r=y||i}x.aborted&&(a("upload aborted"),i=null),x.status&&(i=x.status>=200&&x.status<300||304===x.status?"success":"error"),"success"===i?(m.success&&m.success.call(m.context,E,"success",x),S.resolve(x.responseText,"success",x),d&&e.event.trigger("ajaxSuccess",[x,m])):i&&(void 0===r&&(r=x.statusText),m.error&&m.error.call(m.context,x,i,r),S.reject(x,"error",r),d&&e.event.trigger("ajaxError",[x,m,r])),d&&e.event.trigger("ajaxComplete",[x,m]),d&&!--e.active&&e.event.trigger("ajaxStop"),m.complete&&m.complete.call(m.context,x,i),F=!0,m.timeout&&clearTimeout(j),setTimeout(function(){m.iframeTarget?v.attr("src",m.iframeSrc):v.remove(),x.responseXML=null},100)}}}var c,l,m,d,p,v,g,x,y,b,T,j,w=f[0],S=e.Deferred();if(S.abort=function(e){x.abort(e)},r)for(l=0;l'),v.css({position:"absolute",top:"-1000px",left:"-1000px"})),g=v[0],x={aborted:0,responseText:null,responseXML:null,status:0,statusText:"n/a",getAllResponseHeaders:function(){},getResponseHeader:function(){},setRequestHeader:function(){},abort:function(t){var r="timeout"===t?"timeout":"aborted";a("aborting upload... "+r),this.aborted=1;try{g.contentWindow.document.execCommand&&g.contentWindow.document.execCommand("Stop")}catch(n){}v.attr("src",m.iframeSrc),x.error=r,m.error&&m.error.call(m.context,x,r,t),d&&e.event.trigger("ajaxError",[x,m,r]),m.complete&&m.complete.call(m.context,x,r)}},d=m.global,d&&0===e.active++&&e.event.trigger("ajaxStart"),d&&e.event.trigger("ajaxSend",[x,m]),m.beforeSend&&m.beforeSend.call(m.context,x,m)===!1)return m.global&&e.active--,S.reject(),S;if(x.aborted)return S.reject(),S;y=w.clk,y&&(b=y.name,b&&!y.disabled&&(m.extraData=m.extraData||{},m.extraData[b]=y.value,"image"==y.type&&(m.extraData[b+".x"]=w.clk_x,m.extraData[b+".y"]=w.clk_y)));var D=1,k=2,A=e("meta[name=csrf-token]").attr("content"),L=e("meta[name=csrf-param]").attr("content");L&&A&&(m.extraData=m.extraData||{},m.extraData[L]=A),m.forceSync?o():setTimeout(o,10);var E,M,F,O=50,X=e.parseXML||function(e,t){return window.ActiveXObject?(t=new ActiveXObject("Microsoft.XMLDOM"),t.async="false",t.loadXML(e)):t=(new DOMParser).parseFromString(e,"text/xml"),t&&t.documentElement&&"parsererror"!=t.documentElement.nodeName?t:null},C=e.parseJSON||function(e){return window.eval("("+e+")")},_=function(t,r,a){var n=t.getResponseHeader("content-type")||"",i="xml"===r||!r&&n.indexOf("xml")>=0,o=i?t.responseXML:t.responseText;return i&&"parsererror"===o.documentElement.nodeName&&e.error&&e.error("parsererror"),a&&a.dataFilter&&(o=a.dataFilter(o,r)),"string"==typeof o&&("json"===r||!r&&n.indexOf("json")>=0?o=C(o):("script"===r||!r&&n.indexOf("javascript")>=0)&&e.globalEval(o)),o};return S}if(!this.length)return a("ajaxSubmit: skipping submit process - no element selected"),this;var u,c,l,f=this;"function"==typeof t?t={success:t}:void 0===t&&(t={}),u=t.type||this.attr2("method"),c=t.url||this.attr2("action"),l="string"==typeof c?e.trim(c):"",l=l||window.location.href||"",l&&(l=(l.match(/^([^#]+)/)||[])[1]),t=e.extend(!0,{url:l,success:e.ajaxSettings.success,type:u||e.ajaxSettings.type,iframeSrc:/^https/i.test(window.location.href||"")?"javascript:false":"about:blank"},t);var m={};if(this.trigger("form-pre-serialize",[this,t,m]),m.veto)return a("ajaxSubmit: submit vetoed via form-pre-serialize trigger"),this;if(t.beforeSerialize&&t.beforeSerialize(this,t)===!1)return a("ajaxSubmit: submit aborted via beforeSerialize callback"),this;var d=t.traditional;void 0===d&&(d=e.ajaxSettings.traditional);var p,h=[],v=this.formToArray(t.semantic,h);if(t.data&&(t.extraData=t.data,p=e.param(t.data,d)),t.beforeSubmit&&t.beforeSubmit(v,this,t)===!1)return a("ajaxSubmit: submit aborted via beforeSubmit callback"),this;if(this.trigger("form-submit-validate",[v,this,t,m]),m.veto)return a("ajaxSubmit: submit vetoed via form-submit-validate trigger"),this;var g=e.param(v,d);p&&(g=g?g+"&"+p:p),"GET"==t.type.toUpperCase()?(t.url+=(t.url.indexOf("?")>=0?"&":"?")+g,t.data=null):t.data=g;var x=[];if(t.resetForm&&x.push(function(){f.resetForm()}),t.clearForm&&x.push(function(){f.clearForm(t.includeHidden)}),!t.dataType&&t.target){var y=t.success||function(){};x.push(function(r){var a=t.replaceTarget?"replaceWith":"html";e(t.target)[a](r).each(y,arguments)})}else t.success&&x.push(t.success);if(t.success=function(e,r,a){for(var n=t.context||this,i=0,o=x.length;o>i;i++)x[i].apply(n,[e,r,a||f,f])},t.error){var b=t.error;t.error=function(e,r,a){var n=t.context||this;b.apply(n,[e,r,a,f])}}if(t.complete){var T=t.complete;t.complete=function(e,r){var a=t.context||this;T.apply(a,[e,r,f])}}var j=e("input[type=file]:enabled",this).filter(function(){return""!==e(this).val()}),w=j.length>0,S="multipart/form-data",D=f.attr("enctype")==S||f.attr("encoding")==S,k=n.fileapi&&n.formdata;a("fileAPI :"+k);var A,L=(w||D)&&!k;t.iframe!==!1&&(t.iframe||L)?t.closeKeepAlive?e.get(t.closeKeepAlive,function(){A=s(v)}):A=s(v):A=(w||D)&&k?o(v):e.ajax(t),f.removeData("jqxhr").data("jqxhr",A);for(var E=0;Ec;c++)if(d=u[c],f=d.name,f&&!d.disabled)if(t&&o.clk&&"image"==d.type)o.clk==d&&(a.push({name:f,value:e(d).val(),type:d.type}),a.push({name:f+".x",value:o.clk_x},{name:f+".y",value:o.clk_y}));else if(m=e.fieldValue(d,!0),m&&m.constructor==Array)for(r&&r.push(d),l=0,h=m.length;h>l;l++)a.push({name:f,value:m[l]});else if(n.fileapi&&"file"==d.type){r&&r.push(d);var v=d.files;if(v.length)for(l=0;li;i++)r.push({name:a,value:n[i]});else null!==n&&"undefined"!=typeof n&&r.push({name:this.name,value:n})}}),e.param(r)},e.fn.fieldValue=function(t){for(var r=[],a=0,n=this.length;n>a;a++){var i=this[a],o=e.fieldValue(i,t);null===o||"undefined"==typeof o||o.constructor==Array&&!o.length||(o.constructor==Array?e.merge(r,o):r.push(o))}return r},e.fieldValue=function(t,r){var a=t.name,n=t.type,i=t.tagName.toLowerCase();if(void 0===r&&(r=!0),r&&(!a||t.disabled||"reset"==n||"button"==n||("checkbox"==n||"radio"==n)&&!t.checked||("submit"==n||"image"==n)&&t.form&&t.form.clk!=t||"select"==i&&-1==t.selectedIndex))return null;if("select"==i){var o=t.selectedIndex;if(0>o)return null;for(var s=[],u=t.options,c="select-one"==n,l=c?o+1:u.length,f=c?o:0;l>f;f++){var m=u[f];if(m.selected){var d=m.value;if(d||(d=m.attributes&&m.attributes.value&&!m.attributes.value.specified?m.text:m.value),c)return d;s.push(d)}}return s}return e(t).val()},e.fn.clearForm=function(t){return this.each(function(){e("input,select,textarea",this).clearFields(t)})},e.fn.clearFields=e.fn.clearInputs=function(t){var r=/^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i;return this.each(function(){var a=this.type,n=this.tagName.toLowerCase();r.test(a)||"textarea"==n?this.value="":"checkbox"==a||"radio"==a?this.checked=!1:"select"==n?this.selectedIndex=-1:"file"==a?/MSIE/.test(navigator.userAgent)?e(this).replaceWith(e(this).clone(!0)):e(this).val(""):t&&(t===!0&&/hidden/.test(a)||"string"==typeof t&&e(this).is(t))&&(this.value="")})},e.fn.resetForm=function(){return this.each(function(){("function"==typeof this.reset||"object"==typeof this.reset&&!this.reset.nodeType)&&this.reset()})},e.fn.enable=function(e){return void 0===e&&(e=!0),this.each(function(){this.disabled=!e})},e.fn.selected=function(t){return void 0===t&&(t=!0),this.each(function(){var r=this.type;if("checkbox"==r||"radio"==r)this.checked=t;else if("option"==this.tagName.toLowerCase()){var a=e(this).parent("select");t&&a[0]&&"select-one"==a[0].type&&a.find("option").selected(!1),this.selected=t}})},e.fn.ajaxSubmit.debug=!1}); -------------------------------------------------------------------------------- /note/img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcNew/simple-framework/5384f16a51d6e38918d259f679ceef905f150872/note/img/1.png -------------------------------------------------------------------------------- /note/note.md: -------------------------------------------------------------------------------- 1 | ## Simple-Framework开发文档 2 | 3 | ### Bean容器的实现 4 | 5 | **实现自定义的类加载工具类:**首先我们会在配置文件里去配置web应用的基础包名,它相当于我们web应用代码存放的一个基础路径。框架初始化时,ClassUtil会利用反射去加载基础包名下的所有.class文件,主要是利用class.forName方法(详情见ClassUtil),并存放到一个BEAN_SET集合里面。IOC容器的实现主要是依赖于一个Map对象,他的key是class对象,value是class对象对应的实例,在项目里面默认是单例的。 6 | 7 | **实现Bean容器:**完成了上述Set集合的初始化后,遍历整个Set集合,对于每一个class对象通过反射的方法实例化一个instance,然后将class对象和instance以key value的形式存入整个bean Map中,这样的话在框架最开始初始化的时候,map里面就已经初始化好了基础包名底下的所有class的实例对象 8 | 9 | ```java 10 | // 初始化,将所有实例化的Bean对象缓存至BEAN_MAP,实现创建好对象放入Bean容器,故可保证单例 11 | static { 12 | Set> beanClassSet = ClassHelper.getBeanClassSet(); 13 | for (Class cls : beanClassSet) { 14 | Object obj = ReflectionUtil.newInstance(cls); 15 | BEAN_MAP.put(cls, obj); 16 | } 17 | } 18 | ``` 19 | 20 | - 关于加载基础包名下所有class文件的实现思路:将包名中的'.'用'/'替换后,获取包名路径下的url列表,遍历所有url,通过url.getProtocol方法判断url头部,从而判断是jar文件还是file文件,如果是file的话递归调用加载所有的class文件,如果是jar的话使用jarURLConnection类去读取jar,遍历jar里的文件加载class结尾的类。 21 | 22 | ### IOC的实现(依赖注入) 23 | 24 | IOC的实现主要依赖于自定义的注解Inject,他是成员变量级别的注解,功能类似于Spring里面的@Autowired 25 | 26 | **实现原理:**框架启动的时候,先去遍历上述Bean容器Bean Map,对于每一个bean而言,我们通过反射的方法getDeclaredFields获得他的所有成员变量,然后变量这些成员变量,判断他们是否带Inject注解(通过isAnnotationPresent方法),如果带Inject注解的话,我们直接从Bean map里面拿到已经实例化好的bean通过反射方法(filed.set)注入到这个实例对象里面。 27 | 28 | ```java 29 | public class IocHelper { 30 | // IocHelperer被加载时,就会自动加载static块 31 | static { 32 | // 获取Bean Map 33 | Map, Object> beanMap = BeanHelper.getBeanMap(); 34 | if (CollectionUtil.isNotEmpty(beanMap)) { 35 | //遍历BeanMap 36 | for (Map.Entry, Object> beanEntry : beanMap.entrySet()) { 37 | Class beanClass = beanEntry.getKey(); 38 | Object beanInstance = beanEntry.getValue(); 39 | // 获取Bean Class中的所有成员变量,即Bean field 40 | Field[] beanFields = beanClass.getDeclaredFields(); 41 | if (ArrayUtil.isNotEmpty(beanFields)) { 42 | //遍历bean fields 43 | for (Field field : beanFields) { 44 | //判断当前field是否有Inject注解 45 | if (field.isAnnotationPresent(Inject.class)) { 46 | //在BeanMap中获取field对应的实例 47 | Class beanFieldClass = field.getType(); 48 | Object beanFiledInstance = beanMap.get(beanFieldClass); 49 | if (beanFiledInstance != null) { 50 | //通过反射对Bean field进行初始化 51 | ReflectionUtil.setField(beanInstance, field, beanFiledInstance); 52 | } 53 | } 54 | } 55 | } 56 | } 57 | } 58 | } 59 | } 60 | ``` 61 | 62 | ### Controller的实现 63 | 64 | Controller的实现也是通过自定义注解实现的,主要通过在类上添加@Controller注解表示这是个Controller类,在方法上添加@Action注解表明这是个Action方法,在@Action注解上需要配置请求方法和请求路径,这有点类似于Spring的Controller+RequestMapping。 65 | 66 | **实现原理:**自定义一个注解类Controller,他是类级别的注解。再定义一个注解Action,他是方法级别的注解,有一个value值用于配置请求方法和路径。 67 | 68 | ```java 69 | @Target(ElementType.TYPE) 70 | @Retention(RetentionPolicy.RUNTIME) 71 | public @interface Controller { 72 | } 73 | 74 | @Target(ElementType.METHOD) 75 | @Retention(RetentionPolicy.RUNTIME) 76 | public @interface Action { 77 | String value(); 78 | } 79 | ``` 80 | 81 | 定义一个类Requst封装两个成员变量:请求方法requestMethod和请求路径requestPath。定义一个类handler封装两个成员变量,分别是controller类和类里面的某方法。 82 | 83 | ```java 84 | public class Request { 85 | 86 | /** 87 | * 请求方法 88 | */ 89 | private String requestMethod; 90 | 91 | /** 92 | * 请求路径 93 | */ 94 | private String requestPath; 95 | //省略构造方法,get,hashcode和equals方法,下同 96 | } 97 | 98 | public class Handler { 99 | /** 100 | * Controller类 101 | */ 102 | private Class controllerClass; 103 | 104 | /** 105 | * Action方法 106 | */ 107 | private Method actionMethod; 108 | } 109 | ``` 110 | 111 | 那么Requst和handler是一一对应的关系,用一个Map,ACTION_MAP存储这种映射关系。提供一个ControllerHelper类,从刚才提到的Set集合中通过isAnnotationPresent方法过滤出所有带Controller注解的class对象,也就是获取整个web应用的controller类对象,然后存入一个集合,遍历这个集合,对每一个controller class,利用反射获取类中的所有方法,遍历这些方法,判断方法是否带Action注解,如果带的话,直接从注解的参数中分离出请求方法和请求路径封装成为requst,再将这个controller类和该方法封装成为hanlder,将这个requst和hanlder的映射关系存到ACTION_MAP 112 | 113 | ```java 114 | public class ControllerHelper { 115 | 116 | /** 117 | * 存放Request与Handler之间的映射关系 118 | */ 119 | private static final Map ACTION_MAP = new HashMap(); 120 | 121 | static { 122 | //获取所有Controller类(通过ClassHelper) 123 | Set> controllerClassSet = ClassHelper.getControllerClassSet(); 124 | if (CollectionUtil.isNotEmpty(controllerClassSet)) { 125 | // 遍历这些Controller类 126 | for (Class controllerClass : controllerClassSet) { 127 | //获取Controller中定义的方法 128 | Method[] methods = controllerClass.getDeclaredMethods(); 129 | if (ArrayUtil.isNotEmpty(methods)) { 130 | // 遍历Controller类中的所有方法 131 | for (Method method : methods) { 132 | // 判断当前方法是否带Action注解 133 | if (method.isAnnotationPresent(Action.class)) { 134 | //从Action注解中获取url映射规则 135 | Action action = method.getAnnotation(Action.class); 136 | String mapping = action.value(); 137 | // 验证URL规则 138 | if (mapping.matches("\\w+:/\\w*")) { 139 | String[] array = mapping.split(":"); 140 | if (ArrayUtil.isNotEmpty(array) && array.length == 2) { 141 | //获取请求方法与请求路径 142 | String requestMethod = array[0]; 143 | String requestPath = array[1]; 144 | Request request = new Request(requestMethod, requestPath); 145 | Handler handler = new Handler(controllerClass, method); 146 | // 初始化Action Map 147 | ACTION_MAP.put(request, handler); 148 | } 149 | } 150 | } 151 | } 152 | } 153 | } 154 | } 155 | } 156 | ``` 157 | 158 | 框架初始化时,将用于完成上述初始化功能的工具类在HelperLoader中加载: 159 | 160 | ```java 161 | public class HelperLoader { 162 | 163 | public static void init(){ 164 | Class[] classList = { 165 | ClassHelper.class, 166 | BeanHelper.class, 167 | // aop要在IOChelper之前加载因为要通过AOP获得代理对象才能通过IOChelper进行依赖注入 168 | AopHelper.class, 169 | IocHelper.class, 170 | ControllerHelper.class 171 | }; 172 | for (Class cls : classList){ 173 | ClassUtil.loadClass(cls.getName()); 174 | } 175 | } 176 | } 177 | ``` 178 | 179 | ### 自定义请求转发器(DispatcherServlet) 180 | 181 | 以上部分都是在为这一步做准备,现在我们需要编写一个Servlet来处理所有的请求,DispatcherServlet继承HttpServlet,在重写的Service方法中,从HttpServletRequest中获取请求方法和路径,得到request,从上述存储requst和hanlder映射关系的map中拿到对应的handler,从而便拿到了处理这一个requst的controller对象和对应的Action方法 182 | 183 | 之后,需要对HttpServletRequest传入的参数进行封装,主要是调用API从HttpServletRequest获取实际传入的参数然后封装成Param对象(过程详情见DispatcherServlet代码及注解),里面主要是一个Map。然后Param参数对拿到的Action方法进行反射调用,针对不同的返回类型View和Data还有对应的处理方法:如果是View的话跳转到JSP页面,如果是Data的话返回JSON。 184 | 185 | ```java 186 | @WebServlet(urlPatterns = "/*", loadOnStartup = 0) 187 | public class DispatcherServlet extends HttpServlet { 188 | 189 | private static final Logger LOGGER = LoggerFactory.getLogger(DispatcherServlet.class); 190 | 191 | /** 192 | * init方法在构造器调用之后马上被调用,用来初始化Servlet,init方法在容器装入Servlet 时执行 193 | * Servlet容器在实例化后只调用一次init方法, init方法必须在servlet接收到任何请求之前完成 194 | * 只有servlet成功被init()方法初始化后,Service方法才会被调用 195 | */ 196 | @Override 197 | public void init(ServletConfig config) throws ServletException { 198 | long startTime = System.currentTimeMillis(); 199 | // 初始化Helper 200 | HelperLoader.init(); 201 | // 获取ServletContext对象(用于注册Servlet),该对象全局唯一,而且工程内部的所有servlet都共享这个对象 202 | ServletContext servletContext = config.getServletContext(); 203 | //注册处理JSP的Servlet,效果等同于web.xml下配置servlet,只不过此处采用编码进行配置 204 | ServletRegistration jspServlet = servletContext.getServletRegistration("jsp"); 205 | jspServlet.addMapping(new String[]{"/index.jsp"}); 206 | jspServlet.addMapping(new String[]{PropertiesConfigHelper.getAppJspPath() + "*"}); 207 | //注册处理静态资源的默认Servlet 208 | ServletRegistration defaultServlet = servletContext.getServletRegistration("default"); 209 | defaultServlet.addMapping(new String[]{"/favicon.ico"}); 210 | defaultServlet.addMapping(new String[]{PropertiesConfigHelper.getAppAssetPath() + "*"}); 211 | 212 | long duration = System.currentTimeMillis()-startTime; 213 | LOGGER.info("初始化DispatcherServlet耗时{}ms", duration); 214 | } 215 | 216 | /** 217 | * service方法是接口中的方法,servlet容器把所有请求发送到该方法,该方法默认行为是转发http请求到doXXX方法中, 218 | * 如果重载了该方法,默认操作被覆盖,不再进行转发操作 219 | */ 220 | @Override 221 | protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 222 | long startTime = System.currentTimeMillis(); 223 | ServletHelper.init(req, resp); 224 | try { 225 | //获取请求方法与路径并进行封装 226 | String requestMethod = req.getMethod().toLowerCase(); 227 | String requestPath = req.getPathInfo(); 228 | // 获取Action处理器 229 | Handler handler = ControllerHelper.getHandler(requestMethod, requestPath); 230 | Map,Object> beanMap = BeanHelper.getBeanMap(); 231 | if (handler != null) { 232 | // 通过hanlder获取controller类及实例 233 | Class controllerClass = handler.getControllerClass(); 234 | Object controllerBean = BeanHelper.getBean(controllerClass); 235 | // 创建请求参数对象 236 | Map paramMap = new HashMap(); 237 | // 请求体类型是application/x- www-form-urlencoded时,对该类型的请求内容提供了request.getParameter()方法来获取请求参数值 238 | Enumeration paramNames = req.getParameterNames(); 239 | while (paramNames.hasMoreElements()) { 240 | String paramName = paramNames.nextElement(); 241 | String paramValue = req.getParameter(paramName); 242 | paramMap.put(paramName, paramValue); 243 | } 244 | // 请求体内容是其它类型时,比如 multipart/form-data或application/json时, 245 | // 无法通过request.getParameter()获取到请求内容,此时只能通过request.getInputStream(),先将URL解码再从输入流中获取字符串进行解析 246 | // request.getInputStream()返回请求内容字节流,多用于文件上传 247 | String body = CodecUtil.decodeURL(StreamUtil.getString(req.getInputStream())); 248 | if (StringUtil.isNotEmpty(body)) { 249 | String[] params = StringUtil.splitString(body, "&"); 250 | if (ArrayUtil.isNotEmpty(params)) { 251 | for (String param : params) { 252 | String[] array = StringUtil.splitString(param, "="); 253 | if (ArrayUtil.isNotEmpty(array) && array.length == 2) { 254 | String paramName = array[0]; 255 | String paramValue = array[1]; 256 | paramMap.put(paramName, paramValue); 257 | } 258 | } 259 | } 260 | } 261 | Param param = new Param(paramMap); 262 | Object res; 263 | // 通过反射调用Action方法 264 | Method actionMethod = handler.getActionMethod(); 265 | if (!param.isEmpty()) { 266 | res = ReflectionUtil.invokeMethod(controllerBean, actionMethod, param); 267 | } else { 268 | res = ReflectionUtil.invokeMethod(controllerBean, actionMethod); 269 | } 270 | // 处理Action方法返回值 271 | if (res instanceof View) { 272 | //返回JSP页面 273 | View view = (View) res; 274 | String path = view.getPath(); 275 | if (StringUtil.isNotEmpty(path)) { 276 | // response.sendRedirect(String location)方法中的参数location,如果不以“/”开头, 277 | // 表示相对于当前源组件的路径;如果以“/”开头,表示相对于当前服务器根路径的URL 278 | if (path.startsWith("/")) { 279 | resp.sendRedirect(req.getContextPath() + path); 280 | } else { 281 | Map model = view.getModel(); 282 | for (Map.Entry entry : model.entrySet()) { 283 | req.setAttribute(entry.getKey(), entry.getValue()); 284 | } 285 | req.getRequestDispatcher(PropertiesConfigHelper.getAppJspPath() + path).forward(req, resp); 286 | } 287 | } 288 | } else if (res instanceof Data) { 289 | //若为Data类型,则返回JSON数据 290 | Data data = (Data) res; 291 | Object model = data.getModel(); 292 | if (model != null) { 293 | resp.setContentType("application/json"); 294 | resp.setCharacterEncoding("UTF-8"); 295 | // 这个out对象的作用是可以通过当前HttpServletResponse以流的方式响应数据到请求html或者jsp页面,可以在客户端输出 296 | PrintWriter writer = resp.getWriter(); 297 | String json = JsonUtil.toJson(model); 298 | writer.write(json); 299 | writer.flush(); 300 | writer.close(); 301 | 302 | } 303 | } 304 | } 305 | }catch (Exception e){ 306 | LOGGER.error("DispatcherServlet错误",e); 307 | throw new RuntimeException(e); 308 | }finally { 309 | ServletHelper.destory(); 310 | } 311 | long duration = System.currentTimeMillis()-startTime; 312 | LOGGER.info("DispatcherServlet service执行耗时{}ms",duration ); 313 | } 314 | ``` 315 | 316 | ### 为框架添加AOP功能 317 | 318 | > AOP,即面向切面编程。切面是AOP里面的一个重要术语,他表示从业务逻辑里面分离出来的横切逻辑,比如性能监控,日志记录,权限控制,异常处理等,这些业务都是横向分散在所有对象层次中的,与对象的核心功能毫无关系,可以从核心业务逻辑里面抽离出去。通过AOP技术,可以将横切逻辑封装成为可重用的模块独立进行维护,减少系统的重复代码,解决代码耦合问题,让职责更加单一,开发人员更加关注核心代码的编写和维护。 319 | > 320 | > https://blog.csdn.net/u011402896/article/details/80369220 321 | 322 | 其他术语: 323 | 324 | - 切点:我们需要通过某些条件去匹配所需要拦截的类,这个条件在AOP中称为切点 325 | - 连接点:被拦截到的方法 326 | - 通知(Advice):在特定的连接点,AOP框架执行的动作.前置/后置/例外/最终/环绕通知 327 | - 织入:对方法的增强叫做织入 328 | 329 | 本框架基于自定义注解和CGLIB动态代理实现AOP功能: 330 | 331 | 定义切面类注解@Aspect,他是类级别的注解,用于表明一个类属于切面类,内部有一个注解类型的成员value,该成员用于指定被拦截的类。如果我在某个切面类上定义@Aspect(Controller.class),表示该切面只会拦截所有被@Controller注解的类 332 | 333 | ```java 334 | @Target(ElementType.TYPE) 335 | @Retention(RetentionPolicy.RUNTIME) 336 | public @interface Aspect { 337 | 338 | /** 339 | * 注解 340 | */ 341 | Class value(); 342 | } 343 | ``` 344 | 345 | 为了实现切面类,我们定义一个抽象切面类abstractProxy,作为模板提供一些钩子方法如begin、end、after、before等方法或者说是切面逻辑供切面类进行扩展,还提供了一个比较关键的方法doProxy,在doProxy方法中对这些钩子方法的执行顺序进行一个整合,中间还会继续调用下一个切面类的doProxy方法,这其实就是所谓的责任链模式,因为我们知道一个连接点可能会被多个切面进行代理,要按切面的添加顺序依次执行切面的代理方法,就要用到**责任链模式**。 346 | 347 | ```java 348 | public abstract class AspectProxy implements Proxy { 349 | 350 | private static final Logger LOGGER = LoggerFactory.getLogger(AspectProxy.class); 351 | 352 | @Override 353 | public final Object doProxy(ProxyChain proxyChain) throws Throwable { 354 | Object result = null; 355 | 356 | Class cls = proxyChain.getTargetClass(); 357 | Method method = proxyChain.getTargetMethod(); 358 | Object[] params = proxyChain.getMethodParams(); 359 | 360 | begin(); 361 | try { 362 | if (intercept(cls, method, params)) { 363 | before(cls, method, params); 364 | result = proxyChain.doProxyChain(); 365 | after(cls, method, params, result); 366 | } else { 367 | result = proxyChain.doProxyChain(); 368 | } 369 | } catch (Exception e) { 370 | LOGGER.error("proxy failure", e); 371 | error(cls, method, params, e); 372 | throw e; 373 | } finally { 374 | end(); 375 | } 376 | return result; 377 | } 378 | 379 | public void end() { 380 | } 381 | 382 | public void error(Class cls, Method method, Object[] params, Throwable e) { 383 | } 384 | 385 | public void after(Class cls, Method method, Object[] params, Object result) throws Throwable { 386 | } 387 | 388 | public void before(Class cls, Method method, Object[] params) throws Throwable { 389 | } 390 | 391 | public boolean intercept(Class cls, Method method, Object[] params) throws Throwable { 392 | return true; 393 | } 394 | 395 | public void begin() { 396 | } 397 | } 398 | ``` 399 | 400 | 然后我们利用CGLIB动态代理,在重写的intercept方法里面启动这条责任链为被代理类执行代理,然后通过Enhancer类的静态方法create创建这么一个代理对象,然后将这个代理对象和对应的class对象以key value的形式存入bean容器,那么在调用getBean方法从bean容器中取出某个class对象实例的时候实际上取到的是被AOP织入增强后的代理类对象。 401 | 402 | ```java 403 | public class ProxyManager { 404 | 405 | /** 406 | * Cglib动态代理 407 | * 408 | * @param targetClass 被代理类 409 | * @param proxyList 代理对象列表 410 | * @param 被代理类实例 411 | * @return 被代理类实例 412 | */ 413 | @SuppressWarnings("unchecked") 414 | public static T createProxy(final Class targetClass, final List proxyList){ 415 | return (T) Enhancer.create(targetClass, new MethodInterceptor() { 416 | @Override 417 | public Object intercept(Object targetObject, Method targetMethod, Object[] methodParams, MethodProxy methodProxy) throws Throwable { 418 | return new ProxyChain(targetClass,targetObject,targetMethod,methodProxy,methodParams,proxyList).doProxyChain(); 419 | } 420 | }); 421 | } 422 | } 423 | ``` 424 | 425 | 所以在框架初始化的时候,我们要去获取一个Map,他维护了代理类和被代理类集合的映射关系(因为一个代理类可能对多个目标类进行代理),所以这个Map的类型是这样的:Map<代理对象,Set<被代理类>),然后再遍历这个Map,对每一个Entry而言再去遍历value,也就是Set<被代理类>,最终会获取另一个Map<被代理类,List<代理类>),也就是被代理类与代理对象列表之间的映射关系(见AopHelper.class),然后在CGLIB动态代理中传入这个List,触发责任链完成最终的代理然后存入bean容器 426 | 427 | ### **实现事务控制特性** 428 | 429 | 创建一个自定义注解类@Transaction,他是方法级别的注解,表明某个方法存在事务,那么其实一般认为凡是对数据库一变更的方法都应该带上事务注解,这样的话方便更新操作失败后进行回滚。 430 | 431 | 创建一个自定义注解@Service,他是类级别的注解,表明是一个服务类,里面包含的方法可能带有Transaction注解 432 | 433 | 然后提供一个事务操作工具类,里面封装的是JDBC里关于事务的一些常用操作,比如开启事务,提交事务,回滚事务。 434 | 435 | 利用自身实现的AOP框架编写事务代理切面类,在doProxy方法里面整合开启事务,提交事务等方法的执行顺序,在catch语句中执行回滚事务操作。那么最终生成的代理对象对应的方法就是带有了事务特性的。 436 | 437 | 事务控制代理其实本质也是一个普通的AOP切面类,只不过他的横切逻辑,或者说钩子方法是关于事务的一系列操作,所以他的执行流程跟一般的AOP没有太大区别,在AOP中,我们把带@Service的类也作为切面类放入上述Map 438 | 439 | 为保证同一个线程中事务控制相关的逻辑只会执行一次,可以使用ThreadLocal封装一个boolean型的flag变量,初始化为false,在执行事务操作的时候会进行一次判断,不为true的时候才执行。 440 | 441 | ```java 442 | /** 443 | * 事务代理 444 | * 445 | * @author xctian 446 | * @date 2020/1/28 447 | */ 448 | public class TransactionProxy implements Proxy { 449 | 450 | private static final Logger LOGGER = LoggerFactory.getLogger(TransactionProxy.class); 451 | 452 | private static final ThreadLocal FLAG_HOLDER = new ThreadLocal(){ 453 | @Override 454 | protected Boolean initialValue() { 455 | return false; 456 | } 457 | }; 458 | 459 | @Override 460 | public Object doProxy(ProxyChain proxyChain) throws Throwable { 461 | Object res; 462 | boolean flag = FLAG_HOLDER.get(); 463 | Method method = proxyChain.getTargetMethod(); 464 | // 被Transaction注解的方法才被拦截 465 | if(!flag && method.isAnnotationPresent(Transaction.class)){ 466 | FLAG_HOLDER.set(true); 467 | try { 468 | DatabaseHelper.beginTransaction(); 469 | LOGGER.debug("begin transaction"); 470 | res = proxyChain.doProxyChain(); 471 | DatabaseHelper.commitTransaction(); 472 | LOGGER.debug("commit transaction"); 473 | }catch (Exception e){ 474 | DatabaseHelper.rollbackTransaction(); 475 | LOGGER.debug("rollback transaction"); 476 | throw e; 477 | }finally { 478 | FLAG_HOLDER.remove(); 479 | } 480 | }else { 481 | res = proxyChain.doProxyChain(); 482 | } 483 | return res; 484 | } 485 | } 486 | ``` 487 | 488 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.xctian 8 | simple-framework 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | org.apache.maven.plugins 14 | maven-compiler-plugin 15 | 16 | 1.7 17 | 1.7 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | javax.servlet 26 | javax.servlet-api 27 | 3.1.0 28 | provided 29 | 30 | 31 | 32 | javax.servlet.jsp 33 | jsp-api 34 | 2.2 35 | provided 36 | 37 | 38 | 39 | javax.servlet 40 | jstl 41 | 1.2 42 | runtime 43 | 44 | 45 | 46 | org.slf4j 47 | slf4j-log4j12 48 | 1.7.7 49 | 50 | 51 | 52 | mysql 53 | mysql-connector-java 54 | 5.1.33 55 | runtime 56 | 57 | 58 | 59 | com.fasterxml.jackson.core 60 | jackson-databind 61 | 2.5.2 62 | 63 | 64 | 65 | cglib 66 | cglib 67 | 2.2.2 68 | 69 | 70 | 71 | org.apache.commons 72 | commons-lang3 73 | 3.3.2 74 | 75 | 76 | 77 | org.apache.commons 78 | commons-collections4 79 | 4.0 80 | 81 | 82 | 83 | commons-dbutils 84 | commons-dbutils 85 | 1.6 86 | 87 | 88 | 89 | org.apache.commons 90 | commons-dbcp2 91 | 2.0.1 92 | 93 | 94 | 95 | commons-fileupload 96 | commons-fileupload 97 | 1.3.1 98 | 99 | 100 | 101 | commons-codec 102 | commons-codec 103 | 1.10 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/DispatcherServlet.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework; 2 | 3 | import com.xctian.framework.bean.Data; 4 | import com.xctian.framework.bean.Handler; 5 | import com.xctian.framework.bean.Param; 6 | import com.xctian.framework.bean.View; 7 | import com.xctian.framework.helper.BeanHelper; 8 | import com.xctian.framework.helper.ControllerHelper; 9 | import com.xctian.framework.helper.PropertiesConfigHelper; 10 | import com.xctian.framework.helper.ServletHelper; 11 | import com.xctian.framework.utils.*; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | import javax.servlet.ServletConfig; 16 | import javax.servlet.ServletContext; 17 | import javax.servlet.ServletException; 18 | import javax.servlet.ServletRegistration; 19 | import javax.servlet.annotation.WebServlet; 20 | import javax.servlet.http.HttpServlet; 21 | import javax.servlet.http.HttpServletRequest; 22 | import javax.servlet.http.HttpServletResponse; 23 | import java.io.IOException; 24 | import java.io.PrintWriter; 25 | import java.lang.reflect.Method; 26 | import java.util.Enumeration; 27 | import java.util.HashMap; 28 | import java.util.Map; 29 | 30 | /** 31 | * 请求转发器 32 | * 33 | * @author xctian 34 | * @date 2020/1/23 35 | */ 36 | @WebServlet(urlPatterns = "/*", loadOnStartup = 0) 37 | public class DispatcherServlet extends HttpServlet { 38 | 39 | private static final Logger LOGGER = LoggerFactory.getLogger(DispatcherServlet.class); 40 | 41 | /** 42 | * init方法在构造器调用之后马上被调用,用来初始化Servlet,init方法在容器装入Servlet 时执行 43 | * Servlet容器在实例化后只调用一次init方法, init方法必须在servlet接收到任何请求之前完成 44 | * 只有servlet成功被init()方法初始化后,Service方法才会被调用 45 | */ 46 | @Override 47 | public void init(ServletConfig config) throws ServletException { 48 | long startTime = System.currentTimeMillis(); 49 | // 初始化Helper 50 | HelperLoader.init(); 51 | // 获取ServletContext对象(用于注册Servlet),该对象全局唯一,而且工程内部的所有servlet都共享这个对象 52 | ServletContext servletContext = config.getServletContext(); 53 | //注册处理JSP的Servlet,效果等同于web.xml下配置servlet,只不过此处采用编码进行配置 54 | ServletRegistration jspServlet = servletContext.getServletRegistration("jsp"); 55 | jspServlet.addMapping(new String[]{"/index.jsp"}); 56 | jspServlet.addMapping(new String[]{PropertiesConfigHelper.getAppJspPath() + "*"}); 57 | //注册处理静态资源的默认Servlet 58 | ServletRegistration defaultServlet = servletContext.getServletRegistration("default"); 59 | defaultServlet.addMapping(new String[]{"/favicon.ico"}); 60 | defaultServlet.addMapping(new String[]{PropertiesConfigHelper.getAppAssetPath() + "*"}); 61 | 62 | long duration = System.currentTimeMillis()-startTime; 63 | LOGGER.info("初始化DispatcherServlet耗时{}ms", duration); 64 | } 65 | 66 | /** 67 | * service方法是接口中的方法,servlet容器把所有请求发送到该方法,该方法默认行为是转发http请求到doXXX方法中, 68 | * 如果重载了该方法,默认操作被覆盖,不再进行转发操作 69 | */ 70 | @Override 71 | protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 72 | long startTime = System.currentTimeMillis(); 73 | ServletHelper.init(req, resp); 74 | try { 75 | //获取请求方法与路径并进行封装 76 | String requestMethod = req.getMethod().toLowerCase(); 77 | String requestPath = req.getPathInfo(); 78 | // 获取Action处理器 79 | Handler handler = ControllerHelper.getHandler(requestMethod, requestPath); 80 | Map,Object> beanMap = BeanHelper.getBeanMap(); 81 | if (handler != null) { 82 | // 通过hanlder获取controller类及实例 83 | Class controllerClass = handler.getControllerClass(); 84 | Object controllerBean = BeanHelper.getBean(controllerClass); 85 | // 创建请求参数对象 86 | Map paramMap = new HashMap(); 87 | // 请求体类型是application/x- www-form-urlencoded时,对该类型的请求内容提供了request.getParameter()方法来获取请求参数值 88 | Enumeration paramNames = req.getParameterNames(); 89 | while (paramNames.hasMoreElements()) { 90 | String paramName = paramNames.nextElement(); 91 | String paramValue = req.getParameter(paramName); 92 | paramMap.put(paramName, paramValue); 93 | } 94 | // 请求体内容是其它类型时,比如 multipart/form-data或application/json时, 95 | // 无法通过request.getParameter()获取到请求内容,此时只能通过request.getInputStream() 96 | // request.getInputStream()返回请求内容字节流,多用于文件上传 97 | String body = CodecUtil.decodeURL(StreamUtil.getString(req.getInputStream())); 98 | if (StringUtil.isNotEmpty(body)) { 99 | String[] params = StringUtil.splitString(body, "&"); 100 | if (ArrayUtil.isNotEmpty(params)) { 101 | for (String param : params) { 102 | String[] array = StringUtil.splitString(param, "="); 103 | if (ArrayUtil.isNotEmpty(array) && array.length == 2) { 104 | String paramName = array[0]; 105 | String paramValue = array[1]; 106 | paramMap.put(paramName, paramValue); 107 | } 108 | } 109 | } 110 | } 111 | Param param = new Param(paramMap); 112 | Object res; 113 | // 通过反射调用Action方法 114 | Method actionMethod = handler.getActionMethod(); 115 | if (!param.isEmpty()) { 116 | res = ReflectionUtil.invokeMethod(controllerBean, actionMethod, param); 117 | } else { 118 | res = ReflectionUtil.invokeMethod(controllerBean, actionMethod); 119 | } 120 | // 处理Action方法返回值 121 | if (res instanceof View) { 122 | //返回JSP页面 123 | View view = (View) res; 124 | String path = view.getPath(); 125 | if (StringUtil.isNotEmpty(path)) { 126 | // response.sendRedirect(String location)方法中的参数location,如果不以“/”开头, 127 | // 表示相对于当前源组件的路径;如果以“/”开头,表示相对于当前服务器根路径的URL 128 | if (path.startsWith("/")) { 129 | resp.sendRedirect(req.getContextPath() + path); 130 | } else { 131 | Map model = view.getModel(); 132 | for (Map.Entry entry : model.entrySet()) { 133 | req.setAttribute(entry.getKey(), entry.getValue()); 134 | } 135 | req.getRequestDispatcher(PropertiesConfigHelper.getAppJspPath() + path).forward(req, resp); 136 | } 137 | } 138 | } else if (res instanceof Data) { 139 | //若为Data类型,则返回JSON数据 140 | Data data = (Data) res; 141 | Object model = data.getModel(); 142 | if (model != null) { 143 | resp.setContentType("application/json"); 144 | resp.setCharacterEncoding("UTF-8"); 145 | // 这个out对象的作用是可以通过当前HttpServletResponse以流的方式响应数据到请求html或者jsp页面,可以在客户端输出 146 | PrintWriter writer = resp.getWriter(); 147 | String json = JsonUtil.toJson(model); 148 | writer.write(json); 149 | writer.flush(); 150 | writer.close(); 151 | 152 | } 153 | } 154 | } 155 | }catch (Exception e){ 156 | LOGGER.error("DispatcherServlet错误",e); 157 | throw new RuntimeException(e); 158 | }finally { 159 | ServletHelper.destory(); 160 | } 161 | long duration = System.currentTimeMillis()-startTime; 162 | LOGGER.info("DispatcherServlet service执行耗时{}ms",duration ); 163 | } 164 | } -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/HelperLoader.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework; 2 | 3 | import com.xctian.framework.helper.*; 4 | import com.xctian.framework.proxy.Proxy; 5 | import com.xctian.framework.utils.ClassUtil; 6 | 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | /** 11 | * 各Helper初始化工具 12 | * 13 | * @author xctian 14 | * @date 2020/1/23 15 | */ 16 | public class HelperLoader { 17 | 18 | public static void init(){ 19 | Class[] classList = { 20 | ClassHelper.class, 21 | BeanHelper.class, 22 | // aop要在IOChelper之前加载因为要通过AOP获得代理对象才能通过IOChelper进行依赖注入 23 | AopHelper.class, 24 | IocHelper.class, 25 | ControllerHelper.class 26 | }; 27 | for (Class cls : classList){ 28 | ClassUtil.loadClass(cls.getName()); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/PropertiesConfigConstant.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework; 2 | 3 | /** 4 | * 提供simple.properties配置文件中的常量 5 | * 6 | * @author xctian 7 | * @date 2020/1/17 8 | */ 9 | public interface PropertiesConfigConstant { 10 | 11 | String CONFIG_FILE_NAME = "simple.properties"; 12 | 13 | String JDBC_DRIVER = "simple.framework.jdbc.driver"; 14 | String JDBC_URL = "simple.framework.jdbc.url"; 15 | String JDBC_USERNAME = "simple.framework.jdbc.username"; 16 | String JDBC_PASSWORD = "simple.framework.jdbc.password"; 17 | 18 | String APP_BASE_PACKAGE = "simple.framework.app.base_package"; 19 | String APP_JSP_PATH = "simple.framework.app.jsp_path"; 20 | String APP_ASSET_PATH = "simple.framework.app.asset_path"; 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/annotation/Action.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.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 xctian 10 | * @date 2020/1/22 11 | */ 12 | @Target(ElementType.METHOD) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | public @interface Action { 15 | /** 16 | * 请求类型与路径 17 | * 如果该元素是唯一需要赋值的一个元素,那么此时无需使用key=value的语法,而只需在括号内给出value元素所需的值即可。 18 | * 这可以应用于任何合法类型的元素,记住,这限制了元素名必须为value 19 | */ 20 | String value(); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/annotation/Aspect.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * 切面注解 7 | * 8 | * @author xctian 9 | * @date 2020/1/25 10 | */ 11 | @Target(ElementType.TYPE) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | public @interface Aspect { 14 | 15 | /** 16 | * 注解 17 | */ 18 | Class value(); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/annotation/Controller.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.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 | * 控制器注解,@Target 用来约束注解可以应用的地方(如方法、类或字段),@Retention用来约束注解的生命周期, 10 | * 分别有三个值,源码级别(source),类文件级别(class)或者运行时级别(runtime) 11 | * 12 | * @author xctian 13 | * @date 2020/1/22 14 | */ 15 | @Target(ElementType.TYPE) 16 | @Retention(RetentionPolicy.RUNTIME) 17 | public @interface Controller { 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/annotation/Inject.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.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 xctian 12 | * @date 2020/1/22 13 | */ 14 | @Target(ElementType.FIELD) 15 | @Retention(RetentionPolicy.RUNTIME) 16 | public @interface Inject { 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/annotation/Service.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.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 xctian 12 | * @date 2020/1/22 13 | */ 14 | @Target(ElementType.TYPE) 15 | @Retention(RetentionPolicy.RUNTIME) 16 | public @interface Service { 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/annotation/Transaction.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.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 xctian 12 | * @date 2020/1/28 13 | */ 14 | @Target(ElementType.METHOD) 15 | @Retention(RetentionPolicy.RUNTIME) 16 | public @interface Transaction { 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/bean/Data.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.bean; 2 | 3 | /** 4 | * 返回Data封装类 5 | * 6 | * @author xctian 7 | * @date 2020/1/24 8 | */ 9 | public class Data { 10 | 11 | /** 12 | * 模型数据 13 | */ 14 | private Object model; 15 | 16 | public Data(Object model) { 17 | this.model = model; 18 | } 19 | 20 | public Object getModel() { 21 | return model; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/bean/Handler.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.bean; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | /** 6 | * @author xctian 7 | * @date 2020/1/23 8 | */ 9 | public class Handler { 10 | 11 | /** 12 | * Controller类 13 | */ 14 | private Class controllerClass; 15 | 16 | /** 17 | * Action方法 18 | */ 19 | private Method actionMethod; 20 | 21 | public Handler(Class controllerClass, Method actionMethod) { 22 | this.controllerClass = controllerClass; 23 | this.actionMethod = actionMethod; 24 | } 25 | 26 | public Class getControllerClass() { 27 | return controllerClass; 28 | } 29 | 30 | public Method getActionMethod() { 31 | return actionMethod; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/bean/Param.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.bean; 2 | 3 | import com.xctian.framework.utils.CastUtil; 4 | import com.xctian.framework.utils.CollectionUtil; 5 | import com.xctian.framework.utils.StringUtil; 6 | 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | /** 12 | * 请求参数封装类 13 | * 14 | * @author xctian 15 | * @date 2020/1/24 16 | */ 17 | public class Param { 18 | 19 | private Map paramMap; 20 | 21 | public Param(Map paramMap) { 22 | this.paramMap = paramMap; 23 | } 24 | 25 | /** 26 | * 根据参数名获取boolean参数 27 | */ 28 | public Boolean getBoolean(String name){ 29 | return CastUtil.castToBoolean(paramMap.get(name)); 30 | } 31 | 32 | /** 33 | * 根据参数名获取double参数 34 | */ 35 | public double getDouble(String name){ 36 | return CastUtil.castToDouble(paramMap.get(name)); 37 | } 38 | 39 | /** 40 | * 根据参数名获取long参数 41 | */ 42 | public long getLong(String name){ 43 | return CastUtil.castToLong(paramMap.get(name)); 44 | } 45 | 46 | /** 47 | * 根据参数名获取int参数 48 | */ 49 | public int getInt(String name){ 50 | return CastUtil.castToInt(paramMap.get(name)); 51 | } 52 | 53 | /** 54 | * 获取paramMap 55 | */ 56 | public Map getParamMap(){ 57 | return paramMap; 58 | } 59 | 60 | /** 61 | * 判空 62 | */ 63 | public boolean isEmpty(){ 64 | return CollectionUtil.isEmpty(paramMap); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/bean/Request.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.bean; 2 | 3 | import org.apache.commons.lang3.builder.EqualsBuilder; 4 | import org.apache.commons.lang3.builder.HashCodeBuilder; 5 | 6 | /** 7 | * 请求信息封装 8 | * 9 | * @author xctian 10 | * @date 2020/1/22 11 | */ 12 | public class Request { 13 | 14 | /** 15 | * 请求方法 16 | */ 17 | private String requestMethod; 18 | 19 | /** 20 | * 请求路径 21 | */ 22 | private String requestPath; 23 | 24 | public Request(String requestMethod, String requestPath) { 25 | this.requestMethod = requestMethod; 26 | this.requestPath = requestPath; 27 | } 28 | 29 | public String getRequestMethod() { 30 | return requestMethod; 31 | } 32 | 33 | public String getRequestPath() { 34 | return requestPath; 35 | } 36 | 37 | /*====使用EqualsBuilder和HashCodeBuilder重写equals、hashCode方法====*/ 38 | 39 | /** 40 | * hashCode取决于该class的所有filed时需要使用反射机制来产生一个hashCode 41 | */ 42 | @Override 43 | public int hashCode() { 44 | return HashCodeBuilder.reflectionHashCode(this); 45 | } 46 | 47 | /** 48 | * 如果两个对象相等当且仅当每个属性值都相等,可以使用下述语句实现 49 | */ 50 | @Override 51 | public boolean equals(Object obj) { 52 | return EqualsBuilder.reflectionEquals(this, obj); 53 | } 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/bean/View.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.bean; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * 返回视图封装对象 8 | * 9 | * @author xctian 10 | * @date 2020/1/24 11 | */ 12 | public class View { 13 | 14 | /** 15 | * 视图路径 16 | */ 17 | private String path; 18 | 19 | /** 20 | * 模型数据 21 | */ 22 | private Map model; 23 | 24 | public View(String path) { 25 | this.path = path; 26 | model = new HashMap<>(); 27 | } 28 | 29 | public View addModel(String key, Object value) { 30 | model.put(key, value); 31 | return this; 32 | } 33 | 34 | public String getPath() { 35 | return path; 36 | } 37 | 38 | public Map getModel() { 39 | return model; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/helper/AopHelper.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.helper; 2 | 3 | import com.xctian.framework.annotation.Aspect; 4 | import com.xctian.framework.annotation.Service; 5 | import com.xctian.framework.proxy.AspectProxy; 6 | import com.xctian.framework.proxy.Proxy; 7 | import com.xctian.framework.proxy.ProxyManager; 8 | import com.xctian.framework.proxy.TransactionProxy; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import java.lang.annotation.Annotation; 13 | import java.util.*; 14 | 15 | /** 16 | * 方法拦截助手类 17 | * 18 | * @author xctian 19 | * @date 2020/1/28 20 | */ 21 | public class AopHelper { 22 | 23 | private static final Logger LOGGER = LoggerFactory.getLogger(AopHelper.class); 24 | 25 | static { 26 | try { 27 | Map, Set>> proxyMap = createProxyMap(); 28 | Map, List> targetMap = createTargetMap(proxyMap); 29 | for(Map.Entry,List> targetEntry:targetMap.entrySet()){ 30 | Class targetClass = targetEntry.getKey(); 31 | List proxyList = targetEntry.getValue(); 32 | // 产生被代理类对象,调用责任链 33 | Object proxy = ProxyManager.createProxy(targetClass,proxyList); 34 | // 放入Bean容器 35 | BeanHelper.setBean(targetClass,proxy); 36 | } 37 | }catch (Exception e){ 38 | LOGGER.error("AOP failure",e); 39 | } 40 | } 41 | 42 | /** 43 | * 获取代理类和目标类集合Map,key:代理类class,value:代理类所代理的所有目标类集合 44 | */ 45 | public static Map, Set>> createProxyMap() { 46 | Map, Set>> proxyMap = new HashMap, Set>>(); 47 | addAspectProxy(proxyMap); 48 | addTransactionProxy(proxyMap); 49 | return proxyMap; 50 | } 51 | 52 | /** 53 | * 添加事务代理到proxyMap 54 | */ 55 | public static void addTransactionProxy(Map, Set>> proxyMap) { 56 | Set> serviceClassSet = ClassHelper.getClassSetByAnnotation(Service.class); 57 | proxyMap.put(TransactionProxy.class,serviceClassSet); 58 | } 59 | 60 | /** 61 | * 添加普通切面代理到proxyMap 62 | */ 63 | public static void addAspectProxy(Map, Set>> proxyMap) { 64 | Set> proxyClassSet = ClassHelper.getClassSetBySuper(AspectProxy.class); 65 | for (Class proxyClass : proxyClassSet) { 66 | if (proxyClass.isAnnotationPresent(Aspect.class)) { 67 | Aspect aspect = proxyClass.getAnnotation(Aspect.class); 68 | Set> targetClassSet = createTargetClassSet(aspect); 69 | proxyMap.put(proxyClass, targetClassSet); 70 | } 71 | } 72 | } 73 | 74 | /** 75 | * 获取Aspect注解中设置的注解类组成的集合Set 76 | */ 77 | public static Set> createTargetClassSet(Aspect aspect) { 78 | Set> targetClassSet = new HashSet>(); 79 | Class annotation = aspect.value(); 80 | if (annotation != null && !annotation.equals(Aspect.class)) { 81 | targetClassSet.addAll(ClassHelper.getClassSetByAnnotation(annotation)); 82 | } 83 | return targetClassSet; 84 | } 85 | 86 | /** 87 | * 获取目标类与代理类对象的映射关系,key:目标类,value:目标类对应的代理对象列表(一个模板类可能被多个代理类代理) 88 | */ 89 | public static Map, List>createTargetMap(Map, Set>> proxyMap)throws Exception{ 90 | Map, List> targetMap = new HashMap, List>(); 91 | for (Map.Entry, Set>> proxyEntry : proxyMap.entrySet()) { 92 | Class proxyClass = proxyEntry.getKey(); 93 | Set> targetClassSet = proxyEntry.getValue(); 94 | // 遍历被代理类(即目标类) 95 | for (Class targetClass : targetClassSet) { 96 | Proxy proxy = (Proxy) proxyClass.newInstance(); 97 | if(targetMap.containsKey(targetClass)){ 98 | targetMap.get(targetClass).add(proxy); 99 | }else { 100 | List proxyList = new ArrayList(); 101 | proxyList.add(proxy); 102 | targetMap.put(targetClass,proxyList); 103 | } 104 | } 105 | } 106 | return targetMap; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/helper/BeanHelper.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.helper; 2 | 3 | import com.xctian.framework.utils.ReflectionUtil; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import java.util.Set; 8 | 9 | /** 10 | * Bean容器实现类 11 | * 12 | * @author xctian 13 | * @date 2020/1/22 14 | */ 15 | public class BeanHelper { 16 | /** 17 | * Bean 映射Map,key为Class,value为对应的Class对象,可以理解为Bean容器 18 | */ 19 | private static final Map, Object> BEAN_MAP = new HashMap, Object>(); 20 | 21 | // 初始化,将所有实例化的Bean对象缓存至BEAN_MAP,实现创建好对象放入Bean容器,故可保证单例 22 | static { 23 | Set> beanClassSet = ClassHelper.getBeanClassSet(); 24 | for (Class cls : beanClassSet) { 25 | Object obj = ReflectionUtil.newInstance(cls); 26 | BEAN_MAP.put(cls, obj); 27 | } 28 | } 29 | 30 | /** 31 | * 获取BeanMap 32 | */ 33 | public static Map, Object> getBeanMap() { 34 | return BEAN_MAP; 35 | } 36 | 37 | /** 38 | * 通过传入Bean类,获取对应的Bean实例 39 | */ 40 | @SuppressWarnings("unchecked") 41 | public static T getBean(Class cls) { 42 | if (!BEAN_MAP.containsKey(cls)) { 43 | throw new RuntimeException("can not get bean by class:" + cls); 44 | } 45 | return (T) BEAN_MAP.get(cls); 46 | } 47 | 48 | /** 49 | * Bean容器中添加Bean实例 50 | */ 51 | public static void setBean(Class cls,Object obj){ 52 | BEAN_MAP.put(cls,obj); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/helper/ClassHelper.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.helper; 2 | 3 | import com.xctian.framework.annotation.Controller; 4 | import com.xctian.framework.annotation.Service; 5 | import com.xctian.framework.utils.ClassUtil; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.lang.annotation.Annotation; 10 | import java.util.HashSet; 11 | import java.util.Set; 12 | 13 | /** 14 | * 类操作helper 15 | * 16 | * @author xctian 17 | * @date 2020/1/22 18 | */ 19 | public class ClassHelper { 20 | 21 | private static final Logger LOGGER = LoggerFactory.getLogger(ClassHelper.class); 22 | /** 23 | * 用于存放所加载的类 24 | */ 25 | private static final Set> CLASS_SET; 26 | 27 | static { 28 | String basePackage = PropertiesConfigHelper.getAppBasePackage(); 29 | CLASS_SET = ClassUtil.getClassSet(basePackage); 30 | } 31 | 32 | /** 33 | * 获取应用包名下的所有类 34 | */ 35 | public static Set> getClassSet() { 36 | return CLASS_SET; 37 | } 38 | 39 | /** 40 | * 获取应用包下的所有Service类 41 | */ 42 | public static Set> getServiceClassSet() { 43 | Set> serviceClassSet = new HashSet>(); 44 | for (Class clz : CLASS_SET) { 45 | if (clz.isAnnotationPresent(Service.class)) { 46 | serviceClassSet.add(clz); 47 | } 48 | } 49 | return serviceClassSet; 50 | } 51 | 52 | /** 53 | * 获取应用包下的所有Controller类 54 | */ 55 | public static Set> getControllerClassSet() { 56 | Set> controllerClassSet = new HashSet>(); 57 | for (Class clz : CLASS_SET) { 58 | if (clz.isAnnotationPresent(Controller.class)) { 59 | controllerClassSet.add(clz); 60 | } 61 | } 62 | return controllerClassSet; 63 | } 64 | 65 | /** 66 | * 获取应用包名下的所有Bean类,含Controller和Service 67 | */ 68 | public static Set> getBeanClassSet() { 69 | Set> beanClassSet = new HashSet>(); 70 | beanClassSet.addAll(getServiceClassSet()); 71 | beanClassSet.addAll(getControllerClassSet()); 72 | return beanClassSet; 73 | } 74 | 75 | /** 76 | * 获取应用包命下某父类的所有子类(获接口的实现类) 77 | */ 78 | public static Set> getClassSetBySuper(Class superClass) { 79 | Set> classSet = new HashSet>(); 80 | for (Class cls : CLASS_SET) { 81 | if (superClass.isAssignableFrom(cls) && !superClass.equals(cls)) { 82 | classSet.add(cls); 83 | } 84 | } 85 | return classSet; 86 | } 87 | 88 | /** 89 | * 获取应用包名下所有带某注解的所有类 90 | */ 91 | public static Set> getClassSetByAnnotation(Class annotationClass) { 92 | Set> classSet = new HashSet>(); 93 | for (Class cls : CLASS_SET) { 94 | if (cls.isAnnotationPresent(annotationClass)) { 95 | classSet.add(cls); 96 | } 97 | } 98 | return classSet; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/helper/ControllerHelper.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.helper; 2 | 3 | import com.xctian.framework.annotation.Action; 4 | import com.xctian.framework.bean.Handler; 5 | import com.xctian.framework.bean.Request; 6 | import com.xctian.framework.utils.ArrayUtil; 7 | import com.xctian.framework.utils.CollectionUtil; 8 | 9 | import javax.servlet.http.HttpServletRequest; 10 | import java.lang.reflect.Method; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | import java.util.Set; 14 | 15 | /** 16 | * 控制器助手类 17 | * 18 | * @author xctian 19 | * @date 2020/1/22 20 | */ 21 | public class ControllerHelper { 22 | 23 | /** 24 | * 存放Request与Handler之间的映射关系 25 | */ 26 | private static final Map ACTION_MAP = new HashMap(); 27 | 28 | static { 29 | //获取所有Controller类(通过ClassHelper) 30 | Set> controllerClassSet = ClassHelper.getControllerClassSet(); 31 | if (CollectionUtil.isNotEmpty(controllerClassSet)) { 32 | // 遍历这些Controller类 33 | for (Class controllerClass : controllerClassSet) { 34 | //获取Controller中定义的方法 35 | Method[] methods = controllerClass.getDeclaredMethods(); 36 | if (ArrayUtil.isNotEmpty(methods)) { 37 | // 遍历Controller类中的所有方法 38 | for (Method method : methods) { 39 | // 判断当前方法是否带Action注解 40 | if (method.isAnnotationPresent(Action.class)) { 41 | //从Action注解中获取url映射规则 42 | Action action = method.getAnnotation(Action.class); 43 | String mapping = action.value(); 44 | // 验证URL规则 45 | if (mapping.matches("\\w+:/\\w*")) { 46 | String[] array = mapping.split(":"); 47 | if (ArrayUtil.isNotEmpty(array) && array.length == 2) { 48 | //获取请求方法与请求路径 49 | String requestMethod = array[0]; 50 | String requestPath = array[1]; 51 | Request request = new Request(requestMethod, requestPath); 52 | Handler handler = new Handler(controllerClass, method); 53 | // 初始化Action Map 54 | ACTION_MAP.put(request, handler); 55 | } 56 | } 57 | } 58 | } 59 | } 60 | } 61 | } 62 | } 63 | 64 | /** 65 | * 获取Handler 66 | */ 67 | public static Handler getHandler(String requestMethod, String requestPath) { 68 | Request request = new Request(requestMethod, requestPath); 69 | return ACTION_MAP.get(request); 70 | } 71 | 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/helper/DatabaseHelper.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.helper; 2 | 3 | import com.xctian.framework.utils.CollectionUtil; 4 | import org.apache.commons.dbcp2.BasicDataSource; 5 | import org.apache.commons.dbutils.QueryRunner; 6 | import org.apache.commons.dbutils.handlers.*; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import javax.sql.DataSource; 11 | import java.sql.Connection; 12 | import java.sql.SQLException; 13 | import java.util.*; 14 | 15 | /** 16 | * 数据库操作助手类 17 | * 18 | * @author xctian 19 | * @date 2020/1/28 20 | */ 21 | public class DatabaseHelper { 22 | 23 | private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseHelper.class); 24 | 25 | private static final ThreadLocal CONNECTION_HOLDER; 26 | 27 | private static final QueryRunner QUERY_RUNNER; 28 | 29 | private static final BasicDataSource DATA_SOURCE; 30 | 31 | static { 32 | CONNECTION_HOLDER = new ThreadLocal(); 33 | 34 | QUERY_RUNNER = new QueryRunner(); 35 | 36 | DATA_SOURCE = new BasicDataSource(); 37 | 38 | DATA_SOURCE.setDriverClassName(PropertiesConfigHelper.getJdbcDriver()); 39 | 40 | DATA_SOURCE.setUrl(PropertiesConfigHelper.getJdbcUrl()); 41 | 42 | DATA_SOURCE.setUsername(PropertiesConfigHelper.getJdbcUserName()); 43 | 44 | DATA_SOURCE.setPassword(PropertiesConfigHelper.getJdbcPassword()); 45 | } 46 | 47 | /** 48 | * 获取DataSource 49 | */ 50 | public static DataSource getDataSource() { 51 | return DATA_SOURCE; 52 | } 53 | 54 | /** 55 | * 获取数据库连接,通过DBCP连接池 56 | */ 57 | public static Connection getConnection() { 58 | Connection connection = CONNECTION_HOLDER.get(); 59 | if (connection == null) { 60 | try { 61 | connection = DATA_SOURCE.getConnection(); 62 | } catch (SQLException e) { 63 | LOGGER.error("get connection failure", e); 64 | throw new RuntimeException(e); 65 | } finally { 66 | CONNECTION_HOLDER.set(connection); 67 | } 68 | } 69 | return connection; 70 | } 71 | 72 | /** 73 | * 开启事务 74 | */ 75 | public static void beginTransaction() { 76 | Connection conn = getConnection(); 77 | if (conn != null) { 78 | try { 79 | conn.setAutoCommit(false); 80 | } catch (SQLException e) { 81 | LOGGER.error("事务开启失败", e); 82 | throw new RuntimeException(e); 83 | } finally { 84 | CONNECTION_HOLDER.set(conn); 85 | } 86 | } 87 | } 88 | 89 | /** 90 | * 提交事务 91 | */ 92 | public static void commitTransaction() { 93 | Connection connection = getConnection(); 94 | if (connection != null) { 95 | try { 96 | connection.commit(); 97 | connection.close(); 98 | } catch (SQLException e) { 99 | LOGGER.error("事务提交失败", e); 100 | throw new RuntimeException(e); 101 | } finally { 102 | CONNECTION_HOLDER.set(connection); 103 | } 104 | } 105 | } 106 | 107 | /** 108 | * 事务回滚 109 | */ 110 | public static void rollbackTransaction() { 111 | Connection connection = getConnection(); 112 | if (connection != null){ 113 | try{ 114 | connection.rollback(); 115 | connection.close(); 116 | }catch (SQLException e){ 117 | LOGGER.error("事务回滚失败",e); 118 | throw new RuntimeException(e); 119 | }finally { 120 | CONNECTION_HOLDER.set(connection); 121 | } 122 | } 123 | } 124 | 125 | /** 126 | * 查询实体 127 | */ 128 | public static T queryEntity(Class entityClass, String sql, Object... params) { 129 | T entity; 130 | try { 131 | Connection conn = getConnection(); 132 | entity = QUERY_RUNNER.query(conn, sql, new BeanHandler(entityClass), params); 133 | } catch (SQLException e) { 134 | LOGGER.error("query entity failure", e); 135 | throw new RuntimeException(e); 136 | } 137 | return entity; 138 | } 139 | 140 | /** 141 | * 查询实体列表 142 | */ 143 | public static List queryEntityList(Class entityClass,String sql,Object... params){ 144 | List entityList; 145 | try{ 146 | Connection connection = getConnection(); 147 | entityList = QUERY_RUNNER.query(connection,sql,new BeanListHandler(entityClass),params); 148 | }catch (SQLException e){ 149 | LOGGER.error("query entity list failure",e); 150 | throw new RuntimeException(e); 151 | } 152 | return entityList; 153 | } 154 | 155 | /** 156 | * 查询并返回单个列值 157 | */ 158 | @SuppressWarnings("unchecked") 159 | public static T query(String sql,Object...params){ 160 | T obj; 161 | try{ 162 | Connection connection = getConnection(); 163 | obj = (T) QUERY_RUNNER.query(connection,sql,new ScalarHandler<>(),params); 164 | }catch (SQLException e){ 165 | LOGGER.error("query failure",e); 166 | throw new RuntimeException(e); 167 | } 168 | return obj; 169 | } 170 | 171 | /** 172 | * 查询并返回多个列值 173 | */ 174 | public static List queryList(String sql, Object... params) { 175 | List list; 176 | try { 177 | Connection conn = getConnection(); 178 | list = QUERY_RUNNER.query(conn, sql, new ColumnListHandler(), params); 179 | } catch (SQLException e) { 180 | LOGGER.error("query list failure", e); 181 | throw new RuntimeException(e); 182 | } 183 | return list; 184 | } 185 | 186 | /** 187 | * 查询并返回多个列值(具有唯一性) 188 | */ 189 | public static Set querySet(String sql, Object... params) { 190 | Collection valueList = queryList(sql, params); 191 | return new LinkedHashSet(valueList); 192 | } 193 | 194 | /** 195 | * 查询并返回数组 196 | */ 197 | public static Object[] queryArray(String sql, Object... params) { 198 | Object[] resultArray; 199 | try { 200 | Connection conn = getConnection(); 201 | resultArray = QUERY_RUNNER.query(conn, sql, new ArrayHandler(), params); 202 | } catch (SQLException e) { 203 | LOGGER.error("query array failure", e); 204 | throw new RuntimeException(e); 205 | } 206 | return resultArray; 207 | } 208 | 209 | /** 210 | * 查询并返回数组列表 211 | */ 212 | public static List queryArrayList(String sql, Object... params) { 213 | List resultArrayList; 214 | try { 215 | Connection conn = getConnection(); 216 | resultArrayList = QUERY_RUNNER.query(conn, sql, new ArrayListHandler(), params); 217 | } catch (SQLException e) { 218 | LOGGER.error("query array list failure", e); 219 | throw new RuntimeException(e); 220 | } 221 | return resultArrayList; 222 | } 223 | 224 | /** 225 | * 查询并返回结果集映射(列名 => 列值) 226 | */ 227 | public static Map queryMap(String sql, Object... params) { 228 | Map resultMap; 229 | try { 230 | Connection conn = getConnection(); 231 | resultMap = QUERY_RUNNER.query(conn, sql, new MapHandler(), params); 232 | } catch (SQLException e) { 233 | LOGGER.error("query map failure", e); 234 | throw new RuntimeException(e); 235 | } 236 | return resultMap; 237 | } 238 | 239 | /** 240 | * 查询并返回结果集映射列表(列名 => 列值) 241 | */ 242 | public static List> queryMapList(String sql, Object... params) { 243 | List> resultMapList; 244 | try { 245 | Connection conn = getConnection(); 246 | resultMapList = QUERY_RUNNER.query(conn, sql, new MapListHandler(), params); 247 | } catch (SQLException e) { 248 | LOGGER.error("query map list failure", e); 249 | throw new RuntimeException(e); 250 | } 251 | return resultMapList; 252 | } 253 | 254 | /** 255 | * 执行更新语句(包括:update、insert、delete) 256 | */ 257 | public static int update(String sql, Object... params) { 258 | int rows; 259 | try { 260 | Connection conn = getConnection(); 261 | rows = QUERY_RUNNER.update(conn, sql, params); 262 | } catch (SQLException e) { 263 | LOGGER.error("execute update failure", e); 264 | throw new RuntimeException(e); 265 | } 266 | return rows; 267 | } 268 | 269 | /** 270 | * 插入实体 271 | */ 272 | public static boolean insertEntity(Class entityClass, Map fieldMap) { 273 | if (CollectionUtil.isEmpty(fieldMap)) { 274 | LOGGER.error("can not insert entity: fieldMap is empty"); 275 | return false; 276 | } 277 | 278 | String sql = "INSERT INTO " + entityClass.getSimpleName(); 279 | StringBuilder columns = new StringBuilder("("); 280 | StringBuilder values = new StringBuilder("("); 281 | for (String fieldName : fieldMap.keySet()) { 282 | columns.append(fieldName).append(", "); 283 | values.append("?, "); 284 | } 285 | columns.replace(columns.lastIndexOf(", "), columns.length(), ")"); 286 | values.replace(values.lastIndexOf(", "), values.length(), ")"); 287 | sql += columns + " VALUES " + values; 288 | 289 | Object[] params = fieldMap.values().toArray(); 290 | 291 | return update(sql, params) == 1; 292 | } 293 | 294 | /** 295 | * 更新实体 296 | */ 297 | public static boolean updateEntity(Class entityClass, long id, Map fieldMap) { 298 | if (CollectionUtil.isEmpty(fieldMap)) { 299 | LOGGER.error("can not update entity: fieldMap is empty"); 300 | return false; 301 | } 302 | 303 | String sql = "UPDATE " + entityClass.getSimpleName() + " SET "; 304 | StringBuilder columns = new StringBuilder(); 305 | for (String fieldName : fieldMap.keySet()) { 306 | columns.append(fieldName).append(" = ?, "); 307 | } 308 | sql += columns.substring(0, columns.lastIndexOf(", ")) + " WHERE id = ?"; 309 | 310 | List paramList = new ArrayList(); 311 | paramList.addAll(fieldMap.values()); 312 | paramList.add(id); 313 | Object[] params = paramList.toArray(); 314 | 315 | return update(sql, params) == 1; 316 | } 317 | 318 | /** 319 | * 删除实体 320 | */ 321 | public static boolean deleteEntity(Class entityClass, long id) { 322 | String sql = "DELETE FROM " + entityClass.getSimpleName() + " WHERE id = ?"; 323 | return update(sql, id) == 1; 324 | } 325 | } 326 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/helper/IocHelper.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.helper; 2 | 3 | import com.xctian.framework.annotation.Inject; 4 | import com.xctian.framework.utils.ArrayUtil; 5 | import com.xctian.framework.utils.CollectionUtil; 6 | import com.xctian.framework.utils.ReflectionUtil; 7 | 8 | import java.lang.reflect.Field; 9 | import java.util.Map; 10 | 11 | /** 12 | * 依赖注入helper,完成IOC容器的初始化工作 13 | * 在IOC框架中管理的对象都是单例的 14 | * 15 | * @author xctian 16 | * @date 2020/1/22 17 | */ 18 | public class IocHelper { 19 | // IocHelperer被加载时,就会自动加载static块 20 | static { 21 | // 获取Bean Map 22 | Map, Object> beanMap = BeanHelper.getBeanMap(); 23 | if (CollectionUtil.isNotEmpty(beanMap)) { 24 | //遍历BeanMap 25 | for (Map.Entry, Object> beanEntry : beanMap.entrySet()) { 26 | Class beanClass = beanEntry.getKey(); 27 | Object beanInstance = beanEntry.getValue(); 28 | // 获取Bean Class中的所有成员变量,即Bean field 29 | Field[] beanFields = beanClass.getDeclaredFields(); 30 | if (ArrayUtil.isNotEmpty(beanFields)) { 31 | //遍历bean fields 32 | for (Field field : beanFields) { 33 | //判断当前field是否有Inject注解 34 | if (field.isAnnotationPresent(Inject.class)) { 35 | //在BeanMap中获取field对应的实例 36 | Class beanFieldClass = field.getType(); 37 | Object beanFiledInstance = beanMap.get(beanFieldClass); 38 | if (beanFiledInstance != null) { 39 | //通过反射对Bean field进行初始化 40 | ReflectionUtil.setField(beanInstance, field, beanFiledInstance); 41 | } 42 | } 43 | } 44 | } 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/helper/PropertiesConfigHelper.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.helper; 2 | 3 | import com.xctian.framework.PropertiesConfigConstant; 4 | import com.xctian.framework.utils.PropsUtil; 5 | 6 | import java.util.Properties; 7 | 8 | /** 9 | * 配置文件读取工具类 10 | * 11 | * @author xctian 12 | * @date 2020/1/17 13 | */ 14 | public class PropertiesConfigHelper { 15 | 16 | private static final Properties CONFIG_PROPERTIES = PropsUtil.loadProps(PropertiesConfigConstant.CONFIG_FILE_NAME); 17 | 18 | /** 19 | * 获取JDBC驱动 20 | */ 21 | public static String getJdbcDriver() { 22 | return PropsUtil.getString(CONFIG_PROPERTIES, PropertiesConfigConstant.JDBC_DRIVER); 23 | } 24 | 25 | /** 26 | * 获取JDBC URL 27 | */ 28 | public static String getJdbcUrl() { 29 | return PropsUtil.getString(CONFIG_PROPERTIES, PropertiesConfigConstant.JDBC_URL); 30 | } 31 | 32 | /** 33 | * 获取JDBC用户名 34 | */ 35 | public static String getJdbcUserName() { 36 | return PropsUtil.getString(CONFIG_PROPERTIES, PropertiesConfigConstant.JDBC_USERNAME); 37 | } 38 | 39 | /** 40 | * 获取JDBC密码 41 | */ 42 | public static String getJdbcPassword() { 43 | return PropsUtil.getString(CONFIG_PROPERTIES, PropertiesConfigConstant.JDBC_PASSWORD); 44 | } 45 | 46 | /** 47 | * 获取应用基础包名 48 | */ 49 | public static String getAppBasePackage() { 50 | return PropsUtil.getString(CONFIG_PROPERTIES, PropertiesConfigConstant.APP_BASE_PACKAGE); 51 | } 52 | 53 | /** 54 | * 获取应用JSP路径,有缺省配置 55 | */ 56 | public static String getAppJspPath() { 57 | return PropsUtil.getString(CONFIG_PROPERTIES, PropertiesConfigConstant.APP_JSP_PATH, "/WEB-INF/view/"); 58 | } 59 | 60 | /** 61 | * 获取应用静态资源路径,有缺省配置 62 | */ 63 | public static String getAppAssetPath() { 64 | return PropsUtil.getString(CONFIG_PROPERTIES, PropertiesConfigConstant.APP_ASSET_PATH, "/asset/"); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/helper/ServletHelper.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.helper; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import javax.servlet.ServletContext; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | import javax.servlet.http.HttpSession; 10 | import java.io.IOException; 11 | 12 | /** 13 | * Servlet助手类,用于解耦Servlet API 14 | * 15 | * @author xctian 16 | * @date 2020/1/29 17 | */ 18 | public class ServletHelper { 19 | 20 | private static final Logger LOGGER = LoggerFactory.getLogger(ServletHelper.class); 21 | 22 | /** 23 | * 每个线程都有一份ServletHelper实例 24 | */ 25 | private static final ThreadLocal SERVLET_HELPER_HOLDER = new ThreadLocal(); 26 | 27 | private HttpServletRequest request; 28 | 29 | private HttpServletResponse response; 30 | 31 | private ServletHelper(HttpServletRequest request, HttpServletResponse response) { 32 | this.request = request; 33 | this.response = response; 34 | } 35 | 36 | /** 37 | * 初始化 38 | */ 39 | public static void init(HttpServletRequest request, HttpServletResponse response) { 40 | SERVLET_HELPER_HOLDER.set(new ServletHelper(request, response)); 41 | } 42 | 43 | /** 44 | * 销毁 45 | */ 46 | public static void destory() { 47 | SERVLET_HELPER_HOLDER.remove(); 48 | } 49 | 50 | /** 51 | * 获取request对象 52 | */ 53 | private static HttpServletRequest getRequest() { 54 | return SERVLET_HELPER_HOLDER.get().request; 55 | } 56 | 57 | /** 58 | * 获取Response对象 59 | */ 60 | private static HttpServletResponse getResponse() { 61 | return SERVLET_HELPER_HOLDER.get().response; 62 | } 63 | 64 | /** 65 | * 获取session对象 66 | */ 67 | private static HttpSession getSession() { 68 | return getRequest().getSession(); 69 | } 70 | 71 | /** 72 | * 获取ServletContext对象 73 | */ 74 | private static ServletContext getServletContext() { 75 | return getRequest().getServletContext(); 76 | } 77 | 78 | /*-----封装常用的Servlet API ----*/ 79 | 80 | /** 81 | * Request中获取属性 82 | */ 83 | @SuppressWarnings("unchecked") 84 | public static T getRequestAttribute(String key) { 85 | return (T) getRequest().getAttribute(key); 86 | } 87 | 88 | /** 89 | * 从Request中移除属性 90 | */ 91 | public static void removeRequestAttribute(String key) { 92 | getRequest().removeAttribute(key); 93 | } 94 | 95 | /** 96 | * 请求重定向 97 | */ 98 | public static void sendRedirect(String loc) { 99 | try { 100 | getResponse().sendRedirect(getRequest().getContextPath() + loc); 101 | } catch (IOException e) { 102 | LOGGER.error("重定向失败", e); 103 | } 104 | } 105 | 106 | /** 107 | * 属性放入Session 108 | */ 109 | public static void setSessionAttribute(String key, Object val) { 110 | getSession().setAttribute(key, val); 111 | } 112 | 113 | /** 114 | * session中取值 115 | */ 116 | @SuppressWarnings("unchecked") 117 | public static T getSessionAttribute(String key) { 118 | return (T) getRequest().getSession().getAttribute(key); 119 | } 120 | 121 | /** 122 | * 从session中移除属性 123 | */ 124 | public static void removeSessionAttribute(String key){ 125 | getRequest().getSession().removeAttribute(key); 126 | } 127 | 128 | /** 129 | * 使session失效 130 | */ 131 | public static void invalidateSession(){ 132 | getRequest().getSession().invalidate(); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/proxy/AspectProxy.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.proxy; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.lang.reflect.Method; 7 | 8 | /** 9 | * 模板模式,切面代理抽象类 10 | * 11 | * @author xctian 12 | * @date 2020/1/28 13 | */ 14 | public abstract class AspectProxy implements Proxy { 15 | 16 | private static final Logger LOGGER = LoggerFactory.getLogger(AspectProxy.class); 17 | 18 | @Override 19 | public final Object doProxy(ProxyChain proxyChain) throws Throwable { 20 | Object result = null; 21 | 22 | Class cls = proxyChain.getTargetClass(); 23 | Method method = proxyChain.getTargetMethod(); 24 | Object[] params = proxyChain.getMethodParams(); 25 | 26 | begin(); 27 | try { 28 | if (intercept(cls, method, params)) { 29 | before(cls, method, params); 30 | result = proxyChain.doProxyChain(); 31 | after(cls, method, params, result); 32 | } else { 33 | result = proxyChain.doProxyChain(); 34 | } 35 | } catch (Exception e) { 36 | LOGGER.error("proxy failure", e); 37 | error(cls, method, params, e); 38 | throw e; 39 | } finally { 40 | end(); 41 | } 42 | return result; 43 | } 44 | 45 | public void end() { 46 | } 47 | 48 | public void error(Class cls, Method method, Object[] params, Throwable e) { 49 | } 50 | 51 | public void after(Class cls, Method method, Object[] params, Object result) throws Throwable { 52 | } 53 | 54 | public void before(Class cls, Method method, Object[] params) throws Throwable { 55 | } 56 | 57 | public boolean intercept(Class cls, Method method, Object[] params) throws Throwable { 58 | return true; 59 | } 60 | 61 | public void begin() { 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/proxy/Proxy.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.proxy; 2 | 3 | /** 4 | * @author xctian 5 | * @date 2020/1/25 6 | */ 7 | public interface Proxy { 8 | 9 | /** 10 | * 执行链式代理 11 | */ 12 | Object doProxy(ProxyChain proxyChain) throws Throwable; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/proxy/ProxyChain.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.proxy; 2 | 3 | import net.sf.cglib.proxy.MethodProxy; 4 | 5 | import java.lang.reflect.Method; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** 10 | * 代理链 11 | * 12 | * @author xctian 13 | * @date 2020/1/25 14 | */ 15 | public class ProxyChain { 16 | 17 | private final Class targetClass; 18 | 19 | private final Object targetObject; 20 | 21 | private final Method targetMethod; 22 | 23 | private final MethodProxy methodProxy; 24 | 25 | private final Object[] methodParams; 26 | 27 | private List proxyList = new ArrayList(); 28 | 29 | private int proxyIndex = 0; 30 | 31 | public ProxyChain(Class targetClass, Object targetObject, Method targetMethod, MethodProxy methodProxy, Object[] methodParams, List proxyList) { 32 | this.targetClass = targetClass; 33 | this.targetObject = targetObject; 34 | this.targetMethod = targetMethod; 35 | this.methodProxy = methodProxy; 36 | this.methodParams = methodParams; 37 | this.proxyList = proxyList; 38 | } 39 | 40 | public Class getTargetClass() { 41 | return targetClass; 42 | } 43 | 44 | public Method getTargetMethod() { 45 | return targetMethod; 46 | } 47 | 48 | public Object[] getMethodParams() { 49 | return methodParams; 50 | } 51 | 52 | public Object doProxyChain() throws Throwable{ 53 | Object methodResult; 54 | if(proxyIndex < proxyList.size()){ 55 | methodResult = proxyList.get(proxyIndex++).doProxy(this); 56 | }else { 57 | methodResult = methodProxy.invokeSuper(targetObject,methodParams); 58 | } 59 | return methodResult; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/proxy/ProxyManager.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.proxy; 2 | 3 | import net.sf.cglib.proxy.Enhancer; 4 | import net.sf.cglib.proxy.MethodInterceptor; 5 | import net.sf.cglib.proxy.MethodProxy; 6 | 7 | import java.lang.reflect.Method; 8 | import java.util.List; 9 | 10 | /** 11 | * 代理管理类 12 | * 13 | * @author xctian 14 | * @date 2020/1/26 15 | */ 16 | public class ProxyManager { 17 | 18 | /** 19 | * Cglib动态代理 20 | * 21 | * @param targetClass 被代理类 22 | * @param proxyList 代理对象列表 23 | * @param 被代理类实例 24 | * @return 被代理类实例 25 | */ 26 | @SuppressWarnings("unchecked") 27 | public static T createProxy(final Class targetClass, final List proxyList){ 28 | return (T) Enhancer.create(targetClass, new MethodInterceptor() { 29 | @Override 30 | public Object intercept(Object targetObject, Method targetMethod, Object[] methodParams, MethodProxy methodProxy) throws Throwable { 31 | return new ProxyChain(targetClass,targetObject,targetMethod,methodProxy,methodParams,proxyList).doProxyChain(); 32 | } 33 | }); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/proxy/TransactionProxy.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.proxy; 2 | 3 | import com.xctian.framework.annotation.Transaction; 4 | import com.xctian.framework.helper.DatabaseHelper; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import sun.rmi.runtime.Log; 8 | 9 | import java.lang.reflect.Method; 10 | 11 | /** 12 | * 事务代理 13 | * 14 | * @author xctian 15 | * @date 2020/1/28 16 | */ 17 | public class TransactionProxy implements Proxy { 18 | 19 | private static final Logger LOGGER = LoggerFactory.getLogger(TransactionProxy.class); 20 | 21 | private static final ThreadLocal FLAG_HOLDER = new ThreadLocal(){ 22 | @Override 23 | protected Boolean initialValue() { 24 | return false; 25 | } 26 | }; 27 | 28 | @Override 29 | public Object doProxy(ProxyChain proxyChain) throws Throwable { 30 | Object res; 31 | boolean flag = FLAG_HOLDER.get(); 32 | Method method = proxyChain.getTargetMethod(); 33 | // 被Transaction注解的方法才被拦截 34 | if(!flag && method.isAnnotationPresent(Transaction.class)){ 35 | FLAG_HOLDER.set(true); 36 | try { 37 | DatabaseHelper.beginTransaction(); 38 | LOGGER.debug("begin transaction"); 39 | res = proxyChain.doProxyChain(); 40 | DatabaseHelper.commitTransaction(); 41 | LOGGER.debug("commit transaction"); 42 | }catch (Exception e){ 43 | DatabaseHelper.rollbackTransaction(); 44 | LOGGER.debug("rollback transaction"); 45 | throw e; 46 | }finally { 47 | FLAG_HOLDER.remove(); 48 | } 49 | }else { 50 | res = proxyChain.doProxyChain(); 51 | } 52 | return res; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/utils/ArrayUtil.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.utils; 2 | 3 | import org.apache.commons.lang3.ArrayUtils; 4 | 5 | /** 6 | * 数组工具类 7 | * 8 | * @author xctian 9 | * @date 2020/1/22 10 | */ 11 | public class ArrayUtil { 12 | /** 13 | * 判断数组是否为空 14 | */ 15 | public static boolean isEmpty(Object[] array){ 16 | return ArrayUtils.isEmpty(array); 17 | } 18 | 19 | /** 20 | * 判断数组是否非空 21 | */ 22 | public static boolean isNotEmpty(Object[] array){ 23 | return !isEmpty(array); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/utils/CastUtil.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.utils; 2 | 3 | 4 | /** 5 | * 数据转型操作工具类 6 | * 7 | * @author xctian 8 | * @date 2020/1/17 9 | */ 10 | public class CastUtil { 11 | 12 | /** 13 | * 将对象转换为String 14 | * 15 | * @param obj 待转换的对象 16 | * @return 转换后的String 17 | */ 18 | public static String castToString(Object obj) { 19 | return CastUtil.castToString(obj, ""); 20 | } 21 | 22 | /** 23 | * 将对象转换为String(提供默认值) 24 | * 25 | * @param obj 待转换的对象 26 | * @param defaultValue 默认值 27 | * @return 转换后的String 28 | */ 29 | private static String castToString(Object obj, String defaultValue) { 30 | return obj != null ? String.valueOf(obj) : defaultValue; 31 | } 32 | 33 | /** 34 | * 将对象转换为doube 35 | * 36 | * @param obj 待转换的对象 37 | * @return 转换后的double 38 | */ 39 | public static double castToDouble(Object obj) { 40 | return CastUtil.castToDouble(obj, 0); 41 | } 42 | 43 | /** 44 | * 将对象转换为double(提供默认值) 45 | * 46 | * @param obj 待转换的对象 47 | * @param defaultValue 默认值 48 | * @return 转换后的double 49 | */ 50 | private static double castToDouble(Object obj, double defaultValue) { 51 | double doubleValue = defaultValue; 52 | if (obj != null) { 53 | String strValue = castToString(obj); 54 | if (StringUtil.isNotEmpty(strValue)) { 55 | try { 56 | doubleValue = Double.parseDouble(strValue); 57 | } catch (NumberFormatException e) { 58 | doubleValue = defaultValue; 59 | } 60 | } 61 | } 62 | return doubleValue; 63 | } 64 | 65 | /** 66 | * 将对象转换为long 67 | * 68 | * @param obj 待转换的对象 69 | * @return 转换后的long 70 | */ 71 | public static long castToLong(Object obj) { 72 | return CastUtil.castToLong(obj, 0); 73 | } 74 | 75 | /** 76 | * 将对象转换为long(提供默认值) 77 | * 78 | * @param obj 待转换的对象 79 | * @param defaultValue 默认值 80 | * @return 转换后的long 81 | */ 82 | private static long castToLong(Object obj, long defaultValue) { 83 | long longValue = defaultValue; 84 | if (obj != null) { 85 | String strValue = castToString(obj); 86 | if (StringUtil.isNotEmpty(strValue)) { 87 | try { 88 | longValue = Long.parseLong(strValue); 89 | } catch (NumberFormatException e) { 90 | longValue = defaultValue; 91 | } 92 | } 93 | } 94 | return longValue; 95 | } 96 | 97 | /** 98 | * 将对象转换为int 99 | * 100 | * @param obj 待转换的对象 101 | * @return 转换后的int 102 | */ 103 | public static int castToInt(Object obj) { 104 | return CastUtil.castToInt(obj, 0); 105 | } 106 | 107 | /** 108 | * 将对象转换为int(提供默认值) 109 | * 110 | * @param obj 待转换的对象 111 | * @param defaultValue 默认值 112 | * @return 转换后的int 113 | */ 114 | private static int castToInt(Object obj, int defaultValue) { 115 | int intValue = defaultValue; 116 | if (obj != null) { 117 | String strValue = castToString(obj); 118 | if (StringUtil.isNotEmpty(strValue)) { 119 | try { 120 | intValue = Integer.parseInt(strValue); 121 | } catch (NumberFormatException e) { 122 | intValue = defaultValue; 123 | } 124 | } 125 | } 126 | return intValue; 127 | } 128 | 129 | /** 130 | * 将对象转换为boolean 131 | * 132 | * @param obj 待转换的对象 133 | * @return 转换后的boolean 134 | */ 135 | public static boolean castToBoolean(Object obj) { 136 | return CastUtil.castToBoolean(obj, false); 137 | } 138 | 139 | /** 140 | * 将对象转换为int(提供默认值) 141 | * 142 | * @param obj 待转换的对象 143 | * @param defaultValue 默认值 144 | * @return 转换后的boolean 145 | */ 146 | private static boolean castToBoolean(Object obj, boolean defaultValue) { 147 | Boolean booleanValue = defaultValue; 148 | if (obj != null) { 149 | String strValue = castToString(obj); 150 | if (StringUtil.isNotEmpty(strValue)) { 151 | try { 152 | booleanValue = Boolean.parseBoolean(strValue); 153 | } catch (NumberFormatException e) { 154 | booleanValue = defaultValue; 155 | } 156 | } 157 | } 158 | return booleanValue; 159 | } 160 | 161 | 162 | } 163 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/utils/ClassUtil.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.utils; 2 | 3 | import com.xctian.framework.helper.PropertiesConfigHelper; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import java.io.File; 8 | import java.io.FileFilter; 9 | import java.net.JarURLConnection; 10 | import java.net.URL; 11 | import java.util.Enumeration; 12 | import java.util.HashSet; 13 | import java.util.Set; 14 | import java.util.jar.JarEntry; 15 | import java.util.jar.JarFile; 16 | 17 | /** 18 | * 类操作工具类 19 | * 20 | * @author xctian 21 | * @date 2020/1/18 22 | */ 23 | public class ClassUtil { 24 | 25 | private static final Logger LOGGER = LoggerFactory.getLogger(CastUtil.class); 26 | 27 | /** 28 | * 获取类加载器 29 | */ 30 | public static ClassLoader getClassLoader() { 31 | return Thread.currentThread().getContextClassLoader(); 32 | } 33 | 34 | /** 35 | * 加载类 36 | * 37 | * @param className 待加载的类的全限定名 38 | * @return 加载后的类 39 | */ 40 | public static Class loadClass(String className, boolean isInitialized) { 41 | Class cls; 42 | try { 43 | cls = Class.forName(className, isInitialized, getClassLoader()); 44 | } catch (ClassNotFoundException e) { 45 | LOGGER.error("类加载失败:" + className, e); 46 | throw new RuntimeException(e); 47 | } 48 | return cls; 49 | } 50 | 51 | /** 52 | * 加载类,默认初始化类 53 | */ 54 | public static Class loadClass(String className) { 55 | return loadClass(className, true); 56 | } 57 | 58 | /** 59 | * 获取指定包下的所有类 60 | * 61 | * @param packageName 指定包的全限定名 62 | * @return 该包下的所有类组成的集合 63 | */ 64 | public static Set> getClassSet(String packageName) { 65 | Set> classSet = new HashSet>(); 66 | try { 67 | Enumeration urls = getClassLoader().getResources(packageName.replace(".", "/")); 68 | while (urls.hasMoreElements()) { 69 | URL url = urls.nextElement(); 70 | if (url != null) { 71 | String protocol = url.getProtocol(); 72 | // 如果url是file类型 73 | if (protocol.equals("file")) { 74 | //每个url path对应的限定名 75 | String packagePath = url.getPath().replaceAll("%20", " "); 76 | addClass(classSet, packagePath, packageName); 77 | // 如果url是jar类型 78 | } else if (protocol.equals("jar")) { 79 | JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection(); 80 | if (jarURLConnection != null) { 81 | JarFile jarFile = jarURLConnection.getJarFile(); 82 | if (jarFile != null) { 83 | Enumeration jarEntries = jarFile.entries(); 84 | while (jarEntries.hasMoreElements()) { 85 | JarEntry jarEntry = jarEntries.nextElement(); 86 | String jarEntryName = jarEntry.getName(); 87 | if (jarEntryName.endsWith(".class")) { 88 | String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/", "."); 89 | doAddClass(classSet, className); 90 | LOGGER.debug("获取类集合成功:" + className); 91 | } 92 | } 93 | } 94 | } 95 | } 96 | } 97 | } 98 | } catch (Exception e) { 99 | LOGGER.error("获取指定包下的类集合失败", e); 100 | throw new RuntimeException(e); 101 | } 102 | return classSet; 103 | } 104 | 105 | /** 106 | * 加载url path对应的所有类并存入集合,如果url path对应文件夹则进行递归 107 | * 108 | * @param classSet 包名下的类集合 109 | * @param packagePath 每个url path对应的全限定名 110 | * @param packageName 包名 111 | */ 112 | private static void addClass(Set> classSet, String packagePath, String packageName) { 113 | File[] files = new File(packagePath).listFiles(new FileFilter() { 114 | // 过滤出文件夹方便后序递归调用,或者直接过滤出.class结尾的文件 115 | @Override 116 | public boolean accept(File file) { 117 | return (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory(); 118 | } 119 | }); 120 | for (File file : files) { 121 | String fileName = file.getName(); 122 | if (file.isFile()) { 123 | String className = fileName.substring(0, fileName.lastIndexOf(".")); 124 | if (StringUtil.isNotEmpty(packageName)) { 125 | className = packageName + "." + className; 126 | } 127 | doAddClass(classSet, className); 128 | } else { 129 | // 递归对文件夹进行操作 130 | String subPackagePath = fileName; 131 | if (StringUtil.isNotEmpty(packagePath)) { 132 | subPackagePath = packagePath + "/" + subPackagePath; 133 | } 134 | String subPackageName = fileName; 135 | if (StringUtil.isNotEmpty(packageName)) { 136 | subPackageName = packageName + "." + subPackageName; 137 | } 138 | addClass(classSet, subPackagePath, subPackageName); 139 | } 140 | 141 | } 142 | } 143 | 144 | private static void doAddClass(Set> classSet, String className) { 145 | Class clz = loadClass(className, false); 146 | classSet.add(clz); 147 | } 148 | 149 | // test case 150 | // public static void main(String[] args) { 151 | // String packageName = "com.xctian.framework"; 152 | // try { 153 | // URL url1 = PropertiesConfigHelper.class.getResource("/new2.class"); 154 | // System.out.println("url1:"+url1); 155 | // URL url2 = ClassUtil.class.getResource("new2.class"); 156 | // System.out.println("url2:"+url2); 157 | // Enumeration urls = getClassLoader().getResources(packageName.replace(".","/")); 158 | // while(urls.hasMoreElements()){ 159 | // URL url = urls.nextElement(); 160 | // // getPath()去掉了protoco 161 | // String path = url.getPath().replaceAll("%20"," "); 162 | // File[] files = new File(path).listFiles(); 163 | // System.out.println("files大小:"+ files.length); 164 | // try { 165 | // for(File each :files){ 166 | // if(each.isDirectory()){ 167 | // System.out.println(each.getName()+"是文件夹"); 168 | // }else{ 169 | // System.out.println(each.getName()+"不是文件夹"); 170 | // } 171 | // System.out.println(each.getName()); 172 | // } 173 | // }catch (Exception e){ 174 | // e.printStackTrace(); 175 | // } 176 | // System.out.println(url.getPath().toString()); 177 | // } 178 | // }catch (Exception e){ 179 | // e.printStackTrace(); 180 | // } 181 | // 182 | // } 183 | } 184 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/utils/CodecUtil.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.utils; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.net.URLDecoder; 7 | import java.net.URLEncoder; 8 | 9 | /** 10 | * URL编解码工具类 11 | * 12 | * @author xctian 13 | * @date 2020/1/23 14 | */ 15 | public class CodecUtil { 16 | 17 | private static final Logger LOGGER = LoggerFactory.getLogger(CodecUtil.class); 18 | 19 | /** 20 | * URL编码 21 | */ 22 | public static String encodeURL(String source) { 23 | String target; 24 | try { 25 | target = URLEncoder.encode(source, "UTF-8"); 26 | } catch (Exception e) { 27 | LOGGER.error("encode url failure", e); 28 | throw new RuntimeException(e); 29 | } 30 | return target; 31 | } 32 | 33 | /** 34 | * URL解码 35 | */ 36 | public static String decodeURL(String source) { 37 | String target; 38 | try { 39 | target = URLDecoder.decode(source, "UTF-8"); 40 | } catch (Exception e) { 41 | LOGGER.error("decode url failure", e); 42 | throw new RuntimeException(e); 43 | } 44 | return target; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/utils/CollectionUtil.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.utils; 2 | 3 | import org.apache.commons.collections4.CollectionUtils; 4 | import org.apache.commons.collections4.MapUtils; 5 | 6 | import java.util.Collection; 7 | import java.util.Map; 8 | 9 | /** 10 | * 集合工具类 11 | * 12 | * @author xctian 13 | * @date 2020/1/17 14 | */ 15 | public class CollectionUtil { 16 | 17 | /** 18 | * 判断Collection是否为空 19 | * 20 | * @param collection 待判断的集合 21 | * @return 判断结果 22 | */ 23 | public static boolean isEmpty(Collection collection) { 24 | return CollectionUtils.isEmpty(collection); 25 | } 26 | 27 | /** 28 | * 判断Collection是否非空 29 | * 30 | * @param collection 待判断的集合 31 | * @return 判断结果 32 | */ 33 | public static boolean isNotEmpty(Collection collection) { 34 | return !isEmpty(collection); 35 | } 36 | 37 | /** 38 | * 判断Map是否为空 39 | * 40 | * @param map 待判断的Map 41 | * @return 判断结果 42 | */ 43 | public static boolean isEmpty(Map map) { 44 | return MapUtils.isEmpty(map); 45 | } 46 | 47 | /** 48 | * 判断Map是否非空 49 | * 50 | * @param map 待判断的Map 51 | * @return 判断结果 52 | */ 53 | public static boolean isNotEmpty(Map map) { 54 | return !isEmpty(map); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/utils/JsonUtil.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.utils; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | /** 8 | * JSON和Java Bean的转换工具类,基于Jackson 9 | * 10 | * @author xctian 11 | * @date 2020/1/23 12 | */ 13 | public class JsonUtil { 14 | 15 | private static final Logger LOGGER = LoggerFactory.getLogger(JsonUtil.class); 16 | 17 | private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); 18 | 19 | /** 20 | * Bean转换为Json 21 | */ 22 | public static String toJson(T obj) { 23 | String json; 24 | try { 25 | json = OBJECT_MAPPER.writeValueAsString(obj); 26 | } catch (Exception e) { 27 | LOGGER.error("convert bean to Json failure", e); 28 | throw new RuntimeException(e); 29 | } 30 | return json; 31 | } 32 | 33 | /** 34 | * Json转换为Bean 35 | */ 36 | public static T toBean(String json, Class type) { 37 | T bean; 38 | try { 39 | bean = OBJECT_MAPPER.readValue(json, type); 40 | } catch (Exception e) { 41 | LOGGER.error("convert Json to bean failure", e); 42 | throw new RuntimeException(e); 43 | } 44 | return bean; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/utils/PropsUtil.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.utils; 2 | 3 | 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import java.io.FileNotFoundException; 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.util.Properties; 11 | 12 | /** 13 | * 配置文件工具类 14 | * 15 | * @author xctian 16 | * @date 2020/1/17 17 | */ 18 | public class PropsUtil { 19 | 20 | private static final Logger LOGGER = LoggerFactory.getLogger(PropsUtil.class); 21 | 22 | /** 23 | * 加载属性文件,获取Properties对象 24 | * 25 | * @param fileName 属性文件名 26 | * @return Properties 27 | */ 28 | public static Properties loadProps(String fileName) { 29 | Properties props = null; 30 | InputStream is = null; 31 | try { 32 | is = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName); 33 | if (null == is) { 34 | throw new FileNotFoundException(fileName + "file is not found"); 35 | } 36 | props = new Properties(); 37 | props.load(is); 38 | } catch (IOException e) { 39 | LOGGER.error("加载配置文件失败", e); 40 | } finally { 41 | if (is != null) { 42 | try { 43 | is.close(); 44 | } catch (IOException e) { 45 | LOGGER.error("inputstream关闭失败", e); 46 | } 47 | } 48 | } 49 | return props; 50 | } 51 | 52 | /** 53 | * 从Properties对象获取字符串属性 54 | * 55 | * @param properties properties对象 56 | * @param key 属性关键字 57 | * @return 对应的字符串属性 58 | */ 59 | public static String getString(Properties properties, String key) { 60 | return getString(properties, key, ""); 61 | } 62 | 63 | /** 64 | * 从Properties对象获取字符串属性,可指定默认值 65 | * 66 | * @param properties properties对象 67 | * @param key 属性关键字 68 | * @param defaultValue 默认值 69 | * @return 对应的字符串属性 70 | */ 71 | public static String getString(Properties properties, String key, String defaultValue) { 72 | String value = defaultValue; 73 | // 注意contains和containsKey方法的区别,避免踩坑 74 | if (properties.containsKey(key)) { 75 | value = properties.getProperty(key); 76 | } 77 | return value; 78 | } 79 | 80 | /** 81 | * 从Properties对象获取int型属性,需经过CastUtil进行数据转型 82 | * 83 | * @param properties properties对象 84 | * @param key 属性关键字 85 | * @return 对应的int属性 86 | */ 87 | public static int getInt(Properties properties, String key) { 88 | return getInt(properties, key, 0); 89 | } 90 | 91 | /** 92 | * 从Properties对象获取int型属性,可指定默认值 93 | * 94 | * @param properties properties对象 95 | * @param key 属性关键字 96 | * @param defaultValue 默认值 97 | * @return 对应的int属性 98 | */ 99 | public static int getInt(Properties properties, String key, int defaultValue) { 100 | int value = defaultValue; 101 | if (properties.contains(key)) { 102 | value = CastUtil.castToInt(properties.getProperty(key)); 103 | } 104 | return value; 105 | } 106 | 107 | 108 | /** 109 | * 从Properties对象获取double型属性,需经过CastUtil进行数据转型 110 | * 111 | * @param properties properties对象 112 | * @param key 属性关键字 113 | * @return 对应的double属性 114 | */ 115 | public static double getDouble(Properties properties, String key) { 116 | return getDouble(properties, key, 0); 117 | } 118 | 119 | /** 120 | * 从Properties对象获取double型属性,可指定默认值 121 | * 122 | * @param properties properties对象 123 | * @param key 属性关键字 124 | * @param defaultValue 默认值 125 | * @return 对应的double属性 126 | */ 127 | public static double getDouble(Properties properties, String key, double defaultValue) { 128 | double value = defaultValue; 129 | if (properties.contains(key)) { 130 | value = CastUtil.castToDouble(properties.getProperty(key)); 131 | } 132 | return value; 133 | } 134 | 135 | /** 136 | * 从Properties对象获取boolean型属性,需经过CastUtil进行数据转型 137 | * 138 | * @param properties properties对象 139 | * @param key 属性关键字 140 | * @return 对应的boolean属性 141 | */ 142 | public static boolean getBoolean(Properties properties, String key) { 143 | return getBoolean(properties, key, false); 144 | } 145 | 146 | /** 147 | * 从Properties对象获取boolean型属性,可指定默认值 148 | * 149 | * @param properties properties对象 150 | * @param key 属性关键字 151 | * @param defaultValue 默认值 152 | * @return 对应的boolean属性 153 | */ 154 | public static boolean getBoolean(Properties properties, String key, boolean defaultValue) { 155 | boolean value = defaultValue; 156 | if (properties.contains(key)) { 157 | value = CastUtil.castToBoolean(properties.getProperty(key)); 158 | } 159 | return value; 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/utils/ReflectionUtil.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.utils; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.io.File; 7 | import java.lang.reflect.Field; 8 | import java.lang.reflect.Method; 9 | 10 | /** 11 | * 反射工具类 12 | * 13 | * @author xctian 14 | * @date 2020/1/22 15 | */ 16 | public class ReflectionUtil { 17 | 18 | private static final Logger LOGGER = LoggerFactory.getLogger(ReflectionUtil.class); 19 | 20 | /** 21 | * 创建实例 22 | */ 23 | public static Object newInstance(Class clz){ 24 | Object instance; 25 | try { 26 | instance = clz.newInstance(); 27 | }catch (Exception e){ 28 | LOGGER.error("new instance failure",e); 29 | throw new RuntimeException(); 30 | } 31 | return instance; 32 | } 33 | 34 | /** 35 | * 调用方法 36 | */ 37 | public static Object invokeMethod(Object obj, Method method,Object... args){ 38 | Object res; 39 | try { 40 | // setAccessible是启用和禁用访问安全检查的开关,设置为true可以提高反射执行速度 41 | method.setAccessible(true); 42 | res = method.invoke(obj,args); 43 | }catch (Exception e){ 44 | LOGGER.error("invoke method failure",e); 45 | throw new RuntimeException(e); 46 | } 47 | return res; 48 | } 49 | 50 | /** 51 | * 设置成员变量的值 52 | */ 53 | public static void setField(Object obj, Field field,Object value){ 54 | try { 55 | field.setAccessible(true); 56 | field.set(obj,value); 57 | }catch (Exception e){ 58 | LOGGER.error("set field failure",e); 59 | throw new RuntimeException(e); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/utils/StreamUtil.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.utils; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.io.BufferedReader; 7 | import java.io.InputStream; 8 | import java.io.InputStreamReader; 9 | 10 | /** 11 | * 流操作工具类 12 | * 13 | * @author xctian 14 | * @date 2020/1/23 15 | */ 16 | public class StreamUtil { 17 | 18 | private static final Logger LOGGER = LoggerFactory.getLogger(StreamUtil.class); 19 | 20 | /** 21 | * 从输入流中获取字符串 22 | */ 23 | public static String getString(InputStream is) { 24 | StringBuffer sb = new StringBuffer(); 25 | try { 26 | BufferedReader reader = new BufferedReader(new InputStreamReader(is)); 27 | String line; 28 | while ((line = reader.readLine()) != null) { 29 | sb.append(line); 30 | } 31 | } catch (Exception e) { 32 | LOGGER.error("get string failure", e); 33 | throw new RuntimeException(e); 34 | } 35 | return sb.toString(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/xctian/framework/utils/StringUtil.java: -------------------------------------------------------------------------------- 1 | package com.xctian.framework.utils; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | 5 | /** 6 | * 字符串工具类 7 | * 8 | * @author xctian 9 | * @date 2020/1/17 10 | */ 11 | public final class StringUtil { 12 | 13 | /** 14 | * 字符串分隔符 15 | */ 16 | public static final String SEPARATOR = String.valueOf((char) 29); 17 | 18 | /** 19 | * 判断字符串是否为空 20 | * 21 | * @param str 目标字符串 22 | * @return 判断结果 23 | */ 24 | public static boolean isEmpty(String str) { 25 | if (str != null) { 26 | // 删除了原始字符串头部和尾部的空格 27 | str.trim(); 28 | } 29 | return StringUtils.isEmpty(str); 30 | } 31 | 32 | /** 33 | * 判断字符串是否非空 34 | * 35 | * @param str 目标字符串 36 | * @return 判断结果 37 | */ 38 | public static boolean isNotEmpty(String str) { 39 | return !isEmpty(str); 40 | } 41 | 42 | /** 43 | * 分割固定格式字符串 44 | */ 45 | public static String[] splitString(String str,String separator){ 46 | return StringUtils.splitByWholeSeparator(str,separator); 47 | } 48 | 49 | } 50 | --------------------------------------------------------------------------------