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));
}
}