【基础系列】实现一个自定义的@Autowired(应用篇)

文章目录
  1. I. 项目环境
  2. II. 实现姿势
    1. 1. 代理封装类
    2. 2. 自定义注解
    3. 3. 自定义注入
    4. 4. 测试
  3. II. 其他
    1. 0. 项目
    2. 1. 一灰灰Blog

在Spring的生态中,借助@Autowired注解来实现依赖注入,可以说是非常普遍的事情了,如果让我们自定义一个注解,也实现类似的功能,那么我们可以怎么做呢?

本文介绍如何实现一个自定义的@Autowired,实现依赖服务注入

主要知识点:

  • BeanPostProcessor
  • 代理类创建

I. 项目环境

本项目借助SpringBoot 2.2.1.RELEASE + maven 3.5.3 + IDEA进行开发

下面是核心的pom.xml(源码可以再文末获取)

1
2
3
4
5
6
7
<!-- 这个依赖是干嘛的,后文会介绍 -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
</dependencies>

II. 实现姿势

1. 代理封装类

借助Spring的Enhance来实现代理类生成,比如一个基础的工具类如下,用于自定义注入的增强

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class ProxyUtil {
public static <T> T newProxyInstance(Class<?> targetClass, InvocationHandler invocationHandler,
ProxyUtil.CallbackFilter filter) {
if (targetClass == null) {
return null;
} else {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetClass);
enhancer.setUseCache(true);
enhancer.setCallback(new ProxyUtil.SimpleMethodInterceptor(invocationHandler, filter));
// 无参构造方法
return (T) enhancer.create();
}
}

public interface CallbackFilter {
boolean accept(Method var1);
}

private static class SimpleMethodInterceptor implements MethodInterceptor, Serializable {
private transient InvocationHandler invocationHandler;
private transient ProxyUtil.CallbackFilter filter;

public SimpleMethodInterceptor(InvocationHandler invocationHandler, ProxyUtil.CallbackFilter filter) {
this.invocationHandler = invocationHandler;
this.filter = filter;
}

@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
return this.filter.accept(method) ? this.invocationHandler.invoke(o, method, objects) : methodProxy.invokeSuper(o, objects);
}
}
}

2. 自定义注解

参照@Autowired的定义,实现一个自定义的注解(缩减版)

1
2
3
4
5
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AutoInject {
}

3. 自定义注入

实现BeanPostProcessor,在bean初始化之后,扫描field/method,为了做一个区分,下面创建一个代理类,注入依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
@Component
public class AutoInjectPostProcessor implements BeanPostProcessor {

private ApplicationContext applicationContext;

public AutoInjectPostProcessor(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Class<?> clazz = bean.getClass();
do {
for (final Field field : clazz.getDeclaredFields()) {
final AutoInject annotation = AnnotationUtils.findAnnotation(field, AutoInject.class);
if (annotation != null) {
ReflectionUtils.makeAccessible(field);
ReflectionUtils.setField(field, bean, processInjectionPoint(field.getType()));
}
}
for (final Method method : clazz.getDeclaredMethods()) {
final AutoInject annotation = AnnotationUtils.findAnnotation(method, AutoInject.class);
if (annotation != null) {
final Class<?>[] paramTypes = method.getParameterTypes();
if (paramTypes.length != 1) {
throw new BeanDefinitionStoreException(
"Method " + method + " doesn't have exactly one parameter.");
}
ReflectionUtils.makeAccessible(method);
ReflectionUtils.invokeMethod(method, bean,
processInjectionPoint(paramTypes[0]));
}
}
clazz = clazz.getSuperclass();
} while (clazz != null);
return bean;
}

// 创建代理类,在具体方法执行前后输出一个日志
protected <T> T processInjectionPoint(final Class<T> injectionType) {
return ProxyUtil.newProxyInstance(injectionType, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("do before " + method.getName() + " | " + Thread.currentThread());
try {
Object obj = applicationContext.getBean(injectionType);
return method.invoke(obj, args);
} finally {
System.out.println("do after " + method.getName() + " | " + Thread.currentThread());
}
}
}, new ProxyUtil.CallbackFilter() {
@Override
public boolean accept(Method var1) {
return true;
}
});
}
}

4. 测试

接下来验证一下自定义注入方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@Component
public class DemoService {

public int calculate(int a, int b) {
doBefore();
return a + b;
}

private void doBefore() {
System.out.println("-------- inner ----------: " + Thread.currentThread());
}
}

@Component
public class DemoService2 {

public int calculate(int a, int b) {
doBefore();
return a + b;
}

private void doBefore() {
System.out.println("-------- inner ----------: " + Thread.currentThread());
}
}

@Service
public class RestService {
@AutoInject
private DemoService demoService;

private DemoService2 demoService2;

@AutoInject
public void setDemoService2(DemoService2 demoService2) {
this.demoService2 = demoService2;
}

public void test() {
int ans = demoService.calculate(10, 20);
System.out.println(ans);

ans = demoService2.calculate(11, 22);
System.out.println(ans);
}
}

执行完毕之后,输出日志如

1
2
3
4
5
6
7
8
9
do before calculate | Thread[main,5,main]
-------- inner ----------: Thread[main,5,main]
do after calculate | Thread[main,5,main]
30

do before calculate | Thread[main,5,main]
-------- inner ----------: Thread[main,5,main]
do after calculate | Thread[main,5,main]
33

II. 其他

0. 项目

1. 一灰灰Blog

尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

一灰灰blog


打赏 如果觉得我的文章对您有帮助,请随意打赏。
分享到