使用反射,cglib,注解结合,实现一个最简单的IOC, 最简单AOP,最简单的MVC(目前只支持restful JSON格式的数据响应), jdbc数据访问封装框架,当然这个框架非常不稳定、不成熟的自我学习、娱乐的框架
像ioc,aop,mybatis,mvc这样的概念就不在阐述了, 我所理解的也不一定正确。
项目结构图:
@Component: 组件注解,该注解标注在类上,表示这个类的实例创建将交由框架维护
@Autowired:属性注入,标注该注解的属性在类实例化的时候会自动进行属性注入,无需手动set或者是赋值
@Value: 配置属性注入,使用这个注解可以注入profile中定义的属性,但是在使用这个注解前,必须先加载系统profile
由于代码量较多原因,本文不贴代码,文章最后会提供git连接
ConfigUtil:类中定义读取系统配置文件代码,将配置文件中属性名和值保存到一个map中
ReflectionUtil:这个工具类中采用反射的方式实现类实例化,方法调用,属性设置三个工具方法
ClassUtil:类加载的工具类,包含获取类加载器,获取指定包下的所有类等工具方法
ClassManager:类管理工具,包含获取标注有指定注解的类集合,获取指定类上的指定注解,判断一个类是否代理(AOP)
BeanManager:实例管理类,包含框架初始化时候生成类实例,对类进行属性注入设置两个大功能,这个类中需要注意的是可能出现属性嵌套注入的情况,该类实例是否单例等
实现以上这些, 最基本的IOC框架就算实现了
AOP的实现相对ioc而言,难了很多,要考虑前置代理, 后置代理,环绕代理,异常代理,如果判断一个方法是否需要做这些代理,还要考虑参数传递,不写代码不知道, 下手的时候发现,到处都是坑,到目前为止,在这个框架中也只是实现了前置代理,后续再拓展其他功能吧
@Aspect 标准有该注解的类表示切面类,及这里边定义的方法为切面方法
@Before,@After,这个注解的内容有点复杂,包含类切面方向(前置,后置,环绕,异常),连接点等信息,value表示需要被代理的方法,modifier表示方法声明,returnType表示返回类型,args参数类型,其中需要注意的是returnType 默认After.class表示匹配所有,args默认Before.class表示匹配所有,vaule中(..)表示多级,(*)表示任意字符样例:(..*com.mjlf..*.ProxyFunction)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Before {
String value();
String modifier() default "public";
Class> returnType() default After.class;
Class>[] args() default {Before.class};
}
AdviseUtil:这个类中主要定义了判断一个方法是否需要被代理的方法
AdviseMethod:切面方法,需要要被代理的方法的属性,方法上定义的注解(@Before),被代理方法本身,被代理方法所在类实例,方法参数类型,方法参数名称,方法返回值类型,返回值名称(还没实现)
AdviseMethodManager:这个类主要是用在框架初始化的时候统计需要切面信息,及标注有@Aspect的类中标注有@Before等方法
ProxyManager:使用cglib方法生成类对象,需要注意的是, 后边实现的jdbc封装也使用到了cglib,所以这个类不单单是生成AOP所以代理的类的实例
对jdbc封装,采用的方式有点类似mybatis,
@Mapper:标注在类上,表示这个类中包含于数据库交互的方法
@Param:标注在参数上,表示需要传递给sql语句的字段属性,例:@param(“id”) int id
@Sql:标注在方法上,表示这个方法需要需要与数据库交互,执行sql语句为@Sql注解中的value属性
sql写法样例:SELECT a, b FROM table_tbl WHERE id = #{id}
@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;
}
}
ConnectionManager:管理连接,采用队列实现,初始化的时候创建指定连接数量,这里需要优化
MapperProxy:主要负责生成标注有@Mapper类实例,这个类是最复杂的,包含sql格式转换,参数绑定,结果封装等
首次使用servlet3.0实现网络模块
@Controller 控制器注解
@RequestMapping 注解映射请求路径
DispatcherServlet,这个类是这个框架的核心,所有初始化工作都在这个类中进行启动,然后在service中进行请求转发
//初始化
@Override
public void init(ServletConfig config) throws ServletException {
try {
BeanManager.init();
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
throw new ServletException("连接失败");
}
RequestManager.init();
}
//请求转发
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
ReqParams reqParams = new ReqParams();
Enumeration paramKeys = req.getParameterNames();
while (paramKeys.hasMoreElements()) {
String key = paramKeys.nextElement();
reqParams.addParam(key, req.getParameter(key));
}
//获取body中的参数
BufferedReader br = req.getReader();
parseParams(br, reqParams);
String reqPath = req.getPathInfo();
String reqMethod = req.getMethod().toUpperCase();
RequestMethod requestMethod = RequestManager.getReqMethod(reqMethod, reqPath);
if (requestMethod != null) {
Method method = requestMethod.getReqMethod();
Object object = requestMethod.getReqObj();
ReflectionUtil.invokeMethod(object, method, req, resp, reqParams);
} else {
resp.sendError(404, "对应的请求处理:" + reqPath);
}
} catch (Exception e) {
logger.error("请求处理过程中出现错误:" + e);
resp.sendError(500, e.getMessage());
}
}
式样样例:
Controller:
package com.mjlf.project.web.unlogin;
@Controller
@RequestMapping("/mjlf")
public class LoginController {
Logger logger = LoggerFactory.getLogger(LoginController.class);
@Autowired
private LoginService loginService;
@RequestMapping(value = "/login", method = "POST")
public void login(HttpServletRequest request, HttpServletResponse response, ReqParams params) throws IOException {
JSONObject jsonObject = null;
String username = params.getParam("username");
String password = params.getParam("password");
User user = new User();
user.setPassword(password);
user.setUsername(username);
if(loginService.login(user)){
jsonObject = new JSONObject();
jsonObject.put("loginStatus", "yes");
}else {
jsonObject = new JSONObject();
jsonObject.put("loginStatus", "no");
}
response.getWriter().write(jsonObject.toString());
}
}
Service:
package com.mjlf.project.service;
/**
* 登录服务类
*/
@Component
public class LoginService {
@Autowired
private UserDao userDao;
public boolean login(User user){
List users = userDao.getUserByName(user.getUsername());
for(User u : users){
if(user.getUsername().equals(u.getUsername()) && user.getPassword().equals(u.getPassword())){
return true;
}
}
return false;
}
public UserDao getUserDao(){
return this.userDao;
}
}
DAO:
@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;
}
}
Aspect:
@Aspect
public class ProjectAspectJ {
@Before(value = "com.mjlf..LoginService.login", returnType = boolean.class)
public void showTime(Method method, User user){
System.out.println(method.getName());
System.out.println(user);
System.out.println(new Date());
}
public void end(){
System.out.println("end");
}
}
结果:可以看到请求之后正常访问都了数据库,而且执行了切面增强方法。
源码地址:https://gitee.com/mjlfto/mjlf_framework.git
实现过程中参考了:架构探险 从零开始写javaweb框架
链接:https://pan.baidu.com/s/1pa-uuaTgi_8WdP3P93zvlA 密码:dvpk