Java annotation - Форум администраторов игровых серверов
Форум администраторов игровых серверов StormWall - Защита от DDos атак
Регистрация Мнения Справка Пользователи Календарь Все разделы прочитаны
Вернуться   Форум администраторов игровых серверов > Полезное / Common > Программирование / Programming > Java

Java В данном форуме вы сможете найти много полезной информации по платформе Java. Подробные статьи, исходные коды и конечно учебники как для новичков так и профессиональных Java разработчиков вы найдёте здесь. Если у вас есть вопрос или хотите поделится своими наработками, пожалуйста, делайте это в этой теме.
Описание темы:Как готовить, с чем есть.

Ответ
Опции темы
Непрочитано 28.08.2011, 18:51   #1
Аватар для Azagthtot
Эксперт

Автор темы (Топик Стартер) Java annotation

Java предлагает разработчику очень интересный механизм аннотаций.
Что такое аннотация? А это все, что начинается с @ например,
@Override
Однако не секрет, что многие неплохие l2 разработчики не пользуются ничем кроме вышеупомянутой @Override, да и вообще плохо представляют преимущества этого механизма. То что описано ниже, предназначено "пролить луч света в темном царстве".
И так.
Что такое аннотация, и как ее можно использовать в мирных целях.
Начнем с того, какие этапы бывают в жизни программы.
Этап первый - написание (зачатие) он нам совершенно не интересен
Этап второй - компиляция. Здесь уже возможно использование аннотаций. Например та же @Override говорит компилятору "пройдись по суперкласам, поищи метод с тем же именем и теми же параметрами. Не найдешь - скажи программисту, что он идиот". Этот этап интересен, но не очень.
Этап третий - выполнение. Вот тут мы и будем творить.
На этом этапе аннотация становится частью RTTI. Что такое RTTI?
Это Run-Time Type Information.Что, не знаете что такое? Однако пользуетесь Пример?
a instanceof Object
это типичное использование RTTI, ибо класс объекта есть его элемент.
Вообще, для работы с RTTI есть куча методов класса Class (собственно класс Class это и есть RTTI).
И так, зачем мы можем использовать аннотации?
Вообще-то, использование аннотаций основано на принципе "лучше полдня потерять, потом за 5 мин долететь". Вот простой, и жизненный пример.
У нас есть xml файл вида
Код:
<?xml version="1.0" ?>
<objects>
 <object name="zzzz"/>
 <object name="mmm"/>
</objects>
И мы хотим загружать из него следующие объекты
Код:
public class NamedObject {
  public String name;
}
Нет ничего проще!
Код:
public class NamedObject {
  public String name;
  public void load(Node node) {
     name = node.getAttributes().getNamedItem("name").getNodeValue();
 }
}
А теперь, представьте, что ваша мысль несется в даль, и у нашего NameObject появилось еще поле title?
- Да не вопрос, строка кода!
А 100500 новых полей?
"у, мля" - сказали программисты и ушли писать код....
А теперь изображу то же самое с использование аннотаций
Код:
public class NamedObject extends AnnotatedXMLNode {
 @XMLField(property="name") 
 public String name;
}
Добавить поле?
Код:
@XMLField(nodename="weight",property="set") 
public int weight;
Добавить 100500 полей? Ну вы поняли....
Все дело в "волшебном классе" AnnotatedXMLNode. Вот его, да и сами аннотации мы будем разбирать ниже.

NB! Буду писать несколькими постами, с интервалом в 10-15 мин. С примерами. Просьба писать ваши вопросы после поста "на сегодня все".

Добавлено через 30 минут
И так, для начала напишем сами используемые аннотации.
Описание аннотации выглядит как описание интерфейса, только перед словом interface добавляется символ @
Код:
public @interface XMLField {
 String node() default "";
 String property();
}
Вы наверное обратили на отсутствие модификаторов видимости, и на слово default.
модификаторы не нужны, а default означает что данное поле можно не указывать.
т.е.
@XMLField(property="zzz",node="mmm") - верно
@XMLField(property="zzz") - верно
@XMLField(node="mmm") - не верно

И так, аннотацию мы сделали. Теперь неплохо бы сказать, когда она используется (компиляция, исполнение) и к какому типу применима (класс, поле). Делается это посредством... вы таки не поверите, но аннотаций

