IOC

原文链接:https://blog.csdn.net/cuterabbitbaby/article/details/80559989

  1. IoC 是编程原则 - 不是特定的产品, 不是具体实现方式, 当然也和具体编程语言无关

  2. 在传统编程范式中, 程序调用可重用的库

  3. 在 IoC 原则下, 程序接受通用框架的调用控制 - 框架调用程序代码

  4. 与 IoC 原则相关的概念包括:

    • 软件框架
    • 回调
    • 调度器
    • 事件循环
    • 依赖注入
    • 设计模式中的模板方法
  5. IoC 的设计目的包括:

    • 将执行任务和任务实现解耦

    • 让模块专注于设计任务

    • 模块仅依赖于设计契约而无需关注其他系统如何工作

    • 避免模块替换时的副作用

IOC和APO区别

简单来讲两者一个是思想一个是原则,我们运用AOP思想抽离东西之后,又运用IOC原则添加其复用性,减少过度依赖。

AspectJ

IOC5.png

使用场景基于AOP的动态权限管理、基于AOP的业务数据埋点、基于AOP的性能监测系统等等。

配置中Gradle说明:https://www.jianshu.com/p/c66f4e3113b3

使用说明:https://blog.csdn.net/innost/article/details/49387395

术语
  • Joinpoint(连接点) 所谓连接点是指那些被拦截到的点

  • Pointcut(切入点) 所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义

  • Advice(通知/增强) 所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知

  • Introduction(引介) 引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类 动态地添加一些方法或 Field

  • Target(目标对象) 代理的目标对象

  • Weaving(织入) 是指把增强应用到目标对象来创建新的代理对象的过程. AspectJ 采用编译期织入和类装在期织入

  • Proxy(代理) 一个类被 AOP 织入增强后,就产生一个结果代理类

  • Aspect(切面) 是切入点和通知(引介)的结合

IOC1.png
Join Points 说明 示例
method call 函数调用 比如调用Log.e(),这是一处JPoint
method execution 函数执行 比如Log.e()的执行内部,是一处JPoint。注意它和method call的区别。method call是调用某个函数的地方。而execution是某个函数执行的内部。
constructor call 构造函数调用 和method call类似
constructor execution 构造函数执行 和method execution类似
field get 获取某个变量 比如读取DemoActivity.debug成员
field set 设置某个变量 比如设置DemoActivity.debug成员
pre-initialization Object在构造函数中做得一些工作。 很少使用
initialization Object 在构造函数中做得工作 很少使用
static initialization 类初始化 比如类的static{}
handler 异常处理 比如try catch(xxx)中,对应catch内的执行 advice execution 这个是AspectJ的内容,稍后再说
advice execution 这个是AspectJ的内容,稍后再说
Advice分类(通知类型)
  • Before
    前置通知, 在目标方法执行之前执行通知方法(应用:各种校验)

  • After
    后置通知, 目标方法执行后执行通知方法(应用:清理现场)

  • Around
    环绕通知, 在通知方法中执行目标方法执行, 控制目标方法执行时机, (应用:十分强大,可以做任何事情)

  • AfterReturning
    后置返回通知, 目标方法正常返回返回后执行通知,所以可以获得方法的返回值。(应用:常规数据处理)

  • AfterThrowing
    异常通知, 目标方法执行后抛出异常时执行通知 (应用:包装异常信息)

切入点指示符

切入点表达式可以通过&&、||和!进行组合,也可以通过名字引用切入点表达式
通过组合,可以建立更加复杂的切入点表达式
1. 选择特定类型的连接点,如:execution,get,set,call,handler

