'FRAMEWORK/SPRING'에 해당되는 글 39건

  1. 2013.08.20 Spring Bean Life Cycle
  2. 2013.07.31 springmodules의 valang을 활용한 Validation 테스트
  3. 2013.07.31 Spring Modules Validation
  4. 2013.07.25 MessageSource 를 이용할 경우 한글문제 (한글 ascii 변환)
  5. 2013.07.15 MessageSource를 이용한 텍스트 메시지 처리
  6. 2013.07.15 다국어 처리 - localeResolver, messageSource
  7. 2013.07.11 Spring AOP용어의 정리
  8. 2013.07.05 스프링 MVC의 주요 구성요소
  9. 2013.01.24 스프링 MVC-개요

Spring Bean Life Cycle

FRAMEWORK/SPRING 2013. 8. 20. 18:05

The life cycle of a Spring bean is easy to understand. When a bean is instantiated, it may be required to perform some initialization to get it into a usable state. Similarly, when the bean is no longer required and is removed from the container, some cleanup may be required.

Though, there is lists of the activities that take place behind the scenes between the time of bean Instantiation and its destruction, but this chapter will discuss only two important bean lifecycle callback methods which are required at the time of bean initialization and its destruction.

To define setup and teardown for a bean, we simply declare the <bean> with init-method and/ordestroy-method parameters. The init-method attribute specifies a method that is to be called on the bean immediately upon instantiation. Similarly, destroy-method specifies a method that is called just before a bean is removed from the container.

Initialization callbacks:

The org.springframework.beans.factory.InitializingBean interface specifies a single method:

void afterPropertiesSet() throws Exception;

So you can simply implement above interface and initialization work can be done inside afterPropertiesSet() method as follows:

public class ExampleBean implements InitializingBean {
   public void afterPropertiesSet() {
      // do some initialization work
   }
}

In the case of XML-based configuration metadata, you can use the init-method attribute to specify the name of the method that has a void no-argument signature. For example:

<bean id="exampleBean" 
         class="examples.ExampleBean" init-method="init"/>

Following is the class definition:

public class ExampleBean {
   public void init() {
      // do some initialization work
   }
}

Destruction callbacks

The org.springframework.beans.factory.DisposableBean interface specifies a single method:

void destroy() throws Exception;

So you can simply implement above interface and finalization work can be done inside destroy() method as follows:

public class ExampleBean implements DisposableBean {
   public void destroy() {
      // do some destruction work
   }
}

In the case of XML-based configuration metadata, you can use the destroy-method attribute to specify the name of the method that has a void no-argument signature. For example:

<bean id="exampleBean" 
         class="examples.ExampleBean" destroy-method="destroy"/>

Following is the class definition:

public class ExampleBean {
   public void destroy() {
      // do some destruction work
   }
}

If you are using Spring's IoC container in a non-web application environment; for example, in a rich client desktop environment; you register a shutdown hook with the JVM. Doing so ensures a graceful shutdown and calls the relevant destroy methods on your singleton beans so that all resources are released.

It is recommended that you do not use the InitializingBean or DisposableBean callbacks, because XML configuration gives much flexibility in terms of naming your method.

Example:

Let us have working Eclipse IDE in place and follow the following steps to create a Spring application:

StepDescription
1Create a project with a name SpringExample and create a package com.tutorialspoint under the src folder in the created project.
2Add required Spring libraries using Add External JARs option as explained in the Spring Hello World Example chapter.
3Create Java classes HelloWorld and MainApp under the com.tutorialspoint package.
4Create Beans configuration file Beans.xml under the src folder.
5The final step is to create the content of all the Java files and Bean Configuration file and run the application as explained below.

Here is the content of HelloWorld.java file:

package com.tutorialspoint;

public class HelloWorld {
   private String message;

   public void setMessage(String message){
      this.message  = message;
   }
   public void getMessage(){
      System.out.println("Your Message : " + message);
   }
   public void init(){
      System.out.println("Bean is going through init.");
   }
   public void destroy(){
      System.out.println("Bean will destroy now.");
   }
}

Following is the content of the MainApp.java file. Here you need to register a shutdown hookregisterShutdownHook() method that is declared on the AbstractApplicationContext class. This will ensures a graceful shutdown and calls the relevant destroy methods.

package com.tutorialspoint;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {
   public static void main(String[] args) {

      AbstractApplicationContext context = 
                          new ClassPathXmlApplicationContext("Beans.xml");

      HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
      obj.getMessage();
      context.registerShutdownHook();
   }
}

Following is the configuration file Beans.xml required for init and destroy methods:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <bean id="helloWorld" 
       class="com.tutorialspoint.HelloWorld"
       init-method="init" destroy-method="destroy">
       <property name="message" value="Hello World!"/>
   </bean>

</beans>

Once you are done with creating source and bean configuration files, let us run the application. If everything is fine with your application, this will print the following message:

Bean is going through init.
Your Message : Hello World!
Bean will destroy now.

Default initialization and destroy methods:

If you have too many beans having initialization and or destroy methods with the same name, you don't need to declare init-method and destroy-method on each individual bean. Instead framework provides the flexibility to configure such situation using default-init-method and default-destroy-methodattributes on the <beans> element as follows:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
    default-init-method="init" 
    default-destroy-method="destroy">

   <bean id="..." class="...">
       <!-- collaborators and configuration for this bean go here -->
   </bean>

</beans>



출처 - http://www.tutorialspoint.com/spring/spring_bean_life_cycle.htm

:

springmodules의 valang을 활용한 Validation 테스트

FRAMEWORK/SPRING 2013. 7. 31. 15:18

Expert Spring MVC and Web Flow (Expert)에 소개된 springmodules의 valang을 활용하면 Validator를 자바 코딩 없이 선언만으로 구현할 수 있다.(XML 설정만으로 가능하다)

복잡한 자체적인 문법을 갖고 있어서 테스트 필요성이 높다. Expert Spring MVC and Web Flow (Expert)의 p281에는 테스트 예제가 있다.

public class PersonValidatorTests extends TestCase {
public void testEmptyPersonValidation() {
Person person = new Person();
Validator validator = new PersonValidator();
BindException errors = new BindException(person, "target");
validator.validate(person, errors);
new ErrorsVerifier(errors) {
{
forProperty("firstName").hasErrorCode("person.firstName.required")
.forProperty("lastName").hasErrorCode("person.lastName.required")
.otherwise().noErrors();
}
}
}
}


ErrorsVerifier를 헬퍼(Helper class)로 사용하여 간결하게 작성했지만 문제가 있다. ErrorsVerifier 클래스는 spring은 물론 spring-modules에도 존재하지 않는다는 점이다. 구글을 통해 Spring 포럼에 올라온 글, 'Where is ErrorsVerifier class mentioned in Expert Spring MVC?'을 확인할 수 있었다.

하지만, spring-modules의 테스트 및 소스 코드를 조금 참조하면 어렵지 않게 테스트를 작성하 수 있다.

public class MemberValidationTest extends TestCase {
private Member member;
private ValangValidator validator;
BindException errors;
@Override
protected void setUp() throws Exception {
member = new Member();
errors = new BindException(member, "member");
validator = new ValangValidator();
}
public void testPasswordValidation() throws Exception{
validator.setValang(
   "{ password : ? is not blank : '' : 'member.password.empty' }"
);
validator.afterPropertiesSet(); // configure validation rules
validator.validate(member, errors);

assertEquals("member.password.empty", errors.getFieldError("password ").getCode());
}
}


spring-modules의 테스트는 spring의 컨텍스트 파일(xml 형식의) 설정을 통해 테스트를 한다. 그러나, 단위 테스트를 목적으로 한다면 외부 리소스에 의존하는 일은 가능한 제거하는 것이 좋다고 생각하여 위와 같이 직접 setValang()을 호출한 것이다. setValang()은 실제로는 다음과 같은 식으로 설정해야 한다.

<bean id="easyAddressValidator" class="org.springmodules.validation.ValangValidator ">
  <property name="valang">
  <value><![CDATA[{ password : ? is not blank : '' : 'member.password.empty' }]]></value>
  </property>
