Android 进阶-设计模式-静态代理和动态代理(aop原理)

2019-11-28  本文已影响0人  伊泽瑞额
可以从小明通过代购买电脑的例子来描述静态代理和动态代理的实现过程
1、使用静态代理

1、定义代理对象和真实对象的公共接口;
2、真实对象实现公共接口中的方法;
3、代理对象实现公共接口中的方法,并把方法的逻辑转发给真实对象

1.1、定义代理对象和真实对象的公共接口
/**
 * 公共接口
 */
public interface IComputer {
    void buy();//买电脑
}

1.2、真实对象实现公共接口中的方法;
/**
 * //被代理人,想要买电脑的小明
 */
public class Xiaoming implements IComputer{
    @Override
    public void buy() {
        Log.i("TAG","买了电脑");
    }
}

1.3、代理对象实现公共接口中的方法,并把方法的逻辑转发给真实对象

/**
 * 代理人  代理小明买电脑
 */
public class ComputerAngcy implements IComputer {

 private   IComputer xiaoming;//持有一个被代理人(小明)的引用

    public ComputerAngcy(IComputer computer){
        this.xiaoming=computer;
    }

    @Override
    public void buy() {
        xiaoming.buy();
    }
}

        Xiaoming xiaoming = new Xiaoming();
        ComputerAngcy computerAngcy = new ComputerAngcy(xiaoming);
        computerAngcy.buy();

2、 使用动态代理

1、定义代理对象和真实对象的公共接口;(与静态代理步骤相同)
2、真实对象实现公共接口中的方法;(与静态代理步骤相同)
3、定义一个实现了InvocationHandler接口的动态代理类;
4、通过Proxy类的newProxyInstance方法创建代理对象,调用代理对象的方法。

2、3步骤1和步骤2 和静态代理一样直接从步骤3 说起 ( 实现了InvocationHandler接口的动态代理类)

/**
 * 实现了InvocationHandler接口的动态代理类
 */
public class DynamicProxy<T>implements InvocationHandler {

    private T mObject;//真实对象的引用

    public DynamicProxy(T mObject){
        this.mObject=mObject;
    }

    @Override
    public Object invoke(Object o, Method method, Object[] args) throws Throwable {
        //通过反射调用真实对象的方法
        Object result = method.invoke(mObject, args);
        return result;
    }
}
2、4通过Proxy类的newProxyInstance方法创建代理对象,调用代理对象的方法。

     IComputer xiaoming = new Xiaoming();
        //构造一个动态代理
        InvocationHandler dynamicProxy = new DynamicProxy(xiaoming);
        //获取被代理类小明的ClassLoader
        ClassLoader classLoader = xiaoming.getClass().getClassLoader();
        //1、通过Proxy类的newProxyInstance方法动态构造一个代理人房产中介
        IComputer computerAgency = (IComputer) Proxy.newProxyInstance(classLoader, new Class[]{IComputer.class}, dynamicProxy);
        //调用代理对象的方法
        computerAgency.buy();

Proxy的newProxyInstance方法会根据传入的类加载器动态生成代理对象实例,生成的代理对象会继承Proxy类并实现传入的接口列表,这里的类加载器是小明的ClassLoader,即真实对象的类加载器,而接口列表则是IComputer,传入的IComputer的Class对象,除了这个两个参数,还传入了动态代理类InvocationHandler实例,这样Proxy类在创建代理对象的实例时就会把这个InvocationHandler引用传给代理对象,接下来当我们调用代理对象的方法时,这个方法的处理逻辑就会委托给InvocationHandler实例的invoke方法执行,invoke方法中就会通过反射调用我们真实对象的方法。

Aop原理就是动态代理, 可以在invoke方法体 在正真代理对象执行方法前后去执行想要的操作,如:登录拦截

登录接口ILogin 验证登录 只有登陆后才能执行该方法

package uidemo.amn.com.aopdemo;

public interface ILogin {
    /**
     * 验证登录 只有登陆后才能执行该方法
     */
    void isLogin();
}

创建MyInvocationHandler 来监听代理类 方法的执行

public class MyInvocationHandler implements InvocationHandler {

    Context mContext;
    Object target;//持有要代理的对象,通过构造传递过来

    public  MyInvocationHandler (Object context){
        this.mContext= (Context) context;
        this.target=context;
    }
    /**
     * 监听代理类 方法的执行
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object invoke=null;
    //  在method.invoke()方法执行前后可以加入自己想要的操作,日志打印,登录判断拦截
        if(Constains.isLogin){
            //已经登录
            invoke=method.invoke(target,args);
        }else {
            //没登录
            Intent intent =new Intent();
            intent.setClass(mContext,LoginActivity.class);
            mContext.startActivity(intent);
        }
        return invoke;
    }
}



//简单存储登录状态
public class Constains {
    public static boolean isLogin=false;
}

在测试的activity中 使用:


public class MainActivity extends AppCompatActivity  implements ILogin {

    ILogin proxy;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //通过这个API创建一个代理对象,第一个类加载器,该类对象一定是第二个参数的实现类,
        // 第二个参数,代理的目标类,第三个回调处理 可以监听代理类每个方法的执行
        proxy = (ILogin) Proxy.newProxyInstance(MainActivity.this.getClassLoader(),
                new Class[]{ILogin.class},new MyInvocationHandler (this));
    }


    public void jumpActivity(View view) {
        proxy.isLogin();
    }



    @Override
    public void isLogin() {
        Intent intent =new Intent();
        intent.setClass(this,TestActivity.class);
        startActivity(intent);
    }
}

通常做APO拦截一般使用 aspectj 。

app gradle 配置

    buildscript {
        repositories {
            mavenCentral()
        }
        dependencies {
            classpath 'org.aspectj:aspectjtools:1.8.8'
            classpath 'org.aspectj:aspectjweaver:1.8.8'
        }
    }

    implementation 'org.aspectj:aspectjrt:1.8.11'


 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;
                }
            }
        }
    }

在 project gradle

    classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.4'

Login

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public  @interface Login {
    int loginDefine() default 0;
}

LoginAspect


@Aspect
public class LoginAspect {

    @Pointcut("execution(@com.csair.pdac.anotation.Login * * (..))")
    public void pointcutLogin(){}


    @Around("pointcutLogin()")
    public void aroundLogin(ProceedingJoinPoint joinPoint) throws Throwable{

        Log.i("aaa","调用前");
        //先得到方法的签名methodSignature,然后得到@Login注解,如果注解为空,就不再往下走。
        Signature signature = joinPoint.getSignature();
        if (!(signature instanceof MemberSignature)){
            throw new RuntimeException("该注解只能用于方法上");
        }

        MethodSignature methodSignature = (MethodSignature) signature;
        Login login = methodSignature.getMethod().getAnnotation(Login.class);
        if (login == null){
            return;
        }

        //判断是否登录,登录了就会继续执行方法体调用方法直到完成,如果没有登录,就拦截跳到登录页面
//AopUtil.getInstance().isLogin
        final Context context = AopUtil.getInstance().getContext();
        User user = SpUtils.getObject(context, User.class);
        if (user != null) {
            //方法执行
            joinPoint.proceed();
        } else {

            switch (login.loginDefine()) {
                case 0:
                    Intent intent = new Intent(context, LoginActivity.class);
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK );
                    context.startActivity(intent);
                    break;
                    // case1 case2 后面预留
                case 1:
                    Toast.makeText(context, "您还没有登录,请登陆后执行", Toast.LENGTH_SHORT).show();
                    break;

                case 2:
                    Toast.makeText(context, "操作失败,因为您还没有登录!", Toast.LENGTH_SHORT).show();

                    break;
            }

        }
    }


}

使用

   /**
     * 已登录 则直接执行打印,没登陆就会拦截
     * @param context
     */

    @Login(loginDefine = 0)
    public void jumpLogin(Activity context) {
        Log.i("aaa", "login");

    }
上一篇下一篇

猜你喜欢

热点阅读