Код:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface XMLField {
 String node() default "";
 String property();
}
Во-первых, мы рассказали компилятору, что аннотация применима для поля (@Target), во вторых, сказали, что бы включил в RTTI (@Retention)
Ну вот, аннотацию написали. Теперь будем учится ползать по классу, и делать много нехороших вещей (например, плевать на модификаторы private и protected )
Собственно "скелет" нашего AnnotatedXMLNode
Код:
public class AnnotatedXMLNode {
 public void load(Node node) {
 }
}
Соответственно, для загрузки мы будем использовать метод load().
А вот теперь начнется самое интересное. Для начала нам надо найти все поля, во всех суперклассах имеющих аннотацию Node.
Как ищется аннотация? Да очень просто!
У класса Field есть метод isAnnotationPresent (впрочем, он есть у любого класса RTTI, как Class, Field, Method и т.д.)
Вот с помощью него мы и будем искать аннотации.
Кто-то спросит, а как полчить все поля класса?. Есть метод getDeclaredField у Class. Но тут есть грабли. Вот пример граблей.
Код:
public  class A {
   private int _a;
   public void check() {
      for(Field f : this.getClass().getDeclaredFields() ) {
           System.out.println(f.getName());
       }
    }
}
public class B extends A {
 public String _b;
}
...
...
B b = new B();
b.check();
Кто то ожидал увидеть вывод
_a
_b
?
А вот фиг вам! getDeclaredFields() работает ТОЛЬКО для текущего класса. Так что будет
_b
Как нам получить все поля?
А очень просто
Код:
public void check() {
      Class<?> clazz = getClass();
      while(clazz!=Object.class) { // Спасибо, поля Object нас не инетерсуют
         for(Field f : clazz.getDeclaredFields() ) {
             System.out.println(f.getName());
         }
         clazz = clazz.getSuperClass();
}
Мы идем по суперклассам до Object. И вытягиваем все поля.
}

Добавлено через 3 часа 53 минуты
Теперь начнем писать наш класс. Для начала соберем все поля, которые требуют чтения.
Код:
private void Map<String,Map<String,Field>> _fields = new HashMap<String,Map<String,Field>>();
private void collectFields() {
  Class<?> clazz = getClass();
  while(clazz!=AnnotatedXMLNode.class) {
       for(java.lang.reflect.Field f : clazz.getDeclaredFields() ) {
                if(f.isAnnotationPresent(XMLField.class)) { 
                         XMLField annotation = f.getAnnotation(XMLField.class);
                         Map<String, java.lang.reflect.Field> m = _fields.get(annotation.node());
                         if(m==null) {
                            m = new HashMap<String, java.lang.reflect.Field>();
                            _fields.put(annotation.node(),m);
                          }
                          m.put(annotation.property(),f);
                  }
               } 
            }
             clazz = clazz.getSuperClass();
       }
}
Таким образом, мы получим map содержащий в качестве ключа имя xml-узлов.
P.S. Уже поздно, дочь не дала дописать все за сегодня. Продолжу с утра

Добавлено через 16 часов 39 минут
Продолжаем изучение аннотаций. И так, мап с сответствием имя-поле мы получили, теперь вроде бы можем писать метод. Однако есть еще один "грабельки". Все поля у нас разного типа, а при работе с xml возвращается только тип String. Тут есть два способа. Первый - написать простейший сеттер. Второй - "подсмотреть" в Aion как сделаны трансфоромации STring --> type. Я приведу первый способ, работающий с двумя типами String и int
Код:
private void setFieldVlue(Field f, String value) throws Exception {
   f.setAccessible(true); // Это магический вызов :) Все protected и private поля становятся доступными. 
// А вы что хотели, область видимости - это не защита данных а инструмент для проектирования
   if(f.getType()==String.class) 
       f.set(this,value);
   else if(f.getType() == int.class)
        f.set(this,Integer.valueOf(value));
   else
      throw new Exception("Invalid field type");
}
Вот такой простенький метод. Ну а теперь поехали делать собственно загрузку
Код:
	public void load(Node node) {

		   collectFields(); // Собираем поля
		   Map<String,java.lang.reflect.Field> fields = _fields.get(""); // Берем поля, для которых надо использовать атрибуты из корневой ноды
		   if(fields!=null) {
		       for(Entry<String, java.lang.reflect.Field> e : fields.entrySet()) {
		          Node n = node.getAttributes().getNamedItem(e.getKey());
		          if(n!=null) try {
		        	   setFieldValue(e.getValue(),n.getNodeValue());
		          } catch(Exception err) {
		        	  System.out.println("Error setting field value");
		        	  err.printStackTrace();
		          }
		       }
		   }
		   for(Node n = node.getFirstChild();n!=null;n=n.getNextSibling()) {
		       fields = _fields.get(n.getNodeName()); 
		       if(fields!=null) {
		         for(Entry<String, java.lang.reflect.Field> e : fields.entrySet()) {
		            Node n1 = n.getAttributes().getNamedItem(e.getKey());
			          if(n1!=null) try {
			        	   setFieldValue(e.getValue(),n1.getNodeValue());
			          } catch(Exception err) {
			        	  System.out.println("Error setting field value");
			        	  err.printStackTrace();
			          }
		         }
		       }
		     }
	}
Теперь приступим к использованию, нашего чудо-класса
Собственно сам наш загружемай класс
Код:
public class MyXMLObject extends AnnotatedXMLNode {
	@XMLField(property="name")
	public String name;

