这段时间一直在学习Spring框架,对IOC和AOP也只是原理上的了解,为了加深理解,参考田小波这篇博客实现了IOC和AOP的基本功能。
一. IOC的简单实现
1.实现流程
- 加载xml配置文件,遍历其中的标签
- 获取标签中的id 和class 属性,加载class 属性对应的类,并利用反射创建bean。
- 遍历标签中的元素,获取属性值,并将属性值填充到bean中。
- 将bean 注册到bean 容器中。
2.实现代码
- SimpleIOC类,ioc的主要实现类
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
/**
* @author Time
* @created 2019/7/2
*/
public class SimpleIOC {
// 用来存放bean的容器
private Map<String , Object> beanMap = new HashMap<>();
public SimpleIOC(String location) throws Exception{
loadBeans(location);
}
// 获取bean
public Object getBean(String name){
System.out.println(beanMap);
Object bean = beanMap.get(name);
if( bean == null){
throw new IllegalArgumentException("there is no bean with name" + name);
}
return bean;
}
private void loadBeans(String location) throws Exception{
// 加载xml配置文件
InputStream inputStream = new FileInputStream(location);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = factory.newDocumentBuilder();
Document doc = docBuilder.parse(inputStream);
Element root = doc.getDocumentElement();
NodeList nodes = root.getChildNodes();
// 遍历bean 标签
for(int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
String id = ele.getAttribute("id");
String className = ele.getAttribute("class");
// 加载beanclass
Class beanClass = null;
try {
beanClass = Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
return;
}
// 创建bean
Object bean = beanClass.newInstance();
// 遍历 property 标签
NodeList propertyNodes = ele.getElementsByTagName("property");
for (int j = 0; j < propertyNodes.getLength(); j++) {
Node propertyNode = propertyNodes.item(j);
if (propertyNode instanceof Element) {
Element propertyElement = (Element) propertyNode;
String name = propertyElement.getAttribute("name");
String value = propertyElement.getAttribute("value");
//利用反射将bean 相关字段访问权限设为可访问
Field declaredField = bean.getClass().getDeclaredField(name);
declaredField.setAccessible(true);
if (value != null && value.length() > 0) {
// 将属性填充到字段中
declaredField.set(bean, value);
} else {
String ref = propertyElement.getAttribute("ref");
if (ref == null || ref.length() == 0) {
throw new IllegalArgumentException("ref config error");
}
// 将引用填充到相关字段中
declaredField.set(bean, getBean(ref));
}
registerBean(id , bean);
}
}
}
}
}
private void registerBean(String id, Object bean){
beanMap.put(id, bean);
}
}
上述代码比较简单,通过dom解析的方式,获取到xml配置文件中的内容,然后生成实例,注入相关参数。注册到bean容器中。
- 注入类
用来检测IOC容器是否成功的类
Wheell类
package test;
/**
* @author Time
* @created 2019/7/2
*/
public class Wheel {
private String brand;
private String specification;
@Override
public String toString() {
return "Wheel{" +
"brand='" + brand + '\'' +
", specification='" + specification + '\'' +
'}';
}
}
Car类
package test;
/**
* @author Time
* @created 2019/7/2
*/
public class Wheel {
private String brand;
private String specification;
@Override
public String toString() {
return "Wheel{" +
"brand='" + brand + '\'' +
", specification='" + specification + '\'' +
'}';
}
}
- xml配置文件
<beans>
<bean id = "wheel" class="test.Wheel">
<property name="brand" value="Michelin"/>
<property name="specification" value="256/60 R18"/>
</bean>
<bean id="car" class="test.Car">
<property name="name" value="Mercedes Benz G 500"/>
<property name="length" value="4717mm"/>
<property name="width" value="1855mm"/>
<property name="height" value="1949mm"/>
<property name="wheel" ref="wheel"/>
</bean>
</beans>
- 测试类
验证是否成功
/**
* @author Time
* @created 2019/7/2
*/
import test.*;
public class test {
public static void main(String[] args) throws Exception {
String location = SimpleIOC.class.getClassLoader().getResource("ioc.xml").getFile();
System.out.println(location);
SimpleIOC bf = new SimpleIOC(location);
Wheel wheel = (Wheel) bf.getBean("wheel");
System.out.println(wheel);
}
}
输出结果:
二. AOP的简单实现
IOC的实现是基于代理模式的,Spring中的IOC有两种实现,当被代理类有接口时,Spring利用jdk的动态代理实现AOP,当被代理类没有继承接口时,Spring采用Cglib实现AOP。
1.SpringAOP中一些概念
- Advice
定义了织入对象的逻辑以及执行时机
- before :目标方法执行之前
- after: 目标方法执行之后,此时不管目标方法返回的结果是什么
- after-returning:目标方法执行后
- after-throwing: 目标方法抛出异常后执行
- around: 目标方法执行前后都会被调用
- PointCut
定义了执行的插入点
- Aspect
前面两个组和起来,就是Aspect。即在何时何处执行什么。
2.具体实现
- 定义切面接口,这个接口需要实现InvocationHandler,该接口中有invoke
()方法。后面生成的代理类执行相应的方法时,实质上是调用这个方法。
package MySpringAOP;
import java.lang.reflect.InvocationHandler;
/**
* @author Time
* @created 2019/7/2
*/
public interface Advice extends InvocationHandler {
}
// Advice 的实现类
package MySpringAOP;
import java.lang.reflect.Method;
/**
* @author Time
* @created 2019/7/2
*/
public class BeforeAdvice implements Advice {
private Object bean;
private MethodInvocation methodInvocation;
public BeforeAdvice(Object bean, MethodInvocation methodInvocation){
this.bean = bean;
this.methodInvocation = methodInvocation;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在目标方法执行之前调用通知
methodInvocation.invoke();
return method.invoke(bean,args);
}
}
- 定义一个需要被代理的类,这个类一定要实现相应的接口。因为jdk的底层就是通过这个接口来实现代理类的。
// 接口
package MySpringAOP;
/**
* @author Time
* @created 2019/7/2
*/
public interface HelloService {
void sayHelloWorld();
}
// 接口实现类
package MySpringAOP;
/**
* @author Time
* @created 2019/7/2
*/
public class HelloServiceImpl implements HelloService {
public void sayHelloWorld() {
System.out.println("hello world!");
}
}
- 添加一个接口,用来定义Advice
package MySpringAOP;
/**
* @author Time
* @created 2019/7/2
*/
public interface MethodInvocation {
void invoke();
}
- 返回代理对象的类
package MySpringAOP;
import java.lang.reflect.Proxy;
/**
* @author Time
* @created 2019/7/2
*/
public class SimpleAOP {
public static Object getProxy(Object bean,Advice advice){
return Proxy.newProxyInstance(SimpleAOP.class.getClassLoader(), bean.getClass().getInterfaces(), advice);
}
}
- 测试AOP功能,其实这里实现了两层代理,首先BeforeAdvice 代理了(包装) HelloServiceImpl类。最后又通过动态代理返回真正的代理类。
package MySpringAOP;
/**
* @author Time
* @created 2019/7/2
*/
public class SimpleAOPTest {
public static void main(String[] args) {
// 创建一个 MethodInvocation 实现类
MethodInvocation logTask = new MethodInvocation() {
public void invoke() {
System.out.println("log task start");
}
};
HelloServiceImpl helloServiceImpl = new HelloServiceImpl();
// 创建一个advice
Advice beforeAdvice = new BeforeAdvice(helloServiceImpl,logTask);
// 为目标类生成代理
HelloService helloServiceImplProxy = (HelloService) SimpleAOP.getProxy(helloServiceImpl,beforeAdvice);
helloServiceImplProxy.sayHelloWorld();
}
}
执行结果: