├── .gitignore ├── pom.xml ├── readme.md └── src └── main ├── java └── com │ └── bcc │ ├── MainLoader.java │ ├── annotation │ ├── Action.java │ ├── Aspect.java │ ├── Controller.java │ ├── Inject.java │ ├── Service.java │ └── Transaction.java │ ├── helper │ ├── AopHelper.java │ ├── BeanHelper.java │ ├── ClassHelper.java │ ├── ConfigHelper.java │ ├── ControllerHelper.java │ ├── DatabaseHelper.java │ └── IocHelper.java │ ├── proxy │ ├── AbstractAspect.java │ ├── Proxy.java │ ├── ProxyChain.java │ ├── ProxyManager.java │ └── TransactionAspect.java │ ├── test │ ├── Test01.java │ ├── TestController.java │ └── TestService.java │ ├── util │ ├── CastUtil.java │ ├── ClassUtil.java │ ├── CodecUtil.java │ ├── ConfigConstant.java │ ├── JsonUtil.java │ ├── MethodEunm.java │ ├── PropsUtil.java │ ├── ReflectionUtil.java │ ├── StreamUtil.java │ └── StringUtil.java │ └── web │ ├── DispatchServlet.java │ ├── Handler.java │ ├── ModelAndView.java │ ├── Request.java │ ├── RequestParam.java │ └── ServerResponse.java └── resources ├── log4j.properties └── smart.properties /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | .sts4-cache 13 | 14 | ### IntelliJ IDEA ### 15 | .idea 16 | *.iws 17 | *.iml 18 | *.ipr 19 | 20 | ### NetBeans ### 21 | nbproject/private/ 22 | build/ 23 | nbbuild/ 24 | dist/ 25 | nbdist/ 26 | .nb-gradle/ -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.bcc 8 | mySpring 9 | 1.0.0 10 | 11 | 12 | UTF-8 13 | 14 | 15 | 16 | 17 | 27 | 28 | 29 | org.apache.maven.plugins 30 | maven-compiler-plugin 31 | 32 | 33 | 1.8 34 | 1.8 35 | 1.8 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | javax.servlet 47 | javax.servlet-api 48 | 3.1.0 49 | provided 50 | 51 | 52 | 53 | javax.servlet.jsp 54 | jsp-api 55 | 2.2 56 | provided 57 | 58 | 59 | 60 | javax.servlet 61 | jstl 62 | 1.2 63 | runtime 64 | 65 | 66 | 67 | 68 | org.slf4j 69 | slf4j-log4j12 70 | 1.7.7 71 | 72 | 73 | 74 | 75 | mysql 76 | mysql-connector-java 77 | 5.1.33 78 | runtime 79 | 80 | 81 | 82 | 83 | org.apache.commons 84 | commons-lang3 85 | 3.3.2 86 | 87 | 88 | 89 | org.apache.commons 90 | commons-collections4 91 | 4.0 92 | 93 | 94 | 95 | com.fasterxml.jackson.core 96 | jackson-databind 97 | 2.9.3 98 | 99 | 100 | 101 | 102 | commons-dbutils 103 | commons-dbutils 104 | 1.6 105 | 106 | 107 | 108 | org.apache.commons 109 | commons-dbcp2 110 | 2.0.1 111 | 112 | 113 | 114 | junit 115 | junit 116 | 4.12 117 | 118 | 119 | 120 | 121 | cglib 122 | cglib 123 | 3.2.6 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # 开发 javaWeb 框架 # 2 | 自己手写一个 Javaweb 框架,仿 spring 和 springMvc 实现相同功能 3 | ## 基础功能 ## 4 | - 快速搭建框架 5 | - 加载读取配置文件 6 | - 加载指定注解的类 7 | - 实现 IOC 8 | - 实现 Aop : 仿照 spring + aspect 的 aop , 自定义注解,自己利用代理实现 aop 功能 9 | - 实现数据库事务 10 | ## 使用方式 ## 11 | 12 | 1. 需要在你的项目中加入框架需要的配置文件 smart.properties 13 | 1. Service 表示业务层注解,Controller 表示 web层注解,Inject bean 注入注解,Transaction 事务注解 14 | 1. 下载项目安装到本地 mvn install 。 在你自己开发的项目 pom 文件导入此项目 ` 15 | com.bcc 16 | myspring 17 | 1.0.0 18 | ` 19 | 1. Action 注解的使用,加在 controller 的方法上 @Action("请求方法:/请求路径") eg. @Action("get:/getById") 20 | 1. Cotroller 里面方法的写法 . 如果想返回页面,返回值为 ModelAndView 对象 ; 只想返回数据,则返回 ServerResponse 对象 . 想要传入参数 , 在方法参数里面放 RequestParam 对象即可 , 目前只支持将参数映射到 map 中 ,以 key-value 形式使用 , 不支持对象 21 | 1. 自定义 AOP 的使用 ,切面类继承 AbstractAspect 类,重写想要拦截的方法。在切面类上加上 @Aspect(Controller.class) , 想要拦截那个类就在那个类上加上 @Controller 注解,或者其它各种自定义注解都可以 22 | 1. 事务的使用 : 目前只支持 添加了 @Service 注解的类的事务,在 service 类的某个方法上加 @Transaction 注解即可实现事务 23 | ## 优化提高 ## 24 | 1. 25 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/MainLoader.java: -------------------------------------------------------------------------------- 1 | package com.bcc; 2 | 3 | import com.bcc.helper.AopHelper; 4 | import com.bcc.helper.BeanHelper; 5 | import com.bcc.helper.ClassHelper; 6 | import com.bcc.helper.ConfigHelper; 7 | import com.bcc.helper.ControllerHelper; 8 | import com.bcc.helper.IocHelper; 9 | import com.bcc.util.ClassUtil; 10 | 11 | import java.util.Arrays; 12 | import java.util.List; 13 | 14 | /** 15 | * 初始化所有类 16 | * 注意加载的顺序 17 | */ 18 | public class MainLoader { 19 | 20 | public static void init() { 21 | List> classes = Arrays.asList(ConfigHelper.class, ClassHelper.class, BeanHelper.class, AopHelper.class, IocHelper.class, ControllerHelper.class); 22 | for (Class aClass : classes) { 23 | ClassUtil.loadClass(aClass.getName()); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/annotation/Action.java: -------------------------------------------------------------------------------- 1 | package com.bcc.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 | * action 方法注解 10 | */ 11 | 12 | @Target(ElementType.METHOD) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | public @interface Action { 15 | 16 | /** 17 | * 请求方法和路径 eg. get:list 18 | * @return 19 | */ 20 | String value(); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/annotation/Aspect.java: -------------------------------------------------------------------------------- 1 | package com.bcc.annotation; 2 | 3 | import java.lang.annotation.Annotation; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * 切面注解 11 | */ 12 | @Target(ElementType.TYPE) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | public @interface Aspect { 15 | 16 | /** 17 | * 切点 18 | */ 19 | Class value(); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/annotation/Controller.java: -------------------------------------------------------------------------------- 1 | package com.bcc.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 | * Controller 10 | */ 11 | 12 | @Target(ElementType.TYPE) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | public @interface Controller { 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/annotation/Inject.java: -------------------------------------------------------------------------------- 1 | package com.bcc.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 | @Target(ElementType.FIELD) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | public @interface Inject { 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/annotation/Service.java: -------------------------------------------------------------------------------- 1 | package com.bcc.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 | @Target(ElementType.TYPE) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface Service { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/annotation/Transaction.java: -------------------------------------------------------------------------------- 1 | package com.bcc.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 | 12 | @Target(ElementType.METHOD) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | public @interface Transaction { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/helper/AopHelper.java: -------------------------------------------------------------------------------- 1 | package com.bcc.helper; 2 | 3 | import com.bcc.annotation.Aspect; 4 | import com.bcc.annotation.Service; 5 | import com.bcc.proxy.AbstractAspect; 6 | import com.bcc.proxy.Proxy; 7 | import com.bcc.proxy.ProxyManager; 8 | import com.bcc.proxy.TransactionAspect; 9 | 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import java.lang.annotation.Annotation; 14 | import java.util.ArrayList; 15 | import java.util.HashMap; 16 | import java.util.HashSet; 17 | import java.util.List; 18 | import java.util.Map; 19 | import java.util.Set; 20 | 21 | public final class AopHelper { 22 | 23 | private static final Logger LOGGER = LoggerFactory.getLogger(AopHelper.class); 24 | 25 | private AopHelper() { 26 | 27 | } 28 | 29 | static { 30 | Map, Set>> proxyMap = createProxyMap(); 31 | try { 32 | Map, List> targetMap = createTargetMap(proxyMap); 33 | for (Map.Entry, List> entry : targetMap.entrySet()) { 34 | Class targetClass = entry.getKey(); 35 | List proxies = entry.getValue(); 36 | Object proxy = ProxyManager.createProxy(targetClass, proxies); // ! ! ! 37 | BeanHelper.addBean(targetClass, proxy); // ! ! ! 如何起作用的 38 | // 将原来 IOC 容器中的 Controller 对象, 已经替换成现在创建的代理对象 39 | // dispatchServlet 中获取到的是代理对象 , 是调用了代理对象的方法进行处理 . 40 | } 41 | } catch (Exception e) { 42 | e.printStackTrace(); 43 | LOGGER.error("aophelper 初始化错误", e); 44 | throw new RuntimeException(e); 45 | } 46 | } 47 | 48 | /** 49 | * 获取某个 Aspect 注解 所代理的所有类 50 | */ 51 | private static Set> createTargetClassSet(Aspect aspect) { 52 | Set> classes = new HashSet<>(); 53 | Class anno = aspect.value(); 54 | if (!anno.equals(Aspect.class)) { 55 | classes.addAll(ClassHelper.getClassesByAnnotation(anno)); 56 | } 57 | return classes; 58 | } 59 | 60 | 61 | /** 62 | * 一个切面类 对应多个目标类 63 | * 1. 先获取所有 AbstractAspect 的子类 , 即先获取到所有的切面类 64 | * 2. 循环,获取每个切面类对应的所有 目标类 集合 65 | */ 66 | private static Map, Set>> createProxyMap() { 67 | HashMap, Set>> map = new HashMap<>(); 68 | addCommoAspectProxy(map); 69 | addTransactionAspectProxy(map); 70 | return map; 71 | } 72 | 73 | // TODO 暂时只支持 service 上的 事务注解 74 | /** 75 | * 将所有 service 类 都添加事务切面 TransactionAspect 76 | */ 77 | private static void addTransactionAspectProxy(HashMap, Set>> map) { 78 | Set> classes = ClassHelper.getClassesByAnnotation(Service.class); 79 | map.put(TransactionAspect.class, classes); 80 | } 81 | 82 | /** 83 | * key = 继承自 AbastractAspect 的所有普通切面类 84 | * value = Aspect 的 value 值 所指向的所有类 85 | */ 86 | private static void addCommoAspectProxy(HashMap, Set>> map) { 87 | Set> aspectClasses = ClassHelper.getClassesBySuper(AbstractAspect.class); 88 | aspectClasses.stream().filter(c -> c.isAnnotationPresent(Aspect.class)) 89 | .forEach(c -> { 90 | Aspect annotation = c.getAnnotation(Aspect.class); 91 | Set> classesByAnnotation = ClassHelper.getClassesByAnnotation(annotation.value()); 92 | map.put(c, classesByAnnotation); 93 | }); 94 | } 95 | 96 | 97 | /** 98 | * 一个目标类 对应 多个 切面类 99 | * 由上个方法得到的集合进行整理 100 | */ 101 | private static Map, List> createTargetMap(Map, Set>> proxyMap) throws IllegalAccessException, InstantiationException { 102 | 103 | // 目标类 和 代理类 一对多关系 104 | HashMap, List> map = new HashMap<>(); 105 | 106 | Set, Set>>> entries = proxyMap.entrySet(); 107 | 108 | for (Map.Entry, Set>> entry : entries) { 109 | Class proxyClass = entry.getKey(); 110 | Set> aimClasses = entry.getValue(); 111 | 112 | for (Class aimClass : aimClasses) { 113 | if (map.containsKey(aimClass)) { 114 | map.get(aimClass).add((Proxy) proxyClass.newInstance()); 115 | } else { 116 | ArrayList proxies = new ArrayList<>(); 117 | proxies.add((Proxy) proxyClass.newInstance()); 118 | map.put(aimClass, proxies); 119 | } 120 | } 121 | } 122 | return map; 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/helper/BeanHelper.java: -------------------------------------------------------------------------------- 1 | package com.bcc.helper; 2 | 3 | import com.bcc.util.ReflectionUtil; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import java.util.Set; 8 | 9 | /** 10 | * bean 容器类 11 | * 生成被框架管理的 bean 12 | * 放入 map 中,方便管理和使用 13 | */ 14 | public class BeanHelper { 15 | 16 | /** 17 | * 管理所有的 bean 18 | */ 19 | private static final Map, Object> BEAN_MAP = new HashMap<>(); 20 | 21 | static { 22 | Set> beanClassSet = ClassHelper.getBeanClassSet(); 23 | for (Class c : beanClassSet) { 24 | Object o = ReflectionUtil.newInstance(c); 25 | BEAN_MAP.put(c, o); 26 | } 27 | } 28 | 29 | public static Map, Object> getBeanMap() { 30 | return BEAN_MAP; 31 | } 32 | 33 | /** 34 | * 获取指定的 bean 35 | */ 36 | public static T getBean(Class clazz) { 37 | Object o = BEAN_MAP.get(clazz); 38 | if (o == null) { 39 | throw new RuntimeException("未找到指定的 bean , clazz = " + clazz); 40 | } 41 | return (T) o; 42 | } 43 | 44 | public static void addBean(Class targetClass, Object proxy) { 45 | BEAN_MAP.put(targetClass, proxy); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/helper/ClassHelper.java: -------------------------------------------------------------------------------- 1 | package com.bcc.helper; 2 | 3 | import com.bcc.annotation.Controller; 4 | import com.bcc.annotation.Service; 5 | import com.bcc.util.ClassUtil; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.util.Set; 9 | import java.util.stream.Collectors; 10 | 11 | /** 12 | * 获取指定路径下的各种 类 Class 13 | */ 14 | public final class ClassHelper { 15 | 16 | private ClassHelper() { 17 | } 18 | 19 | /** 20 | * 存放应用项目包下的所有类 21 | */ 22 | private static final Set> CLASS_SET; 23 | 24 | static { 25 | String basePackage = ConfigHelper.getAppBasePackage(); 26 | CLASS_SET = ClassUtil.getClassSet(basePackage); 27 | } 28 | 29 | public static Set> getClassSet() { 30 | return CLASS_SET; 31 | } 32 | 33 | /** 34 | * 获取所有带 service 注解的类 35 | */ 36 | public static Set> getServiceClassSet() { 37 | return CLASS_SET.stream().filter(c -> c.isAnnotationPresent(Service.class)).collect(Collectors.toSet()); 38 | } 39 | 40 | /** 41 | * 获取所有带 controller 注解的类 42 | */ 43 | public static Set> getControllerClassSet() { 44 | return CLASS_SET.stream().filter(c -> c.isAnnotationPresent(Controller.class)).collect(Collectors.toSet()); 45 | } 46 | 47 | 48 | /** 49 | * 获取所有带注解的 bean (service , controller) 50 | * @return 51 | */ 52 | public static Set> getBeanClassSet() { 53 | return CLASS_SET.stream().filter(c -> c.isAnnotationPresent(Controller.class) || c.isAnnotationPresent(Service.class)).collect(Collectors.toSet()); 54 | } 55 | 56 | /** 57 | * 获取某个类及其所有子类 58 | */ 59 | public static Set> getClassesBySuper(Class superClass) { 60 | return CLASS_SET.stream().filter(c->superClass.isAssignableFrom(c)&&!superClass.equals(c)) 61 | .collect(Collectors.toSet()); 62 | } 63 | 64 | /** 65 | * 获取带有某注解的类 66 | * eg. 获取带有 @Aspect 注解的类 67 | */ 68 | public static Set> getClassesByAnnotation(Class anno) { 69 | return CLASS_SET.stream().filter(c->c.isAnnotationPresent(anno)) 70 | .collect(Collectors.toSet()); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/helper/ConfigHelper.java: -------------------------------------------------------------------------------- 1 | package com.bcc.helper; 2 | 3 | import com.bcc.util.ConfigConstant; 4 | import com.bcc.util.PropsUtil; 5 | 6 | import java.util.Properties; 7 | 8 | /** 9 | * 获取各种配置属性 10 | * 11 | */ 12 | public final class ConfigHelper { 13 | 14 | private static final Properties CONFIG_PROPS = PropsUtil.loadProps(ConfigConstant.CONFIG_FILE); 15 | 16 | private ConfigHelper() { 17 | } 18 | 19 | /** 20 | * 获取 JDBC 驱动 21 | */ 22 | public static String getJdbcDriver() { 23 | return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.JDBC_DRIVER); 24 | } 25 | 26 | /** 27 | * 获取 JDBC URL 28 | */ 29 | public static String getJdbcUrl() { 30 | return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.JDBC_URL); 31 | } 32 | 33 | /** 34 | * 获取 JDBC 用户名 35 | */ 36 | public static String getJdbcUsername() { 37 | return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.JDBC_USERNAME); 38 | } 39 | 40 | /** 41 | * 获取 JDBC 密码 42 | */ 43 | public static String getJdbcPassword() { 44 | return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.JDBC_PASSWORD); 45 | } 46 | 47 | /** 48 | * 获取应用基础包名 49 | */ 50 | public static String getAppBasePackage() { 51 | return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.APP_BASE_PACKAGE); 52 | } 53 | 54 | /** 55 | * 获取应用 JSP 路径 56 | */ 57 | public static String getAppJspPath() { 58 | return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.APP_JSP_PATH, "/WEB-INF/view/"); 59 | } 60 | 61 | /** 62 | * 获取应用静态资源路径 63 | */ 64 | public static String getAppAssetPath() { 65 | return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.APP_ASSET_PATH, "/asset/"); 66 | } 67 | 68 | /** 69 | * 获取应用文件上传限制 70 | */ 71 | // public static int getAppUploadLimit() { 72 | // return PropsUtil.getInt(CONFIG_PROPS, ConfigConstant.APP_UPLOAD_LIMIT, 10); 73 | // } 74 | 75 | /** 76 | * 根据属性名获取 String 类型的属性值 77 | */ 78 | public static String getString(String key) { 79 | return PropsUtil.getString(CONFIG_PROPS, key); 80 | } 81 | 82 | /** 83 | * 根据属性名获取 int 类型的属性值 84 | */ 85 | public static int getInt(String key) { 86 | return PropsUtil.getInt(CONFIG_PROPS, key); 87 | } 88 | 89 | /** 90 | * 根据属性名获取 boolean 类型的属性值 91 | */ 92 | public static boolean getBoolean(String key) { 93 | return PropsUtil.getBoolean(CONFIG_PROPS, key); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/helper/ControllerHelper.java: -------------------------------------------------------------------------------- 1 | package com.bcc.helper; 2 | 3 | import com.bcc.annotation.Action; 4 | import com.bcc.web.Handler; 5 | import com.bcc.web.Request; 6 | 7 | import java.lang.reflect.Method; 8 | import java.util.Arrays; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | import java.util.Set; 12 | 13 | /** 14 | * 获取所有的 controller 。 15 | * 及 request 和 handler 之间的 一对一关系 16 | */ 17 | public final class ControllerHelper { 18 | 19 | private ControllerHelper() { 20 | } 21 | 22 | /** 23 | * request 和 handler 之间的 一对一关系 24 | */ 25 | private static final Map ACTION_MAP = new HashMap<>(); 26 | 27 | static { 28 | Set> classSet = ClassHelper.getControllerClassSet(); 29 | classSet.stream().forEach(c -> { 30 | Method[] methods = c.getDeclaredMethods(); 31 | Arrays.stream(methods).filter(method -> method.isAnnotationPresent(Action.class)).forEach(method -> { 32 | Request request = new Request(); 33 | String[] array = method.getDeclaredAnnotation(Action.class).value().split(":"); 34 | request.setMethod(array[0].toLowerCase()); 35 | request.setPath(array[1].startsWith("/") ? array[1] : "/" + array[1]); 36 | 37 | Handler handler = new Handler(); 38 | handler.setController(c); 39 | handler.setActionMethod(method); 40 | if (ACTION_MAP.containsKey(request)) { 41 | throw new RuntimeException("请求路径不能相同 request = "+request+" , handler = "+handler); 42 | } 43 | ACTION_MAP.put(request, handler); 44 | }); 45 | }); 46 | } 47 | 48 | public static Map getActionMap() { 49 | return ACTION_MAP; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/helper/DatabaseHelper.java: -------------------------------------------------------------------------------- 1 | package com.bcc.helper; 2 | 3 | import com.bcc.util.ConfigConstant; 4 | import com.bcc.util.PropsUtil; 5 | 6 | import org.apache.commons.dbcp2.BasicDataSource; 7 | import org.apache.commons.dbutils.QueryRunner; 8 | import org.apache.commons.dbutils.handlers.ArrayHandler; 9 | import org.apache.commons.dbutils.handlers.BeanListHandler; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import java.sql.Connection; 14 | import java.sql.SQLException; 15 | import java.util.List; 16 | import java.util.Properties; 17 | 18 | public final class DatabaseHelper { 19 | 20 | private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseHelper.class); 21 | private static final QueryRunner QUERY_RUNNER = new QueryRunner(); 22 | private static final BasicDataSource BASIC_DATA_SOURCE; 23 | 24 | private static final String DRIVER; 25 | private static final String URL; 26 | private static final String USERNAME; 27 | private static final String PASSWORD; 28 | 29 | private static ThreadLocal threadLocal = new ThreadLocal<>(); 30 | 31 | 32 | static { 33 | Properties conf = PropsUtil.loadProps("smart.properties"); 34 | DRIVER = conf.getProperty(ConfigConstant.JDBC_DRIVER); 35 | URL = conf.getProperty(ConfigConstant.JDBC_URL); 36 | USERNAME = conf.getProperty(ConfigConstant.JDBC_USERNAME); 37 | PASSWORD = conf.getProperty(ConfigConstant.JDBC_PASSWORD); 38 | BASIC_DATA_SOURCE = new BasicDataSource(); 39 | BASIC_DATA_SOURCE.setDriverClassName(DRIVER); 40 | BASIC_DATA_SOURCE.setUrl(URL); 41 | BASIC_DATA_SOURCE.setUsername(USERNAME); 42 | BASIC_DATA_SOURCE.setPassword(PASSWORD); 43 | 44 | // try { 45 | // Class.forName("com.mysql.jdbc.Driver"); 46 | // } catch (Exception e) { 47 | // LOGGER.error("数据库驱动错误", e); 48 | // } 49 | } 50 | 51 | private DatabaseHelper() { 52 | } 53 | 54 | public static Connection getConnection() throws SQLException { 55 | Connection connection = threadLocal.get(); 56 | if (connection == null) { 57 | connection = BASIC_DATA_SOURCE.getConnection(); 58 | threadLocal.set(connection); 59 | } 60 | return connection; 61 | } 62 | 63 | public static void closeConnection(Connection connection) { 64 | if (connection != null) { 65 | try { 66 | connection.close(); 67 | } catch (SQLException e) { 68 | e.printStackTrace(); 69 | } 70 | threadLocal.remove(); 71 | } else { 72 | LOGGER.error("连接为 null"); 73 | } 74 | } 75 | 76 | public static List getEntityList(Class clazz, String sql, Object... params) { 77 | List list = null; 78 | Connection connection = null; 79 | try { 80 | connection = getConnection(); 81 | list = QUERY_RUNNER.query(connection, sql, new BeanListHandler<>(clazz), params); 82 | } catch (SQLException e) { 83 | throw new RuntimeException(e); 84 | } 85 | return list; 86 | } 87 | 88 | public static int insert(String sql, Object... params) { 89 | Connection connection = null; 90 | Object[] insert; 91 | try { 92 | connection = getConnection(); 93 | insert = QUERY_RUNNER.insert(connection, sql, new ArrayHandler(), params); 94 | } catch (SQLException e) { 95 | LOGGER.error("getConnection 异常 ", e); 96 | throw new RuntimeException("getConnection 异常"); 97 | } 98 | return insert.length; 99 | } 100 | 101 | /** 102 | * 开启事务 103 | */ 104 | public static void beginTransaction() { 105 | Connection connection; 106 | try { 107 | connection = getConnection(); 108 | } catch (SQLException e) { 109 | LOGGER.error("getConnection 异常 ", e); 110 | throw new RuntimeException("getConnection 异常"); 111 | } 112 | if (connection != null) { 113 | try { 114 | connection.setAutoCommit(false); 115 | } catch (SQLException e) { 116 | LOGGER.error("connection.setAutoCommit 异常 ", e); 117 | throw new RuntimeException("connection.setAutoCommit 异常"); 118 | } 119 | } 120 | } 121 | 122 | /** 123 | * 提交事务 124 | */ 125 | public static void commitTransaction() { 126 | Connection connection; 127 | try { 128 | connection = getConnection(); 129 | } catch (SQLException e) { 130 | LOGGER.error("getConnection 异常 ", e); 131 | throw new RuntimeException("getConnection 异常"); 132 | } 133 | if (connection == null) { 134 | LOGGER.error("connection = null "); 135 | throw new RuntimeException("connection = null 异常"); 136 | } 137 | try { 138 | connection.commit(); 139 | } catch (SQLException e) { 140 | LOGGER.error("connection.commit 异常"); 141 | throw new RuntimeException("connection.commit 异常"); 142 | } finally { 143 | closeConnection(connection); 144 | } 145 | } 146 | 147 | /** 148 | * 事务回滚 149 | */ 150 | public static void rollBack() { 151 | Connection connection; 152 | try { 153 | connection = getConnection(); 154 | } catch (SQLException e) { 155 | LOGGER.error("getConnection 异常 ", e); 156 | throw new RuntimeException("getConnection 异常"); 157 | } 158 | if (connection == null) { 159 | LOGGER.error("connection = null "); 160 | throw new RuntimeException("connection = null 异常"); 161 | } 162 | try { 163 | connection.rollback(); 164 | } catch (SQLException e) { 165 | LOGGER.error("connection.rollback 异常"); 166 | throw new RuntimeException("connection.rollback 异常"); 167 | } finally { 168 | closeConnection(connection); 169 | } 170 | } 171 | 172 | } 173 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/helper/IocHelper.java: -------------------------------------------------------------------------------- 1 | package com.bcc.helper; 2 | 3 | import com.bcc.annotation.Inject; 4 | import com.bcc.util.ReflectionUtil; 5 | 6 | import java.lang.reflect.Field; 7 | import java.util.Arrays; 8 | import java.util.Map; 9 | 10 | /** 11 | * IOC 实现依赖注入 12 | * 遍历 beanMap , 若某个成员变量带有 inject 注解, 再从 map 中取出对应的bean ,为他赋值 13 | * eg. Controller 中注入 service 14 | */ 15 | public final class IocHelper { 16 | private IocHelper() { 17 | } 18 | 19 | static { 20 | Map, Object> beanMap = BeanHelper.getBeanMap(); 21 | if (!beanMap.isEmpty()) { 22 | beanMap.entrySet().stream().forEach(entry -> { 23 | Class clazz = entry.getKey(); 24 | Object instance = entry.getValue(); 25 | Field[] fields = clazz.getDeclaredFields(); 26 | 27 | // 对 bean 中成员变量带有 Inject 注解的类 , 进行赋值 28 | Arrays.stream(fields).filter(field -> field.isAnnotationPresent(Inject.class)) 29 | .forEach(field -> { 30 | Class type = field.getType(); 31 | Object fieldInstance = BeanHelper.getBean(type); // TODO 可能为 null 么 ? 32 | ReflectionUtil.setFiled(instance, field, fieldInstance); 33 | }); 34 | }); 35 | } 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/proxy/AbstractAspect.java: -------------------------------------------------------------------------------- 1 | package com.bcc.proxy; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.lang.reflect.Method; 7 | 8 | /** 9 | * 抽象的切面类 10 | */ 11 | public abstract class AbstractAspect implements Proxy { 12 | 13 | private final Logger LOGGER = LoggerFactory.getLogger(this.getClass()); 14 | 15 | @Override 16 | public Object doProxy(ProxyChain proxyChain) throws Throwable { 17 | Object result; 18 | Class targetClass = proxyChain.getTargetClass(); 19 | Method targetMethod = proxyChain.getTargetMethod(); 20 | Object[] params = proxyChain.getMethodParams(); 21 | begin(); 22 | try { 23 | if (intercept(targetClass, targetMethod, params)) { 24 | before(targetClass, targetMethod, params); 25 | result = proxyChain.doProxyChain(); 26 | after(targetClass, targetMethod, params, result); 27 | } else { 28 | result = proxyChain.doProxyChain(); 29 | } 30 | } catch (Throwable e) { 31 | LOGGER.error("代理过程出错", e); 32 | error(targetClass, targetMethod, params, e); 33 | throw e; 34 | } finally { 35 | end(); 36 | } 37 | 38 | 39 | return result; 40 | } 41 | 42 | /** 43 | * 44 | */ 45 | public void end() { 46 | 47 | } 48 | 49 | public void error(Class targetClass, Method targetMethod, Object[] params, Throwable e) { 50 | } 51 | 52 | public void after(Class targetClass, Method targetMethod, Object[] params, Object result) { 53 | } 54 | 55 | public void before(Class targetClass, Method targetMethod, Object[] params) { 56 | } 57 | 58 | public boolean intercept(Class targetClass, Method targetMethod, Object[] params) { 59 | return true; 60 | } 61 | 62 | public void begin() { 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/proxy/Proxy.java: -------------------------------------------------------------------------------- 1 | package com.bcc.proxy; 2 | 3 | public interface Proxy { 4 | 5 | /** 6 | * 执行代理 7 | */ 8 | Object doProxy(ProxyChain proxyChain) throws Throwable; 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/proxy/ProxyChain.java: -------------------------------------------------------------------------------- 1 | package com.bcc.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 | public class ProxyChain { 10 | 11 | private final Class targetClass; 12 | private final Object targetObj; 13 | private final Method targetMethod; 14 | private final MethodProxy methodProxy; 15 | private final Object[] methodParams; 16 | private List proxyList = new ArrayList<>(); 17 | private int proxyIndex = 0; 18 | 19 | public ProxyChain(Class targetClass, Object targetObj, Method targetMethod, MethodProxy methodProxy, Object[] methodParams, List proxyList) { 20 | this.targetClass = targetClass; 21 | this.targetObj = targetObj; 22 | this.targetMethod = targetMethod; 23 | this.methodProxy = methodProxy; 24 | this.methodParams = methodParams; 25 | this.proxyList = proxyList; 26 | } 27 | 28 | public Method getTargetMethod() { 29 | return targetMethod; 30 | } 31 | 32 | public Class getTargetClass() { 33 | return targetClass; 34 | } 35 | 36 | public Object getTargetObj() { 37 | return targetObj; 38 | } 39 | 40 | public Object[] getMethodParams() { 41 | return methodParams; 42 | } 43 | 44 | /** 45 | * 执行代理链 46 | */ 47 | public Object doProxyChain() throws Throwable { 48 | Object result; 49 | if (proxyIndex < proxyList.size()) { 50 | result = proxyList.get(proxyIndex++).doProxy(this); 51 | } else { 52 | result = methodProxy.invokeSuper(targetObj,methodParams); 53 | } 54 | return result; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/proxy/ProxyManager.java: -------------------------------------------------------------------------------- 1 | package com.bcc.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 | public class ProxyManager { 11 | 12 | 13 | /** 14 | * 创建代理对象 15 | */ 16 | @SuppressWarnings("unchecked") 17 | public static T createProxy(Class targetClass, List proxyList) { 18 | return (T)Enhancer.create(targetClass, new MethodInterceptor() { 19 | @Override 20 | public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { 21 | return new ProxyChain(targetClass,o,method,methodProxy,objects,proxyList).doProxyChain(); 22 | } 23 | }); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/proxy/TransactionAspect.java: -------------------------------------------------------------------------------- 1 | package com.bcc.proxy; 2 | 3 | import com.bcc.annotation.Transaction; 4 | import com.bcc.helper.DatabaseHelper; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.lang.reflect.Method; 10 | 11 | /** 12 | * 事务切面类 , 类似于普通切面类 AbstractAspect 13 | */ 14 | public class TransactionAspect implements Proxy { 15 | 16 | private static final Logger LOGGER = LoggerFactory.getLogger(TransactionAspect.class); 17 | 18 | private static ThreadLocal IS_EXECUTE = ThreadLocal.withInitial(() -> false); 19 | 20 | @Override 21 | public Object doProxy(ProxyChain proxyChain) throws Throwable { 22 | Object reult = null; 23 | Boolean flag = IS_EXECUTE.get(); 24 | Method method = proxyChain.getTargetMethod(); 25 | 26 | if (flag || !method.isAnnotationPresent(Transaction.class)) { 27 | return proxyChain.doProxyChain(); 28 | } 29 | IS_EXECUTE.set(true); 30 | try { 31 | DatabaseHelper.beginTransaction(); 32 | System.out.println("beginTransaction"); 33 | reult = proxyChain.doProxyChain(); 34 | DatabaseHelper.commitTransaction(); 35 | System.out.println("commitTransaction"); 36 | } catch (Exception e) { 37 | DatabaseHelper.rollBack(); 38 | System.out.println("rollBack"); 39 | throw new RuntimeException("事务执行过程出错", e); 40 | } finally { 41 | IS_EXECUTE.remove(); 42 | } 43 | 44 | return reult; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/test/Test01.java: -------------------------------------------------------------------------------- 1 | package com.bcc.test; 2 | 3 | import com.bcc.MainLoader; 4 | import com.bcc.helper.ControllerHelper; 5 | import com.bcc.web.Handler; 6 | import com.bcc.web.Request; 7 | 8 | import java.util.Map; 9 | 10 | public class Test01 { 11 | 12 | public static void main(String[] args) { 13 | 14 | MainLoader.init(); 15 | 16 | // Map, Object> beanMap = BeanHelper.getBeanMap(); 17 | // System.out.println(beanMap); 18 | // TestController controller = BeanHelper.getBean(TestController.class); 19 | // System.out.println(controller); 20 | // System.out.println(controller.getTestService()); 21 | 22 | Map actionMap = ControllerHelper.getActionMap(); 23 | System.out.println(actionMap.size()); 24 | for (Map.Entry entry : actionMap.entrySet()) { 25 | System.out.println(entry.getKey()); 26 | System.out.println(entry.getValue()); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/test/TestController.java: -------------------------------------------------------------------------------- 1 | package com.bcc.test; 2 | 3 | import com.bcc.annotation.Action; 4 | import com.bcc.annotation.Controller; 5 | import com.bcc.annotation.Inject; 6 | 7 | import java.util.Arrays; 8 | import java.util.List; 9 | 10 | @Controller 11 | public class TestController { 12 | 13 | @Inject 14 | private TestService testService; 15 | 16 | public TestService getTestService() { 17 | return testService; 18 | } 19 | 20 | @Action("get:/getList") 21 | public List getList() { 22 | return Arrays.asList("aa", "bb", "cc"); 23 | } 24 | 25 | @Action("post:update") 26 | public void updatePersion() { 27 | System.out.println("updatePersion ..."); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/test/TestService.java: -------------------------------------------------------------------------------- 1 | package com.bcc.test; 2 | 3 | import com.bcc.annotation.Service; 4 | 5 | @Service 6 | public class TestService { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/util/CastUtil.java: -------------------------------------------------------------------------------- 1 | package com.bcc.util; 2 | 3 | /** 4 | * 转型操作工具类 5 | */ 6 | public final class CastUtil { 7 | 8 | /** 9 | * 转为 String 型 10 | */ 11 | public static String castString(Object obj) { 12 | return CastUtil.castString(obj, ""); 13 | } 14 | 15 | /** 16 | * 转为 String 型(提供默认值) 17 | */ 18 | public static String castString(Object obj, String defaultValue) { 19 | return obj != null ? String.valueOf(obj) : defaultValue; 20 | } 21 | 22 | /** 23 | * 转为 double 型 24 | */ 25 | public static double castDouble(Object obj) { 26 | return CastUtil.castDouble(obj, 0); 27 | } 28 | 29 | /** 30 | * 转为 double 型(提供默认值) 31 | */ 32 | public static double castDouble(Object obj, double defaultValue) { 33 | double doubleValue = defaultValue; 34 | if (obj != null) { 35 | String strValue = castString(obj); 36 | if (StringUtil.isNotEmpty(strValue)) { 37 | try { 38 | doubleValue = Double.parseDouble(strValue); 39 | } catch (NumberFormatException e) { 40 | doubleValue = defaultValue; 41 | } 42 | } 43 | } 44 | return doubleValue; 45 | } 46 | 47 | /** 48 | * 转为 long 型 49 | */ 50 | public static long castLong(Object obj) { 51 | return CastUtil.castLong(obj, 0); 52 | } 53 | 54 | /** 55 | * 转为 long 型(提供默认值) 56 | */ 57 | public static long castLong(Object obj, long defaultValue) { 58 | long longValue = defaultValue; 59 | if (obj != null) { 60 | String strValue = castString(obj); 61 | if (StringUtil.isNotEmpty(strValue)) { 62 | try { 63 | longValue = Long.parseLong(strValue); 64 | } catch (NumberFormatException e) { 65 | longValue = defaultValue; 66 | } 67 | } 68 | } 69 | return longValue; 70 | } 71 | 72 | /** 73 | * 转为 int 型 74 | */ 75 | public static int castInt(Object obj) { 76 | return CastUtil.castInt(obj, 0); 77 | } 78 | 79 | /** 80 | * 转为 int 型(提供默认值) 81 | */ 82 | public static int castInt(Object obj, int defaultValue) { 83 | int intValue = defaultValue; 84 | if (obj != null) { 85 | String strValue = castString(obj); 86 | if (StringUtil.isNotEmpty(strValue)) { 87 | try { 88 | intValue = Integer.parseInt(strValue); 89 | } catch (NumberFormatException e) { 90 | intValue = defaultValue; 91 | } 92 | } 93 | } 94 | return intValue; 95 | } 96 | 97 | /** 98 | * 转为 boolean 型 99 | */ 100 | public static boolean castBoolean(Object obj) { 101 | return CastUtil.castBoolean(obj, false); 102 | } 103 | 104 | /** 105 | * 转为 boolean 型(提供默认值) 106 | */ 107 | public static boolean castBoolean(Object obj, boolean defaultValue) { 108 | boolean booleanValue = defaultValue; 109 | if (obj != null) { 110 | booleanValue = Boolean.parseBoolean(castString(obj)); 111 | } 112 | return booleanValue; 113 | } 114 | } -------------------------------------------------------------------------------- /src/main/java/com/bcc/util/ClassUtil.java: -------------------------------------------------------------------------------- 1 | package com.bcc.util; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.io.File; 7 | import java.io.FileFilter; 8 | import java.net.JarURLConnection; 9 | import java.net.URL; 10 | import java.util.Enumeration; 11 | import java.util.HashSet; 12 | import java.util.Set; 13 | import java.util.jar.JarEntry; 14 | import java.util.jar.JarFile; 15 | 16 | /** 17 | * 类操作工具类 18 | * 19 | */ 20 | public final class ClassUtil { 21 | 22 | private static final Logger LOGGER = LoggerFactory.getLogger(ClassUtil.class); 23 | 24 | /** 25 | * 获取类加载器 26 | */ 27 | public static ClassLoader getClassLoader() { 28 | return Thread.currentThread().getContextClassLoader(); 29 | } 30 | 31 | /** 32 | * 加载类 33 | */ 34 | public static Class loadClass(String className, boolean isInitialized) { 35 | Class cls; 36 | try { 37 | cls = Class.forName(className, isInitialized, getClassLoader()); 38 | } catch (ClassNotFoundException e) { 39 | LOGGER.error("load class failure", e); 40 | throw new RuntimeException(e); 41 | } 42 | return cls; 43 | } 44 | 45 | /** 46 | * 加载类(默认将初始化类) 47 | */ 48 | public static Class loadClass(String className) { 49 | return loadClass(className, true); 50 | } 51 | 52 | //TODO 项目不能放在磁盘上有中文的路径下 53 | /** 54 | * 获取指定包名下的所有类 55 | */ 56 | public static Set> getClassSet(String packageName) { 57 | Set> classSet = new HashSet>(); 58 | try { 59 | Enumeration urls = getClassLoader().getResources(packageName.replace(".", "/")); 60 | while (urls.hasMoreElements()) { 61 | URL url = urls.nextElement(); 62 | if (url != null) { 63 | String protocol = url.getProtocol(); 64 | if (protocol.equals("file")) { 65 | String packagePath = url.getPath().replaceAll("%20", " "); 66 | addClass(classSet, packagePath, packageName); 67 | } else if (protocol.equals("jar")) { 68 | JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection(); 69 | if (jarURLConnection != null) { 70 | JarFile jarFile = jarURLConnection.getJarFile(); 71 | if (jarFile != null) { 72 | Enumeration jarEntries = jarFile.entries(); 73 | while (jarEntries.hasMoreElements()) { 74 | JarEntry jarEntry = jarEntries.nextElement(); 75 | String jarEntryName = jarEntry.getName(); 76 | if (jarEntryName.endsWith(".class")) { 77 | String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/", "."); 78 | doAddClass(classSet, className); 79 | } 80 | } 81 | } 82 | } 83 | } 84 | } 85 | } 86 | } catch (Exception e) { 87 | LOGGER.error("get class set failure", e); 88 | throw new RuntimeException(e); 89 | } 90 | return classSet; 91 | } 92 | 93 | private static void addClass(Set> classSet, String packagePath, String packageName) { 94 | File[] files = new File(packagePath).listFiles(new FileFilter() { 95 | public boolean accept(File file) { 96 | return (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory(); 97 | } 98 | }); 99 | LOGGER.info("files = "+files); 100 | if (files == null || files.length == 0) { 101 | return; 102 | } 103 | for (File file : files) { 104 | String fileName = file.getName(); 105 | if (file.isFile()) { 106 | String className = fileName.substring(0, fileName.lastIndexOf(".")); 107 | if (StringUtil.isNotEmpty(packageName)) { 108 | className = packageName + "." + className; 109 | } 110 | doAddClass(classSet, className); 111 | } else { 112 | String subPackagePath = fileName; 113 | if (StringUtil.isNotEmpty(packagePath)) { 114 | subPackagePath = packagePath + "/" + subPackagePath; 115 | } 116 | String subPackageName = fileName; 117 | if (StringUtil.isNotEmpty(packageName)) { 118 | subPackageName = packageName + "." + subPackageName; 119 | } 120 | addClass(classSet, subPackagePath, subPackageName); 121 | } 122 | } 123 | } 124 | 125 | private static void doAddClass(Set> classSet, String className) { 126 | Class cls = loadClass(className, false); 127 | classSet.add(cls); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/util/CodecUtil.java: -------------------------------------------------------------------------------- 1 | package com.bcc.util; 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 | * 编码与解码操作工具类 11 | * 12 | * @author huangyong 13 | * @since 1.0.0 14 | */ 15 | public final 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 | /** 48 | * MD5 加密 49 | */ 50 | // public static String md5(String source) { 51 | // return DigestUtils.md5Hex(source); 52 | // } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/util/ConfigConstant.java: -------------------------------------------------------------------------------- 1 | package com.bcc.util; 2 | 3 | /** 4 | * 配置文件 属性常量 5 | */ 6 | public interface ConfigConstant { 7 | 8 | String CONFIG_FILE = "smart.properties"; 9 | 10 | String JDBC_DRIVER = "smart.framework.jdbc.driver"; 11 | String JDBC_URL = "smart.framework.jdbc.url"; 12 | String JDBC_USERNAME = "smart.framework.jdbc.username"; 13 | String JDBC_PASSWORD = "smart.framework.jdbc.password"; 14 | 15 | String APP_BASE_PACKAGE = "smart.framework.app.base_package"; 16 | String APP_JSP_PATH = "smart.framework.app.jsp_path"; 17 | String APP_ASSET_PATH = "smart.framework.app.asset_path"; 18 | 19 | // String APP_UPLOAD_LIMIT = "smart.framework.app.upload_limit"; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/util/JsonUtil.java: -------------------------------------------------------------------------------- 1 | package com.bcc.util; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | /** 8 | * JSON 工具类 9 | * 10 | * @author huangyong 11 | * @since 1.0.0 12 | */ 13 | public final 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 | * 将 POJO 转为 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 POJO to JSON failure", e); 28 | throw new RuntimeException(e); 29 | } 30 | return json; 31 | } 32 | 33 | /** 34 | * 将 JSON 转为 POJO 35 | */ 36 | public static T fromJson(String json, Class type) { 37 | T pojo; 38 | try { 39 | pojo = OBJECT_MAPPER.readValue(json, type); 40 | } catch (Exception e) { 41 | LOGGER.error("convert JSON to POJO failure", e); 42 | throw new RuntimeException(e); 43 | } 44 | return pojo; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/util/MethodEunm.java: -------------------------------------------------------------------------------- 1 | package com.bcc.util; 2 | 3 | public enum MethodEunm { 4 | 5 | GET("GET"),POST("POST"),PUT("PUT"),DELETE("DELETE"); 6 | 7 | private String method; 8 | 9 | MethodEunm(String method) { 10 | this.method = method; 11 | } 12 | 13 | public String getMethod() { 14 | return method; 15 | } 16 | 17 | public void setMethod(String method) { 18 | this.method = method; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/util/PropsUtil.java: -------------------------------------------------------------------------------- 1 | package com.bcc.util; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.io.FileNotFoundException; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.util.Properties; 10 | 11 | /** 12 | * 属性文件工具类 13 | */ 14 | public final class PropsUtil { 15 | 16 | private static final Logger LOGGER = LoggerFactory.getLogger(PropsUtil.class); 17 | 18 | /** 19 | * 加载属性文件 20 | */ 21 | public static Properties loadProps(String fileName) { 22 | Properties props = null; 23 | InputStream is = null; 24 | try { 25 | is = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName); 26 | if (is == null) { 27 | throw new FileNotFoundException(fileName + " file is not found"); 28 | } 29 | props = new Properties(); 30 | props.load(is); 31 | } catch (IOException e) { 32 | LOGGER.error("load properties file failure", e); 33 | } finally { 34 | if (is != null) { 35 | try { 36 | is.close(); 37 | } catch (IOException e) { 38 | LOGGER.error("close input stream failure", e); 39 | } 40 | } 41 | } 42 | return props; 43 | } 44 | 45 | /** 46 | * 获取字符型属性(默认值为空字符串) 47 | */ 48 | public static String getString(Properties props, String key) { 49 | return getString(props, key, ""); 50 | } 51 | 52 | /** 53 | * 获取字符型属性(可指定默认值) 54 | */ 55 | public static String getString(Properties props, String key, String defaultValue) { 56 | String value = defaultValue; 57 | if (props.containsKey(key)) { 58 | value = props.getProperty(key); 59 | } 60 | return value; 61 | } 62 | 63 | /** 64 | * 获取数值型属性(默认值为 0) 65 | */ 66 | public static int getInt(Properties props, String key) { 67 | return getInt(props, key, 0); 68 | } 69 | 70 | // 获取数值型属性(可指定默认值) 71 | public static int getInt(Properties props, String key, int defaultValue) { 72 | int value = defaultValue; 73 | if (props.containsKey(key)) { 74 | value = CastUtil.castInt(props.getProperty(key)); 75 | } 76 | return value; 77 | } 78 | 79 | /** 80 | * 获取布尔型属性(默认值为 false) 81 | */ 82 | public static boolean getBoolean(Properties props, String key) { 83 | return getBoolean(props, key, false); 84 | } 85 | 86 | /** 87 | * 获取布尔型属性(可指定默认值) 88 | */ 89 | public static boolean getBoolean(Properties props, String key, boolean defaultValue) { 90 | boolean value = defaultValue; 91 | if (props.containsKey(key)) { 92 | value = CastUtil.castBoolean(props.getProperty(key)); 93 | } 94 | return value; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/util/ReflectionUtil.java: -------------------------------------------------------------------------------- 1 | package com.bcc.util; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.lang.reflect.Field; 7 | import java.lang.reflect.Method; 8 | 9 | /** 10 | * 反射工具类 11 | */ 12 | public final class ReflectionUtil { 13 | 14 | private ReflectionUtil() { 15 | } 16 | 17 | private static final Logger LOGGER = LoggerFactory.getLogger(ReflectionUtil.class); 18 | 19 | /** 20 | * 创建对象 21 | */ 22 | public static Object newInstance(Class clazz) { 23 | Object instance; 24 | try { 25 | instance = clazz.newInstance(); 26 | } catch (Exception e) { 27 | LOGGER.error("反射创建对象错误", e); 28 | throw new RuntimeException(e); 29 | } 30 | return instance; 31 | } 32 | 33 | /** 34 | * 调用方法 35 | */ 36 | public static Object invokeMethod(Object target, Method method, Object... args) { 37 | Object result; 38 | method.setAccessible(true); 39 | try { 40 | result = method.invoke(target, args); 41 | } catch (Exception e) { 42 | LOGGER.error("反射调用方法错误", e); 43 | throw new RuntimeException(e); 44 | } 45 | return result; 46 | } 47 | 48 | /** 49 | * 设置 filed 值 50 | */ 51 | public static void setFiled(Object target, Field field, Object value) { 52 | field.setAccessible(true); 53 | try { 54 | field.set(target, value); 55 | } catch (IllegalAccessException e) { 56 | LOGGER.error("反射设置 filed 错误", e); 57 | throw new RuntimeException(e); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/util/StreamUtil.java: -------------------------------------------------------------------------------- 1 | package com.bcc.util; 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 | import java.io.OutputStream; 10 | 11 | /** 12 | * 流操作工具类 13 | * 14 | * @author huangyong 15 | * @since 1.0.0 16 | */ 17 | public final class StreamUtil { 18 | 19 | private static final Logger LOGGER = LoggerFactory.getLogger(StreamUtil.class); 20 | 21 | /** 22 | * 从输入流中获取字符串 23 | */ 24 | public static String getString(InputStream is) { 25 | StringBuilder sb = new StringBuilder(); 26 | try { 27 | BufferedReader reader = new BufferedReader(new InputStreamReader(is)); 28 | String line; 29 | while ((line = reader.readLine()) != null) { 30 | sb.append(line); 31 | } 32 | } catch (Exception e) { 33 | LOGGER.error("get string failure", e); 34 | throw new RuntimeException(e); 35 | } 36 | return sb.toString(); 37 | } 38 | 39 | /** 40 | * 将输入流复制到输出流 41 | */ 42 | public static void copyStream(InputStream inputStream, OutputStream outputStream) { 43 | try { 44 | int length; 45 | byte[] buffer = new byte[4 * 1024]; 46 | while ((length = inputStream.read(buffer, 0, buffer.length)) != -1) { 47 | outputStream.write(buffer, 0, length); 48 | } 49 | outputStream.flush(); 50 | } catch (Exception e) { 51 | LOGGER.error("copy stream failure", e); 52 | throw new RuntimeException(e); 53 | } finally { 54 | try { 55 | inputStream.close(); 56 | outputStream.close(); 57 | } catch (Exception e) { 58 | LOGGER.error("close stream failure", e); 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/util/StringUtil.java: -------------------------------------------------------------------------------- 1 | package com.bcc.util; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | 5 | /** 6 | * 字符串工具类 7 | */ 8 | public final class StringUtil { 9 | 10 | /** 11 | * 判断字符串是否为空 12 | */ 13 | public static boolean isEmpty(String str) { 14 | if (str != null) { 15 | str = str.trim(); 16 | } 17 | return StringUtils.isEmpty(str); 18 | } 19 | 20 | /** 21 | * 判断字符串是否非空 22 | */ 23 | public static boolean isNotEmpty(String str) { 24 | return !isEmpty(str); 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/java/com/bcc/web/DispatchServlet.java: -------------------------------------------------------------------------------- 1 | package com.bcc.web; 2 | 3 | import com.bcc.MainLoader; 4 | import com.bcc.helper.BeanHelper; 5 | import com.bcc.helper.ConfigHelper; 6 | import com.bcc.helper.ControllerHelper; 7 | import com.bcc.util.CodecUtil; 8 | import com.bcc.util.JsonUtil; 9 | import com.bcc.util.ReflectionUtil; 10 | import com.bcc.util.StreamUtil; 11 | 12 | import org.apache.commons.lang3.ArrayUtils; 13 | import org.apache.commons.lang3.StringUtils; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | import java.io.IOException; 18 | import java.io.PrintWriter; 19 | import java.lang.reflect.Method; 20 | import java.util.Enumeration; 21 | import java.util.HashMap; 22 | import java.util.Map; 23 | 24 | import javax.servlet.ServletConfig; 25 | import javax.servlet.ServletContext; 26 | import javax.servlet.ServletException; 27 | import javax.servlet.ServletRegistration; 28 | import javax.servlet.annotation.WebServlet; 29 | import javax.servlet.http.HttpServlet; 30 | import javax.servlet.http.HttpServletRequest; 31 | import javax.servlet.http.HttpServletResponse; 32 | 33 | /** 34 | * web 层核心 35 | * 根据请求 request (method:path) , 找到对应的 handler (controller.action) 处理 36 | */ 37 | @WebServlet(urlPatterns = "/*",loadOnStartup = 0) 38 | public class DispatchServlet extends HttpServlet { 39 | 40 | private static final Logger LOGGER = LoggerFactory.getLogger(DispatchServlet.class); 41 | 42 | @Override 43 | public void init(ServletConfig config) throws ServletException { 44 | 45 | MainLoader.init(); 46 | ServletContext context = config.getServletContext(); 47 | 48 | // jsp 49 | ServletRegistration registration = context.getServletRegistration("jsp"); 50 | registration.addMapping(ConfigHelper.getAppJspPath() + "*"); 51 | 52 | // asset 下静态资源 53 | ServletRegistration defaultRegistration = context.getServletRegistration("default"); 54 | defaultRegistration.addMapping(ConfigHelper.getAppAssetPath() + "*"); 55 | 56 | } 57 | 58 | 59 | @Override 60 | protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 61 | 62 | Map actionMap = ControllerHelper.getActionMap(); 63 | 64 | String method = request.getMethod().toLowerCase(); 65 | String path = request.getPathInfo(); 66 | Request requestAction = new Request(method, path); 67 | if (!actionMap.containsKey(requestAction)) { 68 | throw new RuntimeException("请求不存在 requestAction = " + requestAction); 69 | } 70 | Handler handler = actionMap.get(requestAction); 71 | Method actionMethod = handler.getActionMethod(); 72 | Class controllerClass = handler.getController(); 73 | Object controllerBean = BeanHelper.getBean(controllerClass); 74 | 75 | // 获取请求参数 Param 76 | HashMap paramMap = new HashMap<>(); 77 | Enumeration parameterNames = request.getParameterNames(); 78 | while (parameterNames.hasMoreElements()) { 79 | String name = parameterNames.nextElement(); 80 | String val = request.getParameter(name); 81 | paramMap.put(name, val); 82 | } 83 | String body = CodecUtil.decodeURL(StreamUtil.getString(request.getInputStream())); 84 | if (body != null && body.length() > 0) { 85 | String[] params = body.trim().split("&"); 86 | if (!ArrayUtils.isEmpty(params)) { 87 | for (String param : params) { 88 | String[] arr = param.split("="); 89 | if (arr.length == 2) { 90 | paramMap.put(arr[0], arr[1]); 91 | } 92 | } 93 | } 94 | } 95 | RequestParam param = new RequestParam(paramMap); 96 | 97 | // 调用 action 方法 98 | Object result = ReflectionUtil.invokeMethod(controllerBean, actionMethod, param); 99 | 100 | // 跳转页面 101 | if (result instanceof ModelAndView) { 102 | ModelAndView view = (ModelAndView) result; 103 | String viewPath = view.getPath(); 104 | if (StringUtils.isEmpty(viewPath)) { 105 | throw new RuntimeException("跳转路径不能为空"); 106 | } 107 | if (viewPath.startsWith("/")) { 108 | response.sendRedirect(request.getContextPath() + viewPath); // 为什么要把有没有 / 区别对待 ??? 109 | 110 | } else { 111 | Map model = view.getModel(); 112 | for (Map.Entry entry : model.entrySet()) { 113 | request.setAttribute(entry.getKey(), entry.getValue()); 114 | } 115 | request.getRequestDispatcher(ConfigHelper.getAppJspPath() + viewPath).forward(request, response); 116 | } 117 | 118 | } else if (result instanceof ServerResponse) { 119 | // 给浏览器写回 data 数据 120 | ServerResponse data = (ServerResponse) result; 121 | Object model = data.getModel(); 122 | if (model != null) { 123 | response.setContentType("application/json"); 124 | response.setCharacterEncoding("UTF-8"); 125 | PrintWriter writer = response.getWriter(); 126 | String json = JsonUtil.toJson(model); 127 | writer.write(json); 128 | writer.flush(); 129 | writer.close(); 130 | } 131 | 132 | } else { 133 | LOGGER.error("未知的返回类型 result= " + result); 134 | } 135 | 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/web/Handler.java: -------------------------------------------------------------------------------- 1 | package com.bcc.web; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | /** 6 | * 处理器 : controller.action 7 | */ 8 | public class Handler { 9 | 10 | private Class controller; 11 | 12 | private Method actionMethod; 13 | 14 | public Handler() { 15 | } 16 | 17 | public Handler(Class controller, Method actionMethod) { 18 | this.controller = controller; 19 | this.actionMethod = actionMethod; 20 | } 21 | 22 | public Class getController() { 23 | return controller; 24 | } 25 | 26 | public void setController(Class controller) { 27 | this.controller = controller; 28 | } 29 | 30 | public Method getActionMethod() { 31 | return actionMethod; 32 | } 33 | 34 | public void setActionMethod(Method actionMethod) { 35 | this.actionMethod = actionMethod; 36 | } 37 | 38 | @Override 39 | public String toString() { 40 | return "Handler{" + 41 | "controller=" + controller + 42 | ", actionMethod=" + actionMethod + 43 | '}'; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/web/ModelAndView.java: -------------------------------------------------------------------------------- 1 | package com.bcc.web; 2 | 3 | import java.util.Map; 4 | 5 | /** 6 | * handler 返回 view ,跳转页面 7 | */ 8 | public class ModelAndView { 9 | 10 | private String path; 11 | 12 | /** 13 | * 携带的数据 14 | */ 15 | private Map model; 16 | 17 | public ModelAndView(String path) { 18 | this.path = path; 19 | } 20 | 21 | public String getPath() { 22 | return path; 23 | } 24 | 25 | public void setPath(String path) { 26 | this.path = path; 27 | } 28 | 29 | public Map getModel() { 30 | return model; 31 | } 32 | 33 | public void setModel(Map model) { 34 | this.model = model; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/web/Request.java: -------------------------------------------------------------------------------- 1 | package com.bcc.web; 2 | 3 | import java.util.Objects; 4 | 5 | /** 6 | * 请求对象 : 请求方法 + 请求路径 7 | */ 8 | public class Request { 9 | 10 | private String method; 11 | private String path; 12 | 13 | public Request() { 14 | } 15 | 16 | public Request(String method, String path) { 17 | this.method = method; 18 | this.path = path; 19 | } 20 | 21 | public String getMethod() { 22 | return method; 23 | } 24 | 25 | public void setMethod(String method) { 26 | this.method = method; 27 | } 28 | 29 | public String getPath() { 30 | return path; 31 | } 32 | 33 | public void setPath(String path) { 34 | this.path = path; 35 | } 36 | 37 | @Override 38 | public boolean equals(Object o) { 39 | if (this == o) 40 | return true; 41 | if (o == null || getClass() != o.getClass()) 42 | return false; 43 | Request request = (Request) o; 44 | return Objects.equals(method, request.method) && 45 | Objects.equals(path, request.path); 46 | } 47 | 48 | @Override 49 | public int hashCode() { 50 | 51 | return Objects.hash(method, path); 52 | } 53 | 54 | @Override 55 | public String toString() { 56 | return "Request{" + 57 | "method='" + method + '\'' + 58 | ", path='" + path + '\'' + 59 | '}'; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/web/RequestParam.java: -------------------------------------------------------------------------------- 1 | package com.bcc.web; 2 | 3 | import com.bcc.util.CastUtil; 4 | 5 | import java.util.Map; 6 | 7 | /** 8 | * 请求参数 9 | */ 10 | public class RequestParam { 11 | 12 | private Map paramMap; 13 | 14 | public RequestParam(Map paramMap) { 15 | this.paramMap = paramMap; 16 | } 17 | 18 | public Map getParamMap() { 19 | return paramMap; 20 | } 21 | 22 | public long getLong(String name) { 23 | return CastUtil.castInt(paramMap.get(name)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/bcc/web/ServerResponse.java: -------------------------------------------------------------------------------- 1 | package com.bcc.web; 2 | 3 | /** 4 | * 返回数据 5 | */ 6 | public class ServerResponse { 7 | 8 | private Object model; 9 | 10 | public Object getModel() { 11 | return model; 12 | } 13 | 14 | public void setModel(Object model) { 15 | this.model = model; 16 | } 17 | 18 | public ServerResponse(Object model) { 19 | this.model = model; 20 | } 21 | 22 | public ServerResponse() { 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=ERROR,console,file 2 | 3 | log4j.appender.console=org.apache.log4j.ConsoleAppender 4 | log4j.appender.console.layout=org.apache.log4j.PatternLayout 5 | log4j.appender.console.layout.ConversionPattern=%m%n 6 | 7 | log4j.appender.file=org.apache.log4j.DailyRollingFileAppender 8 | log4j.appender.file.File=${user.home}/logs/book.log 9 | log4j.appender.file.DatePattern='_'yyyyMMdd 10 | log4j.appender.file.layout=org.apache.log4j.PatternLayout 11 | log4j.appender.file.layout.ConversionPattern=%d{HH:mm:ss,SSS} %p %c (%L) - %m%n 12 | 13 | log4j.logger.org.smart4j=DEBUG -------------------------------------------------------------------------------- /src/main/resources/smart.properties: -------------------------------------------------------------------------------- 1 | #mysql 2 | #smart.framework.jdbc.driver=com.mysql.jdbc.Driver 3 | #smart.framework.jdbc.url=jdbc:mysql://localhost:3306/javaweb01 4 | #smart.framework.jdbc.username=root 5 | #smart.framework.jdbc.password=root 6 | #smart.framework.app.base_package=com.bcc 7 | #smart.framework.app.jsp_path=/WEB-INF/view 8 | #smart.framework.app.asset_path=/asset/ --------------------------------------------------------------------------------