原文链接:https://blog.csdn.net/cuterabbitbaby/article/details/80559989
IoC 是编程原则 - 不是特定的产品, 不是具体实现方式, 当然也和具体编程语言无关
在传统编程范式中, 程序调用可重用的库
在 IoC 原则下, 程序接受通用框架的调用控制 - 框架调用程序代码
-
与 IoC 原则相关的概念包括:
- 软件框架
- 回调
- 调度器
- 事件循环
- 依赖注入
- 设计模式中的模板方法
-
IoC 的设计目的包括:
将执行任务和任务实现解耦
让模块专注于设计任务
模块仅依赖于设计契约而无需关注其他系统如何工作
避免模块替换时的副作用
IOC和APO区别
简单来讲两者一个是思想一个是原则,我们运用AOP思想抽离东西之后,又运用IOC原则添加其复用性,减少过度依赖。
AspectJ
使用场景基于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(切面) 是切入点和通知(引介)的结合
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
-
下面是一个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(..):- public:选择public访问权限
*..代表任意包名 - TestDerived.new:代表TestDerived的构造函数
(..):代表参数个数和类型都是任意
- public:选择public访问权限
-
再来看Field Signature和Type Signature,
- Field Signature标准格式:
@注解 访问权限 类型 类名.成员变量名
其中,@注解和访问权限是可选的
类型:成员变量类型,代表任意类型
类名.成员变量名:成员变量名可以是,代表任意成员变量
比如,
set(inttest..TestBase.base):表示设置TestBase.base变量时的JPoint - Type Signature:直接上例子
staticinitialization(test..TestBase):表示TestBase类的static block
handler(NullPointerException):表示catch到NullPointerException的JPoint。
- Field Signature标准格式:
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也不行,不知道网上的人是怎么写出来了的。
@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
- ProxyClassFactory.java
- 根据代理类接口先得到代理类的类全限名、方法列表、异常列表
- 根据步骤1中的类全限名、方法列表、异常列表、接口列表生成class文件格式的字节流,其中方法的实现会最终调用InvoationHanlder的invoke方法
@FastNative private static native Class> generateProxy(
String name, Class>[] interfaces,ClassLoader loader, Method[] methods, Class>[][] exceptions);
- 使用类加载器加载步骤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 extends TypeElement> set, RoundEnvironment roundEnvironment) { Set extends Element> 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')