校验业务与主逻辑解耦设计探讨与实践(AOP篇)

说实话,之前把AOP想的太简单太美好了,为了这个实现方式我前后抽时间研究了4天,虽然最后没能实现,但还是记录一下一些探索的经验,许在以后的项目中可以实施。先简单介绍一下

面向侧面的程序设计(aspect-oriented programming,AOP,又译作面向方面的程序设计、观点导向编程、剖面导向程序设计)是计算机科学中的一个术语,指一种程序设计范型。该范型以一种称为侧面(aspect,又译作方面)的语言构造为基础,侧面是一种新的模块化机制,用来描述分散在对象、类或函数中的横切关注点(crosscutting concern)——危急百科
我只了解了的AOP的三种实现方式,ASM(推荐文章),AspectJ(推荐文章)和SpringAOP(配置教程,详细用法)

  1. ASM没有去实践,本文不做讨论
  2. AspectJ
    AspectJ需要特殊的编译,在我的项目里肯定是无法使用的,除此之外AspectJ还是很好用的,完全支持Java语法,学习成本低,但是我用不了TAT。
  3. SpringAOP
    SpringAOP依赖于Spring框架,虽然把Spring框架用到我这系统也是不肯能的事情,但是本着学好Spring总是没有错的原则,我还是写了Demo,贴一下。
    必要的依赖包
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-aopartifactId>
            <version>${org.springframework-version}version>
        dependency>
        <dependency>
            <groupId>org.aspectjgroupId>
            <artifactId>aspectjrtartifactId>
            <version>${org.aspectj-version}version>
        dependency>
        <dependency>
            <groupId>org.aspectjgroupId>
            <artifactId>aspectjweaverartifactId>
            <version>${org.aspectj-version}version>
        dependency>

spring-aspect.xml配置文件

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.0.xsd ">

    <mvc:annotation-driven/>
    
    <aop:aspectj-autoproxy proxy-target-class="true"/>

    <bean id="registerFrame" class="com.gcoreinc.validate.RegisterFrame"/>
    <bean id="userValidateAspect" class="com.gcoreinc.validate.UserValidateAspect" />
beans>

定义切面

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class UserValidateAspect {

    @Pointcut("execution(* com.gcoreinc.validate.RegisterFrame.doRegister(..))")
    public void pointCut(){

    }

//最全的功能了,可以对方法的传入参数进行处理,也可以获取方法的返回值,记住进入Around的方法如果不再次调用将不会执行,因此如果没有异常记得joinPoint.proceed()一下,执行此方法
    @Around("pointCut()")
    public void aroundRegister(ProceedingJoinPoint joinPoint){
        System.out.println("around start");
        Object[] args = joinPoint.getArgs();
        if(args[0] != null){
            User user = (User) args[0];
            System.out.println(user.userName);
            try {
                joinPoint.proceed(args);
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
        }else{
            System.out.println("User can not be empty");
        }
        System.out.println("around end");
    }
}

RegisterFrame类

package com.gcoreinc.validate;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class RegisterFrame extends JFrame{

    JButton registerBtn;

    public RegisterFrame() {
        super("Register");
        this.setSize(200, 200);
        initComps();
        doRegister(null);
    }

    private void initComps() {
        registerBtn = new JButton("Register");
        this.add(registerBtn);
        registerBtn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                User user = new User("XiWenRen","12345678");
                doRegister(user);
            }
        });
    }

    public void doRegister(User user){
        System.out.println("goto register");
    }
}

class User{

    public User(String userName, String passWord){
        this.userName = userName;
        this.passWord = passWord;
    }
    public String userName;
    public String passWord;

    @Override
    public String toString() {
        return "User{" +
                "userName='" + userName + '\'' +
                ", passWord='" + passWord + '\'' +
                '}';
    }
}

测试类

package com.gcoreinc.validate;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class TestAspect {

    public static void main(String[] args) {
        ApplicationContext context = new FileSystemXmlApplicationContext("classpath:spring-aspect.xml");
        RegisterFrame frame = (RegisterFrame) context.getBean("registerFrame");
        frame.setVisible(true);
        frame.doRegister()
    }
}

执行后发现控制台输出了:

around start
User can not be empty
around end

说明我们的切面成功了,但是,如果点击按钮之后,直接输出goto register,切面没有成功,说明只能bean直接调用才能进入切面了,SpringAOP的这个特性,简直让人没法玩,解决方案是这样写

registerBtn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                User user = new User("XiWenRen","12345678");
                ApplicationContext context = new FileSystemXmlApplicationContext("classpath:spring-aspect.xml");
                RegisterFrame frame = (RegisterFrame) context.getBean("registerFrame");
                frame.doRegister(user);
            }
        });

上下文的状态只能通过参数去传递了,这样的话痕迹还是很重的,这个方案也就只能到此为止了。
最近的状态真是差,思路不清晰,到今天第四天已经有些背离初衷了,究其原因还是已经整整一个月没有踢球了,这个周日一定要去找一场,恢复生气才能工作。
下一篇可能讨论一下使用代理模式的实现方式,或者想一个方法将观察者模式放入系统,等思路清晰后进行研究。

你可能感兴趣的:(Java,spring,AOP)