ioc3.png

  • 下面是一个Method Signature的execution

    @Pointcut("execution(@com.dn_alan.myapplication.annotation.BehaviorTrace *  *(..))||withincode()")
    public void methodAnnottatedWithBehaviorTrace() {
      Log.d("alan","methodAnnottatedWithBehaviorTrace BehaviorTraceAspect");
    }
    

    完整的表达式:@注解 访问权限 返回值的类型 包名.函数名(参数)
    1. @注解和访问权限(public/private/protect,以及static/final)属于可选项。
    如果不设置它们,则默认都会选择。以访问权限为例,如果没有设置访问权限作为条件,那么public,private,protect及static、final的函数都会进行搜索。
    2. 返回值类型就是普通的函数的返回值类型。
    如果不限定类型的话,就用通配符表示
    3. 包名.函数名用于查找匹配的函数。
    可以使用通配符,包括*和..以及+号。其中*号用于匹配除.号之外的任意字符,而..则表示任意子package,+号表示子类。
    比如:
    java.*.Date:可以表示java.sql.Date,也可以表示java.util.Date
    Test
    :可以表示T estBase,也可以表示TestDervied
    java..*:表示java任意子类
    java..*Model+:表示Java任意package中名字以Model结尾的子类,比如TabelModel,TreeModel

    4. 最后来看函数的参数。
    参数匹配比较简单,主要是参数类型,比如:
    (int, char):表示参数只有两个,并且第一个参数类型是int,第二个参数类型是char
    (String, ..):表示至少有一个参数。并且第一个参数类型是String,后面参数类型不限。在参数匹配中,
    ..代表任意参数个数和类型
    (Object ...):表示不定个数的参数,且类型都是Object,这里的...不是通配符,而是Java中代表不定参数的意思

  • Constructorsignature和Method Signature类似,只不过构造函数没有返回值,而且函数名必须叫new。
    比如:public *..TestDerived.new(..):

    1. public:选择public访问权限
      *..代表任意包名
    2. TestDerived.new:代表TestDerived的构造函数
      (..):代表参数个数和类型都是任意
  • 再来看Field Signature和Type Signature,

    1. Field Signature标准格式:
      @注解 访问权限 类型 类名.成员变量名
      其中,@注解和访问权限是可选的
      类型:成员变量类型,代表任意类型
      类名.成员变量名:成员变量名可以是
      ,代表任意成员变量
      比如,
      set(inttest..TestBase.base):表示设置TestBase.base变量时的JPoint
    2. Type Signature:直接上例子
      staticinitialization(test..TestBase):表示TestBase类的static block
      handler(NullPointerException):表示catch到NullPointerException的JPoint。

2:确定连接点的范围,如:within,withincode

关键词 说明 示例
within(TypePattern) TypePattern标示package或者类。TypePatter可以使用通配符 表示某个Package或者类中的所有JPoint。比如within(Test):Test类中(包括内部类)所有JPoint。
withincode(Constructor Signature| Method Signature) 表示某个构造函数或其他函数执行过程中涉及到的JPoint 比如withinCode(* TestDerived.testMethod(..))表示testMethod涉及的JPoint,withinCode( *.Test.new(..))表示Test构造函数涉及的JPoint
cflow(pointcuts) cflow是call flow的意思cflow的条件是一个pointcut 比如cflow(call TestDerived.testMethod):表示调用TestDerived.testMethod函数时所包含的JPoint,包括testMethod的call这个JPoint本身
cflowbelow(pointcuts) cflow是call flow的意思。 比如 cflowblow(call TestDerived.testMethod):表示调用TestDerived.testMethod函数时所包含的JPoint,不包括testMethod的call这个JPoint本身
this(Type) JPoint的this对象是Type类型。(其实就是判断Type是不是某种类型,即是否满足instanceof Type的条件) JPoint是代码段(不论是函数,异常处理,static block),从语法上说,它都属于一个类。如果这个类的类型是Type标示的类型,则和它相关的JPoint将全部被选中。 示例的testMethod是TestDerived类。所以 this(TestDerived)将会选中这个testMethod JPoint
target(Type) JPoint的target对象是Type类型 和this相对的是target。不过target一般用在call的情况。call一个函数,这个函数可能定义在其他类。比如testMethod是TestDerived类定义的。那么 target(TestDerived)就会搜索到调用testMethod的地方。但是不包括testMethod的execution JPoint
args(TypeSignature) 用来对JPoint的参数进行条件搜索的 比如args(int,..),表示第一个参数是int,后面参数个数和类型不限的JPoint。
  • within(com.itheima.aop..*)
  • this(com.itheima.aop.user.UserDAO)
  • target(com.itheima.aop.user.UserDAO)
  • args(int,int)
  • bean('userServiceId')
