写给自己的AOP简单实现
2021-04-08 本文已影响0人
无良安生
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方
式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个
热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑
的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高
了开发的效率。
一、需求
项目中有很多操作需要在登陆后才能操作的功能
普通oop操作就是每个功能开启前都要去写一段判断登录的代码
二、动态代理 proxy
使用动态代理,在执行的方法中去做自己想做的事情
上代码:
如果我们要进入 购物车、积分商城、 我的订单都要去做登录处理
比如我么能进入积分商城,代码如下
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityHomeBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
binding.bntLogin.setOnClickListener(this);
binding.bntOrderList.setOnClickListener(this);
binding.bntPointsMall.setOnClickListener(this);
binding.bntShopCart.setOnClickListener(this);
}
//就这样,每次都要去判断是否是登录
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bnt_login:
login();
break;
case R.id.bnt_order_list:
if(!isLogin()){
login();
}else{
orderList();
}
break;
case R.id.bnt_points_mall:
if(!isLogin()){
login();
}else{
pointsMall();
}
break;
case R.id.bnt_shop_cart:
if(!isLogin()){
login();
}else{
shopCart();
}
break;
}
}
public void shopCart() {
Log.e(TAG, "--------------------->购物车");
}
public void pointsMall() {
Log.e(TAG, "--------------------->积分商城");
}
public void orderList() {
Log.e(TAG, "--------------------->我的订单");
}
public void login() {
Log.e(TAG, "--------------------->登录");
}
public void isLogin():Boolean {
Log.e(TAG, "--------------------->是否登录");
}
看到oop写法,下面看看aop写法:
- 先定义一个接口:
public interface UserManager {
void shopCart();
void pointsMall();
void orderList();
}
- 然后在界面中动态代理
public class AOPProxyActivity extends AppCompatActivity implements UserManager {
private UserManager user;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityHomeBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
binding.bntLogin.setOnClickListener(this);
binding.bntOrderList.setOnClickListener(this);
binding.bntPointsMall.setOnClickListener(this);
binding.bntShopCart.setOnClickListener(this);
db = (UserManager) Proxy.newProxyInstance(UserManager.class.getClassLoader(), new Class[]{UserManager.class}, new UserManager(this));
}
class DBHander implements InvocationHandler {
private DBManager user;
public DBHander(DBManager user) {
this.user = user;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (db != null) {
if(isLogin()){
//这个就是你要执行的方法
return method.invoke(db, args);
}else{
//没登陆就先登录
login();
}
}
return null;
}
}
//就这样,每次都要去判断是否是登录
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bnt_login:
login();
break;
case R.id.bnt_order_list:
orderList();
break;
case R.id.bnt_points_mall:
pointsMall();
break;
case R.id.bnt_shop_cart:
shopCart();
break;
}
}
public void shopCart() {
Log.e(TAG, "--------------------->购物车");
}
public void pointsMall() {
Log.e(TAG, "--------------------->积分商城");
}
public void orderList() {
Log.e(TAG, "--------------------->我的订单");
}
public void login() {
Log.e(TAG, "--------------------->登录");
}
public void isLogin():Boolean {
Log.e(TAG, "--------------------->是否登录");
}
}
比如进入订单,打印如下
- 未登录情况
- --------------------->是否登录
- --------------------->登录
- 登录情况
- --------------------->是否登录
- --------------------->我的订单
以上就是简单的AOP
三、预编译AOP
需要用到 Aspect
- 配置
- 工程gradle:
dependencies {
//这个是自己项目的
classpath "com.android.tools.build:gradle:4.1.2"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// 版本界限:As-3.0.1 + gradle4.4-all (需要配置r17的NDK环境)
// 或者:As-3.2.1 + gradle4.6-all (正常使用,无警告)
classpath 'org.aspectj:aspectjtools:1.8.9'
classpath 'org.aspectj:aspectjweaver:1.8.9'
}
- 项目gradle
//这个要在plugins模块之上
// 版本界限:As-3.0.1 + gradle4.4-all (需要配置r17的NDK环境)
// 或者:As-3.2.1 + gradle4.6-all (正常使用,无警告)
// 版本界限:As-3.0.1 + gradle4.4-all (需要配置r17的NDK环境)
// 或者:As-3.2.1 + gradle4.6-all (正常使用,无警告)
buildscript { // 编译时用Aspect专门的编译器,不再使用传统的javac
repositories {
mavenCentral()
}
dependencies {
classpath 'org.aspectj:aspectjtools:1.8.9'
classpath 'org.aspectj:aspectjweaver:1.8.9'
}
}
plugins {
//自己项目的//
}
android {
//自己项目的//
}
dependencies {
//自己项目的引用//
//引入包
implementation 'org.aspectj:aspectjrt:1.8.13'
}
// 版本界限:As-3.0.1 + gradle4.4-all (需要配置r17的NDK环境)
// 或者:As-3.2.1 + gradle4.6-all (正常使用,无警告)
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
final def log = project.logger
final def variants = project.android.applicationVariants
variants.all { variant ->
if (!variant.buildType.isDebuggable()) {
log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
return;
}
JavaCompile javaCompile = variant.javaCompile
javaCompile.doLast {
String[] args = ["-showWeaveInfo",
"-1.8",
"-inpath", javaCompile.destinationDir.toString(),
"-aspectpath", javaCompile.classpath.asPath,
"-d", javaCompile.destinationDir.toString(),
"-classpath", javaCompile.classpath.asPath,
"-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]
log.debug "ajc args: " + Arrays.toString(args)
MessageHandler handler = new MessageHandler(true);
new Main().run(args, handler);
for (IMessage message : handler.getMessages(null, true)) {
switch (message.getKind()) {
case IMessage.ABORT:
case IMessage.ERROR:
case IMessage.FAIL:
log.error message.message, message.thrown
break;
case IMessage.WARNING:
log.warn message.message, message.thrown
break;
case IMessage.INFO:
log.info message.message, message.thrown
break;
case IMessage.DEBUG:
log.debug message.message, message.thrown
break;
}
}
}
}
然后看看项目是否报错
前方高能哈
注意:
- 版本界限:As-3.0.1 + gradle4.4-all (需要配置r17的NDK环境)
- 或者:As-3.2.1 + gradle4.6-all (正常使用,无警告)
- 版本界限:As-3.0.1 + gradle4.4-all (需要配置r17的NDK环境)
- 或者:As-3.2.1 + gradle4.6-all (正常使用,无警告)
这个才完成配置而已
接下来,我们做个登录和行为统计
先来个注解(主要用来传行为注释)
/**
* 行为统计
*/
@Target(ElementType.METHOD)//目标作用在方法之上:ElementType.METHOD
@Retention(RetentionPolicy.RUNTIME)
public @interface ClickBehavior {
String value();
}
然后来个aspect
@Aspect
public class ClickBehaviorAspect {
private final String TAG = getClass().getSimpleName();
// 1、应用中用到了哪些注解,放到当前的切入点进行处理(找到需要处理的切入点)
// execution,以方法执行时作为切点,触发Aspect类
// * *(..)) 可以处理ClickBehavior这个类所有的方法
@Pointcut("execution(@com.cxz.demo.ClickBehavior * *(..))")
public void methodPointCut() {
}
// 2、对切入点如何处理
@Around("methodPointCut()")
public Object jointPoint(ProceedingJoinPoint joinPoint) throws Throwable {
Log.e(TAG,"》》》》》》》》》》》》》》》》》》》 开始执行");
//获取签名方法
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
//获取方法所属的类名
String className = methodSignature.getDeclaringType().getSimpleName();
String methodName = methodSignature.getName();
String funName = methodSignature.getMethod().getAnnotation(ClickBehavior.class).value();
long beginTime = System.currentTimeMillis();
Object procce = joinPoint.proceed();
long drationTime = System.currentTimeMillis()-beginTime;
Log.e(TAG,String.format("开始了开始了,方法:%s 行为:%s 需要的时间:%s " ,className,funName,drationTime+"ms"));
Log.e(TAG,"》》》》》》》》》》》》》》》》》》》 执行结束");
return procce;
}
}
然后可以开始浪了
看看界面
public class HomeActivity extends AppCompatActivity implements View.OnClickListener {
final String TAG = this.getClass().getSimpleName();
private ActivityHomeBinding binding;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityHomeBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
binding.bntLogin.setOnClickListener(this);
binding.bntOrderList.setOnClickListener(this);
binding.bntPointsMall.setOnClickListener(this);
binding.bntShopCart.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bnt_login:
login();
break;
case R.id.bnt_order_list:
orderList();
break;
case R.id.bnt_points_mall:
pointsMall();
break;
case R.id.bnt_shop_cart:
shopCart();
break;
}
}
@ClickBehavior("购物车")
public void shopCart() {
Log.e(TAG, "--------------------->购物车");
}
@ClickBehavior("积分商城")
public void pointsMall() {
Log.e(TAG, "--------------------->积分商城");
}
@ClickBehavior("我的订单")
public void orderList() {
Log.e(TAG, "--------------------->我的订单");
}
@ClickBehavior("登录")
public void login() {
Log.e(TAG, "--------------------->登录");
}
}
运行后
2021-04-08 16:27:41.740 3219-3219/com.cxz.demo E/ClickBehaviorAspect: 》》》》》》》》》》》》》》》》》》》 开始执行
2021-04-08 16:27:41.740 3219-3219/com.cxz.demo E/HomeActivity: --------------------->购物车
2021-04-08 16:27:41.742 3219-3219/com.cxz.demo E/ClickBehaviorAspect: 开始了开始了,方法:HomeActivity 行为:购物车 需要的时间:0ms
2021-04-08 16:27:41.742 3219-3219/com.cxz.demo E/ClickBehaviorAspect: 》》》》》》》》》》》》》》》》》》》 执行结束
补充 这个是和接口 来看一下和注解一起使用的
注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UserAnnotation {
}
Aspect中的写法
@Pointcut("execution(@com.cxz.demo.UserAnnotation * *(..))")
public void methodPointCut() {
}
界面中使用
@UserAnnotation
public void shopCart() {
Log.e(TAG, "--------------------->购物车");
}
@UserAnnotation
public void pointsMall() {
Log.e(TAG, "--------------------->积分商城");
}
运行结果
2021-04-13 18:10:37.629 25254-25254/com.cxz.demo E/ClickBehaviorAspect: 》》》》》》》》》》》》》》》》》》》 开始执行
2021-04-13 18:10:37.631 25254-25254/com.cxz.demo E/ClickBehaviorAspect: 开始了开始了,方法:HomeActivity 行为:shopCart 需要的时间:0ms
2021-04-13 18:10:37.631 25254-25254/com.cxz.demo E/ClickBehaviorAspect: 》》》》》》》》》》》》》》》》》》》 执行结束
nice 这段时间的相关学习到这就差不多了,更多的需要项目中实现