@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)@Documentedpublic @interface Service {    String value() default "";}
@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Repository {    String value() default "";}

主动拆卸的注解

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

事务注解 Transactional

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

而后 在类上标注注解,以及依赖注入

事务注解以及DI 以及Bean主动拆卸

我的项目构造

配置信息

版本 JDK8 , tomcat 7 , IDEA 2019 03

所需依赖

    <!-- servlet -->    <dependency>      <groupId>javax.servlet</groupId>      <artifactId>javax.servlet-api</artifactId>      <version>3.1.0</version>      <scope>provided</scope>    </dependency>        <!--引入cglib依赖包-->    <dependency>      <groupId>cglib</groupId>      <artifactId>cglib</artifactId>      <version>2.1_2</version>    </dependency>

tomcat插件

   <plugin>        <groupId>org.apache.tomcat.maven</groupId>        <artifactId>tomcat7-maven-plugin</artifactId>        <version>2.2</version>        <configuration>          <port>8080</port>          <path>/</path>        </configuration>      </plugin>

这里咱们应用一个StartApplication类来示意以后的顶级包下的启动类,相当于SpringBoot里的Main办法所在的类(目标仅仅是指定包,也能够在xml外面配置包名)

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

<!DOCTYPE web-app PUBLIC        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"        "http://java.sun.com/dtd/web-app_2_3.dtd" ><web-app>    <display-name>tomcat启动时候启动IOC容器</display-name>    <listener>        <!-- 启动容器-->        <!--      注解实现-->        <listener-class>com.udeam.edu.factory.impl.AnnotationBeanFactory</listener-class>        <!--    xml实现ioc-->        <!--    <listener-class>com.udeam.edu.factory.impl.ClassPathXmlBeanFactory</listener-class>-->    </listener></web-app>

定义BeanFactory接口类

/** * 底层BeanFactory工厂接口 * @author Pilgrim */public interface BeanFactory {    /**     * 存储bean单例     */    public final static Map<String, Object> 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<String, Object> 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<String> 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<String> 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<String, Object> 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<String, Object> 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