文章目录
- 自己实现SpringMVC 底层机制[二]
- 实现任务阶段3- 从web.xml 动态获取myspringmvc.xml
- 分图析示意
- 代码实现
- 实现任务阶段4- 完成自定义@Service 注解功能。
- 分析示意图
- 代码实现
- 完成测试(启动Tomcat, 自动加载MyDispatcherServlet, 完成IOC 容器的注入)。
自己实现SpringMVC 底层机制[二]
实现任务阶段3- 从web.xml 动态获取myspringmvc.xml
前面我们加载myspringmvc.xml 是硬编码, 现在做活, 从web.xml 动态获取,
分图析示意
MyDispatcherServlet 在创建并初始化MyWebApplicationContext,动态的从web.xml 中获取到配置文件.
代码实现
修改my-springmvc\src\main\java\com\myspringmvc\context\MyWebApplicationContext.java
public class MyWebApplicationContext {//定义属性classFullPathList, 保存扫描包/子包的类的全路径private List<String> classFullPathList =new ArrayList<>();//定义属性ioc, 存放反射生成的Bean对象 /Controller/Servicepublic ConcurrentHashMap<String, Object> ioc =new ConcurrentHashMap<>();//无参构造器public MyWebApplicationContext() {}private String configLocation;//属性,表示spring容器配置文件public MyWebApplicationContext(String configLocation) {this.configLocation = configLocation;}//编写方法,完成自己的spring容器的初始化public void init() {//这里是写的固定的spring容器配置文件=>做活//String basePackage = XMLParser.getBasePackage("myspringmvc.xml");String basePackage =XMLParser.getBasePackage(configLocation.split(":")[1]);//这时basePackage => com.controller,com.serviceString[] basePackages = basePackage.split(",");//判断包是否为空if (basePackages.length > 0) {//遍历basePackages, 进行扫描for (String pack : basePackages) {scanPackage(pack);//扫描包保存路径放在集合,便于后面进行反射到容器}}System.out.println("扫描后的= classFullPathList=" + classFullPathList);//测试得到的路径集合是否正确//将扫描到的类, 反射到ico容器executeInstance();System.out.println("扫描后的 ioc容器= " + ioc);}
}
修改my-springmvc\src\main\java\com\myspringmvc\servlet\MyDispatcherServlet.java
@Overridepublic void init(ServletConfig servletConfig) throws ServletException {//获取到web.xml中的 contextConfigLocation/*<init-param><param-name>contextConfigLocation</param-name><param-value>classpath:myspringmvc.xml</param-value></init-param>*/String configLocation =servletConfig.getInitParameter("contextConfigLocation");//创建自己的spring容器myWebApplicationContext =new MyWebApplicationContext(configLocation);myWebApplicationContext.init();//完成容器初始化//调用 initHandlerMapping , 完成url和控制器方法的映射initHandlerMapping();//输出handlerListSystem.out.println("handlerList初始化的结果= " + handlerList);}
完成测试(启动tomcat 方式, 修改后,redeploye 即可生效)。
实现任务阶段4- 完成自定义@Service 注解功能。
功能说明: 如果给某个类加上@Service, 则可以将其注入到我们的Spring 容器
分析示意图
给Service 类标注@Service, 可以将对象注入到Spring 容器中。
可以通过接口名支持多级类名来获取到Service Bean。
如下
//扫描后的ioc
ioc={monsterService=com.service.impl.MonsterServiceImpl@6323a1c1,
orderController=com.controller.OrderController@4c96ec4f,
monsterController=com.controller.MonsterController@81664b3}
代码实现
创建my-springmvc\src\main\java\com\entity\Monster.java
public class Monster {private Integer id;private String name;private String skill;private Integer age;public Monster(Integer id, String name, String skill, Integer age) {this.id = id;this.name = name;this.skill = skill;this.age = age;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getSkill() {return skill;}public void setSkill(String skill) {this.skill = skill;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}@Overridepublic String toString() {return "Monster{" +"id=" + id +", name='" + name + '\'' +", skill='" + skill + '\'' +", age=" + age +'}';}
}
创建my-springmvc\src\main\java\com\myspringmvc\annotation\Service.java
// Service 注解,用于标识一个Service对象,并注入到spring容器
@Target(ElementType.TYPE)//该注解只能声明在一个类前
@Retention(RetentionPolicy.RUNTIME)//不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
@Documented// java 在生成文档,显示注解
public @interface Service {String value() default "";
}
创建my-springmvc\src\main\java\com\service\MonsterService.java
public interface MonsterService{//增加方法-返回monster列表public List<Monster> listMonster();
}
创建my-springmvc\src\main\java\com\service\impl\MonsterServiceImpl.java
@Service
public class MonsterServiceImpl implements MonsterService {@Overridepublic List<Monster> listMonster() {//这里模拟数据->DBList<Monster> monsters = new ArrayList<>();monsters.add(new Monster(100, "牛魔王", "芭蕉扇", 400));monsters.add(new Monster(200, "老猫妖怪", "抓老鼠", 200));return monsters;}
}
修改my-springmvc\src\main\resources\myspringmvc.xml
<?xml version="1.0" encoding="utf-8" ?>
<beans><!--指定要扫描的基本包以及子包的java类--><component-scan base-package="com.controller,com.service"></component-scan>
</beans>
修改my-springmvc\src\main\java\com\myspringmvc\context\MyWebApplicationContext.java
//编写方法,将扫描到的类, 在满足条件的情况下,反射到ioc容器
//实例化扫描到的类->创建对象->放入到IOC 容器[ConcurrentHashMap]public void executeInstance() {//判断是否扫描到类if (classFullPathList.size() == 0) {//说明没有扫描到类return;}try {//遍历classFullPathList,进行反射for (String classFullPath : classFullPathList) {Class<?> clazz = Class.forName(classFullPath);//说明当前这个类有@Controllerif (clazz.isAnnotationPresent(Controller.class)) {//得到类名首字母小写String beanName = clazz.getSimpleName().substring(0, 1).toLowerCase() +clazz.getSimpleName().substring(1);ioc.put(beanName, clazz.newInstance());} //如果有其它的注解,可以扩展 , 来处理@Serviceelse if (clazz.isAnnotationPresent(Service.class)) {//如果类有@Serivce注解//先获取到Service的value值=> 就是注入时的beanNameService serviceAnnotation =clazz.getAnnotation(Service.class);String beanName = serviceAnnotation.value();if ("".equals(beanName)) {//说明没有指定value, 我们就使用默认的机制注入Service//可以通过接口名/类名[首字母小写]来注入ioc容器//1.得到所有接口的名称=>反射Class<?>[] interfaces = clazz.getInterfaces();Object instance = clazz.newInstance();//2. 遍历接口,然后通过多个接口名来注入for (Class<?> anInterface : interfaces) {//接口名->首字母小写String beanName2 = anInterface.getSimpleName().substring(0, 1).toLowerCase() +anInterface.getSimpleName().substring(1);ioc.put(beanName2, instance);}} else {//如果有指定名称,就使用该名称注入即可ioc.put(beanName, clazz.newInstance());}}}} catch (Exception e) {e.printStackTrace();}}
完成测试(启动Tomcat, 自动加载MyDispatcherServlet, 完成IOC 容器的注入)。
扫描后的ioc= {monsterService=com.service.impl.MonsterServiceImpl@6323a1c1,
orderController=com.controller.OrderController@4c96ec4f,
monsterController=com.controller.MonsterController@81664b3}