Spring AOP 自定义注解

Spring是IOC和AOP的容器框架,SpringMVC是基于Spring功能之上添加的Web框架,想用SpringMVC必须先依赖Spring.

spring aop中@Around @Before @After三个注解的区别@Before是在所拦截方法执行之前执行一段逻辑。@After 是在所拦截方法执行之后执行一段逻辑。@Around是可以同时在所拦截方法的前后执行一段逻辑。

方式一:

package com.itsoft.action;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Controller;

/**
 * 
 * @author zxf
 * 演示aop测试类
 */
@Controller
public class UserAction {

    public void queryUsers(){

        System.out.println("查询所有用户【all users list】");
    }

    public static void main(String[] args) {

        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("application-aop.xml");

        UserAction userAction = (UserAction)ctx.getBean("userAction");
        userAction.queryUsers();

        ctx.destroy();
    }
}

[Java]代码

package com.itsoft;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * 
 * @author Administrator
 * 通过aop拦截后执行具体操作
 */
@Aspect
@Component
public class LogIntercept {

    @Pointcut("execution(public * com.itsoft.action..*.*(..))")
    public void recordLog(){}

    @Before("recordLog()")
    public void before() {
        this.printLog("已经记录下操作日志@Before 方法执行前");
    }

    @Around("recordLog()")
    public void around(ProceedingJoinPoint pjp) throws Throwable{
        this.printLog("已经记录下操作日志@Around 方法执行前");
        pjp.proceed();
        this.printLog("已经记录下操作日志@Around 方法执行后");
    }

    @After("recordLog()")
    public void after() {
        this.printLog("已经记录下操作日志@After 方法执行后");
    }

    private void printLog(String str){
        System.out.println(str);
    }
}

演示@Around @Before @After 区别

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:aop="http://www.springframework.org/schema/aop"
      xmlns:context="http://www.springframework.org/schema/context"
      xsi:schemaLocation="
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd
       http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/aop 
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
    <context:annotation-config />
    <context:component-scan base-package="com.itsoft"/>
    <aop:aspectj-autoproxy />
</beans>

方式二:http://ju.outofmemory.cn/entry/237180

准备环境

首先准备开发需要的jar包,请到spring-framework-3.0.5.RELEASE-dependencies.zip和spring-framework-3.0.5.RELEASE-with-docs中查找如下jar包:

org.springframework.aop-3.0.5.RELEASE.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.net.sf.cglib-2.2.0.jar

将这些jar包添加到“Build Path”下。

 定义目标类

1)定义目标接口:

java代码:

package cn.javass.spring.chapter6.service; 
public interface IHelloWorldService { 
 public void sayHello(); 
}

2)定义目标接口实现:

java代码:

package cn.javass.spring.chapter6.service.impl; 
import cn.javass.spring.chapter6.service.IHelloWorldService; 
public class HelloWorldService implements IHelloWorldService { 
 @Override 
 public void sayHello() { 
 System.out.println("============Hello World!"); 
 } 
}

注:在日常开发中最后将业务逻辑定义在一个专门的service包下,而实现定义在service包下的impl包中,服务接口以IXXXService形式,而服务实现就是XXXService,这就是规约设计,见名知义。当然可以使用公司内部更好的形式,只要大家都好理解就可以了。

定义切面支持类

有了目标类,该定义切面了,切面就是通知和切入点的组合,而切面是通过配置方式定义的,因此这定义切面前,我们需要定义切面支持类,切面支持类提供了通知实现:

java代码:

package cn.javass.spring.chapter6.aop; 
public class HelloWorldAspect { 
 //前置通知 
 public void beforeAdvice() { 
 System.out.println("===========before advice"); 
} 
//后置最终通知 
 public void afterFinallyAdvice() { 
 System.out.println("===========after finally advice"); 
 } 
}

此处HelloWorldAspect类不是真正的切面实现,只是定义了通知实现的类,在此我们可以把它看作就是缺少了切入点的切面。

注:对于AOP相关类最后专门放到一个包下,如“aop”包,因为AOP是动态织入的,所以如果某个目标类被AOP拦截了并应用了通知,可能很难发现这个通知实现在哪个包里,因此推荐使用规约命名,方便以后维护人员查找相应的AOP实现。

  在XML中进行配置

有了通知实现,那就让我们来配置切面吧:
1)首先配置AOP需要aop命名空间,配置头如下:

java代码:

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 xmlns:aop="http://www.springframework.org/schema/aop" 
 xsi:schemaLocation=" 

http://www.springframework.org/schema/beans


http://www.springframework.org/schema/beans/spring-beans-3.0.xsd


http://www.springframework.org/schema/aop


http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

</beans>

2)配置目标类:

java代码:

<bean id="helloWorldService" 
class="cn.javass.spring.chapter6.service.impl.HelloWorldService"/>

3)配置切面:

java代码:

