SpringMVC(1) 自定义注解及反射赋值

自定义注解

根据不同的注解使用的范围来定义@Target,譬如Controller,Service能注解到类,RequestMapping能注解到类和方法,AutoWired只能注解到属性。

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
    String value() default "";
}

//----------------------------------

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Controller {
 
    String value() default "";
}


//----------------------------------

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestMapping {
    String value() default "";
}


//----------------------------------
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
    String value() default "";
 
    boolean required() default true;
}


//----------------------------------
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Service {
    String value() default "";
}

扫描项目类,并实例化参数

这一步的目的是得到一个类名和类实例的映射对象,如 “webController”->WebController的实例,“searchService”->SearchServiceImpl的实例,类似于Spring的BeanFactory,将对应的实例赋给对应的beanName。譬如,searchService在Controller中被定义了,它并不需要去做new这个对象的处理,而是由我们主动注入进去。前提就是我们需要保存一个beanName->bean对象的映射关系。这种处理就是ioc,也就是早期在spring配置文件application.xml经常配的bean id=“XXX” class=“XXX”.

下面来看怎么实例化参数。
思路就是扫描目录下所有需要被我们的山寨Spring托管的类,并将其实例化,然后保存映射关系。在Spring里就是所有标注了@Component注解的类,需要被托管。我们这里就只处理@Controller和@Service注解的类,然后实例化。

扫描所有需要被映射的类

修改一下web.xml,指定我们需要扫描的包。

这里添加了一个init-param,属性名为scanPackage(可任意起名),value为包名。

然后在DispatcherServlet的init方法中,接收并处理。

@Override
public void init(ServletConfig config) throws ServletException {
   System.out.println("我是初始化方法");
   scanPackage(config.getInitParameter("scanPackage"));
   System.out.println(classNames);
}
 
/**
 * 第一步 扫描包下的所有类
 */
private void scanPackage(String pkgName) {
	// 获取指定的包的实际路径url,将com.ternence.mvc变成目录结构com/ternence/
	URL url = getClass().getClassLoader().getResource("/" + pkgName.replaceAll("\\.", "/"));
	// 转化成file对象
	File dir = new File(url.getFile());
	// 递归查询所有的class文件
	for (File file : dir.listFiles()) {
		// 如果是目录,就递归目录的下一层,如com.ternence.mvc.controller
		if (file.isDirectory()) {
			scanPackage(pkgName + "." + file.getName());
		} else {
			// 如果是class文件,并且是需要被spring托管的
			if (!file.getName().endsWith(".class")) {
				continue;
			}
			// 举例,className = com.ternence.controller.WebController
			String className = pkgName + "." + file.getName().replace(".class", "");
			// 判断是否被Controller或者Service注解了,如果没注解,那么我们就不管它,譬如annotation包和DispatcherServlet类我们就不处理
			try {
				Class<?> clazz = Class.forName(className);
				if (clazz.isAnnotationPresent(Controller.class) || clazz.isAnnotationPresent(Service.class)) {
					classNames.add(className);
				}
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}

		}
	}
}

这里定义了一个scanPackage的方法,是用于递归处理web.xml定义的需要被扫描的包下的所有等待被接管的类。我们将所有需要被托管的类,保存到一个list中。供下一步实例化时使用。

实例化bean

我们取到了所有被托管的类,下一步就是要实例化这些类了,也就是像Spring的ioc一样,按规则给bean注入实例值。
像如果在任何地方定义webController,那么我们就默认给他赋值为WebController的实例,定义了modifyService,那么就默认给它注入ModifyServiceImpl的实例。倘若用户自定义了beanName,那么就给beanName注入值,如果不定义,就走上面默认的。如果在ModifyServiceImpl上写了@Service(“abc”),那么我们就保存一个"abc"->ModifyServiceImpl的映射,如果没有定义,就保存一个"modifyService"->ModifyServiceImpl的映射。
保存映射的目的是在将来给AutoWired注解的属性注入值时,好根据beanName来判断该注入什么实例。

添加一个map保存beanName和实例的映射。

// 添加一个map保存beanName和实例的映射。
private Map<String, Object> instanceMapping = new HashMap<>();


@Override
public void init(ServletConfig config) throws ServletException {
   System.out.println("我是初始化方法");
   scanPackage(config.getInitParameter("scanPackage"));

   doInstance();

   System.out.println(instanceMapping);
}

 /**
* 实例化
*/
private void doInstance() {
   if (classNames.size() == 0) {
       return;
   }
   //遍历所有的被托管的类,并且实例化
   for (String className : classNames) {
       try {
           Class<?> clazz = Class.forName(className);
           //如果是Controller
           if (clazz.isAnnotationPresent(Controller.class)) {
               //举例:webController -> new WebController
               instanceMapping.put(lowerFirstChar(clazz.getSimpleName()), clazz.newInstance());
           } else if (clazz.isAnnotationPresent(Service.class)) {
               //获取注解上的值
               Service service = clazz.getAnnotation(Service.class);
               //举例:QueryServiceImpl上的@Service("myQueryService")
               String value = service.value();
               //如果有值,就以该值为key
               if (!"".equals(value.trim())) {
                   instanceMapping.put(value.trim(), clazz.newInstance());
               } else {//没值时就用接口的名字首字母小写
                   //获取它的接口
                   Class[] inters = clazz.getInterfaces();
                   //此处简单处理了,假定ServiceImpl只实现了一个接口
                   for (Class c : inters) {
                       //举例 modifyService->new ModifyServiceImpl()
                       instanceMapping.put(lowerFirstChar(c.getSimpleName()), clazz.newInstance());
                       break;
                   }
               }
           }

       } catch (Exception e) {
           e.printStackTrace();
       }
   }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值