代码

世界上没有凭空调用,不管你是动态代理或者注解或者AspectJ 都是有注入口存在。
APJ还对我们主动调用的方法做增加,并在静态代码块中做了初始化。我们调用这些有标识符的方法,它就会主动调用我们的加了Aspect标识的类中符合逻辑的方法,这样我们就可以抽离出共性逻辑,做一个单独的模块。

另外withincode这个标识符有问题,省略缺少,各种写法都尝试了都不行,偶尔还爆空指针,从1.9降到1.8也不行,不知道网上的人是怎么写出来了的。


IOC4.png
@Aspect
public class BehaviorTraceAspect {
    //定义切面的规则
    //1、就再原来的应用中那些注解的地方放到当前切面进行处理
    @Pointcut("execution(@com.example.apjdemo.annotation.BehaviorTrace *  *(..))"
//            + "&& args() "
//            + "&& args(android.view.View) "
//            + //"&& within(MainActivity)"
            +"&& !withincode(@com.example.apjdemo.annotation.BehaviorTrace void com.example.apjdemo.MainActivity.mAudio())"
            +"")
    public void methodAnnottatedWithBehaviorTrace() {
    }

    @Pointcut("execution(@com.example.apjdemo.annotation.BehaviorTrace *  *(..)) " +
            "&& methodAnnottatedWithBehaviorTrace() " +
            "&& !within(MainActivity)")
    public void all() {
    }

    //2、对进入切面的内容如何处理
    //@Before 在切入点之前运行
//    @After("methodAnnottatedWithBehaviorTrace()")
    //@Around 在切入点前后都运行
    @Around("methodAnnottatedWithBehaviorTrace()||all()")
    public Object weaveJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String className = methodSignature.getDeclaringType().getSimpleName();
        String methodName = methodSignature.getName();
        String value = methodSignature.getMethod().getAnnotation(BehaviorTrace.class).value();

        long begin = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        SystemClock.sleep(100);
        long duration = System.currentTimeMillis() - begin;
        Log.e("pp", String.format("%s功能:%s类的%s方法执行了,用时%d ms",
                value, className, methodName, duration));
        return result;
    }
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BehaviorTrace {
    String value();
}
@BehaviorTrace("语音消息")
public void mShake(View view) {
    Log.e("mShake","mShake");
}

//语音消息
public void mAudio(View view) {
    Log.e("mAudio","mAudio");
    mShake(view);
    kewudewithin();
}

//视频通话
public void mVideo(View view) {
    Log.e("mVideo","mVideo");
    kewudewithincode();
}

public void kewudewithin(){
    Log.e("kewudewithin","kewudewithin");
    bumingsuoyi();
}

public void kewudewithincode() {
    Log.e("kewudewithincode","kewudewithincode");
    bumingsuoyi();
}
@BehaviorTrace("bumingsuoyi")
public void bumingsuoyi() {
}

当期两个项目对比下gradle配置

动态代理

摘至作者:jxiang112https://www.jianshu.com/p/85d181d7d09a

流程总结:

校验代理类方法调用处理程序h不能为空
动态生成代理类class文件格式字节流
通过loader加载创建代表代理类的class对象
根据代理类的构造器创建代理类,
根据代理类接口先得到代理类的类全限名、方法列表、异常列表通过ProxyClassFactory内部调用了native层方法
返回动态创建生成的代理类

  • Proxy.java
    static final 私有内部类 ProxyClassFactory和KeyFactory

getProxyClass0,内有判断实现的接口数量不能超过65535就是Short类型的最大值

private static final WeakCache[], Class>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

Proxy.newProxyInstance()->getProxyClass0(loader, intfs)
->proxyClassCache.get(loader, interfaces)->WeakCache.get(K key, P parameter)
  • WeakCache.java
    new KeyFactory(), new ProxyClassFactory() 一个是key的生成器,一个value的生成器
    Factory是WeakCache的final内部类实现了Supplier接口
