注解方式自定义实现Spring Ioc容器 + 事务 + 动态代理

@TOC

前言

上一篇点击查看使用xml来实现自定义IOC以及依赖注入关系维护,动态代理,以及事务操作;

这次使用注解来实现IOC以及依赖关系维护

步骤以及思路分析

基于xml实现方式时,仅仅只需要在xml里面配置好bean的id以及bean的权限定命名,然后反射实例化对象,最后加入到ioc容器中
依赖注入时候仅仅需要获取property标签以及父级标签,根据property名从ioc容器中获取到需要注入的bean示例即可;

如果是基于注解实现呢!

  • 1 首先需要自定义注解
  • 2 如何获取自定义到的注解
  • 3 实例化打了注解的实例
  • 4 在指定bean成员变量上是否包含需要注入的注解,然后依赖注入
  • 5 生成代理对象,基于接口判断是否选择JDK动态代理或者CGLIB代理

代码实现

首先自定义注解

实例bean的注解 @Repository@Service

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Service {
    String value() default "";
}

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Repository {
    String value() default "";
}

自动装配的注解

@Target({ ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
    String name() default "";
}

事务注解 Transactional

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Transactional {
}

然后 在类上标注注解,以及依赖注入
注解方式自定义实现Spring Ioc容器 + 事务 + 动态代理_第1张图片

项目结构

注解方式自定义实现Spring Ioc容器 + 事务 + 动态代理_第2张图片

配置信息

版本 JDK8 , tomcat 7 , IDEA 2019 03

所需依赖

    
    
      javax.servlet
      javax.servlet-api
      3.1.0
      provided
    
    
    
    
      cglib
      cglib
      2.1_2
    

tomcat插件

   
        org.apache.tomcat.maven
        tomcat7-maven-plugin
        2.2
        
          8080
          /
        
      

这里我们使用一个StartApplication类来表示当前的顶级包下的启动类,相当于SpringBoot里的Main方法所在的类(目的仅仅是指定包,也可以在xml里面配置包名)

在Web.xml里面进行配置一下这个启动类



    tomcat启动时候启动IOC容器
    
        
        
        com.udeam.edu.factory.impl.AnnotationBeanFactory
        
        
    

定义BeanFactory接口类

/**
 * 底层BeanFactory工厂接口
 * @author Pilgrim
 */
public interface BeanFactory {

    /**
     * 存储bean单例
     */
    public final static Map IOC_MAP = new HashMap<>();


    /**
     * 对外提供获取bean接口
     *
     * @param id
     * @return bean对象
     */
    public Object getBean(String id);


    /**
     * 根据类型对外提供获取bean示例
     *
     * @param classz
     * @return bean
     */
    public Object getBean(Class classz);

    /**
     * 获取容器中所有的bean名字
     *
     * @return
     */
    public Object getAllBeanName();
}

抽象类AbstractBeanFactory扩展一些属性

public abstract class AbstractBeanFactory implements BeanFactory {

    /**
     * 存储bean单例
     */
    public final static Map IOC_MAP = new HashMap<>();

    /**
     * 容器执行一次 标识
     */
    public static boolean isTag = false;

    public static final String CLASS_STR = ".class";

}

然后编写bean工厂实现类AnnotationBeanFactory

定义初始化方法
initBeanFactory(String packageName);

初始化方法包含以下方法

文件扫描路径
    /**
     * 递归处理路径下文件夹是否包含文件夹,如不包含则获取当前类的权限定命名存入set中
     *
     * @param packName
     * @param classNameSet
     * @param path
     */
    public static void parseFilePackName(String packName, Set classNameSet, String path) 
bean实例化方法
 private void setBean();
bean依赖注入方法
 beanAutoWired()
事务处理注解方法
doScanTransactional()
/**
 * 注解方式 实现 Bean工厂
 *
 * @author Pilgrim
 */
public class AnnotationBeanFactory extends AbstractBeanFactory {

    /**
     * 2  注解 + 扫描包 方式实现 ioc 容器
     * tomcat启动的时候去初始化容器
     */
    public AnnotationBeanFactory() {
            if (isTag) {
            return;
        }
        try {
            String packageName = StartApplication.class.getPackage().getName();
            //扫描启动类的包名
            System.out.println("------------------- [容器]正在初始化 ------------ ");
            System.out.println(String.format("------------------- 扫描当前包是%s  ------------ ", packageName));
            initBeanFactory(packageName);
            System.out.println("------------------- [容器]初始化完成 ------------ ");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        isTag = true;
    }


}

包扫描

递归扫描包下的文件
com.xxx.xxx 包名需要转换成磁盘目录 c:/xxx/xx这样的形式

获取包名

   String packageName = StartApplication.class.getPackage().getName();

转换包名以及扫描包得到所有的class文件名


        if (Objects.isNull(packName) || packName.length() == 0) {
            throw new RuntimeException("无效的包路径");
        }
        packName = packName.replace(".", File.separator);
        URL resource = AnnotationBeanFactory.class.getClassLoader().getResource(packName);
        String path = resource.getPath();
        //解析中文
        String filePath = URLDecoder.decode(path, "UTF-8");

递归处理

    /**
     * 递归处理路径下文件夹是否包含文件夹,如不包含则获取当前类的权限定命名存入set中
     *
     * @param packName
     * @param classNameSet
     * @param path
     */
    public static void parseFilePackName(String packName, Set classNameSet, String path) {

        File packNamePath = new File(path);

        if (!packNamePath.isDirectory() || !packNamePath.exists()) {
            return;
        }
        //递归路径下所有文件和文件夹
        for (File file : packNamePath.listFiles()) {
            boolean directory = file.isDirectory();
            String classNamePath = packName + File.separator + file.getName().replace(File.separator, ".");
            if (directory) {
                parseFilePackName(classNamePath, classNameSet, file.getPath());
            } else if (file.isFile() && file.getName().endsWith(CLASS_STR)) {
                //存入set
                classNameSet.add(classNamePath.replace(File.separator, ".").replace(CLASS_STR, ""));
            }
        }

    }

bean实例化

得到所有的java文件名,然后去实例化bean

判断是否包含我们刚才自定义的注解

    private void setBean() {
        stringSet.forEach(x -> {
            try {
                //排除指定包 servlet 类不能被实例化 这儿排除
                if (!x.contains("servlet")) {
                    Class aClassz = Class.forName(x);
                    serviceAnnotation(aClassz);
                    repositoryAnnotation(aClassz);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        });
    }

判断是否含有ServiceRepository注解

获取bean的名字 并且判断当前类是否有 Service 注解,如有则存入Ioc 如包含属性value不为空,则设置value属性为bean的key
    public void serviceAnnotation(Class aClass1) throws InstantiationException, IllegalAccessException {
        Service annotation = (Service) aClass1.getAnnotation(Service.class);
        if (Objects.nonNull(annotation)) {
            setIocNameMap(annotation.value(), aClass1.getSimpleName(), aClass1);
        }
    }

Repository 同理

    public void repositoryAnnotation(Class aClass) throws InstantiationException, IllegalAccessException {
        Repository annotation = (Repository) aClass.getAnnotation(Repository.class);
        if (Objects.nonNull(annotation)) {
            setIocNameMap(annotation.value(), aClass.getSimpleName(), aClass);
        }
    }

获取bean的name setIocNameMap方法然后实例化bean加入到容器
这儿判断一下是否是单例bean 这儿的单例指的是是否已经有一个bean了

    public void setIocNameMap(String value, String className, Class clasz) throws IllegalAccessException, InstantiationException {
        String iocNameString = value;
        Object beanDefinition =  clasz.newInstance() ;
        if (value.length() > 0) {
            if (IOC_MAP.containsKey(value)) {
                throw new RuntimeException("the named" + className + ",  had one ... ");
            }
        } else {
            //默认设置bean首字母小写的
            iocNameString = getIocNameString(className);
            if (IOC_MAP.containsKey(iocNameString)) {
                throw new RuntimeException("the named  " + className + ",  had one ... ");
            }
        }

        // 根据父接口类型注入
        Class[] interfaces = clasz.getInterfaces();
        if (interfaces != null) {
            for (Class anInterface : interfaces) {
                IOC_MAP.put(anInterface.getSimpleName(), beanDefinition);
            }
        }
        IOC_MAP.put(iocNameString, beanDefinition);
    }

设置首字母小写

   public static String getIocNameString(String className) {
        return (String.valueOf(className.toCharArray()[0])).toLowerCase() + className.substring(1, className.length());
    }

依赖注入

依赖注入方法,获取成员变量上有 Autowired 注解的字段,然后根据当前类类型去自动装配

    public static void beanAutoWired() throws ClassNotFoundException {
        //获取成员变量上有 Autowired 注解的字段,然后根据当前类类型去自动装配
        for (Map.Entry stringObjectEntry : IOC_MAP.entrySet()) {

            Object beanDefinition = stringObjectEntry.getValue();
            Class aClass = beanDefinition.getClass();
            Field[] declaredFields = aClass.getDeclaredFields();

            if (Objects.isNull(declaredFields) && declaredFields.length == 0) {
                continue;
            }

            for (Field field : declaredFields) {
                //字段含有 Autowired 注解的需要被自动装配对象
                Autowired autowired = field.getAnnotation(Autowired.class);

                if (Objects.nonNull(autowired)) {
                    //根据当前key获取需要注入示例对象
                    //先根据名字注入,如果名字获取不到,再根据类型去注入
                    String beanName = autowired.name();

                    if (StringUtils.isEmpty(beanName)) {
                        beanName = field.getType().getSimpleName();
                    }

                    //反射设置值
                    try {
                        field.setAccessible(true);
                        //自动装配 线程不安全,Spring中默认单例
                        field.set(stringObjectEntry.getValue(), IOC_MAP.get(beanName));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

    }

扫描事务注解

    public void doScanTransactional() throws IllegalAccessException, InstantiationException, ClassNotFoundException {

        for (Map.Entry classBeanDefinitionEntry : IOC_MAP.entrySet()) {
            Object beanDefinition = classBeanDefinitionEntry.getValue();
            //判断生成代理对象
            Object proxy = getProxy(beanDefinition);
            if (proxy==null){
                proxy = beanDefinition;
            }
            //更新bean
            IOC_MAP.put(classBeanDefinitionEntry.getKey(), proxy);
        }


    }

判断选择哪个代理实现方式 根据是否实现接口

    public Object getProxy(Object aClass) {
        Object jdkProxy = null;

        Transactional annotation =  aClass.getClass().getDeclaredAnnotation(Transactional.class);
            if (Objects.nonNull(annotation)) {
                //有接口使用jdk动态代理
                 if (aClass.getClass().getInterfaces() == null || aClass.getClass().getInterfaces().length <= 0) {

                    //cglib动态代理
                    jdkProxy = ProxyFactory.getCglibProxy(aClass);
                } else {
                    /*for (Class anInterface : aClass.getClass().getInterfaces()) {
                        System.out.println(anInterface.getSimpleName());
                    }*/
                    jdkProxy = ProxyFactory.getJdkProxy(aClass);
                }
        }
      return jdkProxy;

    }

代理对象实现 以及方法执行前后处理事务

/**
 * 代理类工厂
 *
 * @author Pilgrim
 */
public class ProxyFactory {

    /**
     * 事务管理器
     */
  private final TransferServiceManager t = TransferServiceManager.get();

    /**
     * Jdk动态代理
     *
     * @param obj 被代理的对象
     * @return 返回代理对象
     */
    public static Object getJdkProxy(Object obj) {

        Object o = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object o, Method method, Object[] objects) throws Throwable {

                Object invoke = null;
                try {
                    // 开启事务(关闭事务的自动提交)
                    TransferServiceManager.get().start();
                    invoke = method.invoke(obj, objects);
                    // 提交事务
                    TransferServiceManager.get().commit();
                } catch (Exception e) {
                    e.printStackTrace();
                    // 回滚事务
                    TransferServiceManager.get().rowback();
                    throw e;
                }

                return invoke;
            }
        });

        return o;

    }


    /**
     * cglib动态代理
     *
     * @param object 被代理的对象
     * @return 返回代理对象
     */
    public static Object getCglibProxy(Object object) {

        //生成代理对象
        return Enhancer.create(object.getClass(), new MethodInterceptor() {

            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                Object result = null;
                try {

                    //开启事务
                    TransferServiceManager.get().start();

                    result = method.invoke(object, objects);

                    //提交事务
                    TransferServiceManager.get().commit();
                } catch (Exception e) {
                    //回滚事务
                    TransferServiceManager.get().rowback();
                    throw e;
                }
                return result;

            }
        });

    }

初始化方法步骤

    /**
     * 初始化bean
     * 1 递归扫描包获取类权限定命名
     * 2 实例化bean
     * 3 依赖注入
     * 4 扫描事务注解 生成代理对象
     *
     * @param packName
     * @throws UnsupportedEncodingException
     */
    public void initBeanFactory(String packName) throws UnsupportedEncodingException, InstantiationException, IllegalAccessException, ClassNotFoundException {

        if (Objects.isNull(packName) || packName.length() == 0) {
            throw new RuntimeException("无效的包路径");
        }
        packName = packName.replace(".", File.separator);
        URL resource = AnnotationBeanFactory.class.getClassLoader().getResource(packName);
        String path = resource.getPath();
        //解析中文
        String filePath = URLDecoder.decode(path, "UTF-8");

        //解析包成java权限定命名com
        parseFilePackName(packName, stringSet, filePath);
        //实例化bean
        setBean();

        //System.out.println(String.format("获取到的bean : %s ", IOC_MAP));

        //自动装配
        beanAutoWired();

        //扫描事务注解
        doScanTransactional();

    }

对外提供getBean(方法)

实现getBean方法

    @Override
    //根据id名获取
    public Object getBean(String id) {
        if (Objects.nonNull(id) && id.length() > 0) {
            Object beanDefinition = IOC_MAP.get(id);
            return beanDefinition;
        }
        return null;
    }


    @Override
    //根据类型获取
    public Object getBean(Class aClass) {
        if (Objects.isNull(aClass)) {
            return null;
        }
        return IOC_MAP.get(aClass.getSimpleName());
    }

    @Override
    public Object getAllBeanName() {
        return IOC_MAP.keySet();
    }

测试

启动tomcat可以看到启动成功,bean实例化完成
在这里插入图片描述

在servlet init方法里可以调用看一下

    private TransferService transferService  ;

    @Override
    public void init() throws ServletException {
        BeanFactory  beanFactory = new AnnotationBeanFactory();
        transferService= (TransferService)beanFactory.getBean("transferServiceImpl");
        TransferService transferService2  = (TransferService) beanFactory.getBean(TransferService.class);
        super.init();
    }

debug可以看到 根据类型还是id都可以获取到代理之后的bean
注解方式自定义实现Spring Ioc容器 + 事务 + 动态代理_第3张图片

你可能感兴趣的:(spring,java)