Spring原理学习笔记

Spring 5原理学习笔记

2020.6.28

1、Spring版本与Java版本的对应关系:

在这里插入图片描述

2、IOC:

IOC的底层原理核心思想:

前置知识:反射+xml解析

ioc概念:控制反转。控制是指new对象的控制权,反转是指控制权交给Spring框架。

思想:对象工厂,尽可能实现解耦(加载xml解析、工厂模式中用反射创建对象)

模拟ioc加载:

配置文件(bean.properties):

BEAN=com.znk.entity.Student

Java类:

Student.java

package com.znk.entity;

public class Student {
    public void print(){
        System.out.println("学生类");
    }
}

ObjectFactory.java

package com.znk;

import java.io.IOException;
import java.util.Properties;

public class ObjectFactory {

    private static Properties properties = null;
    private static String BEAN = null;

    static {
        properties = new Properties();
        try {
            properties.load(ObjectFactory.class.getClassLoader().getResourceAsStream("bean.properties"));
            BEAN = properties.getProperty("BEAN");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public Object getBean() throws Exception {
        return Class.forName(BEAN).newInstance();
    }

}

Test.java

package com.znk.test;

import com.znk.ObjectFactory;
import com.znk.entity.Student;

public class Test {
    public static void main(String []args) throws Exception{
        Student student = (Student) new ObjectFactory().getBean();
        student.print();
    }
}

spring格式xml解析(剥洋葱,一层一层剥开):

package com.znk.entity;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.util.Iterator;

public class XmlTest {
    public static void main(String[] args) {
        SAXReader saxReader = new SAXReader();
        try {
            //将xml转换成一个document对象
            Document document = saxReader.read("src/main/resources/spring.xml");
            //根节点是beans
            Element root = document.getRootElement();
            //迭代根节点
            Iterator<Element> iterator = root.elementIterator();
            while (iterator.hasNext()) {
                Element bean = iterator.next();
                //取bean标签里的内容
                if (bean.getName().equals("bean")) {
                    
                    System.out.println(bean.attributeValue("id"));
                    System.out.println(bean.attributeValue("class"));
                }
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }
}

spring提供的两种接口实现IOC:(BeanFactory、ApplicationContext)

1、BeanFactory:spring里面内部使用接口,一般不提供给我们开发人员进行使用。有个特点:加载配置文件时不会创建里边的对象,而是在你获取或者是使用时才创建对象。

<bean id="Student" class="com.znk.entity.Student"></bean>
BeanFactory beanFactory=new ClassPathXmlApplicationContext("spring.xml");//这时候并没有创建Student对象
Student student = beanFactory.getBean("Student", Student.class);//此时才创建了Student对象

2、ApplicationContext:是BeanFactory的一个子接口,提供了更强大的功能,供开发人员进行使用。(核心)

ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring.xml");//此时已创建Student对象

区别:BeanFactory只有在调用时才会创建对象,而ApplicationContext在加载配置文件时已经创建了对应的对象。

误区:既然这样,用BeanFactory好一点,节约资源,什么时候用什么时候创建。

正解:一般情况下用ApplicationContext会点,因为大多数情况下是服务于web项目,在服务器项目启动时一次性加载,这样就不会在服务时候去创建,而是提前准备好对象

Ioc 的实现:

1、ioc读取xml文件,获取相关信息类:信息、属性信息(xml解析)

2、根据第一步获取的信息动态创建对象(反射)

spring.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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="user" class="com.znk.entity.User">
        <property name="id" value="1"/>
        <property name="name" value="张三"/>
        <property name="age" value="18"/>
    </bean>

    <bean id="user2" class="com.znk.entity.User">
        <property name="id" value="2"/>
        <property name="name" value="李四"/>
        <property name="age" value="19"/>
    </bean>

</beans>

User.java

package com.znk.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    private Long id;
    private String name;
    private Integer age;

}

MyClassPathApplicationContext.java

package com.znk.ioc;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.MessageSourceResolvable;
import org.springframework.context.NoSuchMessageException;
import org.springframework.core.ResolvableType;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

public class MyClassPathApplicationContext implements ApplicationContext {

    private String xmlPath = null;
    private SAXReader saxReader = null;
    private static Map<String, Object> iocContainer = null;

    static {
        iocContainer = new HashMap<>();
    }

    public MyClassPathApplicationContext(String xmlPath) {
        this.xmlPath = xmlPath;
        saxReader = new SAXReader();
        try {
            Document document = saxReader.read("src/main/resources/" + xmlPath);
            Element root = document.getRootElement();
            Iterator<Element> iteratorRoot = root.elementIterator();
            Object object = null;
            while (iteratorRoot.hasNext()) {
                Element bean = iteratorRoot.next();
                if (bean.getName().equals("bean")) {
                    Class aClass = Class.forName(bean.attributeValue("class"));
                   
                    Constructor constructor = aClass.getConstructor();
                    object = constructor.newInstance();
                    Iterator iterator = bean.elementIterator();
                    Object value = null;
                    while (iterator.hasNext()) {
                        Element property = (Element) iterator.next();
                        String name = property.attributeValue("name");
                        String methodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
                        //直接getField()是拿不到私有的成员变量,要getDeclaredField();
                        Field field = aClass.getDeclaredField(name);
                        Method method = aClass.getMethod(methodName, field.getType());
                        value = property.attributeValue("value");
                        switch (field.getType().getName()) {
                            case "java.lang.Long":
                                value = Long.parseLong(property.attributeValue("value"));
                                break;
                            case "java.lang.Integer":
                                value = Integer.parseInt(property.attributeValue("value"));
                                break;
                        }
                        method.invoke(object, value);
                    }
                }
                iocContainer.put(bean.attributeValue("id"), object);
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
    
    //只实现这两个方法,其他方法省略

    @Override
    public Object getBean(String s) throws BeansException {
        return iocContainer.get(s);
    }

    @Override
    public <T> T getBean(Class<T> aClass) throws BeansException {
        Collection<Object> objects = iocContainer.values();
        for (Object object : objects) {
            if (object.getClass().equals(aClass)) {
                return (T) object;
            }
        }
        return null;
    }

测试类

package com.znk.test;

import com.znk.entity.User;
import com.znk.ioc.MyClassPathApplicationContext;
import org.springframework.context.ApplicationContext;

public class MyDemo {
    public static void main(String[] args) {
        ApplicationContext context = new MyClassPathApplicationContext("spring.xml");
        User user = (User) context.getBean("user");
        System.out.println(user);
        User user2 = (User) context.getBean("user2");
        System.out.println(user2);
        User user1 = context.getBean(User.class);
        System.out.println(user1);

    }
}

3、AOP:

AOP的底层原理核心思想:

底层是用动态代理方式来实现的。

两种情况:1、有接口(jdk的动态代理) 2、无接口(CGLIB动态代理)

AopProxy的实现类:

在这里插入图片描述

JdkDynamicAopProxy的核心方法:

  • getProxy():返回动态代理对象

    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);

    作用是动态创建类和动态创建对象

    Interfaces:目标类的接口(因为要有目标类一样的功能)

    ClassLoader:类加载器把目标类加载到内存里

    this

  • invoke():代理对象的调用

步骤:

1、目标类添加@Component注解和切面类添加@Component注解和@Aspect,则会在程序启动时自动创建对应的对象加入到ioc容器中。

2、当加载配置文件时,扫描到<aop:aspectj-autoproxy/>标签,则会在ioc容器中自动创建动态代理对象。(程序是通过JdkDynamicProxy类来动态创建一个代理类进而创建一个类,并为其创建这个类的对象,即代理对象)

当没有改标签时:

在这里插入图片描述

当有标签时:

在这里插入图片描述

4、自动创建的动态代理对象会覆盖掉原来的目标类对象,拥有切面功能的同时兼具目标类功能。

需要注意的是:

代理对象调用的是invoke方法里执行的方法。

主要的业务方法还是由目标类的对象(委托对象)去调用方法执行。(写在invoke方法里)

aop的实现:

MyJdkDynamicProxy.java:

package com.znk.aop;

import org.springframework.aop.framework.AopProxy;

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

public class MyJdkDynamicProxy implements AopProxy, InvocationHandler, Serializable {

    //目标对象(委托对象)
    private Object target = null;

    //接收目标对象,返回动态代理对象
    public Object bind(Object o) {
        target = o;
        return this.getProxy(MyJdkDynamicProxy.class.getClassLoader());
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(target, args);
        System.out.println(method.getName() + "的方法参数是:" + Arrays.asList(args));
        System.out.println(method.getName() + "的方法结果是:" + result);
        return result;
    }

    @Override
    public Object getProxy() {
        return null;
    }

    @Override
    public Object getProxy(ClassLoader classLoader) {
        return Proxy.newProxyInstance(classLoader, target.getClass().getInterfaces(), this);
    }
}

Cal接口:

package com.znk.aop;

public interface Cal {
    int add(int num1, int num2);
}

CalImpl.java:

package com.znk.aop.impl;

import com.znk.aop.Cal;
import org.springframework.stereotype.Component;

@Component
public class CalImpl implements Cal {

    private int result = 0;

    public int add(int num1, int num2) {
        result = num1 + num2;
        return result;
    }

}

测试类:

package com.znk.test;

import com.znk.aop.Cal;
import com.znk.aop.MyJdkDynamicProxy;
import com.znk.aop.impl.CalImpl;

public class MyTest {
    public static void main(String[] args) {
        Cal cal = new CalImpl();
        MyJdkDynamicProxy myJdkDynamicProxy = new MyJdkDynamicProxy();
        Cal proxy = (Cal) myJdkDynamicProxy.bind(cal);
        System.out.println(proxy.add(1, 1));
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值