содержание
- Принцип и ручная реализация ИОК серии Spring
- Принцип и ручная реализация DI в серии Spring
- Принцип и ручная реализация АОП в серии Spring
- Анализ рукописных аннотаций и конфигурационных файлов серии Spring
- Напишите SpringMVC для серии Spring
Введение
Spring — это многоуровневая полнофункциональная (универсальная) облегченная среда JavaSE/EE с открытым исходным кодом. Это также одна из сред, которую должны освоить почти все, кто работает с Java.Его отличные дизайнерские идеи и искусство реализации кода — это то, что нам нужно освоить. Чтобы изучить Spring, помимо использования его в нашем проекте, нам также необходимо изучить его исходный код, но реализация Spring охватывает много знаний, и количество классов в ней тоже очень велико. Чтение кода может быть перемежено между десятками занятий, и есть большая вероятность, что вы случайно запутаетесь. Ввиду этого я сначала выполню ручную и простую реализацию нескольких важных модулей в Spring.Во-первых, я знаком с принципами этих модулей, а также имитирую структуру в Spring, чтобы заложить основу для чтения исходного кода позже .
IOC(Inversion of Control)
Инверсия управления — это инверсия управления, что означает, что объекты, созданные клиентским кодом, передаются в контейнер IOC для управления.Создание, инициализация и последующее управление объектами выполняются IOC.
Преимущества МОК
- Разделение: Появление IOC устраняет связь между классами и классами.В эпоху сервлетов веб-разработки, если сервлет должен зависеть от некоторых реализаций другого класса, нам нужно создать и отделить зависимые классы в текущем классе. Инициализация, если от этого класса зависят и другие классы, то его также необходимо создать и инициализировать, а если передать в управление в ИОЦ, то обращаться к ИОЦ нужно только по мере необходимости, без повторного создания и инициализации. Конечно, IOC также позволяет каждый раз воссоздавать новый объект.
- Удобно сотрудничать с АОП: АОП также является очень часто используемой функцией, и может быть очень удобно сотрудничать с АОП через IOC.
Шаблоны проектирования, разработанные в IOC
Заводской узор. Контейнер IOC отвечает за создание объекта экземпляра класса управления, обращение к IOC при необходимости и получение его от IOC. Таким образом, контейнер IOC также называют бобовой фабрикой.
Фабричный паттерн — это относительно простой и понятный паттерн проектирования, который мы здесь представлять не будем.заводской узор.
Ручная реализация IOC
Определение бина
Основной функцией IOC является управление бобами, включая создание, инициализацию, управление и экстази. Первый вопрос, с которым мы сталкиваемся, заключается в том, как мы позволяем IOC создавать Bean? Что нам нужно предоставить для создания bean-компонента?
Как создать бобы
Существует два объектных способа создания экземпляра класса без создания его вручную с помощью нового ключевого слова:
- Отражение: экземпляр класса может быть создан посредством отражения:
clazz.getClass().newInstance();
. - Фабричный шаблон: Фабричный шаблон позволяет нам создавать экземпляры, не касаясь класса экземпляра.
public class PersonFactory{
public Person getPerson(){
return new Person();
}
}
Что нам нужно предоставить для создания bean-компонента
Ответ можно легко найти, проанализировав два вышеуказанных метода.
Для метода отражения нам нужно только предоставить объект класса экземпляра.
Для фабричного метода все, что нам нужно предоставить, — это имя фабрики (factoryName) и имя метода (methodName), которые создают класс;
Что нужно сделать, кроме создания бина
IOC-контейнер управляет всем жизненным циклом бина, помимо создания, он также должен инициализировать бин и уничтожать бин, когда он не нужен (например, высвобождение ресурсов и т. д.). Поэтому нам также необходимо обеспечить такие операции, как инициализация и уничтожение.
На этом базовый анализ, необходимый для создания bean-компонента, завершен, взгляните на диаграмму классов:
Бобовая фабрика
Определение bean-компонента решено, но где должны быть размещены определение bean-компонента и созданный экземпляр bean-компонента?Нам нужно единое место для хранения этих вещей, чтобы мы могли легко брать их при использовании.
Мы определяем фабрику бинов для хранения бинов, и мы можем получить их от фабрики бинов, когда они нам понадобятся. То, что фабрика бинов предоставляет извне, — это только метод для получения бинов. Поскольку тип бина неизвестен, возвращаемое значение определяет местонахождение Object .
Зарегистрировать определение компонента
Теперь, когда у нас есть определение bean-компонента для создания bean-компонентов и bean-фабрика для хранения bean-компонентов и управления ими, нам нужно подумать о том, как соединить эти два класса.Нам также нужен еще один интерфейс.Функция интерфейса состоит в том, чтобы позволить нам регистрировать и Получите определение bean-компонента, здесь мы различаем разные bean-компоненты по beanName.
Код
На данный момент мы в основном завершили то, что нам нужно для реализации простого контейнера IOC.Давайте посмотрим на базовую диаграмму классов:
Базовая реализация кода:
DefaultBeanDefinition:
public class DefaultBeanDefinition implements BeanDefinition{
private Class<?> clazz;
private String beanFactoryName;
private String createBeanMethodName;
private String staticCreateBeanMethodName;
private String beanInitMethodName;
private String beanDestoryMethodName;
private boolean isSingleton;
// setter
public void setSingleton(boolean singleton) {
isSingleton = singleton;
}
@Override
public Class<?> getBeanClass() {
return this.clazz;
}
@Override
public String getBeanFactory() {
return this.beanFactoryName;
}
@Override
public String getCreateBeanMethod() {
return this.createBeanMethodName;
}
@Override
public String getStaticCreateBeanMethod() {
return this.staticCreateBeanMethodName;
}
@Override
public String getBeanInitMethodName() {
return this.beanInitMethodName;
}
@Override
public String getBeanDestoryMethodName() {
return this.beanDestoryMethodName;
}
@Override
public String getScope() {
return this.isSingleton?BeanDefinition.SINGLETION :BeanDefinition.PROTOTYPE;
}
@Override
public boolean isSingleton() {
return this.isSingleton;
}
@Override
public boolean isPrototype() {
return !this.isSingleton;
}
}
DefaultBeanFactory:
public class DefaultBeanFactory implements BeanFactory, BeanDefinitionRegistry, Closeable {
private Log log = LogFactory.getLog(this.getClass());
//ConcurrentHashMap应对并发环境
private Map<String, BeanDefinition> bdMap = new ConcurrentHashMap<>();
private Map<String, Object> beanMap = new ConcurrentHashMap<>();
@Override
public void register(BeanDefinition bd, String beanName) {
Assert.assertNotNull("beanName不能为空 beanName", beanName);
Assert.assertNotNull("BeanDefinition不能为空", bd);
if(bdMap.containsKey(beanName)){
log.info("[" + beanName + "]已经存在");
}
if(!bd.validate()){
log.info("BeanDefinition不合法");
}
if(!bdMap.containsKey(beanName)){
bdMap.put(beanName, bd);
}
}
@Override
public boolean containsBeanDefinition(String beanName) {
return bdMap.containsKey(beanName);
}
@Override
public BeanDefinition getBeanDefinition(String beanName) {
if(!bdMap.containsKey(beanName)){
log.info("[" + beanName + "]不存在");
}
return bdMap.get(beanName);
}
public Object doGetBean(String beanName) throws InstantiationException, IllegalAccessException {
if(!beanMap.containsKey(beanName)){
log.info("[" + beanName + "]不存在");
}
Object instance = beanMap.get(beanName);
if(instance != null){
return instance;
}
//不存在则进行创建
if(!this.bdMap.containsKey(beanName)){
log.info("不存在名为:[" + beanName + "]的bean定义");
}
BeanDefinition bd = this.bdMap.get(beanName);
Class<?> beanClass = bd.getBeanClass();
if(beanClass != null){
instance = createBeanByConstruct(beanClass);
if(instance == null){
instance = createBeanByStaticFactoryMethod(bd);
}
}else if(instance == null && StringUtils.isNotBlank(bd.getStaticCreateBeanMethod())){
instance = createBeanByFactoryMethod(bd);
}
this.doInit(bd, instance);
if(instance != null && bd.isSingleton()){
beanMap.put(beanName, instance);
}
return instance;
}
private void doInit(BeanDefinition bd, Object instance) {
Class<?> beanClass = instance.getClass();
if(StringUtils.isNotBlank(bd.getBeanInitMethodName())){
try {
Method method = beanClass.getMethod(bd.getBeanInitMethodName(), null);
method.invoke(instance, null);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 构造方法创建实例
* @param beanClass
* @return
*/
private Object createBeanByConstruct(Class<?> beanClass) {
Object instance = null;
try {
instance = beanClass.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return instance;
}
/**
* 普通工厂方法创建实例
* @param bd
* @return
*/
private Object createBeanByFactoryMethod(BeanDefinition bd) {
Object instance = null;
try {
//获取工厂类
Object factory = doGetBean(bd.getBeanFactory());
//获取创建实例的方法
Method method = factory.getClass().getMethod(bd.getCreateBeanMethod());
//执行方法
instance = method.invoke(factory, null);
} catch (Exception e) {
e.printStackTrace();
}
return instance;
}
/**
* 静态方法创建实例
* @param bd
* @return
*/
private Object createBeanByStaticFactoryMethod(BeanDefinition bd) {
Object instance = null;
try {
Class<?> beanClass = bd.getBeanClass();
//获取创建实例的方法
Method method = beanClass.getMethod(bd.getStaticCreateBeanMethod());
instance = method.invoke(beanClass, null);
} catch (Exception e) {
e.printStackTrace();
}
return instance;
}
@Override
public Object getBean(String beanName) {
if(!beanMap.containsKey(beanName)){
log.info("[" + beanName + "]不存在");
}
return beanMap.get(beanName);
}
@Override
public void close() throws IOException {
Set<Map.Entry<String, BeanDefinition>> entries = bdMap.entrySet();
for(Map.Entry<String, BeanDefinition> entry: entries){
BeanDefinition value = entry.getValue();
String destoryMethodName = value.getBeanDestoryMethodName();
try {
Method method = value.getBeanClass().getMethod(destoryMethodName, null);
method.invoke(value.getBeanClass(), null);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Просто проверьте это: Экземплярный компонент:
public class User {
private String name;
private int age;
//getter setter
public void init(){
System.out.println("init...");
}
public void destory(){
System.out.println("destory...");
}
}
Заводской класс:
public class TestFactory {
public Object createMethod(){
return new User();
}
public static Object staticCreateMethod(){
return new User();
}
}
Тестовый класс:
public class MySpringTest {
static DefaultBeanFactory factory = new DefaultBeanFactory();
@Test
public void test() throws IllegalAccessException, InstantiationException {
DefaultBeanDefinition bd = new DefaultBeanDefinition();
bd.setClazz(User.class);
bd.setSingleton(true);
bd.setBeanFactoryName("TestFactory");
bd.setCreateBeanMethodName("createMethod");
bd.setStaticCreateBeanMethodName("staticCreateMethod");
bd.setBeanInitMethodName("init");
factory.register(bd, "user");
System.out.println(factory.doGetBean("user"));
}
}
резюме
Простой контейнер реализован таким образом, конечно, здесь у нас есть только базовые функции, но по факту он еще далеко позади, например, инстанцирование бинов с параметрами. Но основной принцип МОК выражен, и на его основе нужно только добавить новые функции.