注解工作原理
注解本身只是元数据,即描述数据的数据,被描述的数据可以是类、方法、属性、参数、构造器等。注解仅是标记本身没有任何可执行的功能代码,但是我们只要标注了注解,我们就能得到想要的功能,这是怎么做到的呢?我们理解肯定是有某处代码根据我们标注的注解找到我们注解的类、方法、属性、参数等数据本身,并根据注解本身的含义以及注解属性里的内容执行功能代码。
第一种:BeanPostProcessor
BeanPostProcessor
实现中有一类名称为*AnnotationBeanPostProcessor
都是针对处理注解的,对容器内标注了指定注解的Bean,进行功能处理。如:
AutowiredAnnotationBeanPostProcessor
:让@Autowired
、@Value
、@Inject
注解起效;CommonAnnotationBeanPostProcessor
:让@PostConstruct
、@PreDestroy
注解起效;AsyncAnnotationBeanPostProcessor
:让@Async
或@Asynchronous
注解起效;ScheduledAnnotationBeanPostProcessor
:让@Scheduled
注解起效;PersistenceAnnotationBeanPostProcessor
:让@PersistenceUnit
、@PersistenceContext
注解起效;JmsListenerAnnotationBeanPostProcessor
:让@JmsListener
注解起效。 它们都会在构造器里指定其能处理的注解类型,并在对应的方法进行功能处理,我们下面演示一个简单的例子帮助大家理解,我们定义注解@InjectLogger
向Bean注入org.slf4j.Logger
来做系统日志。
我们先定义要使用的注解,只能注解在类上,默认的后缀为“-Bean”
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectLogger {
}
下面我们定义处理注解的
InjectLoggerAnnotationBeanPostPorcessor
,我们在上节已经学习过,我们只需要实现BeanPostPorcessor
接口即可:
@Component
public class InjectLoggerAnnotationBeanPostPorcessor implements BeanPostProcessor {
private Class<? extends Annotation> changeAnnotationType; //1
public InjectLoggerAnnotationBeanPostPorcessor() {
this.changeAnnotationType = InjectLogger.class; //1
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
ReflectionUtils.doWithFields(bean.getClass(), field -> { //2
ReflectionUtils.makeAccessible(field); //3
if(field.isAnnotationPresent(changeAnnotationType)){ //4
Logger logger = LoggerFactory.getLogger(bean.getClass()); //5
field.set(bean, logger); //6
}
});
return bean;
}
}
- 指明当前类处理
@InjectLogger
注解; - 通过反射机制对类的每个属性(
Field
)进行处理,第一个参数是Bean的Class,第二参数是入参为Field无返回值的函数接口的Lambda实现; - 通过反射机制让当前属性可访问;
- 新建Logger的实例logger
通过反射将logger值设置到bean实例的当前属性(field)上。 我们将注解使用到其他Bean上使用:
@Component public class DemoLoggerService { @InjectLogger private Logger log; public void doSomething(){ log.info("通过自定义InjectLoggerAnnotationBeanPostPorcessor让注解@InjectLogger注入Logger对象"); } }
通过在
JavaConfig
运行:@Bean CommandLineRunner changeAnnotationBeanPostProcessorClr(DemoLoggerService demoLoggerService){ return args -> { demoLoggerService.doSomething(); }; }
第二种:BeanFactoryPostProcessor
前面我们讲了针对Bean进行处理的BeanPostProcessor
,而BeanFactoryPostProcessor
是针对Bean的配置元数据(注解等)进行处理操作,而这项工作属于BeanFactory
的职责范畴。
ConfigurationClassPostProcessor
:使@PropertySource
、@ComponentScan
、@Component
类、@Configuration
、@Bean
、@Import
和@ImportResource
注解起效;EventListenerMethodProcessor
:使@EventListener
注解起效; 我们也通过一个自定义的注解@CustomBean
,来自己自动注册Bean,我们的`@CustomBean的作用即是配置元数据。
自定义的注解为:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomBean {
}
使用在Bean上:
@CustomBean
public class CustomBeanService {
public void doSomething(){
System.out.println("通过自定义的注解成功注册bean");
}
}
同样,我们也实现BeanFactoryPostProcessor
接口:
@Component
public class CustomBeanDefinitionRegistryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner((BeanDefinitionRegistry) beanFactory); //1
scanner.addIncludeFilter(new AnnotationTypeFilter(CustomBean.class)); //2
scanner.scan("top.wisely.springfundamentals.custom_scan"); //3
}
}
- 定义一个类路径Bean定义扫描器,它的入参是
BeanDefinitionRegistry
类型,而ConfigurableListableBeanFactory
是它的子类,可强制转换使用,当然我们可以让我们的类直接实现BeanDefinitionRegistryPostProcessor
接口,它的方法void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
直接提供了BeanDefinitionRegistry
的对象; - 为扫描器添加包含的注解
@CustomBean
的过滤器; - 在包
top.wisely.springfundamentals.custom_scan
下扫描注解; 在JavaConfig
中注入我们自定义的Bean,因为是我们自定义的,IDE不能自动检测而显示红色,但可以正常执行:@Bean CommandLineRunner customBeanDefinitionRegistryPostProcessorClr(CustomBeanService customBeanService){ return args -> { customBeanService.doSomething(); }; }
第三种:AOP
我们还可以通过基于AOP来让注解具备功能,通过拦截标注了指定注解的方法或类,然后再建言执行功能代码。如:
AnnotationTransactionAspect
:让@Transactional
注解起效;AnnotationCacheAspect
:让@Cacheable
注解起效;
第四种:组合元注解
Spring的大部分的元注解,我们可以使用元注解到其它的注解,即用元注解(元数据)描述注解,从而使其它的注解具备元注解的功能,一般我们认为组合注解是元注解在新的语义下的特例。
@Component
元注解:@Service
、@Repository
、@Controller
、@Configuration
:@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component //组合了@Component注解,具备了声明Bean的能力 public @interface Service {}
@Import
元注解:大量的@Enable*
注解:@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AsyncConfigurationSelector.class) //组合了@Import注解,具备了导入配置的能力 public @interface EnableAsync {}
@Conditional
元注解:@Profile
以及Spring Boot的大量条件注解@ConditionalOn*
@Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnClassCondition.class) public @interface ConditionalOnClass {}
总结
Spring框架的注解的主要工作方式大体罗列在本文,如果大家发现还有其他的Spring注解的工作方式,欢迎补充。