# mjlf_framework **Repository Path**: mjlfto/mjlf_framework ## Basic Information - **Project Name**: mjlf_framework - **Description**: 使用基于注解的方式实现简单版ioc, aop, mvc - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 1 - **Created**: 2018-08-25 - **Last Updated**: 2020-12-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # mjlf_framework #### 项目介绍 使用基于注解的方式实现简单版ioc, aop, mvc #### 软件架构 软件架构说明 #### IOC介绍 使用@Component标示类,说明这个类的实例将交由服务维护,提供scope属性表示实例采用单例还是每次都创建新实例 使用@Autowired做实例注入,在本系统中为了简化, 只提供属性注入 实现方式: 首先在通过ConfigUtil类实现配置加载,这样在类中通过@Value注解的属性就可以得到注入 然后通过ClassManager加载系统中所有标有@Componed的类 最后通过RelectionUtil中提供的方法进行实例化 实现注意: 在实例化的过程中, 我们需要进行属性注入, 如果是遇到该属性在系统中是单例模式, 但是又尚未实例化, 那么就需要先实例化该类, 但是在实例化的过程中很有可能产生循环引用的情况,所以在实现的过程我使用栈记录当前正在实例化的类,如果碰见两个相同的类需要实例化, 那么则说明有循环引用的情况 实例化单例的时候,需要将实例对象保存到内存, 方便下次获取对象的时候直接获取,而不是再取生成,但是非单例模式,每次获取的时候都需要重新生成 #### AOP实现 AOP的实现写了两个版本, 另一个在git@github.com:mjlf/iocAopTest.git下 在这个版本中使用cglib做动态代理 使用@Aspect注解标示增强类 使用@Before注解标示前置切面 -value标示切点的路径(类路径+方法名) -retruenType标示切点返回值类型 默认After.class标示匹配任何返回类型 -modifier标示切点标示切点修饰符 默认public -args标示参数类型 默认Before.class标示匹配任意 使用@After注解表示后置切面 过程难点: 参数问题: 如何将传递正确的参数到增强方法中? 如果增强方法没有任何参数, 这个是最简单的, 直接传递null即可 如果增强方法中带有参数, 那么这需要获取从被代理的方法中获取正确的参数,然后传递给增强方法中 1.首先需要获取增强方法的参数类型和参数名称,然后记录到AdviseMethod中,详见AdviseMethodManager类中initAdviseMethod方法 2.在记录增强方法参数类型和参数名称后, 那么在实际动态代理的时候就可以获取被代理方法的实际参数然后将其转换为增强方法的参数, 需要注意的是, 增强方法中的参数必须包含在被代理的方法参数,出来Method方法(获取被代理方法属性)详细实现看ProxyManager类 匹配问题: 如何判断一个方法是否在AOP拦截范围内 1. 这个问题首先要在系统启动时记录所有增强方法,同时记录这些增强方法拦截的规则 2. 然后就是判断方法是否符合拦截条件了 a.判断修饰符是否匹配 b.判断返回值类型是否匹配 c.判断类,方法名是否匹配(这个才是难点) d.判断参数,方法中参数类型是按顺序匹配 3. 重点看类,方法名匹配 增强拦截规则为(..[两个点]表示匹配0个或多个级别, *表示匹配一个级别),但是规则最后不能以(..)结束,可以以(..*)结束 例如:(..com.mjlf)可以匹配(xxx.xxx.com.mjlf) (..*.com.mjlf..*)匹配(xx.mjlf.xx.Bean) 我在这里是将这个规则表达式转换成了正则表达式,具体实现看AdviseUtil类下parseMethodName方法 #### jdbc封装说明 封装jdbc其实也是使用cglib实现动态代理,这里的实现有点类似最简单版的mybatis 1.定义注解@Mapper,用于标示这个类需要类里的方法是执行与数据库操作的方法 2.定义注解@Sql,标注在类上,标示这个方法与数据交互需要执行的sql语句,执行sql语句写法类上mybatis,使用#{xxx}标示变量或占位符 3.定义注解@Param,标准在方法参数上,标示这个参数对应sql语句中的一个变量或占位符 4.在方法中可以使用@Param做sql语句参数,可以使用JOPO,实际执行的过程中会检测该实例中所有的getXxx()方法,将其这些方法返回的值作为sql需要的占位符变量 ``` //样例 @Mapper public class UserDao { @Sql("SELECT user_name AS username, password FROM t_user WHERE user_name = #{username}") public List getUserByName(@Param("username") String username){ return null; } } //需要注意的是, 这里方法不能有任何实现逻辑, 如果返回是基本类型, 只需要返回0,非基本类型返回空就好 //执行与数据库交互逻辑到在代理中实现 ``` #### 启动说明 1.加载系统应用配置,这样就可以使用@Value注解注入属性(ConfigUtil.readConfig()) 2.加载系统配置(scan.package.base)包下的所有类 3.初始化AOP增强,记录这些增强方法的属性 4.实例化有@Componed注解的类(如果不是不需要代理则直接使用反射实例化, 否则使用cglib动态创建) 5.MVC 请求初始化(RequestManager.init()) #### 安装教程 1. xxxx 2. xxxx 3. xxxx #### 使用说明 1. xxxx 2. xxxx 3. xxxx #### 参与贡献 #### 码云特技 1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md 2. 码云官方博客 [blog.gitee.com](https://blog.gitee.com) 3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解码云上的优秀开源项目 4. [GVP](https://gitee.com/gvp) 全称是码云最有价值开源项目,是码云综合评定出的优秀开源项目 5. 码云官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) 6. 码云封面人物是一档用来展示码云会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)