1. IOC
1.1 什么是ioc: Inversion of control(控制反转的思想)
java开发领域对象的创建, 管理的问题。
传统的开发方式: 类A依赖类B, 往往会在类A中new一个B的对象
ioc思想的开发方式: 我们不用自己去new对象, 而是由ioc容器去创建实例化对象并且管理它。我们需要哪个对象, 就去问ioc容器去要。
我们丧失了一个权利(创建, 管理对象)。得到一个福利(不用去考虑对象的创建, 管理等一系列事情)
控制: 对象实例化的权利。
反转: 控制权不在程序本身, 而是交给spring框架(IOC容器)
1.2 ioc解决了对象之间的耦合:

常规情况下, 调用dao层方法需要new对象(new UserDaoImpl)。 此时需要切换成UserDaoImplNew实现类。所以代码需要改造,耦合度很高。如果使用ioc容器的话, 直接引入接口的依赖即可, 具体实现由ioc容器去完成。
1.3 IOC和DI: DI(dependency injection 依赖注入)
IOC和DI其实做的是同一件事情(对象的实例化以及依赖关系的维护)。
IOC站在对象的角度, 将创建对象的权利给到了容器。
DI: 站在容器的角度,将对象依赖的其他对象注入。如果A对象实例化过程中需要B类型的属性, 那么需要容器将B对象注入给A。
2. AOP:
2.1 什么是AOP
Aspect oriented Programming ⾯向切⾯编程/⾯向⽅⾯编程
AOP是OOP的延续, 从OOP说起, OOP三⼤特征: 封装、继承和多态。
OOP编程思想可以解决⼤多数的代码重复问题,但是有⼀些情况是处理不了的,⽐如下⾯的在顶级⽗类
Animal中的多个⽅法中相同位置出现了重复代码,OOP就解决不了横切逻辑代码
横切逻辑代码和业务代码混杂在⼀起,代码臃肿,维护不⽅便
AOP提出横向抽取机制,将横切逻辑代码和业务逻辑代码分开在不改变原有业务逻辑情况下,增强横切逻辑代码,根本上解耦合,避免横切逻辑代码重复
2.2 为什么叫做⾯向切⾯编程
「切」:指的是横切逻辑,原有业务逻辑代码我们不能动,只能操作横切逻辑代码,所以⾯向横切逻辑
「⾯」:横切逻辑代码往往要影响的是很多个⽅法,每⼀个⽅法都如同⼀个点,多个点构成⾯.
3. 手写AOP和IOC:
在没有任何框架的时, 我们在开发的时候采用的如下方式. 首先是代码之间存在强耦合,不符合面向接口开发的原则。 其次, service层没有事务控制, 会导致数据的错乱。
3.1 如何解决new对象的问题
我们可以通过反射的机制来实例化对象, 反射需要全限定名, 我们可以通过xml进行配置。并且我们可以通过工厂模式来控制对象的实例化。(项⽬中往往有很多对象需要实例化)
3.1.2 工厂模式:
https://www.yuque.com/wangyanyang/qz7fwa/qlacwc/edit#8c31ce5b
3.1.3 这里做了什么:
a. 工厂解析xml文件并缓存实例信息
/*** 工厂类,生产对象(使用反射技术)*/public class BeanFactory {/*** 任务一:读取解析xml,通过反射技术实例化对象并且存储待用(map集合)* 任务二:对外提供获取实例对象的接口(根据id获取)*/private static Map<String,Object> map = new HashMap<>(); // 存储对象static {// 任务一:读取解析xml,通过反射技术实例化对象并且存储待用(map集合)// 加载xmlInputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");// 解析xmlSAXReader saxReader = new SAXReader();try {Document document = saxReader.read(resourceAsStream);Element rootElement = document.getRootElement();List<Element> beanList = rootElement.selectNodes("//bean");for (int i = 0; i < beanList.size(); i++) {Element element = beanList.get(i);// 处理每个bean元素,获取到该元素的id 和 class 属性String id = element.attributeValue("id"); // accountDaoString clazz = element.attributeValue("class"); // com.lagou.edu.dao.impl.JdbcAccountDaoImpl// 通过反射技术实例化对象Class<?> aClass = Class.forName(clazz);Object o = aClass.newInstance(); // 实例化之后的对象// 存储到map中待用map.put(id,o);}// 实例化完成之后维护对象的依赖关系,检查哪些对象需要传值进入,根据它的配置,我们传入相应的值// 有property子元素的bean就有传值需求List<Element> propertyList = rootElement.selectNodes("//property");// 解析property,获取父元素for (int i = 0; i < propertyList.size(); i++) {Element element = propertyList.get(i); //<property name="AccountDao" ref="accountDao"></property>String name = element.attributeValue("name");String ref = element.attributeValue("ref");// 找到当前需要被处理依赖关系的beanElement parent = element.getParent();// 调用父元素对象的反射功能String parentId = parent.attributeValue("id");Object parentObject = map.get(parentId);// 遍历父对象中的所有方法,找到"set" + nameMethod[] methods = parentObject.getClass().getMethods();for (int j = 0; j < methods.length; j++) {Method method = methods[j];if(method.getName().equalsIgnoreCase("set" + name)) { // 该方法就是 setAccountDao(AccountDao accountDao)method.invoke(parentObject,map.get(ref));}}// 把处理之后的parentObject重新放到map中map.put(parentId,parentObject);}} catch (DocumentException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}}// 任务二:对外提供获取实例对象的接口(根据id获取)public static Object getBean(String id) {return map.get(id);}}
b. 实际使用:

3.2 解决事务的问题:
3.2.1 核心思路:
数据库事务由Connection去控制, 默认事务是自动提交的。对于一个service方法, 会调用多个dao层的方法, 且每一个dao层的方法都会获取一个Connection对象。属于不同的事务。
解决思路: 给当前线程绑定Connection对象, 这样无论service方法调用多少dao层, 其Connection对象是同一个, 事务是同一个。
public class ConnectionUtils {
private ThreadLocal<Connection> threadLocal = new ThreadLocal<>(); // 存储当前线程的连接
/**
* 从当前线程获取连接
*/
public Connection getCurrentThreadConn() throws SQLException {
/**
* 判断当前线程中是否已经绑定连接,如果没有绑定,需要从连接池获取一个连接绑定到当前线程
*/
Connection connection = threadLocal.get();
if(connection == null) {
// 从连接池拿连接并绑定到线程
connection = DruidUtils.getInstance().getConnection();
// 绑定到当前线程
threadLocal.set(connection);
}
return connection;
}
}
3.2.2 核心方法增强:
默认情况下, 事务是自动提交的, 所以为了解决service事务问题, 需要我们显示的控制事务的开关, 除此之外, service发生异常, 我们的事务要触发回滚。基础代码如下:
上述代码是属于横切代码, 根据AOP原理, 需要抽取,并增强横切逻辑代码(代理模式)
https://www.yuque.com/wangyanyang/qz7fwa/qlacwc/edit#8c31ce5b
a.将核心方法抽取:
/**
*
* 事务管理器类:负责手动事务的开启、提交、回滚
*/
public class TransactionManager {
private ConnectionUtils connectionUtils;
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
// 开启手动事务控制
public void beginTransaction() throws SQLException {
connectionUtils.getCurrentThreadConn().setAutoCommit(false);
}
// 提交事务
public void commit() throws SQLException {
connectionUtils.getCurrentThreadConn().commit();
}
// 回滚事务
public void rollback() throws SQLException {
connectionUtils.getCurrentThreadConn().rollback();
}
}
/**
* <p>
* <p>
* 代理对象工厂:生成代理对象的
*/
public class ProxyFactory {
private TransactionManager transactionManager;
public void setTransactionManager(TransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
/**
* Jdk动态代理
*
* @param obj 委托对象
* @return 代理对象
*/
public Object getJdkProxy(Object obj) {
// 获取代理对象
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try {
// 开启事务(关闭事务的自动提交)
transactionManager.beginTransaction();
result = method.invoke(obj, args);
// 提交事务
transactionManager.commit();
} catch (Exception e) {
e.printStackTrace();
// 回滚事务
transactionManager.rollback();
// 抛出异常便于上层servlet捕获
throw e;
}
return result;
}
});
}
/**
* 使用cglib动态代理生成代理对象
*
* @param obj 委托对象
* @return
*/
public Object getCglibProxy(Object obj) {
return Enhancer.create(obj.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object result = null;
try {
// 开启事务(关闭事务的自动提交)
transactionManager.beginTransaction();
result = method.invoke(obj, objects);
// 提交事务
transactionManager.commit();
} catch (Exception e) {
e.printStackTrace();
// 回滚事务
transactionManager.rollback();
// 抛出异常便于上层servlet捕获
throw e;
}
return result;
}
});
}
}
b.在注入的时候, 注入增强的方法

代码如下:
