根据 【动力节点】最新Spring框架教程,全网首套Spring6教程,跟老杜从零学spring入门到高级 以及老杜的原版笔记 https://www.yuque.com/docs/share/866abad4-7106-45e7-afcd-245a733b073f?# 《Spring6》 进行整理, 文档密码:mg9b
Spring 相关文章整理汇总归纳于:https://www.yuque.com/u27599042/zuisie
回顾反射机制
调用方法的四要素
- 调用方法的四要素
- 第一要素:使用哪个对象进行方法的调用
- 第二要素:调用对象的哪个方法
- 第三要素:调用方法的时候,需要传递什么参数
- 第四要素:方法执行结束之后的返回结果
- 总结:调用哪个对象的哪个方法,需要传递什么参数,该方法返回什么值
- 即使是使用反射机制进行方法的调用,也同样需要具备着四要素
反射机制调用方法
/** * ClassName: SomeService * Package: PACKAGE_NAME * Description: * * @Author tcw * @Create 2023-05-27 10:46 * @Version 1.0 */ public class SomeService { public void doSome() { System.out.println(" public void doSome() 执行"); } public String doSome(String s) { System.out.println(" public void doSome(String s) 执行"); return s; } public String doSome(String s, Integer i) { System.out.println(" public void doSome(String s, Integer i) 执行"); return s + " " + i; } }
import java.lang.reflect.Method; /** * ClassName: Main * Package: PACKAGE_NAME * Description: * * @Author tcw * @Create 2023-05-27 10:45 * @Version 1.0 */ public class Main { public static void main(String[] args) throws Exception { // 加载类,获取类对应的Class对象 Class<?> someServiceClass = Class.forName("SomeService"); // 创建对象 // 调用类的无参构造方法创建对象 SomeService someService = (SomeService) someServiceClass.getDeclaredConstructor().newInstance(); // 获取方法 public String doSome(String s, Integer i) // 参数一:获取方法的方法名 // 参数二:可变参数,获取方法的形参列表类型 Method doSomeMethod = someServiceClass.getDeclaredMethod("doSome", String.class, Integer.class); // 调用方法(四要素:调用哪个对象的哪个方法,需要传递什么参数,该方法返回什么值) // 参数一:调用哪个对象的该方法 someService // 参数二:可变参数,调用方法需要传递的形参 "Hello World", 111 // invoke 的返回值就是调用方法的返回值 Object res Object res = doSomeMethod.invoke(someService, "Hello World", 111); System.out.println(res); } }
Spring DI 核心实现
- 需求:假设你现在已知以下信息:
- 1.有这样一个类,类名叫做:包名.User
- 2.这个类符合javabean规范。属性私有化,对外提供公开的setter和getter方法。
- 3.你还知道这个类当中有一个属性,属性的名字叫做 age
- 4.并且你还知道age属性的类型是int类型。
- 请使用反射机制调用set方法,给User对象的age属性赋值。
package cw.study.spring.pojo; /** * ClassName: User * Package: cw.study.spring.pojo * Description: * * @Author tcw * @Create 2023-05-28 9:59 * @Version 1.0 */ public class User { private String name; private int age; public User() { } public User(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
import cw.study.spring.pojo.User; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * ClassName: Main * Package: PACKAGE_NAME * Description: * * @Author tcw * @Create 2023-05-27 10:45 * @Version 1.0 */ public class Main { public static void main(String[] args) throws Exception { // 1.有这样一个类,类名叫做:包名.User String className = "cw.study.spring.pojo.User"; // 2.这个类符合javabean规范。属性私有化,对外提供公开的setter和getter方法。 // 3.你还知道这个类当中有一个属性,属性的名字叫做 age String fieldName = "age"; // 4.并且你还知道age属性的类型是int类型。 // 请使用反射机制调用set方法,给User对象的age属性赋值。 // 加载类 Class<?> userClass = Class.forName(className); // 创建对象 User user = (User) userClass.getDeclaredConstructor().newInstance(); // 获取属性 Field field = userClass.getDeclaredField(fieldName); // 获取属性的类型 Class<?> fieldType = field.getType(); // 获取方法名 String methodName = "set" + fieldName.toUpperCase().charAt(0) + fieldName.substring(1); // 获取方法 Method method = userClass.getDeclaredMethod(methodName, fieldType); // 调用方法 method.invoke(user, 22); System.out.println(user); } }
手写 Spring 框架 IoC
- Spring IoC 的实现原理:工厂模式 + 解析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>cw.study.spring</groupId> <artifactId>spring-study-011-myspring</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <dependencies> <!-- dom4j 解析XML文件 --> <dependency> <groupId>org.dom4j</groupId> <artifactId>dom4j</artifactId> <version>2.1.3</version> </dependency> <!-- jaxen 需要使用xpath --> <dependency> <groupId>jaxen</groupId> <artifactId>jaxen</artifactId> <version>1.2.0</version> </dependency> <!-- 单元测试 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> </dependencies> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> </properties> </project>
JavaBean
- 这些JavaBean是使用框架的用户写的
package cw.study.spring.bean; /** * ClassName: User * Package: cw.study.spring.bean * Description: * * @Author tcw * @Create 2023-05-31 12:06 * @Version 1.0 */ public class User { private String name; private Integer age; public User() { } public void setName(String name) { this.name = name; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
package cw.study.spring.bean; /** * ClassName: UserDao * Package: cw.study.spring.bean * Description: * * @Author tcw * @Create 2023-05-31 12:07 * @Version 1.0 */ public class UserDao { public void insert() { System.out.println("数据库正在保存用户信息..."); } }
package cw.study.spring.bean; /** * ClassName: UserService * Package: cw.study.spring.bean * Description: * * @Author tcw * @Create 2023-05-31 14:08 * @Version 1.0 */ public class UserService { private UserDao userDao; public void save() { userDao.insert(); } public void setUserDao(UserDao userDao) { this.userDao = userDao; } }
配置文件
- 这个配置文件是使用框架的用户写的
<?xml version="1.0" encoding="UTF-8" ?> <beans> <bean id="user" class="cw.study.spring.bean.User"> <property name="name" value="张三"/> <property name="age" value="21"/> </bean> <bean id="userDao" class="cw.study.spring.bean.UserDao"/> <bean id="userService" class="cw.study.spring.bean.UserService"> <property name="userDao" ref="userDao"/> </bean> </beans>
Spring 框架核心接口实现 ApplicationContext
package org.myspringframework.core; /** * ClassName: ApplicationContext * Package: org.myspringframework.core * Description: * MySpring框架应用上下文接口 * * @Author tcw * @Create 2023-05-31 14:22 * @Version 1.0 */ public interface ApplicationContext { /** * 通过Bean的id从容器中获取相应的Bean对象 * Bean的名称就是配置文件中Bean的id * * @param beanName Bean的id * @return Bean对象 */ Object getBean(String beanName); }
Spring 框架核心接口实现类 ClassPathXmlApplicationContext
package org.myspringframework.core; import org.dom4j.*; import org.dom4j.io.SAXReader; import java.io.InputStream; import java.lang.reflect.Method; import java.util.HashMap; import java.util.List; import java.util.Map; /** * ClassName: ClassPathXmlApplicationContext * Package: org.myspringframework.core * Description: * * @Author tcw * @Create 2023-05-31 14:25 * @Version 1.0 */ public class ClassPathXmlApplicationContext implements ApplicationContext{ // 用于存放Bean对象的map集合,使用Bean的名称可以获取相应的Bean对象 private Map<String, Object> singletonObjects = new HashMap<>(); /** * 根据Spring配置文件创建Spring容器的构造方法 * 该方法一执行就会对配置文件进行解析创建Bean对象 * 使用ClassPathXmlApplicationContext,配置文件应该放在类路径下 * * @param configLocation Spring配置文件的路径 */ public ClassPathXmlApplicationContext(String configLocation) { try { // 解析XML核心配置文件,实例化Bean,将Bean存放到map集合中 // 获取dom4j解析xml文件的核心对象,读取xml文件中的内容 SAXReader reader = new SAXReader(); // 获取xml的输入流,通过类加载器以流的方式获取资源 InputStream inputStream = ClassLoader.getSystemClassLoader().getResourceAsStream(configLocation); // 读取xml文件中的内容,获取xml对应的文档对象 Document document = reader.read(inputStream); // 获取xml文件中所有的bean标签 List<Node> beansNode = document.selectNodes("//bean"); // 遍历所有的bean标签,创建相应的对象放到容器中 beansNode.forEach(bean->{ try { // System.out.println(bean); // org.dom4j.tree.DefaultElement@74ad1f1f // bean实际上是Element类型,向下转型,因为Element中方法更丰富 Element beanElement = (Element) bean; // 获取Bean id String id = beanElement.attributeValue("id"); // 获取bean的类型 String className = beanElement.attributeValue("class"); // System.out.println(id + ": " + className); // 利用反射机制进行对象的创建,并放到map集合中,进行提前曝光 Object beanInstance = createBeanObjectByReflect(className); // 将创建的对象放入map集合中进行曝光 singletonObjects.put(id, beanInstance); // System.out.println(singletonObjects); } catch (Exception e) { e.printStackTrace(); } }); // 再次遍历所有的bean标签,为创建的所有bean对象进行属性的赋值 beansNode.forEach(bean->{ try { // 进行强转 Element beanElement= (Element) bean; // 获取bean的id String id = beanElement.attributeValue("id"); // // 判断bean是否已经被创建了 // if (!singletonObjects.containsKey(id)) { // // 如果没有创建bean对象,则进行bean对象的创建 // Object beanInstance = createBeanObjectByReflect(className); // } // 获取bean的类名 String className = beanElement.attributeValue("class"); // 加载类 Class<?> clazz = Class.forName(className); // 获取bean标签中的所有子标签属性标签 List<Element> properties = beanElement.elements("property"); // 遍历bean标签中的所有子标签属性标签,为bean的属性进行赋值 properties.forEach(property->{ try { // 获取property标签的name属性值,获取要为哪个属性赋值 String fieldName = property.attributeValue("name"); // 获取property标签的value属性值,获取要为属性赋的值(标签中如果无value,返回null) String fieldValue = property.attributeValue("value"); // 获取property标签的ref属性值,获取要为属性赋的值(标签中如果无ref,返回null) String ref = property.attributeValue("ref"); // System.out.println(fieldName + " = " + fieldValue); // 获取set方法的方法名 String setMethodName = "set" + fieldName.toUpperCase().charAt(0) + fieldName.substring(1); // 获取set方法对应的属性的类型 Class<?> fieldType = clazz.getDeclaredField(fieldName).getType(); // 获取set方法 Method setMethod = clazz.getDeclaredMethod(setMethodName, fieldType); // 如果属性的值是通过value进行赋值 if (fieldValue != null) { // 值为简单类型 // 调用id对应的bean对象的set方法(set方法无返回值) // 从标签的属性中获取的值都为字符串类型,但是set方法需要的参数有具体的数据类型,需要进行处理 // 获取set方法参数的简单类型名 String fieldTypeSimpleName = fieldType.getSimpleName(); String propertyValue = fieldValue; // 简单类型的属性值 // 进行数据类型匹配,对参数进行类型转换 Object propertyVal = null; switch (fieldTypeSimpleName) { case "byte": case "Byte": propertyVal = Byte.valueOf(propertyValue); break; case "short": case "Short": propertyVal = Short.valueOf(propertyValue); break; case "int": case "Integer": propertyVal = Integer.valueOf(propertyValue); break; case "long": case "Long": propertyVal = Long.valueOf(propertyValue); break; case "float": case "Float": propertyVal = Float.valueOf(propertyValue); break; case "double": case "Double": propertyVal = Double.valueOf(propertyValue); break; case "boolean": case "Boolean": propertyVal = Boolean.valueOf(propertyValue); break; case "char": case "Character": propertyVal = propertyValue.charAt(0); break; case "String": propertyVal = propertyValue; break; } // 调用id对应的bean对象的set方法(set方法无返回值) setMethod.invoke(singletonObjects.get(id), propertyVal); } // 如果属性的值是通过ref进行赋值 if (ref != null) { // 值为非简单类型 // 调用id对应的bean对象的set方法(set方法无返回值) setMethod.invoke(singletonObjects.get(id), singletonObjects.get(ref)); } } catch (Exception e) { e.printStackTrace(); } }); } catch (Exception e) { e.printStackTrace(); } }); } catch (Exception e) { e.printStackTrace(); } } /** * 通过反射机制进行相应类的对象的创建 * * @param className 类的全限定类名 * @return 类的对象 * @throws Exception 异常 */ private Object createBeanObjectByReflect(String className) throws Exception { // 加载类 Class<?> clazz = Class.forName(className); // 获取类的无参构造器创建对象 return clazz.getDeclaredConstructor().newInstance(); } /** * 根据bean的id获取bean对象 * * @param beanName Bean的id * @return bean对象 */ @Override public Object getBean(String beanName) { return singletonObjects.get(beanName); } }
MySpring 框架测试
@org.junit.Test public void test01() { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("myspring.xml"); UserService userService = (UserService) applicationContext.getBean("userService"); userService.save(); User user = (User) applicationContext.getBean("user"); System.out.println(user); }