我们平日开发时所用的SSM框架,可是你真的了解它吗?技术革新,换代应接不暇,只有理解了源码才能保证在技术快速更迭的时代中真正站稳脚跟。
本系列文章抽丝剥茧,源码分析百度有很多,在这里只列举几个我收录的优秀的博客推荐给大家,本文不过多的分析源码,更注重将代码理解后的实现。
话不多说,开启正题。(由于本人是小白,理解的不深所以写出来的东西比较浅显易懂。如果有错误还望各位大佬指出来,帮助我提高)
项目介绍:此项目是在手写SSM框架的基础上开发了一个简单的登录功能。
首先是spring框架的实现,spring的依赖注入特性是集成其他所有框架的基础。在spring2.5版本之前,只支持配置文件注入。在2.5之后加入了@Autowired注解,实现了注解注入。我们的这个spring框架当然是都支持啦。首先是xml版本注入。目前支持的是构造器注入和set属性注入。
实现思路:
- 用dom4j解析xml文件。获取各个节点的属性和内容。
- 用枚举定义IOC的bean的规则,用BeanFacory的getBean方法读取配置信息,如果xml读取到的属性和IOCRULES的枚举内容匹配的话,用BeanDefinitionMap对象保存,然后用反射实例化一个对象。
- 读取在xml文件中读取到的扫包路径,扫描此路径下的有注解的类按照自下而上的顺序存储在componentList中。
- 将componentList对象按照顺序实例化出对象。
一些高大上的名词,其实就是一些对象或者数据结构。比如我们常说的spring容器其实就指的是一个map对象集合,在spring源码其实就是DefaultListableBeanFactory类中如下对象
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(256);
将这些对象实例化之后就是我们说的注入。再比如springmvc中的handlerMapping,其实就是map集合,key是方法名,value是@RequestMapping的路径。再比如...还有很多,在实战之前我先提出几个我自己想到的问题,希望大家看的时候带着问题提来看:
- spring有xml配置,也有注解配置,那么先实例化的是哪种版本呢?
- 构造器注入,属性注入如何实现的?
- 在指定包的路径下有那么多要注入的bean,service层调用dao,所以dao层一定要在service之前注入,然后在service层属性注入的时候才能将dao层注入,那么如何保证实例化的顺序呢?这也是难点。
- 我们使用的时候只需要用@Autowired标记接口,即可使用。但是我们知道接口并不能实例化对象,那么spring是如何做到的呢?而且有的接口有实现类,在mybatis中连实现类都没有,如何实现的呢?
效果图
下面开始实战:
- 项目必须用jdk1.8,不然会报错.里面一些方法只有1.8才有。
- 用到了lombok这个jar包,主要是简化了代码量,一个注解就可以省略getset方法。
- 解析xml用的dom4j,在小型xml文件的解析和操作中dom4j是最佳选择
- 在最后的demo中,RegisterServiceImpl,UserServiceImpl,RegisterService,UserService等文件没有真正的使用。只是考虑多层注入的时候,为了测试注入的顺序是否是对的。在dao层真正用的是UserMapper这个接口。
先介绍一下工具类吧。省的代码中出现的时候大家看不懂
package spring.Utils; /** * Created by Xiao Liang on 2018/6/27. * 注解工具类:判断注解是否为空 */ public class AnnotationUtils { public static <T> boolean isEmpty(T t){ return t == null ? true : false; } }
package spring.Utils; /** * @ClassName ConvertUtis * @Description 根据传入的属性和类名,将属性名强转为类名的属性 * @Data 2018/7/4 * @Author xiao liang */ public class ConvertUtis { public static Object convert(String className,String parameter){ if (className.equals("String")){ return parameter; } else if (className.equals("Integer")){ return Integer.valueOf(parameter); } else if (className.equals("int")){ return Integer.valueOf(parameter); } else if (className.equals("Float")){ return Float.valueOf(parameter); } else if (className.equals("Double")){ return Integer.valueOf(parameter); } else if (className.equals("Long")){ return Long.valueOf(parameter); } else if (className.equals("Short")){ return Short.valueOf(parameter); } else if (className.equals("Byte")){ return Byte.valueOf(parameter); } else if (className.equals("Boolean")){ return Boolean.valueOf(parameter); } return null; } }
package spring.Utils; /** * @ClassName GetMethodName * @Description 根据属性名拼接set方法 * @Data 2018/7/4 * @Author xiao liang */ public class GetMethodName { public static String getSetMethodNameByField(String propertyName) { String methodName = "set" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); return methodName; } }
package spring.Utils; /** * @ClassName isBasicType * @Description 判断是不是基本数据类型 * @Data 2018/7/4 * @Author xiao liang */ public class isBasicTypeUtils { public static boolean isBasicType(String typeName){ if (typeName.equals("String")){ return true; } else if(typeName.equals("Integer")){ return true; } else if(typeName.equals("int")){ return true; } else if(typeName.equals("Long")){ return true; } else if(typeName.equals("Short")){ return true; } else if(typeName.equals("Float")){ return true; } else if(typeName.equals("Double")){ return true; } else if(typeName.equals("Byte")){ return true; } return false; } }
package spring.Utils; import java.util.HashSet; import java.util.List; import java.util.Set; /** * @Author xiao liang * @Desprition 在链表中添加数据,添加时保证只有一个相同的实例 */ public class ListAddUtils { public static <T> void add(List<T> list ,T t) { Set<T> set1 = new HashSet<>(list); if (set1.add(t)){ list.add(t); } } }
package spring.Utils; /** * @Author xiao liang * 判断字符串是否为空 */ public class StringUtils { public static boolean isEmpty(String string) { if ((string == null) || "".equals(string)) { return true; } return false; } }
还有一个常量类。
package spring.constants; import spring.xml.FileSystemXmlApplicationContext; /** * Created by Xiao Liang on 2018/6/27. * @Description :保存的是各个配置文件的路径 */ public interface Constants { String PATH = FileSystemXmlApplicationContext.class.getResource("/").getPath(); String contextConfigLocation = "application.xml"; String springmvcConfigLocation = "spring-mvc.xml"; String mybatisConfigLocation = "MyUserMapper.xml"; }
还有三个xml文件:
application.xml
<?xml version="1.0" encoding="UTF-8"?> <beans> <bean id="hostess" class="spring.Person"> <property name ="userName" value = "admin"></property> <property name ="passWord" value = "admin"></property> </bean> <bean class="spring.mybatis.JDBCUtils" id="dataSource"> <property name="user" value="root"></property> <property name="password" value="qinliang"></property> <property name="driver" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/test"></property> </bean> <component-scan base-package="spring.demo" > </component-scan> </beans>
springmvc.xml
<?xml version="1.0" encoding="UTF-8"?> <beans> <bean id="viewResolver" class="spring.springmvc.ViewResolver"> <property name="prefix" value="/WEB-INF/views/"></property> <property name="suffix" value=".jsp"></property> </bean> </beans>
MyUserMapper.xml
<?xml version="1.0" encoding="UTF-8"?> <mapper namespace="spring.demo.repository.UserMapper"> <select id="queryUser" resultType="spring.dataObject.User"> select * from login_user where username = #{username} AND password = #{password} </select> </mapper>
这是一个maven项目,pom.xml文件内容如下
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.myspring</groupId> <artifactId>start</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <name>start Maven Webapp</name> <!-- FIXME change it to the project's website --> <url>http://www.example.com</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.0</version> <scope>provided</scope> </dependency> <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.25</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.7.25</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.1.3-b06</version> </dependency> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> <!-- https://mvnrepository.com/artifact/jstl/jstl --> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!--数据库连接池和数据库连接--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.8</version> </dependency> </dependencies> <build> <finalName>start</finalName> <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> <plugins> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.0.0</version> </plugin> <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging --> <plugin> <artifactId>maven-resources-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.7.0</version> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.20.1</version> </plugin> <plugin> <artifactId>maven-war-plugin</artifactId> <version>3.2.0</version> </plugin> <plugin> <artifactId>maven-install-plugin</artifactId> <version>2.5.2</version> </plugin> <plugin> <artifactId>maven-deploy-plugin</artifactId> <version>2.8.2</version> </plugin> </plugins> </pluginManagement> </build> </project>
下面是项目的业务逻辑代码,可以过一下,就是最简单的三层架构。业务逻辑代码中中有一些为了测试用,并没有实际的意义
package spring.demo.controller; import spring.annotation.MyAutowired; import spring.annotation.MyController; import spring.annotation.MyModelAttribute; import spring.annotation.MyRequestMapping; import spring.dataObject.User; import spring.demo.service.UserService; import spring.springmvc.MyModelAndView; import spring.springmvc.MyModelMap; import spring.xmlRules.RequestMethod; /** * Created by Xiao Liang on 2018/6/27. */ @MyController public class LoginController { @MyAutowired private UserService userService; //测试用的@MyRequstParam(value = "userName") String userName, @MyRequstParam(value = "passWord") Integer passWord //返回值只支持MyModelAndView,数据模型和视图模型相结合 @MyRequestMapping(value = "/hello", method = RequestMethod.POST) public MyModelAndView login(@MyModelAttribute("User") User user) { MyModelAndView myModelAndView = new MyModelAndView("success"); MyModelMap myModel = new MyModelMap(); User user1 = userService.queryUser("admin", "admin"); myModel.addAttribute("test", user1.getUserName()); myModelAndView.setModelMap(myModel); return myModelAndView; } @MyRequestMapping("/hello22") public String test() { return "success"; } }
package spring.demo.controller; import spring.annotation.MyAutowired; import spring.annotation.MyController; import spring.annotation.MyRequestMapping; import spring.demo.service.RegisterService; import spring.demo.service.UserService; /** * Created by Xiao Liang on 2018/6/27. */ @MyController public class RegisterController { @MyAutowired private UserService userService; @MyAutowired private RegisterService registerService; @MyRequestMapping("/register") public void regeister(){ userService.queryUser("",""); registerService.register(); } }
package spring.demo.service; import spring.dataObject.User; /** * Created by Xiao Liang on 2018/6/27. */ public interface UserService { User queryUser(String userName, String passWord); }
package spring.demo.service; /** * Created by Xiao Liang on 2018/6/27. */ public interface RegisterService { void register(); }
package spring.demo.service.impl; import spring.annotation.MyAutowired; import spring.annotation.MyService; import spring.dataObject.User; import spring.demo.repository.RegisterDao; import spring.demo.repository.UserDao; import spring.demo.repository.UserMapper; import spring.demo.service.UserService; /** * Created by Xiao Liang on 2018/6/27. */ @MyService public class UserServiceImpl implements UserService { @MyAutowired private UserDao userDao; @MyAutowired private RegisterDao registerDao; @MyAutowired private UserMapper userMapper; @Override public User queryUser(String userName, String passWord) { return userMapper.queryUser(userName,passWord); } }
package spring.demo.service.impl; import spring.annotation.MyAutowired; import spring.annotation.MyService; import spring.demo.repository.RegisterDao; import spring.demo.service.RegisterService; /** * Created by Xiao Liang on 2018/6/27. */ @MyService public class RegisterServiceImpl implements RegisterService{ @MyAutowired private RegisterDao registerDao; @Override public void register() { registerDao.register(); } }
package spring.demo.repository; /** * Created by Xiao Liang on 2018/6/27. */ public interface UserDao { void test(); }
package spring.demo.repository; /** * Created by Xiao Liang on 2018/6/27. */ public interface RegisterDao { void register(); }
package spring.demo.repository.impl; import spring.annotation.MyRepository; import spring.demo.repository.UserDao; /** * Created by Xiao Liang on 2018/6/27. */ @MyRepository public class UserDaoImpl implements UserDao { @Override public void test() { System.out.println("我是UserDao"); } }
package spring.demo.repository.impl; import spring.annotation.MyRepository; import spring.demo.repository.RegisterDao; /** * Created by Xiao Liang on 2018/6/27. */ @MyRepository public class RegisterDaoImpl implements RegisterDao{ @Override public void register() { System.out.println("我是RegisterDao"); } }
package spring.demo.repository; import spring.dataObject.User; /** * @ClassName UserMapper * @Description * @Data 2018/7/7 * @Author xiao liang */ public interface UserMapper { User queryUser(String userName,String passWord); }
我将此项目上传到了github,需要的童鞋可以点击下方阅读原文自行下载。
好文推荐栏