自定义注解模仿Spring思想设计和实现IOC和AOP

一、前言

文章模仿Spring对于IOC和AOP的处理思路,自定义注解实现了对bean的扫描、依赖注入和事务管理。实现思路大体如下:
自定义注解模仿Spring思想设计和实现IOC和AOP_第1张图片

二、具体实现


<dependency>
  <groupId>org.reflectionsgroupId>
  <artifactId>reflectionsartifactId>
  <version>0.9.11version>
dependency>
<dependency>
  <groupId>org.apache.commonsgroupId>
  <artifactId>commons-lang3artifactId>
  <version>3.6version>
dependency>

2.1 自定义注解

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

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {
     
    //要注入的对象名称
    String value() default "";
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Transactional {
     
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
     

}

2.2 定义单例池

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

public class WebApplicationContext {
     

    private static final String ROOT = "com.keduw";
    private static final ConcurrentHashMap<String, Object> beanMap = new ConcurrentHashMap<>(256);
    
}

2.3 扫描文件

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

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

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

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

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

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

2.5 代理实现

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

/**
 * Jdk动态代理
 * @param obj
 * @return
 */
public Object getJdkProxy(Object obj) {
     
    return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() {
     
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
     
            Object result = null;
            try{
     
                transactionManager.beginTransaction();
                result = method.invoke(obj, args);
                transactionManager.commit();
            }catch (Exception e) {
     
                e.printStackTrace();
                transactionManager.rollback();
                throw e;
            }
            return result;
        }
    });
}

/**
 * cglib动态代理
 * @param obj
 * @return
 */
public Object getCglibProxy(Object obj) {
     
    return  Enhancer.create(obj.getClass(), new MethodInterceptor() {
     
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
     
            Object result = null;
            try{
     
                transactionManager.beginTransaction();
                result = method.invoke(obj, objects);
                transactionManager.commit();
            }catch (Exception e) {
     
                e.printStackTrace();
                transactionManager.rollback();
                throw e;
            }
            return result;
        }
    });
}

三、总结

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

你可能感兴趣的:(SpringBoot,spring,ioc,aop)