美文网首页
自定义@Service、@Autowired、@Transact

自定义@Service、@Autowired、@Transact

作者: Scallion | 来源:发表于2021-07-12 10:34 被阅读0次

1. 自定义注解首先要了解一些JDK提供的元注解

元注解

元注解是可以注解到注解上的注解,或者说元注解就是一种基本注解,但是它能应用到其他注解上。

元注解有五种,分别是:@Retention、@Documented、@Target、@Inherited、@Repeatable

自定义@Service、@Autowired、@Transactional主要会用到三种元注解,分别是:@Retention、@Documented、@Target,下面将主要介绍这三种元注解的用法

1.1. @Retention

Retention的英文意为保留期的意思,当@Retention应用到一个注解上的时候,它解释说明了这个注解的存活时间。它的取值如下:

  • RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽略;
  • RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到JVM中;
  • RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到JVM中,所以在程序运行的时候可以获取到它们。

1.2. @Documented

顾名思义,这个元注解肯定是和文档有关。它的作用是能过将注解中的元素包含到 Javadoc 中去。

1.3. @Target

target是目标的意思,@Target指定了注解运用的位置。可以理解为当一个注解被@Target注解时,这个注解就被限定了运用的场景。它的取值如下:

  • ElementType.ANNOTATION_TYPE 可以给一个注解进行注解;
  • ElementType.CONSTRUCTOR 可以给构造方法进行注解;
  • ElementType.FIELD 可以给属性进行注解;
  • ElementType.LOCAL_VARIABLE 可以给局部变量进行注解;
  • ElementType.METHOD 可以给一个方法进行注解;
  • ElementType.PACKAGE 可以给一个包进行注解;
  • ElementType.PARAMETER 可以给一个方法内的参数进行注解;
  • ElementType.TYPE 可以给一个类型进行注解,比如:类、接口、枚举。

2. 自定义注解

2.1. 自定义@Service注解

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

@Service注解一般作用在类上,通过@Target指定注解的运用位置为ElementType.TYPE,实现@Service可以作用在类、接口、枚举上。同时@Service注解也要在运行时生效,需要被加载进入到JVM中,在程序运行的时候可以获取到他们,因此@Retention指定的存活时间是RetentionPolicy.RUNTIME。在使用@Service注解时,可以通value指定创建bean的ID,如果不指定bean 的类名将默认是bean的ID。

2.2. 自定义@Autowired注解

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
    boolean required() default true;
}

@Autowired注解一般作用在成员属性、构造函数等位置,本次实现的代码只需要@Autowired作用在成员属性上,所以@Target指定注解的运用位置是ElementType.FIELD。@Autowired注解需要在运行时生效,能够加载到JVM中,在程序运行的时候可以获取到它们,因此@Retention指定的存活时间是RetentionPolicy.RUNTIME。

2.3. 自定义@Transactional注解

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

自定义的@Transactional注解希望它能作用在类上,@Target指定注解的运行位置时ElementType.TYPE。同时需要@Transactional注解在运行时生效,将@Retention的存活时间指定为RetentionPolicy.RUNTIME。

3. 使用用自定义注解

  1. 自定义注解使用思路,先通过包扫描到所有使用@Service注解的类,然后通过反射实例化对象,判断@Service注解是否设置了value属性,如果设置了value属性将该属性作为Key,实例化的对象作为value存入map集合,如果没有获取到value属性,使用实例化对象的类名作为key,实例化对象作为value存入map集合。

    包扫描可以引用第三方Reflections jar包

        <dependency>
          <groupId>org.reflections</groupId>
          <artifactId>reflections</artifactId>
          <version>0.9.11</version>
        </dependency>
    
  1. 遍历存放对象的Map集合,判断这些对象中的属性是否使用了@Autowired注解,使用了@Autowired注解通过属性获取到类名,通过类名到Map集合中获取该属性实例化的对象,使用反射技术给该属性进行依赖注入。

  2. 遍历存放对象的Map集合,判断这些对象中是否使用了@Transactional注解,若使用了@Transactional先获取ProxyFactory代理工厂类,接着判断该对象是否实现了接口,如果实现了接口就用JDK的动态代理去增强该对象中的方法,进行事务处理。如果没有实现接口就使用CGLIB的动态代理去增强该对象中的方法,进行事务处理。

    private static void parseTheAnnotation() {
        try {
            //通过反射技术,扫描包并获取反射对象集合
            Reflections reflections = new Reflections("com.erxiao.edu");
            Set<Class<?>> clazzs = reflections.getTypesAnnotatedWith(Service.class);
            //遍历对象集合
            for (Class<?> clazz : clazzs) {
                //获取实例化对象
                Object o = clazz.newInstance();
                Service service = clazz.getAnnotation(Service.class);
                //判断Service注解上是否有自定义对象ID
                if (StringUtils.isEmpty(service.value())) {
                    //getName获取到的是全限定类名,所以要分割去掉前面包名部分
                    String[] names = clazz.getName().split("\\.");
                    map.put(names[names.length - 1], o);
                } else {
                    map.put(service.value(), o);
                }
            }

            //维护对象之间的依赖关系-DI注入
            for (Map.Entry<String, Object> entrySet : map.entrySet()) {
                Object object = entrySet.getValue();
                Class<?> clazz = object.getClass();
                //获取每个类的所有属性
                Field[] fields = clazz.getDeclaredFields();
                //遍历属性,确认是否有使用Autowired注解,有使用注解则需要完成注入
                for (Field field : fields) {
                    //判断是否使用注解的参数
                    if (field.isAnnotationPresent(Autowired.class) && field.getAnnotation(Autowired.class).required()) {
                        //获取注解注入的类的名称
                        String[] names = field.getType().getName().split("\\.");
                        String name = names[names.length - 1];
                        //设置属性可以访问
                        field.setAccessible(true);
                        //设置对象属性,完成依赖注入
                        Object o = map.get(name);
                        field.set(object, o);
                    }
                }

            }

            //解析Transactional注解,获取代理对象,用于增加事务处理逻辑
            for (Map.Entry<String, Object> entrySet : map.entrySet()) {
                Object object = entrySet.getValue();
                Class<?> clazz = object.getClass();
                //判断当前类是否有Transactional注解,若有则使用代理对象
                if (clazz.isAnnotationPresent(Transactional.class)) {
                    ProxyFactory proxyFactory = (ProxyFactory) BeanFactory.getBean("ProxyFactory");
                    Class<?>[] face = clazz.getInterfaces();
                    if (face != null && face.length > 0) {
                        //实现jdk动态代理
                        object = proxyFactory.getProxy(object);
                    } else {
                        //没有实现使用CGLIB
                        object = proxyFactory.getCglibProxy(object);
                    }
                }

                //把处理之后的object重新放到map中
                map.put(entrySet.getKey(), object);
            }

        } catch (InstantiationException e) {
            e.printStackTrace();
        }catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

相关文章

网友评论

      本文标题:自定义@Service、@Autowired、@Transact

      本文链接:https://www.haomeiwen.com/subject/gujrpltx.html