先说一下涉及的技术点,filter,servlet,io,反射,Map,注解
整体思路:
一:容器准配
1,先加载properties配置文件, 遍历配置文件中所有的key-value对,key为名,value为类的全路径
2,根据反射创建对象,将对象放入Map容器当中,以配置文件中配置的key作为Map容器的key值,对象作为value
3,遍历map容器,获得对象中的所有field,遍历field数组,如果field字段上有@Resource注解,就根据注解的name值作为key从容器中获得相应对象,然后使用反射将注入到字段当中
4,判断Map容器的key最后一位是否含有%,如果含有说明是一个Controller,获得对象的所有method。遍历中method,获得方法上的注解。如果是@ReuqestMapper注解,则使用注解中的url作为key,Map容器的key加上方法签名作为value,然后放入url映射容器当中,将方法签名作为key,method方法作为value,放入方法映射器中
二:接收请求时的处理思路
1,在web.xml配置filter过滤器,
2,拦截到请求,在处理器映射器中的url映射器查找,如果不存在该key,就放行,存在就拦截
3,拦截后,根据url从处理器映射器中的url映射器获得对应的处理器信息
4,将处理器信息和request,response传给处理器适配器的executeMethod处理
5,executeMethod根据处理器信息,根据参数名和参数类型封装参数并执行相应的方法,获得获得返回结果
6,将返回结果返回给Filter由,filter做转发还是重定向的处理
下面直接撸代码了
代码前奏
由于在封装方法参数时需要方法名作为key,所以需要,jdk1.8以上,如果使用的IDE是Eeclipse需要如下配置
代码正式开撸
/**
* 类描述:
* 作者: xuchang
* 创建日期:2019年2月28日
* 修改人:
* 修改日期:
* 修改内容:
* 版本号: 1.0.0
*/
public class BeanFactory {
// 存放bean的容器
private static final Map
/**
* 类描述:属性注入
* 作者: xuchang
* 创建日期:2019年2月28日
* 修改人:
* 修改日期:
* 修改内容:
* 版本号: 1.0.0
*/
public class AttributeResource {
/**
* 为容器中的属性进行属性注入,和方法映射
* 方法描述:
* @param map
* @throws Exception
*/
public static void beanAttrbuteResource(Map
Set
// 遍历容器对象
for (Entry
// 获得容器中的bean
Object obj = entry.getValue();
// 获得对象的class对象
Class extends Object> clazz = obj.getClass();
// 获得所有字段
Field[] fields = clazz.getDeclaredFields();
// 遍历字段,并根据注解的name值为字段进行属性注入
for (Field field : fields) {
// 判断字段上是否有Resource类型的注解 如果有,为true
if(field.isAnnotationPresent(Resource.class)){
Resource resourceAnno = field.getAnnotation(Resource.class);
// resourceAnno.name() 获得字段注解上name的值
String name = "".equals(resourceAnno.name())?field.getName():resourceAnno.name();
// 设置操作权限为true 可以操作私有属性
field.setAccessible(true);
Object bean = BeanFactory.getBeanFactory().getBean(name);
if(bean!=null){
// 向bean的属性中进行属性注入
field.set(obj, bean);
}else{
new RuntimeException("属性注入时:没有找到,名为"+name+"的bean").printStackTrace();
}
}
}
// 遍历bean字段结束
// 映射容器
HandleMapper mapper = HandleMapper.getInstance();
//获得容器中bean对象对应的key,并判断key是不是controller
String key = (String) entry.getKey();
// 截取key最后一位,并判断是否是 %,如果是说明他是一个web控制器
if( "%".equals(key.substring(key.length()-1,key.length())) ){
// 获得他的所有方法
Method[] methods = clazz.getDeclaredMethods();
//遍历methods
for (Method method : methods) {
// 存放拼接的方法签名
StringBuffer sb = null;
method.setAccessible(true);
// 判断方法上是否有RequestMapper类型的注解,
// 如果有,将方法上的注解值,url作为key,bean容器的key加上方法名作为value ,然后把它放入url映射map
if(method.isAnnotationPresent(RequestMapper.class)){
// 获得方法上的注解对象
RequestMapper rMapper = method.getAnnotation(RequestMapper.class);
String url = rMapper.url();
// 获得方法名
String methodName = method.getName();
// 拼接方法签名
sb = new StringBuffer();
sb.append(methodName+"(");
// 获得方法上所有的参数
Parameter[] params = method.getParameters();
// 遍历参数列表
for (int i=0;i
Parameter parameter = params[i];
// 获得形参参数类型名
String paramTypeName = parameter.getType().getName();
// 获得形参名
String paramName = parameter.getName();
sb.append(paramTypeName+" "+paramName+",");
}
if( ",".equals( sb.substring(sb.length()-1, sb.length()) ) )
sb.delete(sb.length()-1, sb.length());
sb.append(")");
// 放入method方法映射map
mapper.putMethod(sb.toString(), method);
// 放入属性url映射map
mapper.putURL(url, key+sb.toString());
}
// 注解判断结束
}
// method方法遍历结束
}
// 判断是否是web控制器结束
}
// 遍历容器结束
}
}
/**
* 类描述:Bean工具类
* 作者: xuchang
* 创建日期:2019年2月28日
* 修改人:
* 修改日期:
* 修改内容:
* 版本号: 1.0.0
*/
public class BeanUtil {
// 映射容器
private static final HandleMapper mapper = HandleMapper.getInstance();
public static Object arrayTransform(String[] values,Class paramType) throws Exception{
// 获得参数类型名
String typeName = paramType.getName();
// 动态创建数组
Object paramValue = Array.newInstance(paramType, values.length);
// 构造器
Constructor constructor = null;
if( mapper.getConstructorByClassName(typeName)!=null ){// 可以在存放类型集合的map中找到说明是 包装类型或基本类型
// constructor = BASIC_TYPE_CONSTRUCTOR.get(flag);
constructor = mapper.getConstructorByClassName(typeName);
}else{ // TODO: 其他类型
// 待定
}
// 通过for循环遍历从中requerst获取的数组, 然后将其中的值进行转换然后动态赋值,paramValue数组动态赋值
for(int v=0;v
}
return paramValue;
}
/**
* 方法描述:将字符串转换成指定的基本类型数据,只有数字类型的可以转换成char类型
* @param value 被转换的值
* @param typeName 转换的类型
* @return
*/
public static Object basicTypeTran(String value,String paramTypeName){
if(ToolUtil.isNullOrBlank(value)||ToolUtil.isNullOrBlank(paramTypeName)){
return null;
}
Constructor c = mapper.getConstructorByClassName(paramTypeName);
try {
// 根据相应的参数类型从map中获得相应的构造器
if("char".equals(paramTypeName)){
return c.newInstance((char)Integer.parseInt(value));
}else{
return c.newInstance(value);
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
}
}
@Retention(RUNTIME)
@Target(METHOD)
/**
* 类描述:
* 作者: xuchang
* 创建日期:2019年2月28日
* 修改人:
* 修改日期:
* 修改内容:
* 版本号: 1.0.0
*/
public @interface RequestMapper {
/**
*
* 方法描述:注入的值得名称
* @return
*/
String url() default "";
}
/**
* 类描述:
* 作者: xuchang
* 创建日期:2019年2月28日
* 修改人:
* 修改日期:
* 修改内容:
* 版本号: 1.0.0
*/
@Retention(RUNTIME)
@Target(FIELD)
public @interface Resource {
/**
*
* 方法描述:注入的值得名称
* @return
*/
String name() default "";
}
/**
* 类描述:
* 作者: xuchang
* 创建日期:2019年2月28日
* 修改人:
* 修改日期:
* 修改内容:
* 版本号: 1.0.0
*/
public class HandleMapper{
private HandleMapper(){}
private static final HandleMapper HANDLE_MAPPER = new HandleMapper();
// url映射器 key:url value:类名%方法签名
private static final Map
// method映射器 key:方法签名 value:method对象
private static final Map
// 存放基本类型和包装类的有参构造器
@SuppressWarnings("rawtypes")
private final static Map
// 存放基本类型和包装类的有参构造器
@SuppressWarnings("rawtypes")
private final static Map
static{
try {
BASIC_TYPE_CONSTRUCTOR.put(Byte.class.getName(), Byte.class.getDeclaredConstructor(String.class));
BASIC_TYPE_CONSTRUCTOR.put(Character.class.getName(), Character.class.getDeclaredConstructor(char.class));
BASIC_TYPE_CONSTRUCTOR.put(Short.class.getName(), Short.class.getDeclaredConstructor(String.class));
BASIC_TYPE_CONSTRUCTOR.put(Integer.class.getName(), Integer.class.getDeclaredConstructor(String.class));
BASIC_TYPE_CONSTRUCTOR.put(Long.class.getName(), Long.class.getDeclaredConstructor(String.class));
BASIC_TYPE_CONSTRUCTOR.put(Float.class.getName(), Float.class.getDeclaredConstructor(String.class));
BASIC_TYPE_CONSTRUCTOR.put(Double.class.getName(), Double.class.getDeclaredConstructor(String.class));
BASIC_TYPE_CONSTRUCTOR.put(Boolean.class.getName(), Boolean.class.getDeclaredConstructor(String.class));
BASIC_TYPE_CONSTRUCTOR.put("byte", Byte.class.getDeclaredConstructor(String.class));
BASIC_TYPE_CONSTRUCTOR.put("char", Character.class.getDeclaredConstructor(char.class));
BASIC_TYPE_CONSTRUCTOR.put("short", Short.class.getDeclaredConstructor(String.class));
BASIC_TYPE_CONSTRUCTOR.put("int", Integer.class.getDeclaredConstructor(String.class));
BASIC_TYPE_CONSTRUCTOR.put("long", Long.class.getDeclaredConstructor(String.class));
BASIC_TYPE_CONSTRUCTOR.put("float", Float.class.getDeclaredConstructor(String.class));
BASIC_TYPE_CONSTRUCTOR.put("double", Double.class.getDeclaredConstructor(String.class));
BASIC_TYPE_CONSTRUCTOR.put("boolean", Boolean.class.getDeclaredConstructor(String.class));
BASIC_TYPE_CONSTRUCTOR.put("java.lang.String", String.class.getDeclaredConstructor(String.class));
BASIC_TYPE.put(Byte.class.getName(), Byte.class);
BASIC_TYPE.put(Character.class.getName(), Character.class);
BASIC_TYPE.put(Short.class.getName(), Short.class);
BASIC_TYPE.put(Integer.class.getName(), Integer.class);
BASIC_TYPE.put(Long.class.getName(), Long.class);
BASIC_TYPE.put(Float.class.getName(), Float.class);
BASIC_TYPE.put(Double.class.getName(), Double.class);
BASIC_TYPE.put(Boolean.class.getName(), Boolean.class);
BASIC_TYPE.put("java.lang.String", String.class);
BASIC_TYPE.put("byte", Byte.class);
BASIC_TYPE.put("char", Character.class);
BASIC_TYPE.put("short", Short.class);
BASIC_TYPE.put("int", Integer.class);
BASIC_TYPE.put("long", Long.class);
BASIC_TYPE.put("float", Float.class);
BASIC_TYPE.put("double", Double.class);
BASIC_TYPE.put("boolean", Boolean.class);
BASIC_TYPE.put("[B", byte.class);
BASIC_TYPE.put("[C", char.class);
BASIC_TYPE.put("[S", short.class);
BASIC_TYPE.put("[I", int.class);
BASIC_TYPE.put("[J", long.class);
BASIC_TYPE.put("[F", float.class);
BASIC_TYPE.put("[D", double.class);
BASIC_TYPE.put("[Z", boolean.class);
} catch (NoSuchMethodException | SecurityException e) {
e.printStackTrace();
}
}
public static HandleMapper getInstance(){
return HANDLE_MAPPER;
}
public Object getClassInfoByURL(String url){
return URL_MAAPPER.get(url);
}
public Object putURL(String key, String value) {
return URL_MAAPPER.put(key, value);
}
/**
*
* 方法描述:根据方法签名获得方法
* @param methodSignature
* @return
* 方法签名对应的方法
*/
public Method getMethodSignatureByMethod(String methodSignature){
return METHOD_MAAPPER.get(methodSignature);
}
/**
*
* 方法描述:将方法放入方法映射容器当中
* @param key 方法签名
* @param value method对象
* @return
* 放入的方法
*/
public Method putMethod(String key, Method value) {
return METHOD_MAAPPER.put(key, value);
}
/**
*
* 方法描述:通过基本类型或String类型的完整类名字符串获得该类型的有参构造器
* @param className 类名
* @return
* 该类型的构造器
*/
public Constructor getConstructorByClassName(String className) {
return BASIC_TYPE_CONSTRUCTOR.get(className);
}
/**
*
* 方法描述:将构造器放入map对象中
* @param key 完整类名
* @param value 构造器
* @return
*/
public Constructor putConstructorMap(String key,Constructor value) {
return BASIC_TYPE_CONSTRUCTOR.put(key, value);
}
/**
*
* 方法描述:将类对象放入映射器中
* @param className 完整类名
* @param clazz Class对象
* @return
*/
public Class putClassMap(String className,Class clazz) {
return BASIC_TYPE.put(className, clazz);
}
/**
*
* 方法描述:根据完整类名获得类对象
* @param className 完整类名
* @return
*/
public Class getClassMap(String className) {
return BASIC_TYPE.get(className);
}
}
/**
* 类描述:
* 作者: xuchang
* 创建日期:2019年2月28日
* 修改人:
* 修改日期:
* 修改内容:
* 版本号: 1.0.0
*/
public class HandleAdapter {
private HandleAdapter(){}
private BeanFactory beanFactory = BeanFactory.getBeanFactory();
private static final HandleAdapter HANDLE_ADAPTER = new HandleAdapter();
// 映射容器
private static final HandleMapper mapper = HandleMapper.getInstance();
public static HandleAdapter getInstance(){
return HANDLE_ADAPTER;
}
@SuppressWarnings({ "rawtypes", "unused" })
public Object executeMethod(HttpServletRequest request, HttpServletResponse response,String info,String separator){
// 将信息进行分割,下标0表示对象在Bean中的key,下标1表示 请求对应的方法
String[] classInfo = info.split(separator);
// 获得对应的对象
Object obj = beanFactory.getBean(classInfo[0]+"%");
try {
Class clazz = obj.getClass();
// Method method = clazz.getDeclaredMethod(classInfo[1], HttpServletRequest.class,HttpServletResponse.class);
// 从方法映射器中根据方法签名获得method
Method method = mapper.getMethodSignatureByMethod(classInfo[1]);
Object[] paramsValue = getMethodParams(request,response,method);
// 通过反射执行方法
return method.invoke(obj, paramsValue);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@SuppressWarnings({ "rawtypes", "unused", "unchecked" })
public Object[] getMethodParams(HttpServletRequest request, HttpServletResponse response,Method method) throws Exception{
// 获得方法上所有的参数
Parameter[] params = method.getParameters();
// 存放参数列表
Object[] paramValues = new Object[params.length];
if(params==null||params.length==0){
return null;
}
// 遍历参数列表
for (int i=0;i
Parameter parameter = params[i];
// 获得参数类型
Class paramType = parameter.getType();
// 获得参数类型名
String paramTypeName = paramType.getName();
// 存放创建的形参参数
String paramName = parameter.getName();
// 存放创建的形参参数
Object paramValue = null;
if( paramType.isAssignableFrom(response.getClass()) ){
paramValue = response;
}else if( paramType.isAssignableFrom(request.getClass()) ) {
paramValue = request;
}else{ // 其他类型对象
paramValue = getParamValue(request,paramType,paramName);
}
paramValues[i] = paramValue;
}
return paramValues;
}
/**
*
* 方法描述:从请求中获得指定参数
* @param request HttpServletRequest 请求对象
* @param paramType 参数类型的类对象
* @param paramName 参数名
* @return
* @throws Exception
*/
@SuppressWarnings("rawtypes")
public Object getParamValue(HttpServletRequest request,Class paramType,String paramName) throws Exception{
// 获得参数类型名
String paramTypeName = paramType.getName();
// 存放创建的形参参数
Object paramValue = null;
// 根据方法形参名或属性名,从request中获得参数
String[] values = request.getParameterValues(paramName);
// 判断形参是不是基本类型获得包装类型
if( mapper.getClassMap(paramTypeName)!=null ){
if("java.lang.String".equals(paramTypeName)){
paramValue = values.length==1?values[0]:Arrays.toString(values);
}else if (values.length==1) {
// 创建对象 ,并向对象设置值
paramValue = BeanUtil.basicTypeTran(values[0],paramTypeName);
}else{
// 创建对象 ,并向对象设置值
paramValue = BeanUtil.basicTypeTran(Arrays.toString(values),paramTypeName);
}
}else if( paramType.isArray() ){// 判断反射对象是不是数组类型 是返回true
//基本数据类型名字的格式[J 引用类型的数据格式 [Ljava.lang.Integer,所以只要从格式的第二位一直截取到最后一位,如果为空就是基本类型
String flag = paramTypeName.substring(2, paramTypeName.length()).replace(";", "");
if(flag.length()==0){ //基本数据类型
Class type = mapper.getClassMap(paramTypeName);
paramValue = BeanUtil.arrayTransform(values, type);
}else{ // 包装数据类型
// 如果是基本类型或包装类就直接从BASIC_TYPE中获取,如果不是;利用完整类名直接创建一个Class
Class type = mapper.getClassMap(paramTypeName)!=null?mapper.getClassMap(paramTypeName):Class.forName(flag);
paramValue = BeanUtil.arrayTransform(values, type);
}
}else{ // 其他类型对象
paramValue = getBean(request,paramType);
}
return paramValue;
}
@SuppressWarnings({ "unchecked", "rawtypes"})
public Object getBean(HttpServletRequest request,Class clazz){
Object bean = null;
try {
bean = clazz.newInstance();
// 获得类中所有的字段
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
String name = field.getName();
if( !ToolUtil.isNullOrBlank(request.getParameter(name)) ){
Class fieldClazz = (Class) field.getGenericType();
Object value = getParamValue(request, fieldClazz, name);
// 拼接set方法
Method method = clazz.getMethod("set"+(
name.substring(0, 1).toUpperCase()+name.substring(1, name.length())
), fieldClazz);
method.invoke(bean, value);
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return bean;
}
}
/**
* 类描述:中央控制器
* 作者: xuchang
* 创建日期:2019年3月1日
* 修改人:
* 修改日期:
* 修改内容:
* 版本号: 1.0.0
*/
public class ControllerFilter implements Filter{
// 重定向
private static final String REDIRECT = "redirect";
// 转发(默认)
private static final String FORWARD = "forward";
// 配置文件存放的类路径
private static final String CONFIG_PATH = "config_path";
// 编码格式
@SuppressWarnings("unused")
private static final String CHARACTER_ENCODING = "character_encoding";
@SuppressWarnings("unused")
private String encoding = "utf-8";
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
String path = filterConfig.getInitParameter(CONFIG_PATH);
String encoding = filterConfig.getInitParameter(CONFIG_PATH);
if(StringUtils.isNotBlank(path)){
// 初始化容器
@SuppressWarnings("unused")
BeanFactory beanFactory = BeanFactory.getBeanFactory(path);
}else{
// 初始化容器
@SuppressWarnings("unused")
BeanFactory beanFactory = BeanFactory.getBeanFactory();
}
if(StringUtils.isNotBlank(encoding)){
this.encoding = encoding;
}
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
request = requestEncoding(request,response);
response.setContentType("text/html;charset=utf-8");
// 如果servlet-mapper配置为/*使用servletPath()的路径为空,使用request.getPathInfo()可以获得值
// servlet-mapper配置不为/*,request.getPathInfo()获得值为空,servletPath()可以获得值
//获得请求的相对url
String url = request.getServletPath();
url = StringUtils.isNotBlank(url)?url:request.getPathInfo();
// 根据请求获得类信息,以及方法中间以%分割
String info = (String) HandleMapper.getInstance().getClassInfoByURL(url);
// 如果info为空,说明在映射器中没有这个路径
if(info!=null){
// 通过适配找到指定的方法 并执行
Object object = HandleAdapter.getInstance().executeMethod(request, response, info, "%");
// object不为空说明有返回值
if(object!=null){
System.err.println( object.getClass() );
// 查看返回值类型 如果是字符串说明是返回值得路径
if(object instanceof String){
// 元素0是转发或重定向方式, 元素1的是地址,如果只有一个元素则是请求地址,默认是转发方式
String[] strings = ((String)object).split(":");
if(REDIRECT.equals(strings[0].trim())){
response.sendRedirect(strings[1]);
return;
};
request.getRequestDispatcher( FORWARD.equals(strings[0].trim())?strings[1]:strings[0] ).forward(request, response);
return;
}
// 字符串判断结束
}
}else{
// 放行
chain.doFilter(request, response);
}
}
/**
* 方法描述:处理请求参数乱码
* @param request
* @param response
* @return
*/
public HttpServletRequest requestEncoding(HttpServletRequest request,HttpServletResponse response) {
return (HttpServletRequest)Proxy.newProxyInstance(request.getClass().getClassLoader(), request.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 获得执行方法的方法名
String methodName = method.getName();
//如果是getParameter方法,进行代理
if("getParameter".equals(method)){
// 获得请求的方式 post直接放行,get进行乱码处理
String type = request.getMethod();
if("get".equalsIgnoreCase(type)){
String result = (String) method.invoke(request, args);
return new String(result.getBytes("ISO-8859-1"),"UTF-8");
} else if("post".equalsIgnoreCase(type)){
// 放行
request.setCharacterEncoding("utf-8");
return method.invoke(request, args);
}
}
// 放行
return method.invoke(request, args);
}
});
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
配置文件是properties后缀名默认是放在sec目录下,
格式普通的bean直接写key即可,controller的后缀要加上 %
userService=com.hpe.service.impl.UserServiceimpl
userDao=com.hpe.dao.impl.UserDaoImpl
# web
userController%=com.hpe.controller.UserController
userLoginController%=com.hpe.controller.UserLoginController
PS:并没有加上日期类型的处理等