模拟spring的IOC(控制反转)和DI(依赖注入)

it2025-05-28  14

1.所需jar包: 2.在src目录下件匹配文件: application.yml如下:

spring: datasource: driver: com.mysql.jdbc.Driver username: root password: 123456 url: jdbc:mysql://127.0.0.1:3306/xxoo?allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai

3.创建spring常用的几个注解:Value,Autowried,Component,Service,Controller,Service ;

package com; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @Authro: QYF * @Time:2020/10/21 0:18 */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Value { //配置文件中的属性名,ognl表达式格式 String name() default ""; //name别名 String value() default ""; } package com; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @Authro: QYF * @Time:2020/10/21 0:43 */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Autowired { } package com; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @Authro: QYF * @Time:2020/10/21 0:22 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Component { } package com; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @Authro: QYF * @Time:2020/10/21 0:32 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Controller { } package com; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @Authro: QYF * @Time:2020/10/21 0:32 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Service { }

4.用来读取解析yml配置文件的工具类

package com; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import ognl.Ognl; import ognl.OgnlException; import java.io.File; import java.net.URLDecoder; import java.util.HashMap; import java.util.Map; /** * @Authro: QYF * @Time:2020/10/20 23:34 */ public class Configure { private static Map<String, String> cfg = new HashMap<String, String>(); /** * 反射获取配置文件内容 * @throws Exception */ public static void load() throws Exception { //获取文件路径 String path = Configure.class.getResource("/application.yml").getPath(); //防止路径有中文 path = URLDecoder.decode(path, "UTF-8"); //定义解析yml文件 ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); //解析yml文件,用map接收结果 cfg = mapper.readValue(new File(path), Map.class); // System.out.println(cfg); } /** * 根据表达式快速获取对应的值 * @param expr * @return */ public static String get(String expr) { /** * ognl - Object Graph Navigation Language * 对象导航图 * {a={b={c="111", d="222"}}} * ${a.b.c} ---- "111" * ${a.b.d} ---- "222" */ // ${a.b.c} ---> a.b.c expr = expr.trim(); expr = expr.substring(2, expr.length()-1); String r; try { r = String.valueOf(Ognl.getValue(expr, cfg)); return r; } catch (OgnlException e) { e.printStackTrace(); return null; } } public static void main(String[] args) throws Exception { Configure.load(); System.out.println(cfg); String driver = get("${spring.datasource.driver}"); String username = get("${spring.datasource.username}"); String password = get("${spring.datasource.password}"); String url = get("${spring.datasource.url}"); System.out.println(driver); System.out.println(username); System.out.println(password); System.out.println(url); } }

测试结果输出:

{spring={datasource={driver=com.mysql.jdbc.Driver, username=root, password=123456, url=jdbc:mysql://127.0.0.1:3306/xxoo?allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai}}} com.mysql.jdbc.Driver root 123456 jdbc:mysql://127.0.0.1:3306/xxoo?allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai

5.最主要的SpringContext

package com; import java.io.File; import java.lang.reflect.Field; import java.net.URLDecoder; import java.util.HashMap; import java.util.Map; /** * @Authro: QYF * @Time:2020/10/21 0:41 * Spring 环境对象, 上下文对象 */ public class SpringContext { private static Map<String, Object> ctx = new HashMap<String, Object>(); static { try { Configure.load(); } catch (Exception e) { e.printStackTrace(); } } /** * 自动扫描,在类文件夹下,自动找到所有的类, * 并判断是否存在 @Component,@Service,@Controller 注解, * 如果存在,就自动新建实例并放入map集合 */ public static void autoScan() throws Exception { String path = SpringContext.class.getResource("/").getPath(); path = URLDecoder.decode(path, "UTF-8"); File dir = new File(path); /* * 需要一个StringBuilder对象,拼接包名类名 * "day16" --- 遇到目录拼包名 * "day16.UserDao" --- 这里要处理这个类 * "day16" --- 处理完一个类,要把类名截掉 * "" --- 处理完包,要把包截掉 */ //在类目录中扫描 scan(dir, new StringBuilder()); //完成所有实例的自动装配 autowire(); } /** * 自动注入 * @throws Exception */ private static void autowire() throws Exception { /* * ctx中所有的实例,一个一个的进行装配 * * 在实例中寻找添加了@Value和@Autowired注解的变量, * 自动为变量注入数据 */ for (Object obj : ctx.values()) {//对map中的value这一列遍历 //获得"类对象" Class<? extends Object> c = obj.getClass(); //获取所有成员变量 Field[] a = c.getDeclaredFields(); //遍历所有的成员变量 for (Field f : a) { if (f.isAnnotationPresent(Value.class)) { //注入配置信息 injectValue(c, obj, f); } if (f.isAnnotationPresent(Autowired.class)) { //注入对象 injectObject(c, obj, f); } } } } /** * 注入Value * @param c * @param obj * @param f * @throws Exception */ private static void injectValue(Class<? extends Object> c, Object obj, Field f) throws Exception { //从变量获得Value注解,并获得ognl表达式 Value v = f.getAnnotation(Value.class); String name = v.name(); if (name == null || name.equals("")) { name = v.value(); } //用ognl获取配置数据 String r = Configure.get(name); f.setAccessible(true); //把配置数据,存到变量 f.set(obj, r); } /** * 注入对象 * @param c * @param obj * @param f * @throws Exception */ private static void injectObject(Class<? extends Object> c, Object obj, Field f) throws Exception { /* * @Autowired * private UserDao userDao * * 获得 UserDao 的完整类名,用类名从ctx提取它的实例 * 取出实例后,保存到这个变量 */ String name = f.getType().getName(); Object r = ctx.get(name); f.setAccessible(true); f.set(obj, r); } /** * 递归获取所以类的全路径 * * @param dir * @param sb * @throws Exception */ private static void scan(File dir, StringBuilder sb) throws Exception { File[] files = dir.listFiles(); //不能获得列表,结束 if (files == null) { return; } //遍历目录列表 for (File f : files) { String name = f.getName(); //f是文件 if (f.isFile()) { //不是class文件,跳过 if (!name.endsWith(".class")) { continue; } // Abc.class // 去掉.class,把".Abc"拼到末尾,例如"day16.Abc" sb.append(".").append(name.substring(0, name.length() - 6)); // System.out.println(sb); //处理这个类 handle(sb.toString()); //f是文件夹 } else { //第一层包名不加点,后面每层包名都先加一个点 if (sb.length() != 0) { sb.append("."); } //拼包名 sb.append(name); //递归处理f文件夹 scan(f, sb); } //处理完后,把末尾一段删除,例如"aa.bb.cc.Abc",把".Abc"删掉 //找最后一个点字符 int start = sb.lastIndexOf("."); //没有点的情况 if (start == -1) { //例如"aa",直接全部删掉 sb.delete(0, sb.length()); } else { //有点,从点位置删 sb.delete(start, sb.length()); } } } /** * 根据全路径名,创建实例 * * @param className * @throws Exception */ private static void handle(String className) throws Exception { //得到"类对象" Class<?> c = Class.forName(className); //是否存在 @Component,@Service,@Controller注解 if (c.isAnnotationPresent(Component.class) || c.isAnnotationPresent(Service.class) || c.isAnnotationPresent(Controller.class)) { //创建实例 Object obj = c.newInstance(); //放入map集合,完整类名--->实例 ctx.put(className, obj); } } /** * 获取实例 * 从"类对象"先获取完整类名,再用类名从map获取实例 * * @param c * @param <V> * @return */ public static <V> V getObject(Class<V> c) { // "day16.UserDao" -----> UserDao实例 return (V) ctx.get(c.getName()); } public static void main(String[] args) throws Exception { autoScan(); // System.out.println(ctx); UserDao dao = getObject(UserDao.class); // System.out.println(dao); dao.test(); } }

6.dao对象

package com; /** *@Authro: QYF *@Time:2020/10/21 0:21 */ @Component public class UserDao { //通过注解,为driver变量自动注入数据 @Value("${spring.datasource.driver}") private String driver; @Value("${spring.datasource.username}") private String username; @Value("${spring.datasource.password}") private String password; @Value("${spring.datasource.url}") private String url; public void test() { System.out.println("\n---UserDao注入的数据:--------------"); System.out.println(driver); System.out.println(username); System.out.println(password); System.out.println(url); } }

7.模拟Controller调Service再调Dao

package com; /** * @Authro: QYF * @Time:2020/10/21 0:36 */ @Controller public class UserController { @Autowired private UserService userService; public void test() { System.out.println("\n---UserController调用service----------------"); userService.test(); } } package com; /** * @Authro: QYF * @Time:2020/10/21 0:36 */ @Service public class UserService { //自动装配,自动注入UserDao的实例 @Autowired private UserDao userDao; public void test() { System.out.println("\n---UserService调用dao-------------"); userDao.test(); } }

最后测试下:

package com; /** * @Authro: QYF * @Time:2020/10/21 1:42 */ public class _Test2 { public static void main(String[] args) throws Exception { SpringContext.autoScan(); UserController c = SpringContext.getObject(UserController.class); c.test(); } }

输出结果:

---UserController调用service---------------- ---UserService调用dao------------- ---UserDao注入的数据:-------------- com.mysql.jdbc.Driver root 123456 jdbc:mysql://127.0.0.1:3306/xxoo?allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai

这样就可以通过@Value注解把配置文件的信息注入给对象属性. 需要对象也直接从SpringContext获取和通过@Autowried注入

最新回复(0)