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. 使用用自定义注解
-
自定义注解使用思路,先通过包扫描到所有使用@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>
遍历存放对象的Map集合,判断这些对象中的属性是否使用了@Autowired注解,使用了@Autowired注解通过属性获取到类名,通过类名到Map集合中获取该属性实例化的对象,使用反射技术给该属性进行依赖注入。
遍历存放对象的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();
}
}