当前位置: 首页 > 工具软件 > M-Aop > 使用案例 >

Spring学习笔记(四)-----AOP

祁凯泽
2023-12-01

AOP

AOP:Aspect-Oriented-Programming,面向切面编程。

如何上下加业务逻辑?

  1. 继承原类,复写原方法,上下业务逻辑+super.XXX();
  2. 实现接口进行组合,增加原类的成员变量,上下加业务逻辑,成员变量调用原方法。
public class UserDAOImpl3 implements UserDAO {
  private UserDAO userDAO = new UserDAOImpl();

  @Override
  public void save(User u) {
    System.out.println("save start...");
    userDAO.save(u);
    System.out.println("save end...");
  }
}

上面的成员变量是父类的引用指向子类的对象,可以通过setUserDao方法进行动态绑定具体实现类。
3. 对于大量的bean都需要加某个业务逻辑时,通过以上2个方法,难以实现,且造成代码的大量重复。此时我们需要一个新的方法,那就是AOP,面向切向编程。

AOP好处:可以动态的添加和删除在切面上的逻辑而不影响原来的执行代码。

AOP底层原理

动态代理实现AOP。
1. 编写业务逻辑类

public class LogInterceptor implements InvocationHandler {

  private Object target;

  public Object getTarget() {
    return target;
  }

  public void setTarget(Object target) {
    this.target = target;
  }

  public void beforeMethod(Method m) {
    System.out.println(m.getName() + " start...");
  }

  @Override
  public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
    beforeMethod(m);
    m.invoke(target, args);
    return null;
  }
}

该业务逻辑类实现了InvocationHandler 接口,重写了invoke方法,在invoke方法中调用beforeMethod()方法的,然后调用被代理对象的方法m。
2. 测试代码

  @Test
  public void testProxy() {
    UserDAO userDAO = new UserDAOImpl();
    LogInterceptor li = new LogInterceptor();
    li.setTarget(userDAO);
    UserDAO userDAOProxy = (UserDAO) Proxy.newProxyInstance(userDAO.getClass().getClassLoader(),
        new Class[] { UserDAO.class }, li);
    userDAOProxy.save(new User());
    userDAOProxy.delete();
  }

(1)实例化业务逻辑类li。
(2)设置被代理对象target。
(3)调用Proxy.newProxyInstance方法生成代理对象userDAOProxy 。参数为:类加载器,实现接口,负责业务处理的类。
(4)代理对象调用相应方法即可。

用处

  • 权限检查
  • 记录日志
  • 检测运行时间
  • Servlet的Filter
  • Struts2的Interceptor
  • 等等等等等等

AOP配置

在beans.xml中增加AOP的配置信息。
(1)xmlns:aop="http://www.springframework.org/schema/aop"
(2)http://www.springframework.org/schema/aop
(3)http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
(4)子标签下增加<aop:aspectj-autoproxy/>使用Aspectj的方式定义AOP。

Aspectj

AspectJ是一个面向切面的框架,它扩展了JAVA语言。AspectJ定义了AOP语法所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。

@AspectJ

表明这是一个切面。
例子如下:

@Aspect
@Component("logInterceptor")
public class LogInterceptor {
  @Before("execution(public * com.shen.dao.impl.UserDAOImpl.save(com.shen.model.User))")
  public void befor() {
    System.out.println("method start...");
  }
}
  1. 声明该类是一个切面。
  2. 声明该类是一个组件,让Spring自动初始化。
  3. @Before注解时在后面的切入点之前执行方法。
  4. “execution…”是一种织入点语法。

一些术语

  • JoinPoint:织入点。

  • PointCut:织入点的集合,且名字等于下面的方法名字。

  @Pointcut("execution(public * com.shen.dao..*.*(..))")
  public void myMethod() {};

  @Before("myMethod()")
  public void before() {……}

Pointcut的名字就是myMethod()。
- Aspect:切面。

  • Advice:在织入点上的建议。比如:@Before、@After

  • Target:被代理对象,织入到哪里

  • Weave:织入

织入点

AspectJ的织入点语法

  • execution(public * *(..)):任何类的任何方法
  • execution(* set*(..)):set开头的任何方法
  • execution(* com.shen.Class1.*(..)):该类里面的任何方法
  • execution(* com.shen..*.*(..)):com.shen包下任何类的任何方法(包括子包)
    PS:Spring AOP也定了自己的语法,少用。

advice

Before advice

@Beforce(切入点语法|全类名.方法())
在织入点函数执行前,会执行被这个建议注解的方法。

After returning advice

@AfterReturning()
织入点包含的函数正常执行完,会执行被这个建议注解的方法。

After throwing advice

@AfterThrowing()
织入点包含的函数抛出异常,会执行被这个建议注解的方法。

After(finally) advice

@After()
当织入点函数正常返回或抛出异常时,会执行被这个建议注解的方法。

Around advice

@Around()
前面也可以加逻辑,后面也可以加逻辑。注意该方法的第一个参数必须是ProceedingJoinPoint类型的,返回值在不确定返回类型情况下,用Object。

@Around("com.shen.dao.impl.UserDAOImpl.addUser(com.shen.dao.UserDAO)")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable{
    //前逻辑
    Object retVal=pjp.proceed();//方法继续运行
    //后逻辑
    return retVal;
}

以上是Annotation的方法


XML方式使用AOP(常用)

基本上同Annotation的原理。
1. 先编写用到的业务逻辑类,比如生成日志的功能类,包含beforeLog()、afterLog(),比如该类为LogCreateor。
2. 配置XML,先给出代码。

    <bean id="logInterceptor" class="com.shen.aop.LogCreateor"></bean>
    <aop:config>
        <!-- <aop:pointcut expression="execution(public * com.shen.service..*.add(..))"
            id="servicePointcut" />
        <aop:aspect id="logAspect" ref="logInterceptor">
            <aop:before method="before" pointcut-ref="servicePointcut" />
        </aop:aspect> -->
        <aop:aspect id="logAspect" ref="logInterceptor">
            <aop:before method="before" pointcut="execution(public * com.shen.service..*.add(..))" />
        </aop:aspect>
    </aop:config>

(1)声明LogCreateor为一个bean,让Spring自动为其初始化。

(2)使用<aop:config>子标签声明AOP的配置。

(3)在外部使用<aop:pointcut>子标签声明一个全局的织入点,给出织入点语法和织入点id。

(4)使用<aop:aspect>子标签声明一个切面,并给出该切面的bean的ID。PS:在该标签内也可声明织入点,该织入点只在该切面管用。

(5)在aspect标签中使用<aop:before(各种advice)>子标签声明一个建议,要给出切面中使用的方法名,以及要在哪个织入点ID使用该方法。也可以在该标签中的属性pointcut="织入点语法"直接定义织入点,而不是用局部或者全局的织入点ID。

XML和Annotation比较

很多情况下,使用别人的切面类逻辑不能使用Annotation(因为没有源码),但此时可以使用XML。故XML更常用。

 类似资料: