485接线分叉接线_一切都自动接线

485接线分叉接线

Even though it felt out of grace since the constructor injection became a best practice, the @Autowired annotation is still worth reviewing.

尽管自从构造函数注入成为最佳实践以来,这感觉很失礼,但@Autowired批注仍然值得回顾。

If you’ve wondered how a single word can inject a whole class with its mere presence: gotcha.The @Autowired annotation is the perfect introduction to metaprogramming.

如果您想知道一个单词如何仅凭其存在就可以注入整个类: gotcha 。@ Autowired注释是元编程的完美介绍。

注释概述 (Annotations overview)

First of all, an annotation is used to mark up classes, fields, parameters, or methods, allowing us to check for their presence in some particular conditions.

首先,注释用于标记类,字段,参数或方法,使我们可以在某些特定条件下检查它们的存在。

A basic annotation implementation looks like this and it fairly resembles an interface.

一个基本的注释实现看起来像这样,并且非常类似于一个接口。

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAnnotation {
int value() default 0;
}

If the first thing that went through your head was why the annotation is annotated, it’s ok, I thought the same thing.Besides, being a special type of interface, an annotation can have its own attributes, but in a form that resembles methods.You can find a nice intro to annotations here.

如果首先想到的是为什么要对批注进行批注,那我认为也是可以的。此外,作为一种特殊的接口,批注可以具有自己的属性,但形式类似于方法。您可以在此处找到注释的简介。

Anyway, the most common annotations you’ll find on custom annotations are:

无论如何,您会在自定义注释上找到最常见的注释:

  • @Target specifies what structures the annotation will target. As I mentioned before parameters, classes, methods, and fields can be annotated, but Packages can be annotated too!

    @Target指定注释将针对的结构。 如前所述,可以对参数,类,方法和字段进行注释,但也可以对包进行注释!

  • @RetentionPolicy declares how much the annotation will be available before it’s dropped. It can be kept during the runtime, discarded by the VM, or not written in the .class file at all.

    @RetentionPolicy声明在删除注释之前将有多少可用的注释。 它可以在运行时保留,被VM丢弃或完全不写入.class文件。

  • @Repeatable which allows an annotation to be placed multiple times on the same entity

    @Repeatable允许将注释多次放置在同一实体上

An annotation does not affect the behavior of our code in any way, but they help us identify how a part of a class should be treated.

注释不会以任何方式影响代码的行为,但是它们可以帮助我们确定应如何对待类的一部分。

输入:注释处理。 (Enter: Annotation Processing.)

As its name made it obvious, annotation processing is the step where the annotation will finally seem valuable.

顾名思义,注释处理是最终看起来有价值的步骤。

There are many ways an annotation processor can be implemented, most popular being:

注释处理器可以通过多种方式实现,最受欢迎的是:

  • the actual Annotation Processing API introduced in Java 8

    Java 8中引入的实际注释处理API

  • Bytecode manipulation

    字节码操作
  • clever hacks — if you’ve heard of the Lombok project, the creators implemented a smart hack using an unpublished API of the Java compiler which allows AST changes so the classes that are marked using a specific annotation can be modified)

    聪明的技巧-如果您听说过Lombok项目,则创建者使用Java编译器的未发布API实施了智能技巧,该API允许AST更改,因此可以修改使用特定注释标记的类)

  • processing annotations at runtime using Java Reflection

    使用Java Reflection在运行时处理注释

The last technique is also the one we will use to implement our spin of the @Autowired annotation.

最后一种技术也是我们将用于实现@Autowired注释的旋转的技术。

编写代码 (Writing the code)

First, we’ll define our special annotation that will act like @Autowired but will be defined as a type-level annotation, so we’ll use it on top of our class instead of our attributes.

首先,我们将定义特殊的注释,该注释的作用类似于@Autowired,但将被定义为类型级别的注释,因此,将在类的顶部而不是属性上使用它。

Why?

为什么?

Because an exact copy of the @Autowired wouldn’t be so cool.Plus, it, for sure, seems more magical than a regular field annotation and will help us visualize exactly how powerful the Java Reflection API is.

因为@Autowired的确切副本并不是那么酷,而且它肯定比常规字段注释更具魔力,并且将帮助我们准确地看到Java Reflection API的强大功能。

And, because we’re very original, let’s call it @ManagedClass.

而且,由于我们非常原始,因此我们将其称为@ManagedClass。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface ManagedClass {}

Besides the type-level annotation, we also defined it as a class retention annotation.Why? I’ll explain later.

除了类型级注释外,我们还将其定义为类保留注释。 我稍后再解释。

Next, we need a context: it’s time for some creativity.

接下来,我们需要一个环境:是时候进行一些创造了。

Let’s suppose we have a cat that’s not so playful with young people. Each time we try to pet it, the chance we’ll get a scratch decreases with our age, but is also dependent on the cat’s disposition to be petted. It’s a fact that cats are moody so making this awful generalization is not that far from the truth. (Disclaimer: I have a cat)

假设我们有一只对年轻人不太好玩的猫。 每次我们尝试抚摸它时,随着年龄的增长,抓伤的机会会减少,但也取决于猫被抚摸的倾向。 猫是喜怒无常的事实,因此做出可怕的概括与事实相差不远。 (免责声明:我有一只猫)

The Human class

人类阶级

public class Human {
private int age;
private Cat cat;public void setAge(int age) {
this.age = age;
}public void pet() {
String result;
result = cat.calculateDisposition(age) > 10 ? "Auch! This surely hurt!" : "The cat seems to like you!";
System.out.println(age + ": " + result);
}
}

and the Cat class

猫类

@ManagedClass
public class Cat {
private Random fakeRandomGenerator = new Random();public int calculateDisposition(int humanAge) {
return fakeRandomGenerator.nextInt() + (int) (0.3 * humanAge);
}
}

Because the Human owns a Cat, we’ve annotated the Cat class with the @ManagedClass we’ve created earlier. Each time a human is instantiated, we’ll somehow manage to give hima pet without explicitly doing so.

因为人类拥有一只猫,所以我们用之前创建的@ManagedClass注释了猫类。 每次实例化一个人时,我们都会设法以某种方式给他宠物,而无需明确地这样做。

Before we delve further, it would be nice if we’d read the official Java Reflection trail.But, because we’re running out of time and our cat needs to be petted, we’ll do that later. Promise!Shortly, using the Reflection API we can extract the class of an object using the .getClass() method and we’ll obtain, guess what, a Class object.If no object is available, we can also use the Class.class syntax which will return the same thing.Through the Class object we can interrogate its methods, fields and even instantiate new objects at runtime in a more abstract way.We’ll play with some of them pretty soon, but, for the record, there’s an exhaustive list ready to be browsed you can check if you want to get more insight.

在深入研究之前,如果我们读过官方的Java Reflection教程,那就太好了。但是,由于时间有限,我们需要抚摸猫,因此我们稍后再做。 很快,使用Reflection API,我们可以使用.getClass()方法提取一个对象的类,我们将猜测一个Class对象,如果没有可用的对象,我们也可以使用Class.class语法将返回相同的结果。通过Class对象,我们可以在运行时以更抽象的方式询问其方法,字段,甚至实例化新对象。我们很快就会使用其中的一些对象,但据记录可供浏览的详尽列表,您可以检查是否想获得更多洞见。

If you’ve recognized the @Autowired annotation, and I’m sure you did because you’ve come so far reading this article!, I’m pretty confident that you’re also familiar with Java Beans.Just for the record, a bean is nothing more than a serializable class that has all its attributes private, has properly named getters and setters, and the default constructor.The classes we defined before respect this convention.Almost.Okay, you got me, they aren’t actual beans, according to the definition, but, for simplicity, we will make an exception.

如果您已经认识到@Autowired批注,并且可以肯定是因为您到目前为止已经阅读本文!!我非常有信心您也已经熟悉Java Beans。 bean只是一个可序列化的类,它具有所有私有属性,具有正确命名的getter和setters,以及默认的构造方法。我们在遵守该约定之前定义的类几乎,好吧,我明白了,它们不是实际的bean根据定义,但是为了简单起见,我们将做一个例外

Is it so important to follow this structure?Well, yes — at least some of it.By using this pattern, we can instantiate an object right-away, without dealing with its attributes, and play with them later using its accessors.The classes our annotation processor will inspect are ready.Let’s define an AnnotationManager, then!

遵循这种结构是否非常重要?是的,至少是其中的某些结构。通过使用这种模式,我们可以立即实例化一个对象,而无需处理其属性,之后再使用其访问器对其进行操作。我们的注释处理器将检查就绪,让我们定义一个AnnotationManager ,然后!

public class AnnotationManager {
private Map<String, Class<?>> injectableTypes;
private Map<String, Object> managedObjects;
private static AnnotationManager instance;private AnnotationManager() {
managedObjects = new HashMap<>();
injectableTypes = new HashMap<>();
initializeInjectableTypes();
}public static AnnotationManager getInstance() {
if (instance == null) {
instance = new AnnotationManager();
}
return instance;
}private void initializeInjectableTypes() {
//TO DO
}private Object getInjectableValue(String className) throws Exception {
if (!managedObjects.containsKey(className)) {
Object managedObject = provideManagedObject(injectableTypes.get(className));
managedObjects.put(className, managedObject);
}return managedObjects.get(className);
}public <T> T provideManagedObject(Class<?> clazz) throws Exception {
Constructor<?> constructor = clazz.getConstructor();
Object providedObject = constructor.newInstance();
for (Field field : clazz.getDeclaredFields()) {
if (injectableTypes.get(field.getType().getName()) != null) {
Object injectableValue = getInjectableValue(field.getType().getName());
field.setAccessible(true);
field.set(providedObject, injectableValue);
}
}
return (T) providedObject;
}
}

Woah, compared to our previous classes, this one is quite big, so we’ll take it step by step, in a way that should be easy to crunch.

哇,与我们之前的课程相比,这是一个很大的课程,因此我们将逐步进行,应该采用易于处理的方式。

First of all, you’ve probably observed that our class is a singleton. It’s a common pattern to ensure that we have a reliable, unique source that will provide all our dependency injections.

首先,您可能已经观察到我们的班级是单身人士。 这是确保我们拥有可靠的唯一来源的通用模式,它将提供我们所有的依赖项注入。

Also, the method initializeInjectableTypes() is not yet implemented.But let’s not forget about it.Here we will get all the classes that are annotated using our special annotation and we will fill the injectableTypes Map, so ourbean manager will know which classes it can provide.You’ve probably noticed that this method is called inside the constructor. This way the map will be populated when the ClassManager is instantiated, an event that happens only once, thanks to the singleton nature we’ve discussed above.

另外,还没有实现initializeInjectableTypes()方法,但是请不要忘记它,这里我们将使用特殊注释获得所有被注释的类,并且将填充InjectableTypes Map,因此我们的bean管理器将知道可以使用哪些类提供。您可能已经注意到,此方法在构造函数内部调用。 这样,当实例化ClassManager时,将填充该地图,由于我们上面已经讨论了单例性质,因此该事件仅发生一次。

The last two methods are, probably, the juiciest parts of this implementation.Let’s start with provideManagedObject().

最后两种方法可能是此实现中最有用的部分。让我们从ProvideManagedObject()开始。

public <T> T provideManagedObject(Class<?> clazz) throws Exception {
Constructor<?> constructor = clazz.getConstructor();
Object providedObject = constructor.newInstance();
for (Field field : clazz.getDeclaredFields()) {
if (injectableTypes.get(field.getType().getName()) != null) {
Object injectableValue = getInjectableValue(field.getType().getName());
field.setAccessible(true);
field.set(providedObject, injectableValue);
}
}
return (T) providedObject;
}

This method will be called each time we want to instantiate an object that requires a dependency injection.That’s why the result is a template type — we need a way to return the class that is requested seamlessly. Inside this method, we get the constructor of our class, which is the default one, and we instantiate a new object of the class we’ve requested.The Java Reflection API is pretty forward and allows access to everything we need.After that, we parse through each field of our class and check if its type is part of our injectableTypes map.

每当我们要实例化需要依赖注入的对象时,都会调用此方法。这就是为什么结果是模板类型的原因-我们需要一种方法来无缝返回所请求的类。 在此方法内部,我们获得了类的构造函数,这是默认的构造函数,并实例化了所请求类的一个新对象.Java Reflection API非常先进并且可以访问所需的所有内容。我们解析类的每个字段,并检查其类型是否属于我们的jectableables类型映射。

Why does it matter?Because if it is, we need to take our role as bean manager seriously and provide an instance of that type.

为何重要?因为确实如此,我们需要认真对待作为bean管理器的角色并提供这种类型的实例。

Then, we proceed in asking our mysterious method, getInjectableValue, for an instance. After that, we use a little bit of reflection magic and allow ourselves to modify a field that,you’re right, is technically private, and inject our freshly obtained instance.

然后,我们继续询问我们的神秘方法getInjectableValue作为实例。 之后,我们使用了一些反射魔术,并允许我们自己修改一个对的,技术上私有的字段,并注入我们新获得的实例。

元编程宝贝! (Metaprogramming baby!)

Why did we called the getInjectableValue method and not directly instantiated the class before injecting it?Well, there’s the beauty of it. Let’s take a closer look.

为什么我们调用getInjectableValue方法而不在注入之前直接实例化该类? 让我们仔细看看。

private Object getInjectableValue(String className) throws Exception {
if (!managedObjects.containsKey(className)) {
Object managedObject = provideManagedObject(injectableTypes.get(className));
managedObjects.put(className, managedObject);
}

return managedObjects.get(className);
}
}

The class we’re trying to inject may contain another field that should be injected.What does this method do?Firstly, it checks, using the managedObjects map, if the required object was already instantiated for a previous request. If it didn’t, it creates a new, lazy instance, puts it inside the map, and returns it.The fact that we reuse the same object may seem weird, at first, but it’s a common practice for the object that’s injected to be the same for each injection.

我们尝试注入的类可能包含另一个应该注入的字段,此方法的作用是:首先,它使用managedObjects映射检查是否已为先前的请求实例化了所需的对象。 如果没有,它将创建一个新的惰性实例,将其放入地图中,然后返回它。我们重用同一对象的事实乍看起来似乎很奇怪,但这是注入对象的一种常见做法每次注射都相同。

In Spring, for example, we usually inject our services, our repositories, or other types of objects that can be used without depending too much on their inner state.We didn’t pick the best example to see why this is, actually, useful, but let’s assume that we’re passing the cat to each involved human.

例如,在Spring中,我们通常会注入我们的服务,我们的存储库或其他类型的对象,这些对象可以在不过多依赖其内部状态的情况下使用。我们没有选择最佳示例来了解为什么这实际上是有用的,但我们假设我们正在将猫传给每个相关人员。

会有很多划痕! (There’s gonna be a lot of scratches!)

We’re almost ready to give it a spin, but we got one more thing to discuss: the initializeInjectableTypes method that lacks an actual implementation.Package scanning is a complex task that can be achieved at multiple times during the lifecycle of a Java program.Most of the time, it’s done at runtime by checking classes and indexing them based on the annotations that are present.How is it done? Many implementations are relying either on direct file system scanning or using some hacks around the URLClassLoader to provide a list with available packages.The most popular one is Google’s Reflections library that can provide exactly this functionality.

我们几乎已经准备好旋转一下,但是我们还要讨论另外一件事:缺少实际实现的initializeInjectableTypes方法。程序包扫描是一项复杂的任务,可以在Java程序的生命周期中多次完成。在大多数情况下,它是在运行时通过检查类并根据存在的注释对它们进行索引来完成的,如何完成的? 许多实现都依赖于直接文件系统扫描或使用URLClassLoader周围的一些技巧来提供包含可用软件包的列表。最受欢迎的是Google的Reflections库,它可以完全提供此功能。

What is the downside of this approach?The scanning is, usually, slow and it also happens at runtime.Yes, if you thought, for a second, that there’s a faster approach, you’re right.

这种方法的缺点是:扫描通常很慢,而且在运行时也会发生。是的,如果您认为有一种更快的方法,那是对的。

We’ve mentioned earlier the Annotation Processing API that operates at compile time. Besides this, it can also query all the classes that are loaded and save the information gathered about them in special files that can be consulted later, at runtime.Perfect for our case. This is also the reason why the level of retention on @ManagedClass is RetentionPolicy.CLASS, instead of RetentionPolicy.RUNTIME, which is more common.The best library that works this way is, probably, ClassIndex.Using this approach let’s implement our method and annotate our …annotation so it can be tracked by the library.

