«Это 15-й день моего участия в ноябрьском испытании обновлений. Подробную информацию об этом событии см.:Вызов последнего обновления 2021 г.".
В реальной бизнес-разработке, чтобы облегчить получение объектов bean-компонентов в контейнере Spring, распространенным случаем является создание класса SpringUtil, который содержит контекст SpringContext внутри, а затем предоставляет статический способ получения объектов bean-компонентов. поза, Случайный может привести к npe
Давайте посмотрим на эту сцену сегодня
воспроизведение сцены
1. Основное инженерное сооружение
Создайте базовый проект SpringBoot, конкретный процесс здесь опущен, а ключевая информация отмечена ниже.
Этот проект используетSpringBoot 2.2.1.RELEASE
+ maven 3.5.3
+ IDEA
развивать
Откройте веб-сервис для тестирования
<dependencies>
<!-- 邮件发送的核心依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
2. SpringUtil
Строительство фонда Spramutil Tools, контекст помогите удерживать SpringContextaware
@Component
public class SpringUtil implements ApplicationContextAware, EnvironmentAware {
private static ApplicationContext applicationContext;
private static Environment environment;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringUtil.applicationContext = applicationContext;
}
@Override
public void setEnvironment(Environment environment) {
SpringUtil.environment = environment;
}
public static <T> T getBean(Class<T> clz) {
return applicationContext.getBean(clz);
}
public static String getProperty(String key) {
return environment.getProperty(key);
}
}
3. Пример использования
Сначала создайте простой объект bean
@Component
public class TestDemo {
public String showCase() {
return UUID.randomUUID().toString();
}
public String testCase() {
return "test-" + Math.random();
}
}
Затем есть еще один объект, основанный на вышеуказанном объекте, основной интерфейс, предоставляемый извне,process
Его внутренняя реализация зависит от класса перечисления, выбор политики;
@Component
public class BasicDemo {
@Autowired
private TestDemo testDemo;
public String process(String data) {
return Data.process(data);
}
private String show() {
return testDemo.showCase();
}
String test() {
return testDemo.testCase();
}
public enum Data {
SHOW("show") {
@Override
String doProcess() {
return SpringUtil.getBean(BasicDemo.class).show();
}
},
CASE("test") {
@Override
String doProcess() {
return SpringUtil.getBean(BasicDemo.class).test();
}
};
private String data;
Data(String data) {
this.data = data;
}
abstract String doProcess();
static String process(String data) {
for (Data d: values()) {
if (d.data.equalsIgnoreCase(data)) {
return d.doProcess();
}
}
return null;
}
}
}
Сосредоточьтесь на классе перечисления в приведенной выше реализации, в классе перечисления он получен согласно SpringUtilBasicDemo
объект, а затем выполнить его частный методshow()
и внутрипакетные методыtest()
Есть ли проблема с этим использованием?
4. Тестовый случай
Затем напишите простые тестовые интерфейсы
@Aspect
@RestController
@SpringBootApplication
public class Application implements WebMvcConfigurer {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
@Autowired
private BasicDemo basicDemo;
@GetMapping(path = "show")
public String show(String data) {
return basicDemo.process(data);
}
}
Посетите следующий, чтобы увидеть, что происходит
Что? Ты имеешь в виду npe? Это ли не нормальный возврат! ! !
Далее - момент, чтобы свидетельствовать об ошибке, тот же код выше, пусть он появится NPE
5. Повторение ошибки
Затем мы добавляем грань, цель состоит в том, чтобы пропуститьSpringUtil.getBean
Полученный объект является прокси-классом
// 注意在这个方法所在类上,添加注解 @Aspect
@Around("execution(public * com.git.hui.boot.web.interceptor.server.BasicDemo.*(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
return joinPoint.proceed();
}
Затем повторно запрашивайте вышеупомянутый доступ
доступ к закрытым методамshow()
Здесь выбрасывается исключение.Из стека сервера видно, что тип исключения NPE.Основная причина в том, чтоtestDemo
нулевой
Просто при доступе к приватному методу класса агента, если есть объект bean-инъекции, я в это время получаю NULL.
Это немного волшебно, так что давайте изменим его еще раз. Внедренный объект bean-компонента не используется напрямую в приватном методе, а вместо этого вызывается общедоступный метод объекта bean-компонента. Что происходит?
Перепишите приведенный выше метод show()
private String show() {
return show2();
}
public String show2() {
return testDemo.showCase();
}
Протестируйте еще раз, выводите как
На самом деле нет никаких проблем! ! !
Это так удивительно, в чем причина?
- Ключевые точки знаний: логика генерации прокси-классов Spring
6. Резюме
Кажется, я только что вошел в основную часть, и результат здесь. Это слишком много 😡, вот краткое изложение сцены, в которой возникает эта проблема. Что касается конкретных причин, они будут представлены в следующем сообщении в блоге. .
Когда мы получаем объект bean-компонента через SpringContext, не обращайтесь напрямую к его закрытым методам, что может привести к npe.
100% первая сцена
- Этот объект bean-компонента имеет прокси-класс (если какой-либо аспект перехватывает его, например, некоторые определенные аннотации внутри класса)
- Инъективные объекты используются в приватных методах.
Увидев вышеизложенное, возникнет вопрос, а кто будет иметь доступ к приватным методам? У меня нет дыры в голове😒, не говоря уже о том, что к приватным методам нельзя получить доступ извне.
Это включает в себя довольно распространенный сценарий.Внутренний метод класса A вызывает метод B, который вы хотите перехватить аспектом.В настоящее время мы часто делаем это
public class A {
@Autowired
private A a;
public void test() {
a.testB();
}
@Point
public String testB() {
return "hello";
}
}
В приведенном выше тестовом методе вы можете получить доступ к методу testB, чтобы следовать логике аспекта.В приведенном выше классе можно напрямую использоватьa.privetMethod()
сцена
Кроме того, возможен доступ к приватным методам, когда отражение выполняет некоторую логику, которая здесь не будет раскрываться;
Заинтересованные друзья могут ответить и пообщаться, и вы также можете подписаться на мой официальный аккаунт:серый блог
III. Исходный код и связанные с ним знания, которые нельзя пропустить
0. Проект
- проект:GitHub.com/JuneB/tickets…
- Исходный код:GitHub.com/JuneB/tickets…
1. Публичный аккаунт WeChat: блог Yihuihui
Это не так хорошо, как письмо. Вышеупомянутое содержание чисто из семьи. Из-за ограниченных личных способностей неизбежно будут упущения и ошибки. Если вы обнаружите ошибки или у вас есть лучшие предложения, вы можете критиковать и поправьте их.
Ниже представлен серый личный блог, в котором записываются все посты в блоге по учебе и работе, приглашаю всех посетить
- One Grey BlogПерсональный блогblog.hhui.top
- Серый блог - специальный весенний блогspring.hhui.top