1. Background
When I was a senior intern, I began to contact with J2EE development work. I also came into contact with and studied Spring framework at the same time. It has been almost two years now. Nevertheless, Spring IOC and AOP have not been copied before, but they have a certain understanding of the principles of Spring IOC and AOP on the macro level. So in order to further understand Spring IOC and AOP principles. In my spare time, I refer to some information and code, and implement a simple IOC and AOP, and achieve the following functions:
Load the related bean s according to the xml configuration file
Support bean s of BeanPostProcessor type
Support bean s of BeanFactoryAware type
Implementation of AOP based on JDK dynamic proxy
Integrating IOC and AOP makes them work well together.
Before I implement my own IOC and AOP, my idea is to implement a very simple IOC and AOP, even if it is implemented in dozens of lines of code. After that, it feels interesting. But that implementation is too simple, far from Spring IOC and AOP. Later on, I thought that I couldn't just satisfy the simple implementation, so I had this copying project. Comparatively speaking, the imitation code is a little more complicated, has a little more functions, and looks a little bit like it. Although the copying project is still toy level, but in the process of copying, we have learned something. Generally speaking, the harvest is still great. In the next article, I will also implement different versions of IOC and AOP from easy to difficult. Okay, let's not say much. Start working.
2. Simple IOC and AOP implementation
2.1 Simple IOC
Starting with a simple IOC container implementation, the simplest IOC container can be implemented in only four steps, as follows:
Load the xml configuration file and traverse the < bean > tag
Get the id and class attributes in the < bean > tag, load the classes corresponding to the class attributes, and create the beans
Traverse the < property > tag in the < bean > tag, get the attribute value, and fill the attribute value into the bean
Register beans into bean containers
As shown above, it only takes four steps. Does it feel simple? All right, Talk is cheap, Show me the code. Next, we need to add the code. But the guest officer is not in a hurry. Before I go to the code, let me give a brief introduction to the code structure.
SimpleIOC // The implementation class of IOC implements the four steps mentioned above. SimpleIOCTest // IOC Test Class Car // Beans for IOC testing Wheel // Ditto ioc.xml // bean configuration file
Container implementation SimpleIOC-like code:
public class SimpleIOC { private Map<String, Object> beanMap = new HashMap<>(); public SimpleIOC(String location) throws Exception { loadBeans(location); } public Object getBean(String name) { 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 { // Load the xml configuration file 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(); // Traversing the < bean > tag 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"); // Loading beanClass Class beanClass = null; try { beanClass = Class.forName(className); } catch (ClassNotFoundException e) { e.printStackTrace(); return; } // Create bean s Object bean = beanClass.newInstance(); // Traverse < property > labels 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"); // Use reflection to set access rights to bean-related fields as accessible Field declaredField = bean.getClass().getDeclaredField(name); declaredField.setAccessible(true); if (value != null && value.length() > 0) { // Fill attribute values in related fields declaredField.set(bean, value); } else { String ref = propertyElement.getAttribute("ref"); if (ref == null || ref.length() == 0) { throw new IllegalArgumentException("ref config error"); } // Fill references in related fields declaredField.set(bean, getBean(ref)); } // Register beans into bean containers registerBean(id, bean); } } } } } private void registerBean(String id, Object bean) { beanMap.put(id, bean); } }
bean code for container testing:
public class Car { private String name; private String length; private String width; private String height; private Wheel wheel; // Eliminate other unimportant code } public class Wheel { private String brand; private String specification ; // Eliminate other unimportant code }
The bean configuration file ioc.xml content:
<beans> <bean id="wheel" class="com.titizz.simulation.toyspring.Wheel"> <property name="brand" value="Michelin" /> <property name="specification" value="265/60 R18" /> </bean> <bean id="car" class="com.titizz.simulation.toyspring.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>
IOC test class SimpleIOCTest:
public class SimpleIOCTest { @Test public void getBean() throws Exception { String location = SimpleIOC.class.getClassLoader().getResource("spring-test.xml").getFile(); SimpleIOC bf = new SimpleIOC(location); Wheel wheel = (Wheel) bf.getBean("wheel"); System.out.println(wheel); Car car = (Car) bf.getBean("car"); System.out.println(car); } }
Test results:
The above is the whole content of a simple IOC implementation. It's not too difficult, and the code is not too difficult to understand. I won't talk more about it here. Let's talk about the implementation of simple AOP.
2.2 Simple AOP Implementation
AOP implementation is based on the proxy model, which I believe you should all know. The proxy mode is the foundation of AOP implementation. It is not difficult to understand the proxy mode. It will not take much time to introduce the proxy mode here. Before introducing the implementation steps of AOP, we first introduce some concepts in Spring AOP, and then we will use these concepts.
Advice
Notification defines the logic to weave into the target object and the timing of execution. Spring corresponds to five different types of notifications: · Before: Execute the notification before the target method is executed · After: After the target method is executed, the notification is executed, regardless of what the result of the target method is returned. · After-returning: After the target method is executed, the notification is executed · After-throwing: Execute the notification after the target method throws an exception · Around notification: The target method is notified of the package, and the notification is invoked before and after the execution of the target method
Pointcut
If a notification defines when to execute a notification, then a pointcut defines where to execute the notification. So the function of the tangent point is By matching rules to find the appropriate join points, AOP weaves notifications into those join points.
Aspect
Cutting bread contains notification and tangent points, which together define what a tangent is, when and where to perform the tangent logic.
After that, let's talk about the steps of simple AOP implementation. AOP is based on JDK dynamic proxy and can be accomplished in only three steps.
Define an object that contains aspect logic, assuming logMethodInvocation
Define an Advice object (implements the InvocationHandler interface) and pass in the logMethod Invocation and the target object above
Pass the above divce object and target object to the JDK dynamic proxy method to generate the proxy for the target object
The above steps are relatively simple, but in the implementation process, there are still some difficulties, here we need to introduce some auxiliary interfaces to achieve. Next, I will introduce the code structure of simple AOP.
MethodInvocation Interface // Implementation classes contain aspect logic, such as logMethodInvocation above Advice Interface // InvocationHandler interface inherited BeforeAdvice class // Implementing the Advice interface is a pre-notification SimpleAOP class // Generating proxy classes SimpleAOPTest // SimpleAOP from test class HelloService Interface // Target Object Interface HelloServiceImpl // Target object
Method Invocation Interface Code:
public interface MethodInvocation { void invoke(); }
Advice interface code:
public interface Advice extends InvocationHandler {}
BeforeAdvice implementation code:
public class BeforeAdvice implements Advice { private Object bean; private MethodInvocation methodInvocation; public BeforeAdvice(Object bean, MethodInvocation methodInvocation) { this.bean = bean; this.methodInvocation = methodInvocation; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // Call notification before execution of target method methodInvocation.invoke(); return method.invoke(bean, args); } }
SimpleAOP implementation code:
public class SimpleAOP { public static Object getProxy(Object bean, Advice advice) { return Proxy.newProxyInstance(SimpleAOP.class.getClassLoader(), bean.getClass().getInterfaces(), advice); } }
HelloService interface and its implementation class code:
public interface HelloService { void sayHelloWorld(); } public class HelloServiceImpl implements HelloService { @Override public void sayHelloWorld() { System.out.println("hello world!"); } }
SimpleAOPTest code:
public class SimpleAOPTest { @Test public void getProxy() throws Exception { // 1. Create a MethodInvocation implementation class MethodInvocation logTask = () -> System.out.println("log task start"); HelloServiceImpl helloServiceImpl = new HelloServiceImpl(); // 2. Create an Advice Advice beforeAdvice = new BeforeAdvice(helloServiceImpl, logTask); // 3. Generating Agents for Target Objects HelloService helloServiceImplProxy = (HelloService) SimpleAOP.getProxy(helloServiceImpl,beforeAdvice); helloServiceImplProxy.sayHelloWorld(); } }
Output results:
The above is a simple implementation of IOC and AOP. The implementation process is relatively simple. At the same time, IOC container and AOP can only run independently, but can not be integrated together. So functionally, the IOC and AOP implemented in this version are still too weak. In the next article, I will implement a more complex IOC and AOP, if you are interested to see. Okay, this article is over. What's wrong with it is welcome to point out. Thank you.
This work adopts Knowledge Sharing Signature - Non-commercial Use - Prohibition of Deduction of 4.0 International Licensing Agreement License.