模仿Spring思想设计和实现IOC和AOP

港控/mmm° 2022-12-08 04:58 166阅读 0赞

一、前言

文章模仿Spring对于IOC和AOP的处理思路,自定义注解实现了对bean的扫描、依赖注入和事务管理。实现思路大体如下:
在这里插入图片描述

二、具体实现

  1. <!--核心依赖文件 -->
  2. <dependency>
  3. <groupId>org.reflections</groupId>
  4. <artifactId>reflections</artifactId>
  5. <version>0.9.11</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>org.apache.commons</groupId>
  9. <artifactId>commons-lang3</artifactId>
  10. <version>3.6</version>
  11. </dependency>

2.1 自定义注解

自定义了三个注解@Service,@Autowired和@Transactional,分别用于对象创建,属性注入和事务管理。

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. public @interface Service {
  4. //要注入的对象名称
  5. String value() default "";
  6. }
  7. @Target(ElementType.TYPE)
  8. @Retention(RetentionPolicy.RUNTIME)
  9. public @interface Transactional {
  10. }
  11. @Target(ElementType.FIELD)
  12. @Retention(RetentionPolicy.RUNTIME)
  13. public @interface Autowired {
  14. }

2.2 定义单例池

创建容器的入口WebApplicationContext,定义ConcurrentHashMap做为单例池,存放扫描过程中创建的对象,同时定义了扫描的类路径ROOT。

  1. public class WebApplicationContext {
  2. private static final String ROOT = "com.keduw";
  3. private static final ConcurrentHashMap<String, Object> beanMap = new ConcurrentHashMap<>(256);
  4. }

2.3 扫描文件

扫描指定路径下的所有文件,获取添加了@Service的所有类信息,通过反射创建指定的对象存到单例池中。

  1. public static void initBeanFactory() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
  2. Reflections reflections = new Reflections(ROOT);
  3. Set<Class<?>> serviceClazz = reflections.getTypesAnnotatedWith(Service.class);
  4. for (Class<?> clazz : serviceClazz) {
  5. // 反射创建指定对象
  6. Object o = clazz.getDeclaredConstructor().newInstance();
  7. // 有指定value则将value作为key
  8. // 没有则将对应的类名首字母转为小写后作为key
  9. Service annotation = clazz.getAnnotation(Service.class);
  10. String key = annotation.value();
  11. if(StringUtils.isEmpty(key)){
  12. String[] split = clazz.getName().split("\\.");
  13. key = CharsetUtils.toLowerCase(split[split.length - 1]);
  14. }
  15. registerBean(key, o);
  16. }
  17. }

2.4 处理单例池中对象的依赖关系

遍历单例池,获取所有的对象和类信息,通过判断类中的属性是否添加了@Autowired注解,存在则获取依赖属性对应的名称作为key,从单例池中获取到指定对象,通过反射调用属性的set方法实现注入。

同时处理@Transactional注解,如果类中存在指定注解,则判断是否实现接口,如果实现接口则调用JDK动态代理创建代理对象,没有则调用Cglib动态代理创建对象。

  1. for (Map.Entry<String, Object> bean : beanMap.entrySet()) {
  2. Object o = bean.getValue();
  3. Class clazz = o.getClass();
  4. // 获取所有属性
  5. Field[] fields = clazz.getDeclaredFields();
  6. for (Field field : fields) {
  7. if (field.isAnnotationPresent(Autowired.class)) {
  8. // 获取依赖属性对应的名称
  9. String[] names = field.getType().getName().split("\\.");
  10. String name = CharsetUtils.toLowerCase(names[names.length - 1]);
  11. // 给指定属性赋值
  12. Field declaredField = clazz.getDeclaredField(name);
  13. declaredField.setAccessible(true);
  14. declaredField.set(o, beanMap.get(name));
  15. }
  16. }
  17. // 处理Transactional注解
  18. if(clazz.isAnnotationPresent(Transactional.class)){
  19. ProxyFactory proxyFactory = (ProxyFactory) getBean("proxyFactory");
  20. //判断对象是否实现接口
  21. Class[] interfaces = clazz.getInterfaces();
  22. o = (interfaces != null && interfaces.length > 0) ? proxyFactory.getJdkProxy(o) : proxyFactory.getCglibProxy(o);
  23. }
  24. registerBean(bean.getKey(), o);
  25. }

2.5 代理实现

代理中处理的事情就相对比较简单,主要是在方法执行前开启事务,在方法执行完后提交事务,如果出现异常则回滚事务。

  1. /** * Jdk动态代理 * @param obj * @return */
  2. public Object getJdkProxy(Object obj) {
  3. return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() {
  4. @Override
  5. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  6. Object result = null;
  7. try{
  8. transactionManager.beginTransaction();
  9. result = method.invoke(obj, args);
  10. transactionManager.commit();
  11. }catch (Exception e) {
  12. e.printStackTrace();
  13. transactionManager.rollback();
  14. throw e;
  15. }
  16. return result;
  17. }
  18. });
  19. }
  20. /** * cglib动态代理 * @param obj * @return */
  21. public Object getCglibProxy(Object obj) {
  22. return Enhancer.create(obj.getClass(), new MethodInterceptor() {
  23. @Override
  24. public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
  25. Object result = null;
  26. try{
  27. transactionManager.beginTransaction();
  28. result = method.invoke(obj, objects);
  29. transactionManager.commit();
  30. }catch (Exception e) {
  31. e.printStackTrace();
  32. transactionManager.rollback();
  33. throw e;
  34. }
  35. return result;
  36. }
  37. });
  38. }

三、总结

到这里基本就实现了对象的扫描、依赖注入和事务控制,整体思路跟Spring的设计是类似的。文章中只显示了核心代码,完整的代码已提交,感兴趣的可以看看。(地址:https://gitee.com/lveex/kd-container.git)

发表评论

表情:
评论列表 (有 0 条评论,166人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Spring IOCAOP

    什么是Spring:   Spring是一个开源的,轻量级的IOC和AOP容器框架,简化了开发流程,方便了对其他框架的整合 控制反转(Inversion Of Contr

    相关 Spring IOCAOP

    一、[IOC实现方式][IOC] (1)接口注入: 接口注入模式因为具备侵入性,它要求组件必须与特定的接口相关联,因此并不被看好,实际使用有限。 (2)Setter

    相关 SpringIoCAOP

    IoC Ioc(Inversion of Control,控制反转)是一种设计思想。 所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东