对象初始化时就会有个缓存map    
private final ConcurrentMap>> map
        = new ConcurrentHashMap<>();

WeakCache.get()方法:
    supplier实际就是Factory
    获取当前key对应的cacheKey的缓存supplier的map,如果没有创建map并放入缓存map
    从缓存supplier的map中获取supplier
    factory = null
    while循环 {
         if (supplier != null) {
                // supplier might be a Factory or a CacheValue instance
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
         }
         如果factory = null
           factory = new Factory(key, parameter, subKey, valuesMap);
         如果supplier  = null
           把factory放入cacheKey对应的缓存supplier的map
           supplier =  factory;
     }

Factory.get->value = Objects.requireNonNull(valueFactory.apply(key, parameter))->valueFactory构造方法传入 new ProxyClassFactory()->ProxyClassFactory.apply(ClassLoader loader, Class[] interfaces)
  • ProxyClassFactory.java
    1. 根据代理类接口先得到代理类的类全限名、方法列表、异常列表
    2. 根据步骤1中的类全限名、方法列表、异常列表、接口列表生成class文件格式的字节流,其中方法的实现会最终调用InvoationHanlder的invoke方法
 @FastNative private static native Class generateProxy(
     String name, Class[] interfaces,ClassLoader loader, Method[] methods,                  Class[][] exceptions);
  1. 使用类加载器加载步骤2中的字节流,创建生成动态代理类对象
  2. 使用步骤3中创建生成的代理类对象
使用
IMy iMy = new MyImpl();
MyProxy myProxy = new MyProxy(iMy);
myProxy.ganni();

IMe iMe = new MeImpl();
MeProxy meProxy = new MeProxy(iMe);
meProxy.dani();

DynamicProxy dynamicProxy = new DynamicProxy(meProxy);
IMe iMeProxy = (IMe) Proxy.newProxyInstance(getClassLoader(), new Class[]{IMe.class}, dynamicProxy);
iMeProxy.dani();
DynamicProxy dynamicProxy2 = new DynamicProxy(myProxy);
IMy iMyProxy = (IMy) Proxy.newProxyInstance(getClassLoader(), new Class[]{IMy.class}, dynamicProxy2);
iMyProxy.ganni();

public interface IMe {
    String dani();
}

public interface IMy {
    String ganni();
}

public class MeImpl implements IMe {
    @Override
    public String dani() {
        return null;
    }
}

public class MeProxy implements IMe{

    private final IMe mIme;

    public MeProxy(IMe iMe) {
        mIme = iMe;
    }

    @Override
    public String dani() {
        da110();
        return mIme.dani();
    }

    private void da110(){
        Log.e("MeProxy","woda110");
    }
}

public class MyImpl implements IMy {
    @Override
    public String ganni() {
        return "laiba";
    }
}

public class MyProxy implements IMy {

    private final IMy mIMy;

    public MyProxy(IMy iMy) {
        mIMy = iMy;
    }

    @Override
    public String ganni() {
        da110();
        return mIMy.ganni();
    }

    private void da110(){
        Log.e("MyProxy","woda110");
    }
}

public class DynamicProxy implements InvocationHandler {

    private final Object mobject;

    public DynamicProxy(Object object) {
        mobject = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Log.e("MyProxy",proxy.getClass().getName());
        Log.e("MyProxy",mobject.getClass().getName());

        return method.invoke(mobject,args);
    }

    private void da110(){
        Log.e("MyProxy","wotisuoyouren da110");
    }
}

AutoService

利用@AutoService(IFunction.class)通过ServiceLoader.load(IFunction::class.java)找到所有实现类

有人说可以用来打包https://www.jianshu.com/p/a94e6a32f10f

这里主要介绍@AutoService(Processor.class)就是APT

APT&&Javapoet