<bean id="aspect" class="cn.javass.spring.chapter6.aop.HelloWorldAspect"/> 
<aop:config> 
<aop:pointcut id="pointcut" expression="execution(* cn.javass..*.*(..))"/> 
 <aop:aspect ref="aspect"> 
 <aop:before pointcut-ref="pointcut" method="beforeAdvice"/> 
 <aop:after pointcut="execution(* cn.javass..*.*(..))" method="afterFinallyAdvice"/> 
 </aop:aspect> 
</aop:config>

切入点使用<aop:config>标签下的<aop:pointcut>配置,expression属性用于定义切入点模式,默认是AspectJ语法,“execution(* cn.javass..*.*(..))”表示匹配cn.javass包及子包下的任何方法执行。

切面使用<aop:config>标签下的<aop:aspect>标签配置,其中“ref”用来引用切面支持类的方法。

前置通知使用<aop:aspect>标签下的<aop:before>标签来定义,pointcut-ref属性用于引用切入点Bean,而method用来引用切面通知实现类中的方法,该方法就是通知实现,即在目标类方法执行之前调用的方法。

最终通知使用<aop:aspect>标签下的<aop:after >标签来定义,切入点除了使用pointcut-ref属性来引用已经存在的切入点,也可以使用pointcut属性来定义,如pointcut=”execution(* cn.javass..*.*(..))”,method属性同样是指定通知实现,即在目标类方法执行之后调用的方法。

  运行测试

测试类非常简单,调用被代理Bean跟调用普通Bean完全一样,Spring AOP将为目标对象创建AOP代理,具体测试代码如下:

java代码:

package cn.javass.spring.chapter6; 
import org.junit.Test; 
import org.springframework.context.ApplicationContext; 
import org.springframework.context.support.ClassPathXmlApplicationContext; 
import cn.javass.spring.chapter6.service.IHelloWorldService; 
import cn.javass.spring.chapter6.service.IPayService; 
public class AopTest { 
 @Test 
 public void testHelloworld() { 
 ApplicationContext ctx = new ClassPathXmlApplicationContext("chapter6/helloworld.xml"); 
 IHelloWorldService helloworldService = 
 ctx.getBean("helloWorldService", IHelloWorldService.class); 
 helloworldService.sayHello(); 
 } 
}

该测试将输出如下如下内容:

java代码:

===========before advice 
============Hello World! 
===========after finally advice

从输出我们可以看出:前置通知在切入点选择的连接点(方法)之前允许,而后置通知将在连接点(方法)之后执行,具体生成AOP代理及执行过程如图6-4所示。

 
图6-4 Spring AOP框架生成AOP代理过程

 

 

 

AOP是Aspect Oriented Programing的简称,面向切面编程。AOP适合于那些具有横切逻辑的应用:如性能监测,访问控制,事务管理、缓存、对象池管理以及日志记录。AOP将这些分散在各个业务逻辑中的代码通过横向切割的方式抽取到一个独立的模块中。AOP 实现的关键就在于 AOP 框架自动创建的 AOP 代理,AOP 代理则可分为静态代理和动态代理两大类,其中静态代理是指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强;而动态代理则在运行时借助于 JDK 动态代理、CGLIB 等在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强。

代理对象的方法 = 增强处理 + 被代理对象的方法

Spring AOP 则采用运行时生成 AOP 代理类,因此无需使用特定编译器进行处理。由于 Spring AOP 需要在每次运行时生成 AOP 代理,因此性能略差一些。

AOP使用场景

AOP用来封装横切关注点,具体可以在下面的场景中使用

Authentication 权限

Caching 缓存

Context passing 内容传递

Error handling 错误处理

Lazy loading 懒加载

Debugging 调试

logging, tracing, profiling and monitoring 记录跟踪 优化 校准

Performance optimization 性能优化

Persistence 持久化

Resource pooling 资源池

Synchronization 同步

Transactions 事务

AOP相关概念

方面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。方面用Spring的 Advisor或拦截器实现。

连接点(Joinpoint): 程序执行过程中明确的点,如方法的调用或特定的异常被抛出

通知(Advice): 在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。Spring中定义了四个advice: BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice

切入点(Pointcut): 指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点:例如,使用正则表达式。 Spring定义了Pointcut接口,用来组合MethodMatcher和ClassFilter,可以通过名字很清楚的理解, MethodMatcher是用来检查目标类的方法是否可以被应用此通知,而ClassFilter是用来检查Pointcut是否应该应用到目标类上

引入(Introduction): 添加方法或字段到被通知的类。 Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现 IsModified接口,来简化缓存。Spring中要使用Introduction, 可有通过DelegatingIntroductionInterceptor来实现通知,通过DefaultIntroductionAdvisor来配置Advice和代理类要实现的接口

目标对象(Target Object): 包含连接点的对象。也被称作被通知或被代理对象。POJO

AOP代理(AOP Proxy): AOP框架创建的对象,包含通知。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。

织入(Weaving): 组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。

 

posted @ 2016-10-09 11:19  He_quotes  阅读(126)  评论(0)    收藏  举报