</bean>


spring-module의 의존성에 따라 테스트를 실행하려면, 다음 라이브러리가 필요하다.

  • commons-collections.jar


assertion을 위한 코드가 늘어서는 것도 가독성을 크게 저해하지는 않는다. 책에서의 의도와 비슷하게 Fluent Interface 스타일을 적용해서 조금 수정해봤다.

public void testPasswordValidation() throws Exception{
validator.setValang(
   "{ password : ? is not blank : '' : 'member.password.empty' }"
);
validator.afterPropertiesSet(); // configure validation rules
validator.validate(member, errors);
new ErrorsVerifier(errors)
  .forProperty("password").hasErrorCode("member.password.empty");
}
class ErrorsVerifier{
private Errors errors;
private String cursor; // one of successive properties

public ErrorsVerifier(BindException errors) {
  this.errors = errors;
}
public ErrorsVerifier forProperty(String property) {
  cursor = property;
  return this;
}
public ErrorsVerifier hasErrorCode(String errorCode) {
  assertEquals(errorCode, errors.getFieldError(cursor).getCode());
  return this;
}

}


아쉬운 것은 정작 감춰야 할 부분인 아래 구문이 그대로 노출된다는 점이다:
validator.afterPropertiesSet();

감추기


추가적으로, valang 사용시 주의할 사항 0순위는 조건문이라고 생각된다. 마치 오른쪽과 왼쪽이 헷갈리는 것처럼 valang으로 기입한 조건이 만족하지 않으면 BindError가 발생한다는 사실을 유념해야 한다. 간단한 valang 설정 샘플을 첨부한다.

validator.setValang(
   "{ id : length(?) >= 3 : '' : 'member.id.tooshort' }" +
   "{ password : length(?) >= 4 : '' : 'member.password.tooshort' }" +
   "{ name : length(?) >= 2 : '' : 'member.name.tooshort' }" +
   "{ alias : ? is blank and length(?) >= 2 : '' : 'member.alias.tooshort' }"
);

또한, valang의 기본적으로 제공하는 함수 중에 앞의 예에서 보는 length()와 함께 email()이 있는데 주의할 점은 is true와 함께 써야 의도한대로 작동한다는 점이다.

"{ email : email(?) is true : '' : 'member.email.invaldformat' }" // 좋은 표현
"{ email : email(?) : '' : 'member.email.invaldformat' }" // parser exception 발생


감추기


감추기


원하는 함수가 없는 경우 직접 만들어서 사용할 수 있다. valang에 의존적인 클래스를 상속해야 하고 테스트가 무척 어렵기 때문에 가능하면 쓰지 않는 것이 좋을듯하나 편리하다.

이하 사용자 함수 작성
public class UrlFunction extends AbstractFunction {
public UrlFunction(Function[] arguments, int line, int column) {
 super(arguments, line, column);
}
/*
  * (non-Javadoc)
  *
  * @see org.springmodules.validation.valang.functions.AbstractFunction#doGetResult(java.lang.Object)
  */
@Override
protected Object doGetResult(Object target) throws Exception {
 String url = getArguments()[0].getResult(target).toString();
 if (url != null) {
  try {
   new URL(url);
   return true;
  } catch (MalformedURLException e) {
   return false;
  }
 }
 return false;
}
}

등록
validator.setValang(
   "{ blog : url(?) is true :  '' : 'member.blog.invaldformat' }"
 );
 validator.addCustomFunction("url", "net.webapp2.util.valang.UrlFunction");


감추기


출처 - http://ahnyounghoe.tistory.com/99

:

Spring Modules Validation

FRAMEWORK/SPRING 2013. 7. 31. 15:09

Spring Modules Validation

David Winterfeldt

2009


The Spring Modules project has a number of subprojects, including validation. This module is based on Spring ModulesValidation Version 0.9 and has a number of enhancements to the Valang part of the project.

Thanks to everyone that has worked on this project previously and currently.

1. Valang

Valang is short for Va-lidation Lang-uage. It provides a very readable language for expressing validation rules, as well as providing the ability to add custom functions to the language. Once the ValangValidator is configured, using it isn't different than any other Spring Validator since it implements org.springframework.validation.Validator.

Below is a list of current enhancements.

Version 0.91

  • Bytecode generation added to DefaultVisitor as a replacement for reflection accessing simple properties (BeanPropertyFunction) for a significant performance improvement.
  • Basic enum comparison support. In the expression below the personType is an enum and the value STUDENT will be convereted to an enum for comparison. The value must match an enum value on the type being compared or an exception will be thrown.
    personType EQUALS ['STUDENT']
    For better performance the full class name can be specified so the enum can be retrieved during parsing. The first example is for standard enum and the second one is for an inner enum class .
    personType EQUALS ['org.springmodules.validation.example.PersonType.STUDENT']
    personType EQUALS ['org.springmodules.validation.example.Person$PersonType.STUDENT']
  • Where clause support. In the expression below, the part of the expression price < 100 will only be evaluated if thepersonType is 'STUDENT'. Otherwise the validation will be skipped.
    price < 100 WHERE personType EQUALS ['STUDENT']
    [Note]Note

    Support for the where clause has not been added to the JavaScript custom tag currently.

  • Improved performance of 'IN'/'NOT IN' if comparing a value to a java.util.Set it will use Set.contains(value). Static lists of Strings (ex: 'A', 'B', 'C') are now stored in a Set instead of an ArrayList.
  • Functions can be configured in Spring, but need to have their scope set as prototype and use a FunctionWrapper that is also a prototype bean with <aop:scoped-proxy> set on it.
  • Removed servlet dependency from Valang project except for the custom JSP tag ValangValidateTag needing it, but running Valang no longer requires it. This involved removing ServletContextAware from it's custom dependency injection. If someone was using this in a custom function, the function can now be configured directly in Spring and Spring can inject any "aware" values.
  • Changed logging to use SLF4J api.

Version 0.92

  • Removed custom dependency injection since functions can be configured in Spring.
  • Added auto-discovery of FunctionWrapper beans from the Spring context to go with existing auto-discovery of FunctionDefinition beans.

Version 0.93

  • Made specific comparison classes for each operator for a performance improvement.
  • Changed IS WORD and IS BLANK to use Commons Lang StringUtils, which will change the behavior slightly but should be more accurate to the description of the validation.
  • Change Operator from interfaces to an enum and removed OperatorConstants.
  • Fixed bytecode generation to handle a Map, a List, and an Array.

Version 0.94

  • Upgraded to Spring 3.0 and changed group & artifact IDs to match standard Spring naming conventions.

Version 0.95

  • Upgraded to Spring 3.1 and minor improvements to bytecode generation.

Rule Syntax

The basic construction of a Valang rule is to have it begin and end with a brace. Within the braces, the default property name for the rule is specified first. Then the Valang expression, followed by the default error message. These are all the required values for a Valang rule. The other optional values for a rule are the error message key and arguments for it. Each of the values of the rule are delimitted by a colon.

{ <property-name> : <expression> : <default-error-message> : <error-message-key> : <error-message-args> }

Table 1. Rule Syntax

Rule ValueDescriptionRequired
property-nameThis is the default property of the bean being targeted for validation, and can be referred to with the shortcut ? in an expression.true
expressionThe Valang expression.true
default-error-messageThe default error message. If this isn't needed, it can be left blank even though it's required.true
error-message-keyThe message resource key for the i18n error message.false
error-message-argIf the error-message-key is specified, arguments for the error message can also be set as the final value of the rule. This accepts a comma delimited list of values.false

Expression Syntax

The expression language provides an English like syntax for expressing validation rules. There are a number of operators for comparing a value to another. Logical expressions, resulting in true or false, can be grouped together with parentheses to form more complex expressions.

Just to give some context to the explanation of all the rules, below is a simple example. The bean being validated has the properties getFirstName()getLastName(), and getAge(). The first two return a String and the last returns an int. The default property is 'firstName', which is referred to by the question mark. The first part of the rule enclosed in parentheses checks if the first name is either 'Joe' or it's length is greater than 5. The next part checks if the last name is one of the values in the list, and the final part checks if the age is over 18.

