Chamando Métodos Privados em Super Classes usando Reflection (C#)

26. fevereiro 2012 20:00 by BBranquinho in C#, Reflection, Tutoriais  //  Tags: ,   //   Comentários (0)


Diagrama de classes do exemplo de como acessar métodos privados em super classes usando <em>Reflection</em>.

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.

Chamando Métodos Privados em Super Classes usando Reflection (Java)

26. fevereiro 2012 18:54 by BBranquinho in Exemplos, Java, Reflection  //  Tags: ,   //   Comentários (0)


Diagrama de classes do exemplo de como acessar métodos privados em super classes usando <em>Reflection</em>.

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.

Criando um Serviço Genérico de Banco de Dados e Mapeamento Automático entre Entidades e Bean (Java)

24. fevereiro 2012 22:19 by BBranquinho in Java, Reflection  //  Tags: , , ,   //   Comentários (0)


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.

Injeção de Valores usando Reflection e Annotation em Java (Testes Unitários) - Parte 05

17. novembro 2011 00:10 by BBranquinho in Annotation, Java, Reflection, Tutoriais  //  Tags: , , ,   //   Comentários (0)

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.

Injeção de Valores usando Reflection e Annotation em Java (Injeção e Validação dos Valores) - Parte 04

16. novembro 2011 23:51 by BBranquinho in Annotation, Java, Reflection, Tutoriais  //  Tags: , , ,   //   Comentários (0)

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.

WPattern

O objetivo deste blog é de disponibilizar os resultados dos meus estudos. Não será tratada apenas uma tecnologia ou área, mas qualquer tópico que seja interessante e possa contribuir com a comunidade de desenvolvedores, comunidade científica e meus alunos. Entretanto, meus trabalhos sempre estão voltado principalmente para as tecnologias .NET, Java e C/C++.

Posts Recentes

Google Translator

JetBrains

JetBrains: Ferramentas de desenvolvimento com .NET.

ReSharper: Excelente ferramenta para .NET.