воскресенье, 5 февраля 2017 г.

Кастомные аннотации в спринг

Попробуем написать свою простенькую аннтацию для спринга. Она будет выводить аргументы вызываемой функции. Подопытным классом будет калькулятор, который умеет складывать и вычитать два числа, вот его интерфейс:

package com.calculator;

public interface Calculator {
    int sum(int a, int b);
    int sub(int a, int b);
}


И его реализация:
package com.calculator;
import org.springframework.stereotype.Component;

@Component
@Profiling
public class CalculatorImpl implements Calculator {
    @Override
    public int sum(int a, int b) {
        return a + b;
    }

    @Override
    public int sub(int a, int b) {
        return a - b;
    }
}


Нас интересует новая аннотация @Profiling, которую мы и будем рализовывать. Для этого создадим следующий интерфейс:

package com.calculator;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface Profiling {
}

Если у аннотации должны быть какие то параметры - создаются обычные филды, но это не наш случай. Так же необходимо обратить внимание на аннотацию @Retention(RetentionPolicy.RUNTIME) . Ее основное назначение - указать, как долго необходимо хранить данную аннотацию. Бывают следующие значния:

  • RetentionPolicy.SOURCE - аннотация используется на этапе компиляции и должна отбрасываться компилятором.
  • RetentionPolicy.CLASS - аннтоация будет записана в class-файл компилятором, но не должна быть доступна во время выполнения.
  • RetentionPolicy.RUNTIME - аннотация будет записана в class-файл и доступна во время выполнения.
Для данной аннотации значение по умолчанию CLASS. Для нас подходит только RUNTIME. 
Аннотация готова, но про нее ничего не знает сам спринг. Чтобы их познакомить необходимо создать BeanPostProcessor, для обработки данной аннотации.

package com.calculator;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

@Component
public class ProfilingAnnotationBeanPostProcessor implements BeanPostProcessor {
    private Map<String, Class>map = new HashMap<>();
    @Override
    public Object postProcessBeforeInitialization(Object bean, String s) throws BeansException {
        if (bean.getClass().isAnnotationPresent(Profiling.class)){
            map.put(s,bean.getClass());
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String s) throws BeansException {
        Class beanClass = map.get(s);
        if (beanClass != null){
            return Proxy.newProxyInstance(beanClass.getClassLoader(), beanClass.getInterfaces(),
                    (proxy, method, args) -> {
                        System.out.println("method:" + method.getName());
                        System.out.println("params:" + Arrays.toString(args));

                        return method.invoke(bean, args);
            });
        }
        return bean;
    }
}

в BeforeInitialization мы запоминаем те бины, которые помечены аннотацией, а в AfterInitialization, проверяем, если это запомненный бин, добавляем логирование. Ну и осталось создать контекст спринга, в котором нет ничего особенного:



и запуск:
package com;

import com.calculator.Calculator;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;


public class App2 {
    public static void main(String[] arg){
        ApplicationContext context = new GenericXmlApplicationContext("context.xml");
        Calculator calc = context.getBean(Calculator.class);
        System.out.println(calc.sum(5,4));
        System.out.println(calc.sub(5,4));

    }


}

следующий вовод:
Connected to the target VM, address: '127.0.0.1:61942', transport: 'socket'
log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
Disconnected from the target VM, address: '127.0.0.1:61942', transport: 'socket'
method:sum
params:[5, 4]
9
method:sub
params:[5, 4]
1

Process finished with exit code 0


Process finished with exit code 0

Комментариев нет :

Отправить комментарий