(? EQUALS 'Joe' OR length(?) > 5) AND lastName IN 'Johnson', 'Jones', 'Smith' AND age > 18

Operator Syntax

The parser is not case sensitive when processing the operators.

Table 2. Expression Operators

Comparison OperatorDescriptionSupportsExample
= | == | IS | EQUALSChecks for equality.Strings, booleans, numbers, dates, and enums.firstName EQUALS 'Joe'
!= | <> | >< | IS NOT | NOT EQUALSChecks for inequality.Strings, booleans, numbers, dates, and enums.firstName NOT EQUALS 'Joe'
> | GREATER THAN | IS GREATER THANChecks if a value is greater than another.Numbers and dates.age > 18
< | LESS THAN | IS LESS THANChecks if a value is less than another.Numbers and dates.age > 18
>= | => | GREATER THAN OR EQUALS | IS GREATER THAN OR EQUALSChecks if a value is greater than or equal to another.Numbers and dates.age >= 18
<= | =< | LESS THAN OR EQUALS | IS LESS THAN OR EQUALSChecks if a value is less than or equal to another.Numbers and dates.age <= 18
NULL | IS NULLChecks if a value is null.Objects.firstName IS NULL
NOT NULL | IS NOT NULLChecks if a value is not null.Objects.firstName IS NOT NULL
HAS TEXTChecks if the value has at least one non-whitespace character.Strings.firstName HAS TEXT
HAS NO TEXTChecks if the value doesn't have a non-whitespace character.Strings.firstName HAS NO TEXT
HAS LENGTHChecks if the value's length is greater than zero.Strings.firstName HAS LENGTH
HAS NO LENGTHChecks if the value's length is zero.Strings.firstName HAS NO LENGTH
IS BLANKChecks if the value is blank (null or zero length).Strings.firstName IS BLANK
IS NOT BLANKChecks if the value isn't blank (not null, length greater than zero).Strings.firstName IS NOT BLANK
IS UPPERCASE | IS UPPER CASE | IS UPPERChecks if the value is uppercase.Strings.firstName IS UPPERCASE
IS NOT UPPERCASE | IS NOT UPPER CASE | IS NOT UPPERChecks if the value isn't uppercase.Strings.firstName IS NOT UPPERCASE
IS LOWERCASE | IS LOWER CASE | IS LOWERChecks if the value is lowercase.Strings.firstName IS LOWERCASE
IS NOT LOWERCASE | IS NOT LOWER CASE | IS NOT LOWERChecks if the value isn't lowercase.Strings.firstName IS NOT LOWERCASE
IS WORDChecks if the value has one or more letters or numbers (no spaces or special characters).Strings.firstName IS WORD
IS NOT WORDChecks if the value doesn't have one or more letters or numbers (no spaces or special characters).Strings.firstName IS NOT WORD
BETWEENChecks if a value is between two other values.Numbers and dates.age BETWEEN 18 AND 65
NOT BETWEENChecks if a value isn't between two other values.Numbers and dates.age NOT BETWEEN 18 AND 65
INChecks if a value is in a list.Strings, booleans, numbers, dates, and enums.firstName IN 'Joe', 'Jack', 'Jane', 'Jill'
NOT INChecks if a value isn't in a list.Strings, booleans, numbers, dates, and enums.firstName NOT IN 'Joe', 'Jack', 'Jane', 'Jill'
NOTChecks for the opposite of the following expression.Any expression.NOT firstName EQUALS 'Joe'
!Changes a boolean expression to it's opposite.Booleansmatches('\\s+', firstName) IS !(TRUE)
ANDUsed to join together the logical comparisons on either side of the operator. Both must evaluate totrue.Any expression.firstName EQUALS 'Joe' AND age > 21
ORUsed to join together the logical comparisons on either side of the operator. Only one must evaluate totrue.Any expression.firstName EQUALS 'Joe' OR age > 21
WHEREIf the where expression is true, then the main expression for validation is performed. Otherwise it isn't evaluated and no errors are generated.Any expression.firstName EQUALS 'Joe' WHERE age > 21
thisA reference to the bean passed in for validation, which could be passed into a custom function for example.Any expression.isValid(this) IS TRUE

Literal Syntax

Table 3. Literals

Literal TypeDescriptionExample
StringString literals are surrounded by single quotes.'Joe'
NumbersNumbers can be expressed without any special syntax. Numbers are all parsed usingBigDecimal.1, 100, 0.73, -2.48
Dates

Date literals are surrounded by brackets.

These are the supported formats supported by the DefaultDateParser.

yyyyMMddyyyy-MM-ddyyyy-MM-dd HH:mm:ssyyyyMMdd HHmmssyyyyMMdd HH:mm:ss,yyyy-MM-dd HHmmss

[20081230], [2008-12-30], [2008-12-30 12:20:31]
BooleansThere are four different constants for boolean values. The values 'TRUE' and 'YES' represent true, and the values 'FALSE' and 'NO' represent falseTRUE, YES, FALSE, NO
EnumsEnums are surrounded by bracket and single quotes. If the full path to the enum isn't specified, it will be resolved when the expression is evaluated by looking up the enum value from enum on the opposite side of the expression.['FAIL'], ['org.springmodules.validation.valang.CreditStatus.FAIL'], ['org.springmodules.validation.valang.Person$CreditRating.EXCELLENT']

Mathematical Operator Syntax

Valang supports basic mathematical formulas based on numeric literals and property values.

Table 4. Mathematical Expression Operators

Mathematical OperatorDescriptionExample
+Addition operator.price + 12
-Subtraction operator.price - 12
*Multiplication operator.price * 1.2
/ | DIVDivision operator.price / 2
% | MODModulo operator.age % 10

Property Syntax

Valang supports standard property and nested property access to the bean passed in for validation.

Table 5. Property Syntax

Property TypeDescriptionExample
StandardUsing standard JavaBean property notation, a value from the bean being validated may be retrieved. The address represents getAddress() on the bean.address IS NOT NULL
NestedUsing standard JavaBean property notation, a nested value from the bean being validated may be retrieved. The address.city represents getAddress().getCity() on the bean.address.city IS NOT BLANK
ListFrom an array, List, or Set, a value from it can be returned by specifying it's index. Only arrays and lists are supported by bytecode generation.addresses[1] IS NOT NULL
MapFrom a Map, the value based on the key specified is retrieved.addresses[home] IS NOT NULL

Functions

These are built in functions that come with Valang. The function framework is pluggable, so it's easy to add custom functions. Adding custom functions will be covered in the next section.

Table 6. Functions

FunctionDescriptionExample
length | len | size | countReturns the size of a collection or an array, and otherwise returns the length of string by called toString() on the object.length(firstName) < 20
match | matchesPerforms a match on a regular expression. The first argument is the regular expression and the second is the value match on.matches('\\w+', firstName) IS TRUE
emailChecks if the value is a valid e-mail address.email(email) IS TRUE
upperConverts the value to uppercase.upper(firstName) EQUALS 'JOE'
lowerConverts the value to lowercase.lower(firstName) EQUALS 'joe'
resolveWraps a string in DefaultMessageSourceResolvable.resolve('personForm.firstName') EQUALS 'First Name'
inRoleChecks if the user authenticated by Spring Security is in a role.inRole('ADMIN') IS TRUE

Custom Functions

Custom functions can either be explicitly registered or instances of FunctionDefinition and FunctionWrapper are automatically registered with a ValangValidator. If just specifying a class name, it must have a constructor with the signature Function[] arguments, int line, int column. The FunctionWrapper is specifically for Spring configured functions. If the Function in a FunctionWrappertakes any arguments, it must implement ConfigurableFunction which allows the parser to configure the arguments, line number, and column number. Otherwise the line & column number will not be set on a Spring configured function.

[Note]Note

It's important for a FunctionWrapper around a custom Function to be of the scope prototype as well as theFunctionWrapper. Also the FunctionWrapper must have <aop:scoped-proxy/> defined so each call to it will get a new instance of the function. This is because as the validation language is parsed a new instance of a function is made each time and has the arguments specific to that function set on it.

