IOC功能简单实现
创建自定义注解,功能与spring自带注解功能一致
元注解(负责注解其他注解)功能说明:
- @Target:说明了Annotation所修饰的对象范围(被标注的注解可用在什么地方)。
- ElementType.FIELD:注解可标注在属性上。
- ElementType.METHOD:注解可标注在方法上。
- ElementType.TYPE:注解可标注在类、接口(包括注解类型) 或enum声明上。
- @Retention:该Annotation被保留的时间长短
- SOURCE:在源文件中有效(即源文件保留)。
- CLASS:在class文件中有效(即class保留)
- RUNTIME:在运行时有效(即运行时保留)
- @Documented:注解表明这个注释是由 javadoc记录的,在默认情况下也有类似的记录工具。 如果一个类型声明被注释了文档化,它的注释成为公共API的一部分。
创建自定义注解
-
@CustomController = @Controller
package custom.annotation; import java.lang.annotation.*; @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface CustomController { String value() default ""; }
-
@CustomRequestMapping = @RequestMapping
package custom.annotation; import java.lang.annotation.*; @Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface CustomRequestMapping { String value() default ""; }
-
@CustomRequestParam = @RequestParam
package custom.annotation; import java.lang.annotation.*; @Target({ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface CustomRequestParam { String value() default ""; }
-
@CustomAutowired = @Autowired
package custom.annotation; import java.lang.annotation.*; @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface CustomAutowired { String value() default ""; }
-
@CustomService = @Service
package custom.annotation; import java.lang.annotation.*; @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface CustomService { String value() default ""; }
创建IOC实现类
package custom.servlet;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import custom.annotation.CustomAutowired;
import custom.annotation.CustomController;
import custom.annotation.CustomRequestMapping;
import custom.annotation.CustomService;
/**
* 简单版的IOC容器实现类
* @author li
*
*/
@SuppressWarnings("serial")
public class CustomDispatcherServlet extends HttpServlet {
private Map<String, Object> mapping = new HashMap<String, Object>();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
try {
doDispath(req,resp);
} catch (Exception e) {
// 发生异常时,页面显示500错误
resp.getWriter().write("500");
}
}
/**
* 用于处理具体的调用请求,根据url在mapping中寻找对应方法
* @param req
* @param resp
* @throws Exception
*/
private void doDispath(HttpServletRequest req, HttpServletResponse resp) throws Exception{
// 获取访问地址 例如:/custom-spring/demo/query
String url = req.getRequestURI();
// 获取属于哪个项目 /custom-spring
String contextPath = req.getContextPath();
// 获取具体调用地址 /demo/query
url = url.replace(contextPath, "").replaceAll("/+", "/");
if(!this.mapping.containsKey(url)){
resp.getWriter().write("404");
return;
}
Method method = (Method) this.mapping.get(url);
Map<String, String[]> params = req.getParameterMap();
method.invoke(this.mapping.get(method.getDeclaringClass().getName()), new Object[]{req,resp,params.get("name")[0]});
}
/**
* 生成初始类对象
*/
@Override
public void init(ServletConfig config) throws ServletException {
// TODO Auto-generated method stub
InputStream is = null;
try {
Properties configContext = new Properties();
is = this.getClass().getClassLoader().getResourceAsStream(config.getInitParameter("contextConfigLocation"));
configContext.load(is);
String scanPackage = configContext.getProperty("scanPackage");
doScanner(scanPackage);
/**
* 根据mapping中所有的key(项目中所有类的全限定名称)生成对应对象,并作为value加入到mapping中
*/
for(String className:mapping.keySet()){
if(!className.contains(".")){
continue;
}
// 根据全限定名获取类对象
Class<?> clazz = Class.forName(className);
// 对于@GPController注解标注的类做处理
if(clazz.isAnnotationPresent(CustomController.class)){
// 根据类对象生成具体实例放入到mapping中
mapping.put(className, clazz.newInstance());
// 映射地址
String baseUrl = "";
if(clazz.isAnnotationPresent(CustomRequestMapping.class)){
CustomRequestMapping requestMapping = clazz.getAnnotation(CustomRequestMapping.class);
baseUrl = requestMapping.value();
}
// Method 用于动态获取方法信息,根据类对象获取该类中所有方法
Method[] methods = clazz.getMethods();
for(Method method:methods){
if(!method.isAnnotationPresent(CustomRequestMapping.class)){
continue;
}
// 标注了@GPRequestMapping注解的方法,以类映射地址+方法映射地址形式作为key,方法对象作为value,存入mapping中
CustomRequestMapping requestMapping = method.getAnnotation(CustomRequestMapping.class);
String url = (baseUrl+requestMapping.value().replaceAll("/+", "/"));
mapping.put(url, method);
System.out.println("Mapped" + url + "," + method);
}
}else // 对于@GPService注解标注的类做处理
if(clazz.isAnnotationPresent(CustomService.class)){
CustomService service = clazz.getAnnotation(CustomService.class);
// 获取@service标签中定义的属性value,若value为空,默认使用class的全限定名
String beanName = service.value();
if("".equals(beanName)){
beanName = clazz.getName();
}
// 根据类对象创建具体实例
Object instance = clazz.newInstance();
// 创建具体实例放入mapping中
mapping.put(beanName, instance);
// clazz.getInterfaces()获取该对象所实现的所有接口 即以表示这个类实现了哪些接口
for(Class<?> i : clazz.getInterfaces()){
mapping.put(i.getName(), instance);
}
}else{
continue;
}
}
for(Object object : mapping.values()){
/**
* 注解等类不会进行实例化
*/
if(object == null){
continue;
}
// 获取类对象
Class clazz = object.getClass();
/**
* 示例方法中只有@GPController注解标识的方法内有@GPAutowired,所以没有对@GPService标示的类进行处理
*/
if(clazz.isAnnotationPresent(CustomController.class)){
// Field:在程序运行状态中,动态地获取或设置字段信息
Field[] fields = clazz.getDeclaredFields();
for(Field field : fields){
if(!field.isAnnotationPresent(CustomAutowired.class)){
continue;
}
CustomAutowired autowired = field.getAnnotation(CustomAutowired.class);
String beanName = autowired.value();
if("".equals(beanName)){
// 获取GPAutowired 标识字段对应的bean名称
beanName = field.getType().getName();
// public以外的类型,只要加了@CustomAutowired的属性都强制进行赋值
// 反射中叫暴力访问
field.setAccessible(true);
try {
// 向这个属性变量设置新值:field.set(具体对象实例,新的值) field类对象中的属性变量
field.set(mapping.get(clazz.getName()), mapping.get(beanName));
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
}
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
} finally{
if(is != null){
try{
is.close();
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
System.out.println("GP MVC Framework is init");
}
/**
* 获取项目下的所有类文件,将类文件的全限定名做为容器的key,value暂时设置为null
* @param scanPackage
*/
private void doScanner(String scanPackage){
// class文件对应地址
URL url = this.getClass().getClassLoader().getResource("/"+scanPackage.replaceAll("\\.", "/"));
File classDir = new File(url.getFile());
for(File file : classDir.listFiles()){
if(file.isDirectory()){
doScanner(scanPackage+"."+file.getName());
}else{
if(!file.getName().endsWith(".class")){
continue;
}
String clazzName = (scanPackage+"."+file.getName().replaceAll(".class", ""));
mapping.put(clazzName, null);
}
}
}
}
测试代码实现
实现controller
package custom.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import custom.annotation.CustomAutowired;
import custom.annotation.CustomController;
import custom.annotation.CustomRequestMapping;
import custom.annotation.CustomRequestParam;
import custom.service.CustomDemoService;
@CustomController
@CustomRequestMapping("/demo")
public class DemoAction {
@CustomAutowired
private CustomDemoService demoService;
@CustomRequestMapping("/query")
public void query(HttpServletRequest req,HttpServletResponse resp,@CustomRequestParam("name") String name){
String result = demoService.get(name);
try {
resp.getWriter().write(result);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
实现service接口
package custom.service;
public interface CustomDemoService {
String get(String name);
}
实现service 实现类
package custom.service.impl;
import custom.annotation.CustomService;
import custom.service.CustomDemoService;
@CustomService
public class DemoService implements CustomDemoService {
public String get(String name){
return "My name is" + name;
}
}
配置配置文件 application.properties
放在src目录下:
# 扫描package名称
scanPackage=custom
配置web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>spring</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>custommvc</servlet-name>
<servlet-class>custom.servlet.CustomDispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>application.properties</param-value>
</init-param>
// servlet init()执行时机,-1表示servlet被调用时执行,只执行一次,便于调试IOC容器加载实例过程
<load-on-startup>-1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>custommvc</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>