IoC
What is IoC?
IoC is the abbreviation of inversion of control. Note that it is a technical idea. It describes the creation and management of objects.
- Traditional development methods: for example, if class a depends on class B, it often creates a new object of class B in class A.
- IoC development method: we don't need to go to the new object. The IoC container helps us instantiate the object and manage it. If we need a B object, just ask the IoC container for it.
Inversion of control means that the power of object creation and management is handed over to the external environment (IoC container).
Role of IoC: it solves the coupling problem between objects.
What is DI?
DI is short for dependency injection, which means that the container injects other objects that the object depends on. For example, if an attribute of B is declared in an A object, the container needs to inject the B object into A.
What is AOP?
AOP is short for Aspect oriented Programming.
In the above code, multiple methods have the same code (which can be called crosscutting logic code). This part of the code is not only repeated, but also has nothing to do with business logic, but mixed together. At this time, AOP appears, which provides a horizontal extraction mechanism to separate this part of crosscutting code from business logic code.
Role of AOP: enhance crosscutting logic code and decoupling without changing the original business logic.
Handwritten IOC
First, let's take a look at how we developed a web program before Spring?
So how can we solve the above two problems?
- In addition to instantiating objects with new, we can also use reflection technology.
- In addition, many objects in the project often need to be instantiated, so you can use the factory pattern for optimization.
To sum up, we can use factory pattern + reflection technology to instantiate all objects and put them in a map. If we need an object, we can get it directly from the map.
In addition, we also need an xml file to define the full class name of the object (reflection needs). If there are dependencies, we also need to define the dependencies between classes.
<beans> <bean id="accountDao" class="com.mmc.ioc.dao.impl.AccountDaoImpl"></bean> <bean id="transferService" class="com.mmc.ioc.service.impl.TransferServiceImpl"> <!--there name Default to set+name Is the method name--> <property name="AccountDao" ref="accountDao"></property> </bean> </beans>
Core code:
public class BeanFactory { private static Map<String,Object> beanMap=new HashMap<>(); static { InputStream inputStream=BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml"); SAXReader saxReader=new SAXReader(); try { Document document = saxReader.read(inputStream); Element rootElement = document.getRootElement(); List<Element> beans = rootElement.selectNodes("//bean"); for (Element element:beans){ String id = element.attributeValue("id"); String clazz = element.attributeValue("class"); Object instance = Class.forName(clazz).newInstance(); beanMap.put(id,instance); } //Fill in the dependency of the object after the instance is completed List<Element> propertys = rootElement.selectNodes("//property"); for (Element element:propertys){ String name = element.attributeValue("name"); String ref = element.attributeValue("ref"); Element parent = element.getParent(); String parentId = parent.attributeValue("id"); Object instance = beanMap.get(parentId); Object refInstance = beanMap.get(ref); Method setMethod = instance.getClass().getDeclaredMethod("set" + name,refInstance.getClass().getInterfaces()); setMethod.invoke(instance,beanMap.get(ref)); } } catch (Exception e) { e.printStackTrace(); } } public static Object getBean(String name){ return beanMap.get(name); } }
Next, when we want to use objects, we don't need new, but get them from beanFactory.
Such a simple AOP is completed.
Handwritten AOP implementation
We have solved the above problem 1, so how to solve the problem 2 transaction control?
Analysis: in the final analysis, a database transaction is a connection transaction. connection.commit() commits the transaction and connection.rollback() rolls back the transaction.
- We want to ensure that many database operations performed in the methods in the service either succeed or fail.
- The dao layer in the same service method must use the same connection, that is, if there is the same connection in the same thread, it can be implemented using ThreadLocal
Completion of transformation:
@Override public void transfer(String fromCardNo, String toCardNo, int money) throws Exception { //Turn off auto submit connectionUtils.getThreadConn().setAutoCommit(false); try { Account from = accountDao.queryAccountByCardNo(fromCardNo); Account to = accountDao.queryAccountByCardNo(toCardNo); from.setMoney(from.getMoney()-money); to.setMoney(to.getMoney()+money); accountDao.updateAccountByCardNo(to); int i=10/0; accountDao.updateAccountByCardNo(from); //Commit transaction connectionUtils.getThreadConn().commit(); } catch (Exception e) { //Rollback transaction connectionUtils.getThreadConn().rollback(); throw e; } }
An exception is manually added between the two update statements. It can be found that both data in the database have not changed, indicating that the transaction control is successful.
However, if multiple methods need transaction control, we need to add the following set of repeated code to multiple methods
connectionUtils.getThreadConn().setAutoCommit(false); try { //Omit some codes // ----- //Commit transaction connectionUtils.getThreadConn().commit(); } catch (Exception e) { //Rollback transaction connectionUtils.getThreadConn().rollback(); throw e; }
How to solve it?
We can proxy each method through the proxy pattern
The code is as follows:
public class ProxyFactory { private ConnectionUtils connectionUtils; public void setConnectionUtils(ConnectionUtils connectionUtils) { this.connectionUtils = connectionUtils; } public Object getJdkProxy(Object object){ return Proxy.newProxyInstance(ProxyFactory.class.getClassLoader(), object.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //Turn off auto submit connectionUtils.getThreadConn().setAutoCommit(false); Object result; try { result= method.invoke(object,args); //Commit transaction connectionUtils.getThreadConn().commit(); } catch (Exception e) { //Rollback transaction connectionUtils.getThreadConn().rollback(); throw e; } return result; } }); } }
For each object that needs to add a transaction, as long as you call the getJdkProxy method to obtain the proxy object, and then use the proxy object to execute the method, you can realize transaction control.
The method of use is as follows:
private ProxyFactory proxyFactory = (ProxyFactory) BeanFactory.getBean("proxyFactory"); private TransferService transferService= (TransferService) proxyFactory.getJdkProxy(BeanFactory.getBean("transferService"));
This is equivalent to extracting the crosscutting logic code. If this mechanism is extracted, it is the implementation of AOP.
If you think this article is helpful to you, you can pay attention to my official account, reply to key words [technology], and then get all kinds of programming materials and interview questions. There are more technical dry goods articles and architecture information sharing. Let's learn and progress together!