Spring Configuration

The example below shows how to explicitly register a custom function directly with a validator. The custom functions 'validLastName' and 'creditApproval' are registered on the customFunctions property as a Map. The key is the name of the function to be used in the validation language and the value if the function being registered, which can either be the fully qualified name of the class or an instance of FunctionWrapper.

ValangValidatorCustomFunctionTest-context.xml
                        
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/aop 
                        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="creditApprovalFunction" 
          class="org.springmodules.validation.valang.CreditApprovalFunction"
          scope="prototype">
        <property name="creditRatingList">
            <list>
                <value>GOOD</value>
                <value>EXCELLENT</value>
            </list>
        </property>
    </bean>

    <bean id="personValidator" class="org.springmodules.validation.valang.ValangValidator">
        <property name="className" value="org.springmodules.validation.valang.Person"/>
        <property name="customFunctions">
            <map>
                <entry key="validLastName">
                    <value>org.springmodules.validation.valang.ValidLastNameFunction</value>
                </entry>
                <entry key="creditApproval">
                    <bean class="org.springmodules.validation.valang.functions.FunctionWrapper"
                          scope="prototype">
                        <aop:scoped-proxy/>
                        
                        <property name="function" ref="creditApprovalFunction" />
                    </bean>
                </entry>
            </map>
        </property>
        <!--
            Final validation tests that the aop:scoped-proxy is working since if the same instance 
            of CreditApprovalFunction is used it will be set to a failing value for both sides of the or.
            While if two instances are made the first condition should pass while the second will fail.
        -->
        <property name="valang">
            <value><![CDATA[
              { lastName : validLastName(?) is true : '' }
              { lastName : creditApproval(age, creditRating) is true : '' }
              { lastName : validLastName(?) is true AND creditApproval(age, creditRating) is true : '' }
              { lastName : validLastName(?) is true AND 
                           (creditApproval(age, creditRating) is true OR 
                            creditApproval(age, ['org.springmodules.validation.valang.Person$CreditRating.FAIR']) is true) : '' }
            ]]<</value>
        </property>
    </bean>
   
</beans>
                        
                    

Instances of FunctionDefinition and FunctionWrapper are automatically registered with a ValangValidator The custom functions 'validLastName' and 'creditApproval' are registered. If a FunctionWrapper doesn't have a function name specified, the name of the bean will be used for the function name.

ValangValidatorCustomFunctionDiscoveryTest-context.xml
                        
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/aop 
                        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean class="org.springmodules.validation.valang.functions.FunctionDefinition"
          p:name="validLastName"
          p:className="org.springmodules.validation.valang.ValidLastNameFunction"/>

    <!-- Uses bean name for function name if not explicitly set on the wrapper -->
    <bean id="creditApproval" 
          class="org.springmodules.validation.valang.functions.FunctionWrapper"
          scope="prototype">
        <aop:scoped-proxy/>
        
        <property name="function">
            <bean id="creditApprovalFunction" 
                  class="org.springmodules.validation.valang.CreditApprovalFunction"
                  scope="prototype">
                <property name="creditRatingList">
                    <list>
                        <value>GOOD</value>
                        <value>EXCELLENT</value>
                    </list>
                </property>
            </bean>
        </property>
    </bean>
    
    <bean id="personValidator" class="org.springmodules.validation.valang.ValangValidator">
        <property name="className" value="org.springmodules.validation.valang.Person"/>
        <!--
            Final validation tests that the aop:scoped-proxy is working since if the same instance 
            of CreditApprovalFunction is used it will be set to a failing value for both sides of the or.
            While if two instances are made the first condition should pass while the second will fail.
        -->
        <property name="valang">
            <value><![CDATA[
              { lastName : validLastName(?) is true : '' }
              { lastName : creditApproval(age, creditRating) is true : '' }
              { lastName : validLastName(?) is true AND creditApproval(age, creditRating) is true : '' }
              { lastName : validLastName(?) is true AND 
                           (creditApproval(age, creditRating) is true OR 
                            creditApproval(age, ['org.springmodules.validation.valang.Person$CreditRating.FAIR']) is true) : '' }
            ]]<</value>
        </property>
    </bean>
   
</beans>
                        
                    

Code Example

Checks if the last name is in a list, and if it isn't false is returned.

Example 1. ValidLastNameFunction

                        
public class ValidLastNameFunction extends AbstractFunction {

    final Logger logger = LoggerFactory.getLogger(ValidLastNameFunction.class);
    
    final Set<String> lValidLastNames = new HashSet<String>();
    
    /**
     * Constructor
     */
    public ValidLastNameFunction(Function[] arguments, int line, int column) {
        super(arguments, line, column);
        definedExactNumberOfArguments(1);

        lValidLastNames.add("Anderson");
        lValidLastNames.add("Jackson");
        lValidLastNames.add("Johnson");
        lValidLastNames.add("Jones");
        lValidLastNames.add("Smith");
    }

    /**
     * Checks if the last name is blocked.
     * 
     * @return      Object      Returns a <code>boolean</code> for 
     *                          whether or not the last name is blocked.
     */
    @Override
    protected Object doGetResult(Object target) {
        boolean result = true;
        
        String symbol = getArguments()[0].getResult(target).toString();
        
        if (!lValidLastNames.contains(symbol)) {
            result = false;
        }
        
        return result;
    }
    
}
                        
                    

The function checks if a person can get credit approval. Their credit rating is checked against a list only if they are over 18 years old.

Example 2. ConfigurableFunction

                        
public class CreditApprovalFunction extends AbstractFunction 
        implements ConfigurableFunction {

    final Logger logger = LoggerFactory.getLogger(CreditApprovalFunction.class);
    
    Set<Person.CreditRating> lCreditRatings = new HashSet<Person.CreditRating>();

    /**
     * Constructor
     */
    public CreditApprovalFunction() {}

    /**
     * Constructor
     */
    public CreditApprovalFunction(Function[] arguments, int line, int column) {
        super(arguments, line, column);
        definedExactNumberOfArguments(2);
        
        lCreditRatings.add(Person.CreditRating.FAIR);
        lCreditRatings.add(Person.CreditRating.GOOD);
        lCreditRatings.add(Person.CreditRating.EXCELLENT);
    }

    /**
     * Gets number of expected arguments.
     * Implementation of <code>ConfigurableFunction</code>.
     */
    public int getExpectedNumberOfArguments() {
        return 2;
    }

    /**
     * Sets arguments, line number, and column number.
     * Implementation of <code>ConfigurableFunction</code>.
     */
    public void setArguments(int expectedNumberOfArguments, Function[] arguments,
                             int line, int column) {
        // important to set template first or can cause a NullPointerException 
        // if number of arguments don't match the expected number since 
        // the template is used to create the exception
        super.setTemplate(line, column);
        super.setArguments(arguments);
        super.definedExactNumberOfArguments(expectedNumberOfArguments);
    }
    
    /**
     * Sets valid credit rating approval list.
     */
    public void setCreditRatingList(Set<Person.CreditRating> lCreditRatings) {
        this.lCreditRatings = lCreditRatings;
    }

    /**
     * If age is over 18, check if the person has good credit, 
     * and otherwise reject.
     * 
     * @return      Object      Returns a <code>boolean</code> for 
     *                          whether or not the person has good enough 
     *                          credit to get approval.
     */
    @Override
    protected Object doGetResult(Object target) {
        boolean result = true;
        
        int age = (Integer) getArguments()[0].getResult(target);
        Person.CreditRating creditRating = (Person.CreditRating)getArguments()[1].getResult(target);

        // must be over 18 to get credit approval
        if (age > 18) {
            if (!lCreditRatings.contains(creditRating)) {
                result = false;
            }
        }
        
        return result;
    }
    
}
                        
                    

Bytecode Generation

If the validator will only be used to validate a specific class, the property 'className' can be specified to avoid reflection. If it's set, a custom Function will be generated that directly retrieves a property to avoid reflection. This provides a significant performance improvement if that is a concern, which typically isn't if the validation is being used to validate a web page since the delay is so small either way.