	@Override
	public String toString() {
		return "Name: "+name;
	}

}
А теперь - загрузчик
Код:
public class Main {
	public static void main(String [] args) {
		DocumentBuilderFactory builder = DocumentBuilderFactory.newInstance();
		builder.setValidating(false);
		builder.setIgnoringComments(true);
		File file = new File("./sample.xml");
		try {
			Document doc = builder.newDocumentBuilder().parse(file);
			XPath xpath = XPathFactory.newInstance().newXPath(); // Итерировать? Нафиг! Есть Xpath!!!!
			XPathExpression expr = xpath.compile("//object");
			NodeList objects = (NodeList)expr.evaluate(doc, XPathConstants.NODESET);
			for(int i=0;i<objects.getLength();i++) {
				Node n = objects.item(i);
				MyXMLObject obj = new MyXMLObject();
				obj.load(n);
				System.out.println(obj);
			}
			
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
}
Обратите внимание, для поиска в XML я использую не поиск по нодам вручную,а Xpath. Это проще и удобнее.
Ну и сам XML-файл
Код:
<?xml version="1.0" ?>
<objects>
  <object name="First">
    <set value="1"/>
  </object>
  <object name="Second">
    <set value="2"/>
  </object>

</objects>
Запускаем...
Код:
G:\Samples\Annotations\build>java -cp ../bin Main
Name: First
Name: Second
Работает!!!! Добавляем поле value


Код:
public class MyXMLObject extends AnnotatedXMLNode {
	@XMLField(property="name")
	public String name;
	@XMLField(property="value",node="set")
	private int val;
	
	@Override
	public String toString() {
		return "Name: "+name+", val :"+val;
	}
}
Запускаем
Код:
G:\Samples\Annotations\build>java -cp ../bin Main
Name: First, val :1
Name: Second, val :2
Опять работает, странно, да?

Ну вот, на сегодня все, задавайте вопросы.
Ах, да. Все примеры в виде Eclipse-проекта - внизу
Вложения
Тип файла: zip Annotations.zip (11.0 Кб, 8 просмотров)

Последний раз редактировалось Azagthtot; 29.08.2011 в 12:03. Причина: Добавлено сообщение
Azagthtot вне форума Отправить сообщение для Azagthtot с помощью ICQ Отправить сообщение для Azagthtot с помощью Skype™ Ответить с цитированием
Непрочитано 02.09.2011, 23:47   #2
Пользователь

По умолчанию Re: Java annotation

Аннотации это хорошо, досихпор работаю с собственным фреймворком конфигурации на базе метаданных (с валидацией данных и кучей удобных плюшек) - доволен я, довольны знакомые.

Зачем изобретать JAXB и XMLBeans чтобы мапить поля объектов на xml? Если надо конфигурировать объекты через XML то целесообразнее использовать аналогичную систему с ANT'овыми файлом сборки.

Код:
private void setFieldVlue(Field f, String value)
прмитивы пойдут в разнос. посмотрите как это сделано в Spring Framework класс ReflectionUtils. Да и setAccessible() может швырнуть в лицо SecurityException при включенном SecurityManager (вечная головная боль). Плюс рекомендуется восстанавливать состояние доступа

Код:
boolean oldAccess = field.isAccessible();
field.setAccessible(true); // вне блока try/finally, можно внутри при случае если там не будет ожидаться другой SecurityException
try {
... код
} finally {
field.setAccessible(oldAccess);
}
P.S. Мало документации в коде, на аннотациях нет @Documented. Слишком комплексные методы - лучше бы их по сервисным классам распихать, либо на более мелкие поделить.
__________________
for(;Forum.getPostCount() < Integer.MAX_VALUE; Forum.writeNewPost()); | TERA Video | GamezTERA Emu
Aquanox вне форума Ответить с цитированием
Сказали спасибо:
Непрочитано 03.09.2011, 06:10   #3
Аватар для Azagthtot
Эксперт

Автор темы (Топик Стартер) Re: Java annotation

Спасибо за замечания. Однако, целью этого "урока" было показать как работают аннотации, а не использование готовых. Я сознательно "изобретал велосипед" в примерах, что бы было понятно как весь этот механизм работает.
То же самое я могу и сказать про setFieldValue. Я не ставил себе задачу сделать рабочий фреймворк.
Azagthtot вне форума Отправить сообщение для Azagthtot с помощью ICQ Отправить сообщение для Azagthtot с помощью Skype™ Ответить с цитированием
Сказали спасибо:
Непрочитано 03.09.2011, 12:26   #4
Аватар для Extez1
Пользователь

По умолчанию Re: Java annotation

Спасибо хорошая тема.
Extez1 вне форума Ответить с цитированием
Ответ


Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
 
Опции темы

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.

Быстрый переход

Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
L2J и java ee sdk 6u1 jdk TieLay Lineage II 2 06.11.2010 10:28
PTS и JAVA teshu Курилка / Yak floor 10 15.08.2010 20:52
Java EXP Shema Серверная часть 38 07.04.2010 02:05
PTS & Java PuShKinG Lineage II 7 20.08.2009 23:08


© 2007–2024 «Форум администраторов игровых серверов»
Защита сайта от DDoS атак — StormWall
Работает на Булке неизвестной версии с переводом от zCarot
Текущее время: 13:00. Часовой пояс GMT +3.

Вверх