传统IoC 容器的实现
Java Beans 作为IoC 容器
特性
• 依赖查找
• 生命周期管理
• 配置元信息
BeanInfo这个API中, 属性描述符 PropertyDescriptor可以查看到当前类的相关元信息
• 事件
BeanInfo这个API中, EventSetDescriptor
• 自定义
• 资源管理
很贫弱, 但是有
• 持久化
规范
• JavaBeans:
https://www.oracle.com/technetwork/java/javase/tech/index-jsp-138795.html
• BeanContext:https://docs.oracle.com/javase/8/docs/technotes/guides/beans/spec/beancontext.html
比较复杂, 主要涉及管理Bean, bean自己容器之间的相互依赖关系, 包括层次性, 双亲委派就可以用到这里来
代码示例
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.geekbang.thinking.in.spring.ioc.overview.domain;
import org.geekbang.thinking.in.spring.ioc.overview.enums.City;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.core.io.Resource;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
/**
* 用户类
* <p>
* spring-core-2-17 | 传统IoC容器实现:JavaBeans也是IoC容器吗?
* 这个类也用于JavaBeans作为IoC容器功能的演示
* <p>
* 常见叫法: Setter方法 / Getter 方法
* Java Beans叫法: 可写方法(writable) / 可读方法(Readable)
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
* @since
*/
public class User implements BeanNameAware {
private Long id;// 属性也称为Property
private String name;
private City city;
private City[] workCities;
private List<City> lifeCities;
private Resource configFileLocation;
private Company company;
private Properties context;
private String contextAsText;
/**
* 当前 Bean 的名称
*/
private transient String beanName;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public City getCity() {
return city;
}
public void setCity(City city) {
this.city = city;
}
public Resource getConfigFileLocation() {
return configFileLocation;
}
public void setConfigFileLocation(Resource configFileLocation) {
this.configFileLocation = configFileLocation;
}
public City[] getWorkCities() {
return workCities;
}
public void setWorkCities(City[] workCities) {
this.workCities = workCities;
}
public List<City> getLifeCities() {
return lifeCities;
}
public void setLifeCities(List<City> lifeCities) {
this.lifeCities = lifeCities;
}
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
public static User createUser() {
User user = new User();
user.setId(1L);
user.setName("小马哥");
return user;
}
@PostConstruct
public void init() {
System.out.println("User Bean [" + beanName + "] 初始化...");
}
@PreDestroy
public void destroy() {
System.out.println("User Bean [" + beanName + "] 销毁中...");
}
@Override
public void setBeanName(String name) {
this.beanName = name;
}
public Properties getContext() {
return context;
}
public void setContext(Properties context) {
this.context = context;
}
public String getContextAsText() {
return contextAsText;
}
public void setContextAsText(String contextAsText) {
this.contextAsText = contextAsText;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", city=" + city +
", workCities=" + Arrays.toString(workCities) +
", lifeCities=" + lifeCities +
", configFileLocation=" + configFileLocation +
", company=" + company +
", context=" + context +
", contextAsText='" + contextAsText + '\'' +
", beanName='" + beanName + '\'' +
'}';
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.geekbang.thinking.in.spring.validation;
import org.geekbang.thinking.in.spring.ioc.overview.domain.User;
import java.beans.*;
import java.util.stream.Stream;
/**
* 17 | 传统IoC容器实现:JavaBeans也是IoC容器吗?
* 后面还有视频详细介绍, 比如针对propertyEditor来实现类型的转换等
* JavaBeans 示例
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
* @since
*/
public class JavaBeansDemo {
public static void main(String[] args) throws IntrospectionException {
// 使用自省的方式获取BeanInfo
// stopClass 排除(截止)类, 这样可以不去读父类的BeanInfo, 这里就不会去找Object.class
BeanInfo beanInfo = Introspector.getBeanInfo(User.class, Object.class);
// 属性描述符 PropertyDescriptor
// 通过 PropertyDescriptor 可以实现很多事情, spring3 之前用到很多这里用于属性的set和get
// 所有 Java 类均继承 java.lang.Object
// class 属性来自于 Object#getClass() 方法
Stream.of(beanInfo.getPropertyDescriptors())
.forEach(propertyDescriptor -> {
// propertyDescriptor.getReadMethod(); // Getter 方法
// propertyDescriptor.getWriteMethod(); // Setter 方法
System.out.println(propertyDescriptor);
// PropertyDescriptor 允许添加属性编辑器 -> PropertyEditor
// GUI -> text(String) -> PropertyType
// 这里有个属性输入是个String的, 输出要为Integer, 可以这么做:
Class<?> propertyType = propertyDescriptor.getPropertyType();
String propertyName = propertyDescriptor.getName();
// 为 "age" 字段/属性增加 PropertyEditor
if ("age".equals(propertyName)) {
propertyDescriptor.setPropertyEditorClass(StringToIntegerPropertyEditor.class);
// ...后面还有后续要写的代码, 暂时不在这里展开
}
});
// 输出 User 定义的方法 MethodDescriptor
Stream.of(beanInfo.getMethodDescriptors()).forEach(System.out::println);
}
/**
* 自定义实现的一个PropertyEditor, 不直接使用 PropertyEditor接口, 因为要实现的方法太多了,
* 继承一个 PropertyEditorSupport, 只需要覆盖里面转换Text的方法即可
* 在 spring3.0 之前, 像这样的类型转换, 大量是基于 PropertyEditorSupport 来进行操作的.
* 这里既满足于我们的元数据或者元信息编程, 另一方面也是一些类型配置和转换的一些依据
*/
static class StringToIntegerPropertyEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) throws java.lang.IllegalArgumentException {
Integer value = Integer.valueOf(text);
setValue(value);
}
}
}