我们前面已经提到了在编译时运行的Annotation Processing API。 除此之外,它还可以查询所有已加载的类,并将收集到的所有类的信息保存在特殊文件中,以便以后在运行时进行查询。 这也是为什么@ManagedClass上的保留级别是RetentionPolicy.CLASS而不是RetentionPolicy.RUNTIME的原因,后者更常见。这种方式的最佳库可能是ClassIndex 。使用这种方法,让我们实现我们的方法给我们的注释添加注释,以便库可以对其进行跟踪。

Adding @IndexAnnotated on top of our own annotation

在我们自己的注释之上添加@IndexAnnotated

@IndexAnnotated
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface ManagedClass {}

and implementing the last method using ClassIndex

并使用ClassIndex实现最后一个方法

private void initializeInjectableTypes() {
for (Class<?> clazz : ClassIndex.getAnnotated(ManagedClass.class)) {
injectableTypes.put(clazz.getName(), clazz);
}
}

Let’s put it all together and write a sample that will put our code to work. A simple main will do.

让我们将它们放在一起,并编写一个示例,以使我们的代码生效。 一个简单的主线就可以了。

public class Main {
public static void main(String[] args) throws Exception {
AnnotationManager annotationManager = AnnotationManager.getInstance();

Human human = annotationManager.provideManagedObject(Human.class);
human.setAge(22);
human.pet();

Human human = annotationManager.provideManagedObject(Human.class);
human.setAge(18);
human.pet();
}
}

The last thing we need to do is to run our program and see if it works.

我们需要做的最后一件事是运行我们的程序,看看它是否有效。

分析结果 (Analyzing the results)

80: The cat seems to like you! 
18: Auch! This surely hurt!

This looks ok, but it doesn’t seem so impressive with only one level of depth.Let’s throw another class in play and see if our ClassManager succeeds to provide an injectable object that also requires a dependency injection.As we observed earlier, our cat is moody. Randomly, instead of scratching us or let us pet it, it will walk away and scratch its toy, then come back.This being said, let’s provide a scratcher implementation.

看起来还不错,但是仅在一个层次上就不那么令人印象深刻了,让我们在播放另一个类,看看我们的ClassManager是否成功提供了需要依赖项注入的可注入对象。喜怒无常。 它会随机走开而不是抓挠我们或让我们抚摸它,而是会走开并刮擦它的玩具,然后再回来。这就是说,让我们提供一个抓取器实现

@ManagedClass
public class Scratcher {
public void scratch() {
System.out.println("\nSccccccchhh");
}
}

Now let’s give our cat one and modify her behavior.

现在,让我们给猫咪一只猫,并修改其行为。

@ManagedClass
public class Cat {
private Scratcher scratcher;
private Random fakeRandomGenerator = new Random();

public int calculateDisposition(int humanAge) {
int disposition = fakeRandomGenerator.nextInt() + (int) (0.3 * humanAge);

if (disposition % 2 == 0) {
scratcher.scratch();
}

return disposition;
}
}

This means that, when we’ll ask the ClassManager for a human before the cat can be provided, the scratcher should, also, be injected, checking if our implementation covers this case and, virtually, any scenario that requires class nesting.

这意味着,当我们在提供猫之前向ClassManager求人时,还应该注入草稿器,检查我们的实现是否涵盖这种情况以及实际上需要类嵌套的任何情况。

80: Auch! This surely hurt! Sccccccchhh 
18: The cat seems to like you!

That was a huge mood swing, but everything looks in check! Our method works even for multiple layers.If you want to take a closer look or change the code in any way, you can find it here.

那是一次巨大的情绪波动,但一切都受到控制! 我们的方法甚至适用于多层结构。如果您想仔细看一下或以任何方式更改代码,都可以在这里找到。

Thank you for sticking until the end!

感谢您坚持到底!

Originally published at https://encapsulated.dev.

最初发布在https://encapsulated.dev

翻译自: https://medium.com/swlh/everything-autowired-4a53c6303de1

485接线分叉接线

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值