
Aew pessoal!
Agora irei mostrar em C# o exemplo de como recuperar informações de super classes usando Reflection. Irei repetir o mesmo texto, trocando apenas o que for necessário para o C#. Inicialmente, apesar de tentar ao máximo evitar o uso de Reflection, em alguns cenários ela é muito útil. Contudo, indico o uso apenas se o durante uma análise ficar claro que seu uso é indicado.
No meu exemplo irei realizar a chamada de um método privado de uma classe abstrata utilizando uma nested (implementação de uma classe dentro de outra). Poderia ser chamado diretamente o método privado usando Reflection. Contudo, o uso de uma classe nested é interessante neste cenário apenas se muitos métodos privados precisam ser acessados. Caso contrário, seria preciso realizar várias chamadas por Reflection. Desta forma, é usado Reflection apenas uma vez, para recuperar a instância da classe Nested.
Esta solução é indicada apenas se as sub classes não podem herdar estes métodos, ou seja, invocar os métodos. Sendo assim, em um grupo de classes podemos restringir o uso de Reflection e em outro grupo permitir este acesso. Em um próximo artigo tratarei de como bloquear o uso de Reflection.
No diagrama de classe são mostradas as classes e seus relacionamentos. O método InvokeNested() na classe ReflectionClass recupera por Reflection a instância de INested. Após recuperar essa instância é executado o método NestedMethod(), que indiretamente realiza a chamada de PrivateMethod() da classe Abstract01.
No método InvokeNested() a partir do tipo da classe ReflectionClass utilizando GetType(), são realizadas chamadas recursivas pelas super classes até ser encontrado a propriedade desejada através de seu nome. Para as chamadas recursivas utilizamos um extension method para Type com o nome GetPropertyFromBaseType(PropertyName). É recuperado o valor da propriedade através da chamada de property.GetValue(this, null). Ao final, é realizada a chamada de nestedValue.NestedMethod(). A seguir é exibido o código da ReflectionClass e ExtensionType.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using WPattern.Example01.ExtensionMethods;
using WPattern.Example01.Abstracts;
using WPattern.Example01.Interfaces;
namespace WPattern.Example01
{
public class ReflectionClass : Abstract03
{
#region Constants
private const String PropertydName = "NestedClass";
#endregion
#region Public Methods
public void InvokeNested()
{
PropertyInfo property = GetType().GetPropertyFromBaseType(PropertydName);
INested nestedValue = (INested)property.GetValue(this, null);
nestedValue.NestedMethod();
}
#endregion
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
namespace WPattern.Example01.ExtensionMethods
{
public static class ExtentionType
{
private const BindingFlags Flags = BindingFlags.Public | BindingFlags.NonPublic
| BindingFlags.Static | BindingFlags.Instance
| BindingFlags.DeclaredOnly;
public static PropertyInfo GetPropertyFromBaseType(this Type type, String propertyName)
{
PropertyInfo property = type.GetProperty(propertyName, Flags);
return (property != null) ? property : GetPropertyFromBaseType(type.BaseType, propertyName);
}
}
}
É possível usar o operador Null-coalescing no lugar da comparação que é retornada. Eu não coloquei aqui no código pois ocorreu um problema no JQuery do Syntax Highlighter rs.
A classe Abstract01 é muito simples, já que possui apenas o método privado PrivateMethod() chamado pela classe Nested. É interessante observar que conseguimos ter acesso ao método privado através da classe Nested caso seja passada a referência de Abstract01. Essa implementação é mostrada abaixo.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WPattern.Example01.Interfaces;
namespace WPattern.Example01.Abstracts
{
public class Abstract01 : IAbstract
{
#region Properties
private INested NestedClass { get; set; }
#endregion
#region Constructors
public Abstract01()
{
NestedClass = new Nested(this);
}
#endregion
#region Private Methods
private void PrivateMethod()
{
Console.WriteLine("Private method invoked in Abstract01.");
}
#endregion
#region Nested Class
private class Nested : INested
{
private Abstract01 AbstractObject01 { get; set; }
public Nested(Abstract01 abstract01)
{
AbstractObject01 = abstract01;
}
public void NestedMethod()
{
AbstractObject01.PrivateMethod();
}
}
#endregion
}
}
O projeto completo é muito simples e pode ser encontrado em wpattern.codeplex.com (examples => c# => WPattern.Example01).
Até mais pessoal, abraços.

Aew pessoal!
Hoje vou falar rapidamente de alguns estudos que estou fazendo para recuperar informações de super classes usando Reflection. Apesar de tentar ao máximo evitar o uso de Reflection, em alguns cenários ela é muito útil. Contudo, indico o uso apenas se o durante uma análise ficar claro que seu uso é indicado.
No meu exemplo irei realizar a chamada de um método privado de uma classe abstrata utilizando uma nested (implementação de uma classe dentro de outra). Poderia ser chamado diretamente o método privado usando Reflection. Contudo, o uso de uma classe nested é interessante neste cenário apenas se muitos métodos privados precisam ser acessados. Caso contrário, seria preciso realizar várias chamadas por Reflection. Desta forma, é usado Reflection apenas uma vez, para recuperar a instância da classe Nested.
Esta solução é indicada apenas se as sub classes não podem herdar estes métodos, ou seja, invocar os métodos. Sendo assim, em um grupo de classes podemos restringir o uso de Reflection e em outro grupo permitir este acesso. Em um próximo artigo tratarei de como bloquear o uso de Reflection.
No diagrama de classe são mostradas as classes e seus relacionamentos. O método invokeNested() na classe ReflectionClass recupera por Reflection a instância de INested. Após recuperar essa instância é executado o método nestedMethod(), que indiretamente realiza a chamada de privateMethod() da classe Abstract01.
No método invokeNested() a partir do tipo da classe ReflectionClass utilizando getClass(), são realizadas chamadas recursivas pelas super classes até ser encontrado o campo desejado através de seu nome. Como este campo é privado é muito importante que seja dada permissão de acesso fazendo a chamada de setAccessible(true). Caso contrário, não é possível recuperar o valor do campo através da chamada de fieldNested.get(this). Ao final, é realizada a chamada de nestedField.nestedMethod(). A seguir é exibido o código da ReflectionClass.
package br.com.wpattern.example;
import java.lang.reflect.Field;
import org.apache.log4j.Logger;
import br.com.wpattern.example.abstracts.Abstract03;
import br.com.wpattern.example.interfaces.INested;
public class ReflectionClass extends Abstract03 {
private static final Logger logger = Logger.getLogger(ReflectionClass.class);
private static final String FIELD_NAME = "nested";
public void invokeNested() {
try {
Field fieldNested = getField(getClass(), FIELD_NAME);
fieldNested.setAccessible(true);
INested nestedField = (INested)fieldNested.get(this);
nestedField.nestedMethod();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
private static Field getField(Class<?> classType, String fieldName) throws NoSuchFieldException {
try {
return classType.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
Class<?> superClass = classType.getSuperclass();
if (superClass == null) {
throw e;
} else {
return getField(superClass, fieldName);
}
}
}
}
A classe Abstract01 é muito simples, já que possui apenas o método privado privateMethod() chamado pela classe Nested. É interessante observar que conseguimos ter acesso ao método privado através da classe Nested. Essa implementação é mostrada abaixo.
package br.com.wpattern.example.abstracts;
import org.apache.log4j.Logger;
import br.com.wpattern.example.interfaces.IAbstract;
import br.com.wpattern.example.interfaces.INested;
class Abstract01 implements IAbstract {
private static final Logger logger = Logger.getLogger(Abstract01.class);
@SuppressWarnings("unused")
private INested nested = new Nested();
private void privateMethod() {
logger.info("Private method invoked in Abstract01.");
}
private class Nested implements INested {
@Override
public void nestedMethod() {
Abstract01.this.privateMethod();
}
}
}
O projeto completo é muito simples e pode ser encontrado em wpattern.codeplex.com (examples => java => wpattern-example01).
No próximo post irei demonstrar o mesmo projeto em .NET (C#).
Até mais pessoal, abraços.

Olá pessoal.
Apesar de já ter muito tempo que não público artigos, passei este período trabalhando em uma aplicação em Java e outra em .NET para postar no blog e ajudar meus alunos. Hoje irei escrever um pouco de uma parte do sistema em Java que estou trabalhando. Futuramente irei postar mais informações no blog, incluindo vídeos, diagramas e os códigos.
O código que irei mostrar é uma melhoria do tutorial de Introdução ao Hibernate. O objetivo é tornar os serviços mais simples de serem implementados e realizar o parser entre Entidades e Beans e vice-versa. Para realizar o parser é utilizada a biblioteca Dozer. Essa biblioteca é muito poderosa, permitindo a configuração de como é realizado o parser. Além disso, ela trata de beans complexos e é fácil de ser utilizada. Para adicionar essa biblioteca utilizando o maven temos o dependency:
<dependency>
<groupId>net.sf.dozer</groupId>
<artifactId>dozer</artifactId>
<version>5.3.2</version>
</dependency>
Porém, com a adição dessa dependência ocorreram conflitos com a slf4j-log4j12. Sendo assim, para o meu cenário foi necessário remover as dependências da SLF4J. Sendo assim, temos
<dependency>
<groupId>net.sf.dozer</groupId>
<artifactId>dozer</artifactId>
<version>5.3.2</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
</exclusion>
</exclusions>
</dependency>
Utilizando a Dozer o parser de objetos mais simples é realizado especificando o objeto que possui os valores e o tipo de destino que será feita a conversão. Uma vez que foi realizado o parser é retornado o objeto do tipo de destino. O código a seguir mostra um exemplo do método de parser de beans para entidades.
private static final Mapper dozerMapper = new DozerBeanMapper();
public static <S extends BaseBean, D extends BaseEntity> D parserBeanToEntity(S sourceObject, Class<D> destinationType) {
return dozerMapper.map(sourceObject, destinationType);
}
Para realizar o parser em geral de entidades, beans e keys, temos:
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import org.dozer.DozerBeanMapper;
import org.dozer.Mapper;
import br.com.wpattern.northwind.utils.beans.BaseBean;
public class ParserDatabase {
private static final Mapper dozerMapper = new DozerBeanMapper();
public static <S extends BaseBean, D extends BaseEntity> D parserBeanToEntity(S sourceObject, Class<D> destinationType) {
return dozerMapper.map(sourceObject, destinationType);
}
public static <S extends BaseBean, D extends BaseEntity> List<D> parserListBeansToEntities(List<S> sourceObjects, Class<D> destinationType) {
List<D> destinationObjects = new ArrayList<D>();
for (Object obj : sourceObjects) {
destinationObjects.add(dozerMapper.map(obj, destinationType));
}
return destinationObjects;
}
public static <S extends BaseEntity, D extends BaseBean> D parserEntityToBean(S sourceObject, Class<D> destinationType) {
return dozerMapper.map(sourceObject, destinationType);
}
public static <S extends BaseEntity, D extends BaseBean> List<D> parserListEntitiesToBeans(List<S> sourceObjects, Class<D> destinationType) {
List<D> destinationObjects = new ArrayList<D>();
for (Object obj : sourceObjects) {
destinationObjects.add(dozerMapper.map(obj, destinationType));
}
return destinationObjects;
}
public static <IDB extends Serializable, IDE extends Serializable> IDE parserKeyBeanToKeyEntity(IDB sourceObject, Class<IDE> destinationType) {
return dozerMapper.map(sourceObject, destinationType);
}
public static <IDE extends Serializable, IDB extends Serializable> IDB parserKeyEntityToKeyBean(IDE sourceObject, Class<IDB> destinationType) {
return dozerMapper.map(sourceObject, destinationType);
}
}
Com a classe ParserDatabase conseguimos realizar o parser em geral dos objetos usados em um módulo de acesso ao banco de dados e serviços.
Além do ParserDatabase, implementei uma classe abstrata para tornar o desenvolvimento de serviços básicos mais simples. Nem sempre um serviço utilizara esses métodos de "CRUD", porém, essa classe deixa o trabalho de criação destes serviços muito simples. Inicialmente, temos a classe GenericService que possui alguns métodos otimizados quando o identificador de uma entidade é um tipo de dado básico (em geral é um Long). Está classe é mostrada a seguir.
import java.io.Serializable;
import java.util.List;
import br.com.wpattern.northwind.utils.beans.BaseBean;
import br.com.wpattern.northwind.utils.database.interfaces.IServiceBase;
public abstract class GenericService<T extends BaseBean, K extends BaseEntity, ID extends Serializable>
extends GenericServiceWithKey<T, K, ID, ID> implements IServiceBase<T, ID> {
/////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS (IServiceBase)
/////////////////////////////////////////////////////////////////////////////////
@Override
public T findById(ID id) {
return parserEntity(this.getConcreteDao().findById(id));
}
@Override
public List<T> findAll() {
return parserEntity(this.getConcreteDao().findAll());
}
@Override
public ID insert(T bean) {
return getConcreteDao().save(parserBean(bean));
}
@Override
public void update(T bean) {
this.getConcreteDao().update(parserBean(bean));
}
@Override
public void delete(T bean) {
this.getConcreteDao().delete(parserBean(bean));
}
}
Na GenericService especificamos o tipo da entidade e o bean correspondente. Além disso, definimos o tipo da chave usada pela entidade. É interessante observar que T estende BaseBean e K estende BaseEntity. Com isso evitamos possíveis erros quando as classes de serviço herdarem este abstract. Abaixo apresento a classe GenericServiceWithKey que é herdada pela GenericService. A classe GenericServiceWithKey trabalha da mesma forma que GenericService. Contudo, GenericServiceWithKey trata de chaves compostas de entidades. A seguir mostro a implementação da classe GenericServiceWithKey.
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import org.apache.log4j.Logger;
import br.com.wpattern.northwind.database.utils.interfaces.IGenericDao;
import br.com.wpattern.northwind.utils.beans.BaseBean;
import br.com.wpattern.northwind.utils.database.interfaces.IServiceBase;
public abstract class GenericServiceWithKey<T extends BaseBean, K extends BaseEntity, IDE extends Serializable, IDB extends Serializable> implements IServiceBase<T, IDB> {
private final Logger logger = Logger.getLogger(this.getClass());
private final Class<T> typeOfBean;
private final Class<K> typeOfEntity;
private final Class<IDE> typeOfKeyEntity;
private final Class<IDB> typeOfKeyBean;
/////////////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
/////////////////////////////////////////////////////////////////////////////////
@SuppressWarnings("unchecked")
public GenericServiceWithKey() {
try {
ParameterizedType parameterizedType = (ParameterizedType)getClass().getGenericSuperclass();
Type[] genericTypes = parameterizedType.getActualTypeArguments();
this.typeOfBean = (Class<T>)genericTypes[0];
this.typeOfEntity = (Class<K>)genericTypes[1];
this.typeOfKeyEntity = (Class<IDE>)genericTypes[2];
this.typeOfKeyBean = (Class<IDB>)genericTypes[(genericTypes.length == 4) ? 3 : 2];
} catch (RuntimeException e) {
this.logger.error(e.getMessage(), e);
// Re-throw the exception.
throw e;
}
if (this.logger.isInfoEnabled()) {
this.logger.info(String.format("Creating a service with the bean [%s], entity [%s], key bean [%s] and key entity [%s].",
getTypeOfBean(), getTypeOfEntity(), getTypeOfKeyBean(), getTypeOfKeyEntity()));
}
}
/////////////////////////////////////////////////////////////////////////////////
// ABSTRACT METHODS
/////////////////////////////////////////////////////////////////////////////////
protected abstract IGenericDao<K, IDE> getConcreteDao();
/////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS (IServiceBase)
/////////////////////////////////////////////////////////////////////////////////
@Override
public T findById(IDB id) {
return parserEntity(this.getConcreteDao().findById(parserKeyBean(id)));
}
@Override
public List<T> findAll() {
return parserEntity(this.getConcreteDao().findAll());
}
@Override
public IDB insert(T bean) {
return parserKeyEntity(getConcreteDao().save(parserBean(bean)));
}
@Override
public void update(T bean) {
this.getConcreteDao().update(parserBean(bean));
}
@Override
public void delete(T bean) {
this.getConcreteDao().delete(parserBean(bean));
}
/////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS (GET)
/////////////////////////////////////////////////////////////////////////////////
public final Class<T> getTypeOfBean() {
return this.typeOfBean;
}
public final Class<K> getTypeOfEntity() {
return this.typeOfEntity;
}
public final Class<IDE> getTypeOfKeyEntity() {
return this.typeOfKeyEntity;
}
public final Class<IDB> getTypeOfKeyBean() {
return this.typeOfKeyBean;
}
/////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
/////////////////////////////////////////////////////////////////////////////////
public final T parserEntity(K entity) {
return ParserDatabase.parserEntityToBean(entity, getTypeOfBean());
}
public final K parserBean(T bean) {
return ParserDatabase.parserBeanToEntity(bean, getTypeOfEntity());
}
public final IDB parserKeyEntity(IDE idEntity) {
return ParserDatabase.parserKeyEntityToKeyBean(idEntity, getTypeOfKeyBean());
}
public final IDE parserKeyBean(IDB idBean) {
return ParserDatabase.parserKeyBeanToKeyEntity(idBean, getTypeOfKeyEntity());
}
public final List<T> parserEntity(List<K> entities) {
return ParserDatabase.parserListEntitiesToBeans(entities, getTypeOfBean());
}
public final List<K> parserBean(List<T> beans) {
return ParserDatabase.parserListBeansToEntities(beans, getTypeOfEntity());
}
}
Entretanto, existe um problema na implementação da GenericServiceWithKey. Na chamada por reflexão da super classe não necessariamente teremos um classe GenericService ou GenericServiceWithKey. Sendo assim, é preciso que recursivamente seja(m) recuperada(s) a(s) super(s) classe(s), e assim sejam extraídos os tipos genéricos. Em um próximo post irei falar sobre a solução deste problema.
A partir dessas classes podemos criar os serviços. A seguir apresento um exemplo de serviço desenvolvida seguindo estes conceitos.
import javax.inject.Inject;
import javax.inject.Named;
import br.com.wpattern.northwind.database.entities.CustomerCustomerDemoEntity;
import br.com.wpattern.northwind.database.entities.keys.CustomerCustomerDemoKeyEntity;
import br.com.wpattern.northwind.database.interfaces.ICustomerCustomerDemoDao;
import br.com.wpattern.northwind.database.utils.GenericServiceWithKey;
import br.com.wpattern.northwind.database.utils.interfaces.IGenericDao;
import br.com.wpattern.northwind.utils.database.beans.CustomerCustomerDemoBean;
import br.com.wpattern.northwind.utils.database.interfaces.ICustomerCustomerDemoService;
import br.com.wpattern.northwind.utils.database.keys.CustomerCustomerDemoKeyBean;
@Named
public class CustomerCustomerDemoService extends GenericServiceWithKey<CustomerCustomerDemoBean, CustomerCustomerDemoEntity, CustomerCustomerDemoKeyBean, CustomerCustomerDemoKeyEntity>
implements ICustomerCustomerDemoService {
@Inject
private ICustomerCustomerDemoDao customerCustomerDemoDao;
@Override
protected IGenericDao<CustomerCustomerDemoEntity, CustomerCustomerDemoKeyEntity> getConcreteDao() {
return this.customerCustomerDemoDao;
}
}
Um melhoria que estou planejando de fazer é de recuperar no próprio abstract a instância do DAO. O código completo do projeto que estou trabalhando pode ser encontrada em wpattern.codeplex.com (sample projects => java => wpattern-northwind-sample => wpattern-northwind-all).
Nos próximos posts irei explicar em detalhes este sistema.
Até mais a todos.
Este é o último post que trata da injeção de valores usando reflexão e anotação.
É importante lembrar que este projeto pode ser baixado em http://iar.codeplex.com/
Para finalizar nosso trabalho foram criados alguns testes unitários que mostram o uso da injeção de valores em algumas classes. Existem diversos cenários em que este processo pode ser aplicado. Sendo assim, apenas alguns exemplos foram mostrados para demonstrar a abrangência do trabalho.
Um exemplo de classe com as anotações criadas é mostrado a seguir na classe PrivateStub03.
package br.com.wpattern.annotation.test.stub;
import java.util.Date;
import br.com.wpattern.annotation.WPatternClass;
import br.com.wpattern.annotation.WPatternField;
import br.com.wpattern.annotation.WPatternValue;
@WPatternClass(description="Private Stub 03")
public class PrivateStub03 {
@WPatternField(name="PARAM01")
private boolean booleanParameter;
@WPatternField(name="PARAM02", values={ @WPatternValue(value="1"), @WPatternValue(value="2"), @WPatternValue(value="3"), @WPatternValue(value="4") })
private int intParameter;
@WPatternField(name="PARAM03")
protected long longParameter;
@WPatternField(name="PARAM04")
protected char charParameter;
@WPatternField(name="PARAM05")
public double doubleParameter;
@WPatternField(name="PARAM06")
public float floatParameter;
@WPatternField(name="PARAM07")
public Date dateParameter;
@Override
public String toString() {
return "PrivateStub03 [booleanParameter=" + this.booleanParameter
+ ", intParameter=" + this.intParameter + ", longParameter="
+ this.longParameter + ", charParameter=" + this.charParameter
+ ", doubleParameter=" + this.doubleParameter + ", floatParameter="
+ this.floatParameter + ", dateParameter=" + this.dateParameter + "]";
}
}
Na classe PrivateStub03 existe a demonstração do uso das anotações, incluindo a validação dos valores que são aceitados em um dos parâmetros. O teste unitário exibido no próximo código demonstra a injeção dos valores para a classe PrivateStub03.
private final Logger logger = Logger.getLogger(this.getClass());
private MapFields mapFields;
@Before
public void startUp() {
this.mapFields = new MapFields();
try {
this.mapFields.AddField("PARAM01", "true");
this.mapFields.AddField("PARAM02", "4");
this.mapFields.AddField("PARAM03", "6");
this.mapFields.AddField("PARAM04", "c");
this.mapFields.AddField("PARAM05", "5.45");
this.mapFields.AddField("PARAM06", "9.78");
this.mapFields.AddField("PARAM07", "01-01-2000 01:01:01");
} catch (MapFieldException e) {
this.logger.error(e.getMessage());
Assert.fail(e.getMessage());
}
}
@Test
public void privateStub03_testAllInjection_success() {
this.logger.debug("Test injection of PrivateStub03.");
PrivateStub03 instanceObject = new PrivateStub03();
try {
InjectorManager.injectAllValues(instanceObject, this.mapFields);
} catch (InjectionException e) {
this.logger.error(e.getMessage());
Assert.fail(e.getMessage());
}
this.logger.debug(instanceObject.toString());
}
O resultado da execução deste teste é:
17-11-2011 02:17:06,587; [main]; DEBUG; br.com.wpattern.annotation.test.AnnotationTest; - Test injection of PrivateStub03.
17-11-2011 02:17:06,777; [main]; DEBUG; br.com.wpattern.annotation.test.AnnotationTest; - PrivateStub03 [booleanParameter=true, intParameter=4, longParameter=6, charParameter=C, doubleParameter=5.45, floatParameter=9.78, dateParameter=Sat Jan 01 01:01:01 BRST 2000]
Como podemos observar os valores foram automaticamente injetados nas variáveis.
O próximo vídeo mostra a implementação e a execução dos testes unitários.
Foi um prazer poder apresentar este trabalho.
Até o próximo estudo, abraços.
Neste post irei tratar da injeção dos valores e da validação dos parâmetros presentes no MapFields. Este trabalho continua os textos apresentados anteriormente.
É importante lembrar que este projeto pode ser baixado em http://iar.codeplex.com/
Existem duas classes usadas para realizar a injeção dos valores, sendo elas: ValidatorManager e InjectorManager. A classe ValidatorManager gerencia a validação das anotações. Essa validação é bem simples e corresponde a um simples exemplo de como trabalhar com as anotações. A classe InjectorManager realiza a injeção dos valores nas variáveis que estão anotadas. A seguir são mostradas estas classes.
package br.com.wpattern.annotation.injection;
import java.lang.reflect.Field;
import java.util.Date;
import br.com.wpattern.annotation.WPatternField;
import br.com.wpattern.annotation.exception.InjectionException;
import br.com.wpattern.annotation.util.ErrorMessages;
class ValidatorManager {
//=====================================================================================
// PUBLIC METHODS
//=====================================================================================
public static void validateFields(Object objectInstance) throws InjectionException {
Class<?> objectClass = objectInstance.getClass();
Field[] fields = objectClass.getDeclaredFields();
for (Field field : fields) {
WPatternField annotationField = field.getAnnotation(WPatternField.class);
if (annotationField != null) {
if (annotationField.required()) {
if (!isValidRequeridField(field)) {
throw new InjectionException(String.format(ErrorMessages.FIELD_WITH_INVALID_TYPE,
annotationField.name(), field.getType()));
}
} else if (!isValidNotRequeridField(field)) {
throw new InjectionException(String.format(ErrorMessages.FIELD_WITH_INVALID_TYPE,
annotationField.name(), field.getType()));
}
}
}
}
//=====================================================================================
// PRIVATE METHODS
//=====================================================================================
private static boolean isValidRequeridField(Field field) {
return !((field.getType() != int.class) && (field.getType() != long.class) && (field.getType() != boolean.class) &&
(field.getType() != char.class) && (field.getType() != double.class) && (field.getType() != float.class) &&
(field.getType() != Date.class));
}
private static boolean isValidNotRequeridField(Field field) {
return !((field.getType() != int.class) && (field.getType() != Integer.class) &&
(field.getType() != long.class) && (field.getType() != Long.class) &&
(field.getType() != boolean.class) && (field.getType() != Boolean.class) &&
(field.getType() != char.class) && (field.getType() != Character.class) &&
(field.getType() != double.class) && (field.getType() != Double.class) &&
(field.getType() != float.class) && (field.getType() != Float.class) &&
(field.getType() != Date.class));
}
}
package br.com.wpattern.annotation.injection;
import java.lang.reflect.Field;
import java.util.Date;
import br.com.wpattern.annotation.WPatternField;
import br.com.wpattern.annotation.WPatternValue;
import br.com.wpattern.annotation.exception.FormatFieldException;
import br.com.wpattern.annotation.exception.InjectionException;
import br.com.wpattern.annotation.exception.MapFieldException;
import br.com.wpattern.annotation.util.ErrorMessages;
import br.com.wpattern.annotation.util.MapFields;
public class InjectorManager {
//=====================================================================================
// PUBLIC METHODS
//=====================================================================================
/**
* Inject just public fields.
*
* @param objectInstance
*/
public static void injectValues(Object objectInstance, MapFields mapFields) throws InjectionException {
ValidatorManager.validateFields(objectInstance);
Class<?> objClass = objectInstance.getClass();
Field[] fields = objClass.getFields();
for (Field field : fields) {
WPatternField annotationField = field.getAnnotation(WPatternField.class);
if (annotationField != null) {
injectValue(field, annotationField, objectInstance, mapFields);
}
}
}
/**
* Inject all types (public, protected, private, final) of fields.
*
* @param objectInstance
*/
public static void injectAllValues(Object objectInstance, MapFields mapFields) throws InjectionException {
ValidatorManager.validateFields(objectInstance);
Class<?> objClass = objectInstance.getClass();
Field[] fields = objClass.getDeclaredFields();
for (Field field : fields) {
WPatternField annotationField = field.getAnnotation(WPatternField.class);
if (annotationField != null) {
field.setAccessible(true);
injectValue(field, annotationField, objectInstance, mapFields);
}
}
}
//=====================================================================================
// PRIVATE METHODS
//=====================================================================================
private static void injectValue(Field field, WPatternField fieldAnnotation, Object objectInstance, MapFields mapFields) throws InjectionException {
Object value = null;
try {
validateValue(fieldAnnotation, mapFields);
if ((field.getType() == int.class) || (field.getType() == Integer.class)) { // INTEGER
value = mapFields.getIntValue(fieldAnnotation.name());
} else if ((field.getType() == long.class) || (field.getType() == Long.class)) { // LONG
value = mapFields.getLongValue(fieldAnnotation.name());
} else if ((field.getType() == boolean.class) || (field.getType() == Boolean.class)) { // BOOLEAN
value = mapFields.getBooleanValue(fieldAnnotation.name());
} else if ((field.getType() == char.class) || (field.getType() == Character.class)) { // CHARACTER
value = mapFields.getCharacterValue(fieldAnnotation.name());
} else if ((field.getType() == double.class) || (field.getType() == Double.class)) { // DOUBLE
value = mapFields.getDoubleValue(fieldAnnotation.name());
} else if ((field.getType() == float.class) || (field.getType() == Float.class)) { // FLOAT
value = mapFields.getFloatValue(fieldAnnotation.name());
} else if (field.getType() == Date.class) { // DATE
value = mapFields.getDateValue(fieldAnnotation.name());
}
} catch (MapFieldException e) {
if (fieldAnnotation.required()) {
throw new InjectionException(e.getMessage(), e);
}
} catch (FormatFieldException e) {
throw new InjectionException(e.getMessage(), e);
}
try {
field.set(objectInstance, value);
} catch (IllegalArgumentException e) {
throw new InjectionException(e.getMessage(), e);
} catch (IllegalAccessException e) {
throw new InjectionException(e.getMessage(), e);
}
}
private static void validateValue(WPatternField fieldAnnotation, MapFields mapFields) throws MapFieldException, InjectionException {
if (fieldAnnotation.values().length <= 0) {
return;
}
String value = mapFields.getValue(fieldAnnotation.name());
for (WPatternValue wPatternValue : fieldAnnotation.values()) {
if (wPatternValue.value().equalsIgnoreCase(value)) {
return;
}
}
throw new InjectionException(String.format(ErrorMessages.FIELD_WITH_INVALID_VALUE, fieldAnnotation.name(), value));
}
}
A injeção dos valores é realizada de acordo com os tipos das variáveis. Se o campo não é requerido a falta do parâmetro no MapFields tem como resultado a injeção de um valor null na variável anotada. Os detalhes deste código são mostrados no vídeo a seguir.
Finalizamos o trabalho de injeção neste post. No próximo e último post eu apresento testes unitários para demonstrar o uso da injeção de valores baseado nas anotações.
Até o próximo post.