[Note]Note

Only a Map, a List, or an Array is supported by bytecode generation, not a Set. Primitive arrays currently aren't supported, but any object one is. Also, nested properties are currently not supported.

This is a small excerpt from the logging of the performance unit test. As you can see from the logging, as the validator is initialized it generates bytecode and shows for which class and method, as well as what the generated class name is. The package and name of the original class is used and then has what property is being retrieved appended along with 'BeanPropertyFunction$$Valang' to make a unique class name to try to avoid any collisions.

DefaultVisitor - Generated bytecode for org.springmodules.validation.valang.Person.getLastName() 
    as 'org.springmodules.validation.valang.PersonLastNameBeanPropertyFunction$$Valang'.
DefaultVisitor - Generated bytecode for org.springmodules.validation.valang.Person.getAge() 
    as 'org.springmodules.validation.valang.PersonAgeBeanPropertyFunction$$Valang'.
DefaultVisitor - Generated bytecode for org.springmodules.validation.valang.Person.getCreditRating() 
    as 'org.springmodules.validation.valang.PersonCreditRatingBeanPropertyFunction$$Valang'.
DefaultVisitor - Generated bytecode for org.springmodules.validation.valang.Person.getFirstName() 
    as 'org.springmodules.validation.valang.PersonFirstNameBeanPropertyFunction$$Valang'.
DefaultVisitor - Generated bytecode for org.springmodules.validation.valang.Person.getCreditStatus() 
    as 'org.springmodules.validation.valang.PersonCreditStatusBeanPropertyFunction$$Valang'.
ValangValidatorPerformanceTest - Took 7098.0ns.
ValangValidatorPerformanceTest - Took 2124.0ns.
ValangValidatorPerformanceTest - Message validator took 7098.0ns, and bytecode message valdiator took 2124.0ns.
                

Results from ValangValidatorPerformanceTest which was run on a Macbook Pro (2.3GHz Intel Core i7 with 8 GB RAM with OS X 10.6.8) with Java 6. All the expressions are identical, but adjusted to either retrieve the values being compared from a JavaBean, MapList, or an array.

Table 7. Bytecode Generation Performance Comparison

ExpressionReflectionBytcode Generation
{ lastName : validLastName(?) is true AND creditApproval(age, creditRating) is true WHERE firstName IN 'Joe', 'Jack', 'Jill', 'Jane' AND creditStatus IN ['org.springmodules.validation.valang.CreditStatus.PENDING'], ['org.springmodules.validation.valang.CreditStatus.FAIL'] AND creditRating EQUALS ['org.springmodules.validation.valang.Person$CreditRating.EXCELLENT'] AND age > 18 : '' }1176ns327ns
{ mapVars[lastName] : validLastName(?) is true AND creditApproval(mapVars[age], mapVars[creditRating]) is true WHERE mapVars[firstName] IN 'Joe', 'Jack', 'Jill', 'Jane' AND mapVars[creditStatus] IN ['org.springmodules.validation.valang.CreditStatus.PENDING'], ['org.springmodules.validation.valang.CreditStatus.FAIL'] AND mapVars[creditRating] EQUALS ['org.springmodules.validation.valang.Person$CreditRating.EXCELLENT'] AND mapVars[age] > 18 : '' }905ns48ns
{ listVars[1] : validLastName(?) is true AND creditApproval(listVars[2], listVars[4]) is true WHERE listVars[0] IN 'Joe', 'Jack', 'Jill', 'Jane' AND listVars[3] IN ['org.springmodules.validation.valang.CreditStatus.PENDING'], ['org.springmodules.validation.valang.CreditStatus.FAIL'] AND listVars[4] EQUALS ['org.springmodules.validation.valang.Person$CreditRating.EXCELLENT'] AND listVars[2] > 18 : '' }575ns43ns
{ vars[1] : validLastName(?) is true AND creditApproval(vars[2], vars[4]) is true WHERE vars[0] IN 'Joe', 'Jack', 'Jill', 'Jane' AND vars[3] IN ['org.springmodules.validation.valang.CreditStatus.PENDING'], ['org.springmodules.validation.valang.CreditStatus.FAIL'] AND vars[4] EQUALS ['org.springmodules.validation.valang.Person$CreditRating.EXCELLENT'] AND vars[2] > 18 : '' }563ns40ns

Spring Configuration

By specifying the 'className' property, bytecode will be generated for each method being called to avoid reflection. This gives a significant performance improvement.

Excerpt from ValangValidatorCustomFunctionTest-context.xml

                        
<!-- 
    Only perform validation if valid first name, credit status is failed or pending, 
    and the credit rating is excellent where the person's age is over 18.
-->
<bean id="expression" class="java.lang.String">
    <constructor-arg>
        <value><![CDATA[
          { lastName : validLastName(?) is true AND creditApproval(age, creditRating) is true
                       WHERE firstName IN 'Joe', 'Jack', 'Jill', 'Jane' AND
                             creditStatus IN ['org.springmodules.validation.valang.CreditStatus.PENDING'], 
                                             ['org.springmodules.validation.valang.CreditStatus.FAIL'] AND
                             creditRating EQUALS ['org.springmodules.validation.valang.Person$CreditRating.EXCELLENT'] AND
                             age > 18 : '' } 
        ]]<</value>
    </constructor-arg>
</bean>

...

<bean id="bytecodePersonValidator" class="org.springmodules.validation.valang.ValangValidator">
    <property name="className" value="org.springmodules.validation.valang.Person"/>
    <property name="valang" ref="expression" />
</bean>
                        
                    

Date Examples

The default date parser provides support for a number of different date literals, and also has support for shifting and manipulating dates. Below are a few examples, but see the DefaultDateParser for more detailed information.

Spring Configuration

ValangValidatorDateTest-context.xml
                        
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/aop 
                        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="personValidator" class="org.springmodules.validation.valang.ValangValidator">
        <property name="className" value="org.springmodules.validation.valang.Person"/>
        <property name="valang">
            <!-- 
                Third to last validation shifts '2008-12-30<y' to '2008-01-01 00:00:00'
                Second to last validation shifts '2005-04-09 23:30:00<M+10d+8H' to '2005-04-11 08:00:00'.
                Last shifts '2009-02-06 00:00:00<M+20y' to '2029-02-01 00:00:00'.
            -->
            <value><![CDATA[            
              { lastUpdated : ? > [20081230] : '' }
              { lastUpdated : ? > [2008-12-30] : '' }
              { lastUpdated : ? > [2008-12-30 12:20:31] : '' }
              { lastUpdated : ? > [20081230 122031] : '' }
              { lastUpdated : ? > [20081230 12:20:31] : '' }
              { lastUpdated : ? > [2008-12-30 122031] : '' }
              
              { lastUpdated : ? BETWEEN [20081230] AND [2009-02-06 00:00:00<M+20y] : '' }
              
              { lastUpdated : ? > [2008-12-30<y] : '' }
              { lastUpdated : ? > [2005-04-09 23:30:00<M+10d+8H] : '' }
              { lastUpdated : ? < [2009-02-06 00:00:00<M+20y] : '' }
            ]]<</value>
        </property>
    </bean>
   
</beans>
                        
                    


:

MessageSource 를 이용할 경우 한글문제 (한글 ascii 변환)

FRAMEWORK/SPRING 2013. 7. 25. 22:17


org.springframework.context.MessageSource 를 사용할 경우 Locale 에 해당하는 메시지들을 보여줄 수 있다.

그런데, 한글과 같이 2byte 를 사용할 경우 한글이 깨져서 나타나게 된다. 이럴경우 한글을 ascii 코드로 변환을 시켜줘야한다. 

만약 Message.properties 라는 파일이 있고, 한글에 해당하는 Message_ko.properties 라는 파일이 있을경우 
%JAVA_HOME%\bin\native2ascii.exe 를 이용하여 한글을 ascii 코드로 변환을 시켜줄 수 있다.

사용방법은 다음과 같다.