中间还加如了javapoet

  • 注解处理Moduel

    /**
     * 这个类就是APT
     */
    @AutoService(Processor.class)
    public class MyAnotationCompile extends AbstractProcessor {
     //1.定义一个用于生成文件的对象
     Filer filer;
    
     @Override
     public synchronized void init(ProcessingEnvironment processingEnvironment) {
         super.init(processingEnvironment);
         filer = processingEnvironment.getFiler();
     }
    
     //2.需要确定当前APT处理所有模块中哪些注解
     //可以用注解标识@SupportedAnnotationTypes()
     @Override
     public Set getSupportedAnnotationTypes() {
         Set set = new HashSet<>();
         set.add(MyAnntation.class.getCanonicalName());
         return set;
     }
    
     //3.支持的JDK的版本
     //没有会报错
     @Override
     public SourceVersion getSupportedSourceVersion() {
         return SourceVersion.latestSupported();
     }
    
     /**
      * 在这个方法中,我们去生成实现类
      * @param set
      * @param roundEnvironment
      * @return
      */
     @Override
    public boolean process(Set set, 
                           RoundEnvironment roundEnvironment) {
    
         Set elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(MyAnntation.class);
         Map> map = new HashMap<>();
    
         for (Element element : elementsAnnotatedWith) {
             VariableElement variableElement = (VariableElement) element;
             String activityName = variableElement.getEnclosingElement().getSimpleName().toString();
             List elementList = map.get(activityName);
             if (elementList == null) {
                 elementList = new ArrayList<>();
                 map.put(activityName, elementList);
             }
             elementList.add(variableElement);
         }
    
         if (map.size() > 0) {
             Writer writer = null;
             Iterator iterator = map.keySet().iterator();
             Writer writer1 = null;
             while (iterator.hasNext()) {
                 String activityName = iterator.next();
                 List elementList = map.get(activityName);
    
                 TypeElement enclosingElement = (TypeElement) elementList.get(0).getEnclosingElement();
                 String packageName = processingEnv.getElementUtils().getPackageOf(enclosingElement).toString();
    
                 for (VariableElement variableElement : elementList) {
                     String variableName = variableElement.getSimpleName().toString();
                     String name = variableElement.getAnnotation(MyAnntation.class).value();
    //                    try {
    //                        JavaFileObject classFile = filer.createSourceFile(packageName+"." + name);
    //                        writer1 = classFile.openWriter();
    //                        writer1.write("package "+packageName+"." + name);
    //                    } catch (IOException e) {
    //                        e.printStackTrace();
    //                    } finally {
    //                        try {
    //                            writer1.close();
    //                        } catch (IOException e) {
    //                            e.printStackTrace();
    //                        }
    //                    }
                     MethodSpec main = MethodSpec.methodBuilder("main")
                             .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                             .returns(void.class)
                             .addParameter(String[].class, "args")
                             .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
                             .build();
    
                     TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
                             .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                             .addMethod(main)
                             .build();
    
                     JavaFile javaFile = JavaFile.builder(packageName+"." + name, helloWorld)
                             .build();
                     try {
                         javaFile.writeTo(filer);
                     } catch (IOException e) {
                         e.printStackTrace();
                     } finally {
                     }
                 }
             }
         }
         return true;
    
    //        //获取控件的名字
    //        String variableName=variableElement.getSimpleName().toString();
    //        //获取ID
    //        int id=variableElement.getAnnotation(BindView.class).value();
    //        //获取控件的类型
    //        TypeMirror typeMirror=variableElement.asType();
    //        如果APT过程中生成的类也需要进行注解处理的话则需要返回false,方便再一次执行。
    //        上层是 processImpl方法
       }
    }
    
    implementation project(path: ':libannotation')
    
    implementation "com.squareup:javapoet:1.11.1"
    annotationProcessor'com.google.auto.service:auto-service:1.0-rc4'
    compileOnly'com.google.auto.service:auto-service:1.0-rc4'
    
  • 注解module(libannotation):

    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.SOURCE)
    public @interface MyAnntation {
     String value();
    }
    
  • appmodule:

    public class MainActivity extends AppCompatActivity {
       @MyAnntation("nihaoa")
       View view;
     
       @Override
       protected void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);
           setContentView(R.layout.activity_main);
       }
    }
    
    implementation project(path: ':libannotation')
    implementation "com.squareup:javapoet:1.11.1"
    annotationProcessor project(path: ':libanotationcompile')
    

你可能感兴趣的:(IOC)