4.1 介绍
Spring EL表达是一种强大的表达式语言, 可以支持在运行时查询和操作对象.它与EL类似, 但提供了更多扩展功能,如方法调用,字符串模板功能.
Spring el表达式支持以下功能:
- 字面值表达式
- boolean和关系操作符
- 正则表达式
- 类表达式
- 访问属性,数组,集合
- 方法调用
- 关系运算
- 调用构造函数
- Bean引用
- 构建数组
- 内联列表
- 内联map
- 三元操作
- 变量
- 用户定义函数
- 集合选择
- 模板表达式
4.2 计算
SpEL相关的类和接口位于org.springframework.expression
包中.
ExpressionParser
接口负责解析表达式字符串(用单引号包裹起来).
Expression
接口负责计算表达式字符串.
此处可能抛出两个异常:ParseException
和EvaluationException`.
SpEL支持多种功能,如调用方法, 访问属性,调用构造函数.
调用方法示例:
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'.concat('!')");
String message = (String) exp.getValue(); // Hello World!
访问属性示例:
ExpressionParser parser = new SpelExpressionParser();
// 此时会调用getBytes()方法
Expression exp = parser.parseExpression("'Hello World'.bytes");
byte[] bytes = (byte[]) exp.getValue();
SpEL也支持嵌套属性.如p1.p2.p3
示例:
ExpressionParser parser = new SpelExpressionParser();
// 会调用 getBytes().length
Expression exp = parser.parseExpression("'Hello World'.bytes.length");
int length = (Integer) exp.getValue();
调用构造函数示例:
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("new String('hello world').toUpperCase()");
String msg = exp.getValue(String.class);
注意: 使用public <T> T getValue(Class<T> clazz);
方法可以无需强转.但是如果结果转换为T类型失败,则会抛出EvaluationException.
SpEL最常见的用法是根据一个特定的对象实例(称为根对象)求值.
// Create and set a calendar
GregorianCalendar c = new GregorianCalendar();
c.set(1856, 7, 9);
// The constructor arguments are name, birthday, and nationality.
Inventor tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian");
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("name");
String name = (String) exp.getValue(tesla);
// name == "Nikola Tesla"
exp = parser.parseExpression("name == 'Nikola Tesla'");
boolean result = exp.getValue(tesla, Boolean.class);
// result == true
4.2.1 EvaluationContext
EvaluationContext
接口在计算表达式时解析属性,方法,字段及提供类型转换.它有两个开箱即用的实现:
- SimpleEvaluationContext: 提供了部分SpEL配置项.它只支持SpEL的一个子集,它不支持JAVA类型引用,构造函数,及bean的引用. 要求显式的配置对表达式中方法和属性的支持级别.默认情况下, 仅支持读取属性.但可以通过获取一个builder来具体配置所需要的支持:如自定义
PropertyAccessor
(无反射), 数据绑定属性的只读访问或读写. - StandardEvaluationContext: 提供了全部的SpEL配置项
类型转换
默认情况下SpEL使用org.springframework.core.convert.ConversionService
作为类型转换服务.
示例:
class Simple {
public List<Boolean> booleanList = new ArrayList<Boolean>();
}
Simple simple = new Simple();
simple.booleanList.add(true);
EvaluationContext context = SimpleEvaluationContext().forReadOnlyDataBinding().build();
// 此处false是一个字符串. SpEL and the conversion service 将正确识别它并将其转换为Boolean型.
parser.parseExpression("booleanList[0]").setValue(context, simple, "false");
// b will be false
Boolean b = simple.booleanList.get(0);
4.2.2 转换配置
可以使用SpelParserConfiguration
对象配置ExpressionParser. 配置对象能控制一些表达式组件的行为.如对于数组或集合, 指定其某个元素为null, 则可以自动创建这个元素,如果索引超过了数组或列表的长度, 则会自动增加数组或列表以适应这个索引.
class Demo {
public List<String> list;
}
// Turn on:
// - auto null reference initialization
// - auto collection growing
SpelParserConfiguration config = new SpelParserConfiguration(true,true);
ExpressionParser parser = new SpelExpressionParser(config);
Expression expression = parser.parseExpression("list[3]");
Demo demo = new Demo();
Object o = expression.getValue(demo);
// demo.list will now be a real collection of 4 entries
// Each entry is a new empty String
4.2.3 SpEL编译
spring 4.1引入了一个基本的表达式编译器, 编译器将在表达式计算期间动态地生成一个真正的java类, 以便更快的解析表达式. 由于编译器无法从表达式中知道引用的属性的类型, 但是在第一次运行时是可以知道的,当然,如果你表达式引用的属性类型会经常变化, 这可能会带来麻烦, 所以编译只适合用于表达式引用的属性类型不常变化的场景.
对于以下表达式:
someArray[0].someProperty.someOtherProperty < 0.1
这包含了数组访问,属性引用和数值操作.编译后的性能提升非常明显,在50000次迭代中, 不编译需要75ms来完成, 编译后只需要3ms.
编译器配置
编译器默认是关闭的,但是它可以通过parser配置来开启或一个系统属性来开启.
spring定义了几种编译器的操作模式(expression.spel.SpelCompolerMode):
- OFF: 关闭编译器
- IMEDIATE: 立即模式, 表达式尽可能快的被编译,这通常是在第一次计算表达式之后完成的,如果编译失败则会抛出异常.
- MIXED: 混合模式, 在解释模式(编译器关闭时所用的方式)与编译模式之间自动切换, 如果编译模式下发生了错误,则会自动切换到解释模式.
IMEDIATE模式的存在是由于MIXED模式可能会存在副作用, 比如在MIXED模式下,表达式在编译模式下运行了一半,就出现了异常,这时可能对某些系统状态作了更改,此时它会自动切换到解释模式下,之前运行过的部分会被再运行一次,因此这时可能会引发某些问题.
示例, 使用SpelParserConfiguration
配置编译模式:
SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, this.getClass().getClassLoader());
SpelExpressionParser parser = new SpelExpressionParser(config);
Expression exp = parser.parseExpression("payload");
MyMessge message = new MyMessage();
Object payload = exp.getValue(message);
在指定编译器模式时也可以指定一个类加载器(可以为null), 编译后的表达式将在提供了类加载器创建的子类加载器中定义.重要的是要确保提供的类加载器能够看到表达式计算过程中所要调用的所有类.如果没有提供, 则使用默认加载器(通常在运行时线程所在的上下文类加载器).
另一种配置方式是当SpEL在其他组件中使用时可能无法通过上面的方式进行配置,这时可以通过系统属性spring.expression.compiler.mode
来配置(值为前面提到的三种之一).
编译器的限制
spring提供的编译器不以编译所有类型的表达式.以下几种不被支持:
- 赋值表达式
- 依赖于转换服务的表达式
- 使用自定义解析器或访问器的表达式
- expressions using selection or projection
4.3 bean定义中的表达式
SpEL可以用在XML或注解配置中.语法格式为#{ expression }
.
4.3.1 XML配置
示例: 属性或构造函数中使用
<bean id="numberGuess" class="org.spring.samples.NumberGuess">
<property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/>
</bean>
使用systemProperties
, 它是预定义的, 可以直接使用且不用在其前面添加#
.如下:
<bean id="taxCalculator" class="org.spring.samples.TaxCalculator">
<property name="defaultLocale" value="#{ systemProperties['user.region'] }"/>
</bean>
通过bean名称引用另一个bean的属性:
<bean id="numberGuess" class="org.spring.samples.NumberGuess">
<property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/>
</bean>
<bean id="shapeGuess" class="org.spring.samples.ShapeGuess">
<property name="initialShapeSeed" value="#{ numberGuess.randomNumber }"/>
</bean>
4.3.2 注解配置
在字段或方法上, 或者方法或构造函数的参数中使用@Value
注解.
public static class FieldValueTestBean
// 在字段上使用
@Value("#{ systemProperties['user.region'] }")
private String defaultLocale;
// setter getter
}
如下示例与上面示例等价:
public static class PropertyValueTestBean
private String defaultLocale;
// set方法上使用
@Value("#{ systemProperties['user.region'] }")
public void setDefaultLocale(String defaultLocale) {
this.defaultLocale = defaultLocale;
}
public String getDefaultLocale() {
return this.defaultLocale;
}
}
在@Autowired注解的构造函数参数中使用:
public class SimpleMovieLister {
private MovieFinder movieFinder;
private String defaultLocale;
@Autowired
public void configure(MovieFinder movieFinder,
@Value("#{ systemProperties['user.region'] }") String defaultLocale) {
this.movieFinder = movieFinder;
this.defaultLocale = defaultLocale;
}
}
4.4 语法参考
4.4.1 字面值表达式
字面值表达式支持String, 数字(整数,实数,十六进制), boolean, null.字面值要用单引号分隔, 要将单引号本身也引入其中,请使用两个单引号.通常不会单独这样使用,而是将其作为复杂表达式的一部分:
ExpressionParser parser = new SpelExpressionParser();
// 返回"Hello World"
String helloWorld = (String) parser.parseExpression("'Hello World'").getValue();
// 实数
double avogadrosNumber = (Double) parser.parseExpression("6.0221415E+23").getValue();
// 计算十六进制值并返回2147483647
int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue();
// 计算布尔值
boolean trueValue = (Boolean) parser.parseExpression("true").getValue();
Object nullValue = parser.parseExpression("null").getValue();
数字支持使用负号,指数符号和小数点.
4.4.2 属性, 数组, 集合, Map, 索引
访问属性,用.
符号.允许对属性名的第一个字母不分大小写.
int year = (Integer) parser.parseExpression("Birthdate.Year + 1900").getValue(context);
String city = (String) parser.parseExpression("placeOfBirth.City").getValue(context);
数组或List
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
// Inventions Array
// evaluates to "Induction motor"
String invention = parser.parseExpression("inventions[3]").getValue(
context, tesla, String.class);
// Members List
// evaluates to "Nikola Tesla"
String name = parser.parseExpression("Members[0].Name").getValue(
context, ieee, String.class);
// List and Array navigation
// evaluates to "Wireless communication"
String invention = parser.parseExpression("Members[0].Inventions[6]").getValue(
context, ieee, String.class);
访问Map, 由于Map的Key为String类型,因此可以直接指定key值.
// Officer's Dictionary
Inventor pupin = parser.parseExpression("Officers['president']").getValue(
societyContext, Inventor.class);
// evaluates to "Idvor"
String city = parser.parseExpression("Officers['president'].PlaceOfBirth.City").getValue(
societyContext, String.class);
// setting values
parser.parseExpression("Officers['advisors'][0].PlaceOfBirth.Country").setValue(
societyContext, "Croatia");
内联List
可以内联使用{}
直接表示List:
// evaluates to a Java list containing the four numbers
List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue(context);
List listOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context);
注:{}
本身就表示一个空的集合.
4.4.4 内联Map
Map也可以通过{key:value}
格式来直接定义.
// 将会得到包含两个元素的java Map
Map inventorInfo = (Map) parser.parseExpression("{name:'Nikola',dob:'10-July-1856'}").getValue(context);
Map mapOfMaps = (Map) parser.parseExpression("{name:{first:'Nikola',last:'Tesla'},dob:{day:10,month:'July',year:1856}}").getValue(context);
注: {:}
表示一个空的Map.Map的key上的引号是可选的.
4.4.5 构建数组
可以用熟悉的java语法来构建数组,同时也可提供初始化值.
int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue(context);
// Array with initializer
int[] numbers2 = (int[]) parser.parseExpression("new int[]{1,2,3}").getValue(context);
// Multi dimensional array
int[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue(context);
注:日前不支持在构建多维数据时提供初始化值.
4.4.6 方法调用
通过java语法格式, 可以直接对字符串调用方法,也支持可变参数.
// string literal, evaluates to "bc"
String bc = parser.parseExpression("'abc'.substring(1, 3)").getValue(String.class);
// evaluates to true
boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(
societyContext, Boolean.class);
4.4.7 操作符
关系操作符
关系操作符包括:==, !=, <, <=, >, >=
.
// evaluates to true
boolean trueValue = parser.parseExpression("2 == 2").getValue(Boolean.class);
// evaluates to false
boolean falseValue = parser.parseExpression("2 < -5.0").getValue(Boolean.class);
// evaluates to true
boolean trueValue = parser.parseExpression("'black' < 'block'")
.getValue(Boolean.class);
大于或小于比较null值时遵循如下规则,
null
表示什么也没有,因此任何值X与null进行X>null
都将为true
, 反之也都将为false
. 在使用数值比较时,请不要与null
进行比较, 而是与0
进行比较.
除了标准的关系运算符之外, SpEL还支持使用instanceof
和正则表达式匹配计算:
/ evaluates to false
boolean falseValue = parser.parseExpression(
"'xyz' instanceof T(Integer)").getValue(Boolean.class);
// evaluates to true
boolean trueValue = parser.parseExpression(
"'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
//evaluates to false
boolean falseValue = parser.parseExpression(
"'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
在对基本类型使用
instanceof
时要注意, 因为它会被装箱,所以1 instanceof int
为false
, 而1 instanceof Integer
为true
.
操作符可以用字母来代替, 这在xml配置中可以避免冲突.其字母代替为:lt(<), gt(>), le(<=), ge(>=), eq(==), ne(!=), div(/), mod(%), not(!)
. 这些不区分大小写.
逻辑操作符
逻辑操作符为and, or, not
.
// -- AND --
// evaluates to false
boolean falseValue = parser.parseExpression("true and false").getValue(Boolean.class);
// evaluates to true
String expression = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')";
boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
// -- OR --
// evaluates to true
boolean trueValue = parser.parseExpression("true or false").getValue(Boolean.class);
// evaluates to true
String expression = "isMember('Nikola Tesla') or isMember('Albert Einstein')";
boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
// -- NOT --用!表示
// evaluates to false
boolean falseValue = parser.parseExpression("!true").getValue(Boolean.class);
// -- AND and NOT --
String expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')";
boolean falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
数学操作
加号可以在操作数字和字符串, 减号,乘号和除号, 取余, 幂运算只支持数字.
// Addition 加号
int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2
String testString = parser.parseExpression(
"'test' + ' ' + 'string'").getValue(String.class); // 'test string'
// Subtraction 减号
int four = parser.parseExpression("1 - -3").getValue(Integer.class); // 4
double d = parser.parseExpression("1000.00 - 1e4").getValue(Double.class); // -9000
// Multiplication 乘号
int six = parser.parseExpression("-2 * -3").getValue(Integer.class); // 6
double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); // 24.0
// Division 除号
int minusTwo = parser.parseExpression("6 / -3").getValue(Integer.class); // -2
double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(Double.class); // 1.0
// Modulus 取余
int three = parser.parseExpression("7 % 4").getValue(Integer.class); // 3
int one = parser.parseExpression("8 / 5 % 2").getValue(Integer.class); // 1
// Operator precedence
int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class); // -21
4.4.8 赋值操作
设置属性是通过赋值操作符来完成的,这通常是在setValue
中完成的,但也可以在getValue
中完成.
Inventor inventor = new Inventor();
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
parser.parseExpression("Name").setValue(context, inventor, "Aleksandar Seovic");
// alternatively
String aleks = parser.parseExpression(
"Name = 'Aleksandar Seovic'").getValue(context, inventor, String.class);
4.4.9 类
特殊的T
操作符可用来表示一个类的实例, 静态方法也是用它来调用的.T操作符可以自动发现java.lang
包中的类,因此lang
包中的类用简单类名即可,但是对于其它包中的类名, 必须使用全限定类名.
Class dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class);
Class stringClass = parser.parseExpression("T(String)").getValue(Class.class);
boolean trueValue = parser.parseExpression(
"T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR")
.getValue(Boolean.class);
4.4.10 构造函数
可以通过new操作符调用构造函数.除了基本类型和String类之外, 都必须使用全限定类名.
Inventor einstein = p.parseExpression(
"new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')")
.getValue(Inventor.class);
//create new inventor instance within add method of List
p.parseExpression(
"Members.add(new org.spring.samples.spel.inventor.Inventor(
'Albert Einstein', 'German'))").getValue(societyContext);
4.4.11 变量
可以使用#varName
格式引用变量.
Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
context.setVariable("newName", "Mike Tesla");
parser.parseExpression("Name = #newName").getValue(context, tesla);
System.out.println(tesla.getName()) // "Mike Tesla"
#this和#root
#this
总是表示当前的evaluation
对象.#root
总是代表根上下文对象.虽然#this
可能会随着表达式组件的变化也变化, 但#root
却总是指向根对象的.
// 创建一个Integer类型的数组
List<Integer> primes = new ArrayList<Integer>();
primes.addAll(Arrays.asList(2,3,5,7,11,13,17));
// create parser and set variable 'primes' as the array of integers
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataAccess();
context.setVariable("primes", primes);
// all prime numbers > 10 from the list (using selection ?{...})
// evaluates to [11, 13, 17]
List<Integer> primesGreaterThanTen = (List<Integer>) parser.parseExpression(
"#primes.?[#this>10]").getValue(context);
4.4.12 函数
你可以通过定义能在表达式字符串中被调用的自定义函数来扩展SpEL.
Method method = ...;
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
context.setVariable("myFunction", method);
public abstract class StringUtils {
public static String reverseString(String input) {
StringBuilder backwards = new StringBuilder(input.length());
for (int i = 0; i < input.length(); i++)
backwards.append(input.charAt(input.length() - 1 - i));
}
return backwards.toString();
}
}
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
context.setVariable("reverseString",
StringUtils.class.getDeclaredMethod("reverseString", String.class));
String helloWorldReversed = parser.parseExpression(
"#reverseString('hello')").getValue(context, String.class);
4.4.13 Bean引用
如果配置了evaluation context中配置了bean解析器, 则可以用@
符号查找bean.
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new MyBeanResolver());
// This will end up calling resolve(context,"foo") on MyBeanResolver during evaluation
Object bean = parser.parseExpression("@foo").getValue(context);
如果要访问factory bean本身, 应使用&
标识符.
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new MyBeanResolver());
// This will end up calling resolve(context,"&foo") on MyBeanResolver during evaluation
Object bean = parser.parseExpression("&foo").getValue(context);
4.4.14 三元操作符
String falseString = parser.parseExpression(
"false ? 'trueExp' : 'falseExp'").getValue(String.class);
parser.parseExpression("Name").setValue(societyContext, "IEEE");
societyContext.setVariable("queryName", "Nikola Tesla");
expression = "isMember(#queryName)? #queryName + ' is a member of the ' " +
"+ Name + ' Society' : #queryName + ' is not a member of the ' + Name + ' Society'";
String queryResultString = parser.parseExpression(expression)
.getValue(societyContext, String.class);
// queryResultString = "Nikola Tesla is a member of the IEEE Society"
```
######4.4.15 Elvis 操作符
这是三元操作符的简写形式
```
ExpressionParser parser = new SpelExpressionParser();
String name = parser.parseExpression("name?:'Unknown'").getValue(String.class);
System.out.println(name); // 'Unknown'
```
> 可用它来为属性设置默认值`@Value("#{systemProperties['pop3.port'] ?: 25}")`
######4.4.16 安全导航操作
当引用一个对象的属性时通过要对其作null判断, 如果为null, 则会抛出NPE, 安全导航操作则是为了避免NPE, 并返回简单的null.
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan"));
String city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, tesla, String.class);
System.out.println(city); // Smiljan
tesla.setPlaceOfBirth(null);
city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, tesla, String.class);
System.out.println(city); // null - does not throw NullPointerException!!!
######4.4.17 集合选择
集合选择是一个强大的表达式语言特性, 它允许你选择源集合中的一些元素来组成一个新的集合.
其语法格式为:`.?[expression]`.这会过滤集合并返回包含原集合的一个子集的新集合.
```
List<Inventor> list = (List<Inventor>) parser.parseExpression(
"Members.?[Nationality == 'Serbian']").getValue(societyContext);
```
在列表和Map上都可以使用,list是对每个元素进行选择, map则是对每个entry进行选择.因此Map可以将key或value作为表达式属性进行选择.
如下,选择集合的value小于27的值.
```
Map newMap = parser.parseExpression("map.?[value<27]").getValue();
```
**注:**除了返回被选择的元素, 还可以检索第一个或最后一个元素.其语法分别是`.^[expression]`和`.$[experssion]`.
######4.4.18 集合Projection
Projection允许一个集合驱动一个子表达式并返回一个新的集合, 其语法是: `.![expression]`.
假设我们有一个inventors list, 现在我们想要找出他们出生的城市的集合.
```
// returns ['Smiljan', 'Idvor' ]
List placesOfBirth = (List)parser.parseExpression("Members.![placeOfBirth.city]");
```
针对Map也可以使用.获取的结果是一个list.
######4.4.19 表达式模板
表达式模板是指可以通过表达式前缀和后缀来组合表达式.
```
String randomPhrase = parser.parseExpression(
"random number is #{T(java.lang.Math).random()}",
new TemplateParserContext()).getValue(String.class);
```
这个表达式的执行结果是将`random number is`和`#{}`中的执行结果拼接起来, 第二个参数是一个`ParserContext`类型, 用来定义表达式的解析方法, 其定义如下:
```
public class TemplateParserContext implements ParserContext {
public String getExpressionPrefix() {
return "#{";
}
public String getExpressionSuffix() {
return "}";
}
public boolean isTemplate() {
return true;
}
}
```
#####4.5 前面示例中使用的类
Inventor.java
```
package org.spring.samples.spel.inventor;
import java.util.Date;
import java.util.GregorianCalendar;
public class Inventor {
private String name;
private String nationality;
private String[] inventions;
private Date birthdate;
private PlaceOfBirth placeOfBirth;
public Inventor(String name, String nationality) {
GregorianCalendar c= new GregorianCalendar();
this.name = name;
this.nationality = nationality;
this.birthdate = c.getTime();
}
public Inventor(String name, Date birthdate, String nationality) {
this.name = name;
this.nationality = nationality;
this.birthdate = birthdate;
}
public Inventor() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNationality() {
return nationality;
}
public void setNationality(String nationality) {
this.nationality = nationality;
}
public Date getBirthdate() {
return birthdate;
}
public void setBirthdate(Date birthdate) {
this.birthdate = birthdate;
}
public PlaceOfBirth getPlaceOfBirth() {
return placeOfBirth;
}
public void setPlaceOfBirth(PlaceOfBirth placeOfBirth) {
this.placeOfBirth = placeOfBirth;
}
public void setInventions(String[] inventions) {
this.inventions = inventions;
}
public String[] getInventions() {
return inventions;
}
}
```
PlaceOfBirth.java
```
package org.spring.samples.spel.inventor;
public class PlaceOfBirth {
private String city;
private String country;
public PlaceOfBirth(String city) {
this.city=city;
}
public PlaceOfBirth(String city, String country) {
this(city);
this.country = country;
}
public String getCity() {
return city;
}
public void setCity(String s) {
this.city = s;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}
```
Society.java
```
package org.spring.samples.spel.inventor;
import java.util.*;
public class Society {
private String name;
public static String Advisors = "advisors";
public static String President = "president";
private List<Inventor> members = new ArrayList<Inventor>();
private Map officers = new HashMap();
public List getMembers() {
return members;
}
public Map getOfficers() {
return officers;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isMember(String name) {
for (Inventor inventor : members) {
if (inventor.getName().equals(name)) {
return true;
}
}
return false;
}
}
```