Message_ko.properties.txt 같이 임시 파일을 만들고 MessageSource 의 한글내용을 채워넣고 아래와 같이 실행하면 된다.

native2ascii.exe Message_ko.properties.txt Message_ko.properties 

Message_ko.properties 파일을 열어보면 Message_ko.properties.txt 파일안에 있었던 내용이 ascii 코드로 변경된 것을 확인할 수 있다. 또한, 실행시켜보면 해당 내용이 잘 동작하는 것을 확인할 수도 있다.

해당내용은 ant 작업으로도 등록을 할 수 있다. build 시에 작업을 같이 등록을 하면 변경시마다 따로 위의작업을 돌릴 필요가 없어진다. (그래도 뭔가가 찜찜하긴 하다..  ㅡㅡ;;; 한글을 바로 써도 아무런 문제가 없는 방법이 있었으면 좋겠당 쩝..)


출처 - http://gerions.egloos.com/3165973


:

MessageSource를 이용한 텍스트 메시지 처리

FRAMEWORK/SPRING 2013. 7. 15. 15:03

□ MessageSource 인터페이스
    ■ 정의
      - org.springframework.context.MessageSource interface
      - 메시지의 국제화를 지원.
    ■ 메서드
      - 지역 및 언어에 따라 알맞은 메시지를 구할 수 있음.

 public interface MessageSource {
    String getMessage(String code, Object[] args, Locale locale) 
        throws NoSuchMessageException;

    String getMessage(String code, Object[] args, String defaultMessage, Locale locale);

    String getMessage(MessageSourceResolvable resolvable, Locale locale)
        throws NoSuchMessageException;
 }

    ■ ApplicationContext 클래스
      ○ 메시지 처리
        - MessageSource 인터페이스를 상속 받고 있음. 
          (getMessage() 메서드를 이용하여 ApplicationContext로부터 지역 및 언어에 알맞은 메시지를 가져올 수 있음.)
        - 등록된 빈 객체 중에서 이름이 'messageSource'인 MessageSource 타입의 빈 객체를 이용하여 메시지를 가져옴.
      ○ 'messageSource'가 이름인 빈 객체 정의

 <bean id="messageSource"
     class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basename">
        <value>message.greeting</value>
    </property>
 </bean>

         - basename 프로퍼티의 값은 메시지를 로딩할 때 사용할 ResourceBundle 의 베이스 이름.
           (베이스 이름 : 패키지를 포함한 완전한 이름이어야 함.)
         - message.greeting 값은 message 패키지에 있는 greeting 프로퍼티 파일로부터 메시지를 가져옴.
    ■ ResourceBundleMessageSource 클래스
      ○ 정의
        - MessageSource 인터페이스의 구현 클래스.
        - java.util.ResourceBundle을 이용하여 메시지를 읽어오는 MessageSource 구현체.
      ○ 한 개 이상의 프로퍼티 파일로부터 메시지 로딩
        - <list> 태그 이용.

 <bean id="messageSource"
     class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basenames">
        <list>
            <value>message.greeting</value>
            <value>message.error</value>
        </list>
    </property>
 </bean>

    ■ 프로퍼티 파일
      ○ 특징
        - ResourceBundle은 프로퍼티 파일의 이름을 이용하여 언어 및 지역에 따른 메시지를 로딩.
      ○ message 프로퍼티 파일 파일명
        - message.properties : 기본 메시지. 시스템의 언어 및 지역에 맞는 프로퍼티 파일이 존재하지 않을 경우에 사용.
        - message_en.properties : 영어 메시지.
        - message_ko.properties : 한글 메시지.
        - message_en_UK.properties : 영국을 위한 영어 메시지
      ○ message 프로퍼티 파일
        - 각 프로퍼티 파일은 해당 언어에 알맞은 메시지를 포함.

 /* message_en.properties */

 greeting = Hello!
 /* message_ko.properties */

 <!-- '안녕하세요!'를 유니코드 값으로 변환한 값. -->
 greeting = \uc548\ub155\ud558\uc138\uc694!

    ■ 메시지 로딩
      - ApplicationContext.getMessage() 메서드를 이용.

 Local locale = Locale.getDefault();
 String greeting = context.getMessage("greeting", new Object[0], locale);

 Locale englishLocale = Locale.ENGLISH;
 String englishGreeting = context.getMessage("greeting", new Object[0], englishLocale);


  □ 빈 객체에서 메시지 이용
    ■ 빈 객체에서 MessageSource 사용 방법
      - ApplicationContextAware 인터페이스 구현 뒤, setApplicationContext() 메서드를 통해 전달받은 ApplicationContext의

        getMessage() 메서드를 이용하여 메시지 사용.
      - MessageSourceAware 인터페이스 구현 뒤, setMessageSource() 메서드를 통해 전달받은 MessageSource의 getMessage()

        메서드를 이용하여 메시지 사용.
    ■ MessageSourceAware 인터페이스를 사용하는 방법
      ○ 메서드
        - MessageSourceAware 인터페이스는 MessageSource를 전달받을 수 있는 메서드를 정의.

 public interface MessageSourceAware {
    void setMessageSource(MessageSource messageSource);
 }

      ○ 사용
        - MessageSourceAware 인터페이스를 구현한 빈 클래스는 setMessageSource() 메서드를 통해 전달받은 MessageSource를

          이용하여 필요한 메시지를 가져와 사용.

 public class LoginProcessorimplements MessageSourceAware {
    private MessageSource messageSource;

   public void setMessageSource(MessageSource messageSource) {
        this.messageSource = messageSource;
    }

    public void login(String username, String password) {
        ...
        Object[] args = new String[]{username};
        String failMessage = messageSource.getMessage("login.fail", args, locale);
        ...
    }
 }



[출처] 2. 스프링 DI (MessageSource를 이용한 텍스트 메시지 처리)|작성자 외계인셩

:

다국어 처리 - localeResolver, messageSource

FRAMEWORK/SPRING 2013. 7. 15. 15:02

스프링에서 다국어를 처리하려면

LocaleChangeInterceptor + messageSource

조합으로 가능하다.
국가별로 페이지들 별도로 만드는짓은 더이상 하지 말자.

만약 서버단에서 위의 조합으로 다국어 처리된 문자열을
jsp단으로 보내주고 싶을 경우에는
추가로 taglib를 사용하면 된다.


1. LocaleResolver

말그대로 현재 접속된 사용자에게 적합한 언어셋을 제공하기 위해
브라우저의 useragent 값이나 쿠키, 세션을 통해 locale 값을 처리한다.
그래서 우선 해당 xml에 아래처럼 추가해준다.

<applicationContext.xml>

<mvc:interceptors>  
        <bean  id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
            <property name="paramName" value="lang"></property>
        </bean>
</mvc:interceptors>

<bean id="localeResolver"  class="org.springframework.web.servlet.i18n.CookieLocaleResolver"/>


localeResolver의 종류는 3가지가 있다.
defalut는 AceeptHeaderLocaleResolver이다.
reqest 헤더의 accept-language부분에서 locale을 읽어온다.
나머지는 이름 그대로 쿠키나 세션을 사용한다.

<bean id="localeResolver" class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver" />


<bean id="localeResolver"
    class="org.springframework.web.servlet.i18n.CookieLocaleResolver" >
    <property name="cookieName" value="clientlanguage"/>    
    <property name="cookieMaxAge" value="100000"/>
    <property name="cookiePath" value="web/cookie"/>
</bean>


<bean id="localeResolver"
    class="org.springframework.web.servlet.i18n.SessionLocaleResolver" />



2. MessageSource

locale값이 정해지면 Spring에서해는 해당 locale의 messagesource의 값으로 변환해서 돌려준다.
그래서 우선 localechangeinterceptor처럼 xml에 등록먼저 해주자.

<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="basename" value="WEB-INF/messages/Messages"/>
        <property name="defaultEncoding" value="UTF-8" />
        <property name="cacheSeconds" value="180"/>
</bean>

※ basename : Messages 파일 위치

본인의 설정에서는
WEB-INF/messages폴더밑에
'Messages_[locale]' 형태의 파일을 만들었다.







ko와 en두가지 버전이 있다.
만약 locale값이 ko,en이 아닐 경우
맨 마지막에 있는 Message.properties를 참조하게 된다.


왼쪽은 변수명이고,
오른쪽은 locale값이 ko 일경우에
돌려주는 값이다.
Messages 파일에서는 영어가 아닐 경우
자동으로 유니코드값으로 변환되어 기록된다.
그러나 출력시에는 저렇게 유니코드값이 아닌 정상적으로 출력된다.


3. message 호출

위처럼 만들고 써먹으려면 아래 함수를 호출하면 된다.
code는 2번에서 작성한 좌측의 변수명이고
locale은 말그대로 locale이다

import org.springframework.context.MessageSource;
......
messageSource.getMessage(code, null, locale)


임의적으로 locale값을 설정하고 싶으면

localeResolver.setLocale(request, response, Locale.KOREA);


처럼 하면 된다.


4. jsp에서 처리 (taglib)

localeResolver와 message 프로퍼티 설정이 끝나면
jsp에서도 바로 다국어 처리가 가능하다.

우선 jsp 상단에

<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>


를 추가하고,
다국어 처리가 필요한 부분에

<spring:message code="code_name"/>

를 삽입해주면 된다.


출처 - http://changpd.blogspot.kr/2013/05/localeresolver-messagesource.html

:

Spring AOP용어의 정리

FRAMEWORK/SPRING 2013. 7. 11. 15:06

Spring AOP용어의 정리


- JointPoint

 + 모듈이 삽입되어 동작할수 있는 실행 가능한 특정 위치 (메소드호출,리턴,필드 액세스,인트턴스 생성, 예외 처리)

- PointCut

 + 어느 JointPoint를 사용할 것인지를 결정 하는 선택 기능(Pattern Matching Examples,Pointcut Designators)

- Advice

 + 각 JoinPoint에 삽입되어져 동작할 수 있는 코드(동작시점: before, after, after returning, after throwing, around 중에서 선택)

- Aspect

 + 어디에서(Pointcut) 무엇을 할 것인지(Advice)를 합쳐놓은 것을 말한다.



=============================================================================================



◎ Advice : 언제 무엇을 할지 나타냅니다. 즉 애스펙트가 해야하는 작업과 언제 그 작업을 수행해야 하는지를 정의한 것이 어드바이스 입니다. (실제로 Target 객체에 적용할 일을 나타냄.)


◎ Joinpoint : Advice를 적용할 시점을 말합니다. (생성자 호출, 필드에 접근, 필드의 값 변셩, 메소드 호출등의 시점.) Spring AOP는 메소드 호출 Joinpoint만 지원합니다.


◎ Pointcut : Joinpoint의 부분집합으로 애스펙트가 어드바이스 할 조인포인트의 영역을 좁히는 일을합니다.


◎ Advisor : 하나의 Advice와 하나의 Piontcut을 묶은 것. Aspect는 여러개의 Advice와 Pointcut을 가지고 있는 것을 말합니다. 즉 Advisor도 하나의 Advice와 Pointcut을 가지고 있는 Aspect라고 할 수 있습니다.


◎ Aspect : Advice + Pointcut 

어드바이스와 포인트컷을 합친 것이 애스펙트 입니다. 두가지 정보가 합쳐지면 애스팩트가 무엇을 언제해야할지, 즉 애스펙트에 필요한 모든 정보가 정의됩니다.


◎ Introduction : 기존 클래스의 코드 변경 없이도 새 메소드나 멤버변수를 추가하는 기능입니다.


◎ Target : Advice의 대상이 되는 객체를 말합니다.


◎ Proxy : 프록시 객체

Spring AOP는 Proxy객체를 사용합니다. 프록시는 어드바이스를 타깃 객체에 적용하면 생성되는 객체입니다.


◎ Weaving : Target 객체에 Aspect를 적용하는 과정.

타깃 객체에 애스펙트를 적용해서 새로운 프록시 객체를 생성하는 절차를 위빙이라고 합니다. 애스펙트는 타깃 객체의 조인포인트로 위빙됩니다. 

위빙은 대상 객체의 생애중 컴파일 시점, 런타임 시점, 클래스 로딩 시점에 할 수 있는데, Spring AOP는 런타임시에 동시적으로 Proxy객체를 생성합니다.

:

스프링 MVC의 주요 구성요소

FRAMEWORK/SPRING 2013. 7. 5. 16:15

스프링 MVC의 주요 구성요소

 

spring-mvc 처리 흐름 요약

웹브라우저의 요청이 들어오면 DispatcherServlet의 객체가 이를 받는다. DispatcherServlet의 객체는 다시HandlerMapping객체를 통해(참조하여) 어떤 Controller에게 처리를 위임해야할지를 통보받아서 그 Controller객체에게 처리를 위임한다.

Controller객체는  모델(Service-DAO)단과 통신을 하여 비지니스 로직을 호출하고 그 결과를 ModelAndView객체로 다시 DispatcherServlet객체로 반환한다.

마지막으로 DispatcherServlet객체는 ViewResolver객체에게 사용할 View객체를 반환받아 그 View객체에 Controller객체가 반환한 정보(ModelAndView)를 포함시켜 그 결과를 브라우즈에 반환한다.   



 

 요소 역활 
 DispatcherServlet 브라우저가 보낸 요청을 일괄 관리하는 FrontController
 HandlerMapping 요청url과 controller클래스를 맵핑
 Controller 비즈니스 로직(Service)을 호출하고, 결과로ModelAndView를 반환
 ViewResolver 컨트롤러 클래스가 반환한 뷰 이름으로 이동할 뷰를 결정
 View 프리젠테이션 계층으로의 출력 데이터를 설정


출처 - http://cafe.naver.com/jjdev/248

:

스프링 MVC-개요

FRAMEWORK/SPRING 2013. 1. 24. 11:34

스프링 프레임워크는 DI나 AOP와 같은 기능 뿐 아니라 기본적으로 웹 개발을 위한 MVC 프레임워크도 함께 제공하고 있다. 스프링 MVC 프레임워크는 스프링을 기반으로 하고 있기 때문에 스프링이 제공하는 트랜잭션 처리나 DI 및 AOP 적용 등을 손쉽게 사용할 수 있다는 장점을 갖는다.

또한 스트럿츠와 같은 프레임워크와 스프링 프레임워크를 연동하기 위해 추가적인 설정을 하지 않아도된다는 장점을 가지고 있다

 

 

1. 스프링 MVC의 주요 구성요소

 


DispatcherServlet

클라이언트의 요청을 전달받는다. 컨트롤러에게 클라이언트의 요청을 전달하고 컨트롤러가 리턴한 결과 값을 View에 전달하여 알맞은 응답을 생성하도록 한다.

 

HandlerMapping

클라이언트의 요청 URL을 어떤 컨트롤러가 처리할지 결정한다

 

컨트롤러(Controller)

클라이언트의 요청을 처리한 뒤 그 결과를 DispatcherServlet에 알려준다. 스트럿츠의 Action과 동일한 역할을 수행한다.(ModelAndView 리턴)

 

ModelAndView

컨트롤러가 처리한 결과 정보 및 뷰 선택에 필요한 정보를 담는다

 

ViewResolver

컨트롤러의 처리 결과를 생성할 뷰를 선택한다

 

View

컨트롤러의 처리 결과 화면을 생성한다

 

구성요소간 프로세스 흐름

1. 클라이언트의 요청이 DispatcherServlet에 전달된다

2. DispatcherServlet은 HandlerMapping을 사용하여 클라이언트의 요청을 처리할 컨트롤러 객체를 구한다

3. DispatcherServlet은 컨트롤러 객체의 HandleRequest() 메서드를 호출하여 클라이언트의 요청을 처리한다.

4. 컨트롤러의 handleRequest() 메서드는 처리 결과 정보를 담은 ModelAndView 객체를 리턴한다

5. DispatcherServlet은 ViewResolver로부터 응답결과를 생성할 뷰 객체를 구한다

6. 뷰는 클라이언트에 전송할 응답을 생성한다

 

