自定义@Service、@Autowired、@Transactional

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();
        }
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,525评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,203评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,862评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,728评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,743评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,590评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,330评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,244评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,693评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,885评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,001评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,723评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,343评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,919评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,042评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,191评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,955评论 2 355

推荐阅读更多精彩内容