### 1、什么是 IOC？

IOC （控制反转），也称之：DI（依赖注入），是一种设计原则，也是一套控制体系。可以通俗的理解为：

```
通过一个“媒介”中转获取对象的方式（媒介，一般补称为“容器”）。便是 IOC。
```

下面设计一个演进过程，希望更利于理解：

* 这是直接获取对象


```java
public class DemoCom{
    DemoService demoService = new DemoServiceImpl();
    String demoTitle        = "demo";
}
```

这种方式获取的对象，1就是1，2就是2。（就是不会再变了）

* 通过“媒介”（内部是个黑盒）获取对象


```java
//获取侧
public class DemoCom{
    DemoService demoService = Media.getBean(DemoService.class);
    String demoTitle        = Media.getString("demoTtile");
}
```

这种方式获得的对象到底是什么？这就由“媒介”决定了。那，“媒介”的东西又是从哪来？比如这样：

```java
//推入侧
Media.putBean(DemoService.class, ()-> new DemoServiceImpl())；
Media.putString("demoTtile", "demo")
```

相关的对象，是由另外一侧推入。这一套控制体系，就称为：控制反转。


* 你觉得上面这个太土了？变成自动后，就时尚了：

上面的算是“原理”形态（也可称为手动形态），常见的应该是下面这样的（可称为自动形态）：

```java
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;

//获取侧
@Component
public class DemoCom{
    //自动注入
    @Inject
    DemoService demoService; //自动获取：Media.getBean(DemoService.class);
    @Inject("${demoTitle}")
    String demoTitle;
}

//推入侧
@Component
public class DemoService{ //自动推入：Media.putBean(DemoService.class, ()-> new DemoServiceImpl())；
}
```

自动形态下的“媒介”，一般就称作“容器”了（显得，高大上些）。那，怎么的就能自动呢？一般是这样的：

1. 应用启动时，容器会遍历一定范围的所有类（一般也称为：扫描）
2. 找到带 "@Component" 注解的类，便会完成对象“推入”
3. 然后，找到带 "@Inject" 注解的字段，便会“获取”对象，为字段赋值
4. ...

### 2、什么是 AOP ？

AOP（面向切面编程），是一种编程范式。允许我们为对象“附加”额外能力。这种编程思想的重点有三个：

* 要 “能” 切开对象
* 怎么 “确定” 切口（或叫，切入点）
* 切开之后，“附加” 额外能力
 
下面，也是一套演进过程：

* 怎么样才能切开？基础是构建“代理”层（静态代理，或动态代理）

```java
//原始类
public class DemoService{
    public void saveUser(User user){
       ...
    }
}

//代理类（用静态代理的方式演示）
public class DemoService$Proxy extends DemoService{
    DemoService real;
    public DemoService$Proxy(DemoService real){
        this.real = real;
    }
    @Override
    public void saveUser(User user){
       //todo: 这算是切开了! //假装给它加个存储事务
       TranUtil.tran(()->{
           real.saveUser(user);
       });
    }
}

public class DemoCom{
    //用代理类进行赋值
    DemoService demoService = new DemoService$Proxy(new DemoService());
   
    public void addUser(User user){
        demoService.saveUser();
    }
}
```

如果有个 “媒介” 偷偷把中间的活干掉了（即，容器提供自动化处理），代码可以美化成：

```java
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
import org.noear.solon.data.annotation.Transaction;

@Component //todo: 会自动构建代理层，提供 “能” 切开的基础
public class DemoService{
    @Transaction //todo: 这是“切口”标识（即注解）
    public void saveUser(User user){
       ...
    }
}

@Component
public class DemoCom{
    @Inject
    DemoService demoService;
   
    public void addUser(User user){
        demoService.saveUser();
    }
}
```


* 现在，聊聊怎么确定切口

把具体的改成抽象的，并且体系化。就可以根据“规则”确定切口了。关于代理的更多内容可以看下，“[动态代理的本质](/article/442)”。

```java
public class DemoService$Proxy extends DemoService{
    InvocationHandler handler;
    Method saveUser1;
    public DemoService$Proxy(InvocationHandler handler){
        //...略
    }
    public void saveUser(User user){
       handler.invoke(this, saveUser1, new Object[]{user});
    }
}

public class InvocationHandler$Proxy implements InvocationHandler{
    Object real;
    public InvocationHandlerImpl(Object real){
        this.real = real;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //todo: 所有的代理，最终汇到这里；就可以按一个规则确定切口了（比如某个注解）
        if(method.isAnnotationPresent(DbTran.class)){
            TranUtil.tran(()->{
                method.invoke(real, args);
            });
        }else{
            method.invoke(real, args);
        }
    }
}

public class DemoCom{
    DemoService demoService = new DemoService$Proxy(new InvocationHandler$Proxy(new DemoService()));
   
    public void addUser(User user){
        demoService.saveUser();
    }
}
```

把具体的规则，改成体系化的规则

```java
import org.noear.solon.core.AppContext;

public class InvocationHandler$Proxy implements InvocationHandler{
    AppContext context;
    Object real;
    
    public InvocationHandlerImpl(AppContext context, Object real){
        this.context = context;
        this.real = real;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //内部代码就略了。可以参考 solon 的源码
        context.methodGet(method).invokeByAspect(real, args);
    }
}
```

有了体系化的规则支持后，我们就可以聊怎么切开了（即确定切口的方式）：

* 比如按“表达式”切开：“@annotation(org.example.annotation.OperationAnno)”
* 比如按“注解”切开："OperationAnno"（<mark>Solon 用的就是这个方案</mark>）
* 再或者别的方式



体系化之后，相关处理（“附加”额外能力）就具有框架特性了。详细内容看下，“[切面与函数环绕拦截](/article/35)”

```java
//定义一个“附加”额外能力的处理（就是个拦截处理）
import org.noear.solon.Solon;
import org.noear.solon.core.aspect.MethodInterceptor;
import org.noear.solon.core.aspect.Invocation;
import org.noear.solon.data.annotation.Transaction;
import org.noear.solon.data.tran.TranUtils;

import java.util.concurrent.atomic.AtomicReference;

public class TransactionInterceptor implements MethodInterceptor {
    public static final TransactionInterceptor instance = new TransactionInterceptor();

    @Override
    public Object doIntercept(Invocation inv) throws Throwable {
         //加料了。。。为 @Transaction 注解增加对应规则的处理
        if (inv.context().app().enableTransaction()) {
            Transaction anno = inv.getMethodAnnotation(Transaction.class);
            if (anno == null) {
                anno = inv.getTargetAnnotation(Transaction.class);
            }

            if (anno == null) {
                return inv.invoke();
            } else {
                AtomicReference val0 = new AtomicReference();
                Transaction anno0 = anno;

                TranUtils.execute(anno0, () -> {
                    val0.set(inv.invoke());
                });

                return val0.get();
            }
        } else {
            return inv.invoke();
        }
    }
}

//注册“切口”的处理能力
context.beanInterceptorAdd(Transaction.class, new TranInterceptor())；
```