2. 스프링 MVC를 사용하여 Hello World 출력

1. 클라이언트의 요청을 받을 DispatcherServlet을 web.xml 파일에 설정한다.

2. HandlerMapping을 이용하여 요청 URL과 컨트롤러의 매핑방식을 결정한다.

3. 클라이언트의 요청을 처리할 컨트롤러를 작성한다.

4. 어떤 뷰를 이용하여 컨트롤러의 처리 결과 응답 화면을 생성할지 결정하는 ViewResolver를 설정한다

5. JSP나 Velocity 등을 이용하여 뷰 영역의 코드를 작성한다

 

2.1 단계 1, DispatcherServlet 설정 및 스프링컨텍스트 설정

스프링 MVC를 사용하기에 앞서 가정 먼저 해야 할 작업은 자바 웹 어플리케이션의 설정 파일인 web.xml 파일에 다음의 두 가지 정보를 추가하는 것이다

    (1) 클라이언트의 요청을 전달받을 DispatcherServlet 설정

    (2) 공통으로 사용할 어플리케이션 컨텍스트 설정

 

DispatcherServlet 은 클라이언트의 요청을 전달받는 서블릿으로서 컨트롤러나 뷰와 같은 스프링 MVC의 구성요소를 이용하여 클라이언트에게 서비스를 제공하게 된다. DispatcherServlet의 설정은 웹 어플리케이션의 /WEB-INF/web.xml 파일에 추가하며 다음과 같이 서블릿과 서블릿 매핑 정보를 추가하면 된다.

 <?xml version="1.0" encoding="UTF-8"?>

<web-app ...>

<servlet>

    <servlet-name>dispatcher</servlet-name>

    <servlet-class>

         org.springframework.weg.servlet.DispatcherServlet

    </servlet-class>

</servlet>

 

<servlet-mapping>

    <servlet-name>dispatcher</servlet-name>

    <url-pattern>*.htm</url-pattern>

</servlet-mapping>

</weg-app>

 

위 코드에서는 *.htm으로 들어오는 클라이언트의 요청을 DispatcherServlet이 처리하도록 설정하였다.

DispatcherServlet은 WEB-INF/ 디렉터리에 위치한 [서블릿이름]-servlet.xml 파일을 스프링 설정 파일로 사용한다. 예를 들어 위 코드의 경우 dispatcher-servlet.xml 파일을 설정 파일로 사용하게 된다. 이 파일에서 스프링 MVC의 구성요소인 HandlerMapping, Controller, ViewResolver, View등의 빈을 설정하게 된다.

 

2.2 단계2, 설정 파일에 HandlerMapping 설정 추가

HandlerMapping은 클라이언트의 요청을 어떤 컨트롤러가 처리할지에 대한 정보를 제공한다. 스프링은 기본적으로 몇 가지 HandlerMapping을 제공하고 있는데, 여기서는 BeanNameUrlHandlerMapping을 사용할 것이다.

 

DispatcherServlet이 사용하는 스프링 설정 파일에(예를 들어, 이번 예제에서는 dispatcher-servlet.xml) 다음과 같이 사용할 HandlerMapping을 빈으로 등록한다

 <bean id="beanNameUrlMapping"

    class = "org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>

 

BeanNameUrlHandlerMapping은 간단하게 설명하면 클라이언트의 요청 URL과 동일한 이름을 갖는 빈을 컨트롤러로 사용하도록 매핑한다.

 

2.3 단계3, 컨트롤러 구현 및 설정

스프링은 Controller 인터페이스를 제공하고 있으며, 컨트롤러 클래스는 Controller 인터페이스를 구현하면 된다. 실제로는 Controller 인터페이스를 직접 구현하는 경우는 드물며, 스프링이 제공하는 몇 가지 기본 컨트롤러 클래스 중에서 알맞은 클래스를 상속받아 구현하게 된다

 

간단한 컨트롤러(HelloController.java)

package xxx.xxx.xxx.xxx

 

import java.util.Calendar;

 

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

import org.springframework.web.servlet.ModelAndView;

import org.springframework.web.servlet.mvc.AbstractController;

 

public class HelloController extends AbstractController {

    @Override

    protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {

        ModelAndView mav = new ModelAndView();

        mav.setViewName("hello");

        mav.addObject("greeting",getGreeting());

        return mav;

    }

 

    private String getGreeting(){

        int hour = Calandar.getInstance().get(Calendar.HOUR_OF_DAY);

        if(hour >= 6 && hour <= 10){

            return "좋은 아침입니다.";

        }else if(hour >=12 && hour <= 15){

            return "점심 식사는 하셨나요?";

        }else if(hour >=18 && hour <= 22){

           return "좋은 밤 되세요";

        } 

     

        return "안녕하세요";   

}

 

컨트롤러는 처리 결과를 ModelAndView에 담아 DispatcherServlet에 전달한다. ModelAndView는 컨트롤러의 처리 결과에 대한 응답 화면을 생성할 뷰 정보와 응답 화면을 생성할 때 필요한 정보를 저장한다.

 

 <bean id="handlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>

 <bean name="/hello.htm" class="xxx.xxx.xxx.xxx.HelloController"/>

 

위 코드에서 BeanNameUrlHandlerMapping은 URL과 일치하는 이름을 갖는 컨트롤러를 사용하므로http://somehost[/servletContextPath]/hello.htm으로 요청이 들어올 경우 이름이 "/hello.htm"인 컨트롤러를 이용하여 클라이언트의 요청을 처리하게 된다

 

2.4 단계4, 설정 파일에 ViewResolver 설정 추가

컨트롤러가 리턴하는 ModelAndView는 뷰 정보를 담고 있다 위 코드에서는 아래 코드를 이용하여 뷰를 설정하였다

 ModelAndView mav = new ModelAndView();

 mav.setViewName("hello");

 

위 코드에서 뷰 이름을 "hello"로 지정했는데, ViewResolver는 이 뷰 이름을 이용하여 알맞은 View 객체를 선택하게 된다. 아래 코드는 이번 예제에서 사용하는 InternalResourceViewResolver의 설정 예이다

 

 <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">

     <property name="prefix" value="/WEB-INF/view/" />

     <property name="suffix" value=".jsp" />

 </bean>

 

InternalResourceViewResolver는 JSP를 이용하여 뷰를 구현할 때 사용되는 ViewResolver이다.

위 코드는 컨트롤러가 리턴한 ModelAndView객체의 뷰 이름이 "hello"인 경우 /WEB-INF/view/hello.jsp를 뷰로 사용하는 View 객체를 생성하도록 설정하고 있다. 스프링은 이외에도 다양한 ViewResolver를 제공하고 있다

 

2.5 단계5, 뷰 코드 구현

InternalResourceViewResolver는 JSP를 이용하여 뷰를 생성하는 InternalResourceView를 View객체로 사용하며, InternalResourceView는 JSP와 같은 웹 어플리케이션의 자원을 이용하여 뷰를 생성하게 된다.

 

InternalResourceViewResolver를 통해 JSP를 뷰로 사용할 경우 다음 예제와 같이 JSP로 표현언어를 사용하여 ModelAndView객체에 저장한 값에 접근할 수 있다.

 <%@ page language="java" contentType="text/html; charset=EUC-KR" %>

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">

<title>인사</title>

</head>

<body>

    인사말:<strong>${greeting}</strong>

</body>

</html>

 

앞 예제에서 ModelAndView 객체에 "greeting"이라는 이름으로 값을 저장하였다

InternalResourceView는 ModelAndView에 저장된 객체를 HttpServletRequest 객체의 속성(attribute)에 그대로 저장한다. 따라서 JSP에서는 ${greeting}과 같은 표현언어를 이용하여 ModelAndView에 저장된 값에 접근할 수 있다 또는 다음과 같이 저장된 객체를 구할 수도 있다

<%

    String greetingValue = (String)request.getAttribute("greeting");

%>


출처 - http://blog.naver.com/koola78?Redirect=Log&logNo=20112431417

[출처] 스프링 MVC-개요|작성자 

: