首页 > 代码库 > spring AOP

spring AOP

本节要点:

  • 掌握AOP概念
  • 掌握AOP的有关术语
  • 掌握spring AOP框架的实现方式

 

在文章“spring静态代理和动态代理”中演示了如何使用jdk动态代理功能实现一个最简单的AOP。使用代理对象将日志记录与业务逻辑无关的动作或任务提取出来,设计为一个服务类,这样的类可以称之为aspect(切面).

1  AOP定义

  • u  AOP把软件系统分成两部分:核心关注点和横切关注点。所谓核心关注点,是业务处理的主要流程,也就是说这个解决方案要做的事。所谓横切关注点,是与核心关注点无关的部分,常常发生在核心关注点的多处,而各处基本相似,如日志 、权限等。
  • u  将Cross-cutting concern设计为通用,不介入特定业务对象的一个职责清楚的Aspect对象,这就是Aspect-oriented programming,缩写即AOP.

AOP并不会取代OOP,而是作为OOP的补充。

  • u  AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。

2  AOP有关术语

  • u  Advice (通知).

Aspect的具体实现称之为Advice(处理).上例中DynaProxyHandler类中invoke方法就是Advice的一个具体实现.

  • u  Joinpoint(连接点)

Advice在应用程序中被执行的时机称为Joinpoint(连接点),这个时机可能是某个方法被执行之前或之后(或两者都有).

  • u  Pointcut(切入点)

一组连接点的集合,用于指定哪些Aspect在哪些Joinpoint中织入到应用程序之上.

  • u  Target(目标对象)

一个Advice被应用的目标对象.(被AOP框架进行增强处理的对象)

  • u  Weave(织入)

Advice被应用至目标对象之上的过程称这为织入(Weave)

  • u  AOP代理(AOP Proxy):

由AOP框架创建的目标对象的代理对象。是被插入了advice的Target Object 。

  • u  引入:

将方法者字段添加到被处理的类中。

术语图解

  技术分享

3  spring AOP框架的实现方式

Spring中的AOP代理由Spring的Ioc容器负责生成、管理,其依赖关系也由Ioc容器负责管理。

AOP编程只需要程序员参加3个部分:

  • u  定义普通业务组件
  • u  定义切入点
  • u  定义增强处理 (定义切面)

    进行AOP的关键就是定义切入点和增强处理。一旦定义了合适的切入点和增强处理,AOP框架将会自动生成AOP代理。

通常采用AspectJ方式来定义切入点和增强处理,在这种方式下,Spring有如下两种选择来定义切入点和增强处理:

  • 基于注解(Annotation)方式:使用@aspect、@Pointcut等Annotation来标注切入点和增强处理。
  • 基于XML配置文件的管理方式:使用spring配置文件来定义切入点和增强处理

3.1 通过xml配置的方式实现

加入jar包: aopalliance-1.0.jar,aspectjweaver.jar

Log.java前置通知

public class Log implements MethodBeforeAdvice{

    /**

     * @param method 被调用的方法对象,如addUser()方法

     * @param args 被调用的方法的参数

     * @param target 被调用的方法的目标对象

     */

    @Override

    public void before(Method method, Object[] args, Object target)

            throws Throwable {

        System.out.println("执行"+target.getClass().getName()+"的   "+method.getName()+"方法");

    }

}

 

afterLog.java后置通知

public class AfterLog implements AfterReturningAdvice{

    /**

     * 目标方法执行后执行的通知

     * returnValue 返回值

     * method 被调用的方法对象

     * args 被调用方法对象的参数

     * target 被调用方法对象的目标对象

     */

    @Override

    public void afterReturning(Object returnValue, Method method, Object[] args,

            Object target) throws Throwable {

        System.out.println("after "+target.getClass().getName()+" 的 "+method.getName()+" 被执行");

    }

}

 UserService

public interface UserService {

    public void getUser();

    public void addUser();

    public void delUser(String ss);

    public void updUser();

}

 

UserServiceImpl

public class UserServiceImpl implements UserService{

    @Override

    public void getUser() {

        System.out.println("get user");

    }

    @Override

    public void addUser() {

        System.out.println("add user");

    }

    @Override

    public void delUser( String ss) {

        System.out.println("delete user="+ss);

    }

    @Override

    public void updUser() {

        System.out.println("update user");

    }

}

 Beans.xml

<?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.xsd

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

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

    <bean name="userService" class="com.silvan.service.UserServiceImpl"></bean>

    <!-- 定义切面 -->

    <bean name="log" class="com.silvan.log.Log"></bean>

    <bean name="afterLog" class="com.silvan.log.AfterLog"/>

    <aop:config>

    <!-- aop:pointcut指定哪些对象的哪些方法订阅切面, expression指定该切入点关联的切入点表达式  id指定该切入点的标识符-->

    <aop:pointcut expression="execution(* com.silvan.service.*.*(..))" id="pointcut"/>

    <!--aop:advisor指定切面关联哪个切入点   advice-ref指定切面  pointcut-ref指定切入点 -->

    <aop:advisor advice-ref="log" pointcut-ref="pointcut" />

    <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>

    </aop:config>

</beans>

 

Test

public class Test {

    public static void main(String[] args) {

        ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");

        UserService us = (UserService) ac.getBean("userService");

//      us.delUser("zhaoliyin");

        us.updUser();

    }

}

 

使用log4j输出日志:

加入jar包:log4j-1.2.14.jar

配置文件:log4j.properties

public class Log implements MethodBeforeAdvice{

    private Logger logger = Logger.getLogger(Log.class) ;

    @Override

    public void before(Method method, Object[] args, Object target)

            throws Throwable {

        logger.info("执行"+target.getClass().getName()+"的   "+method.getName()+"方法");

    }

}

 

 

3.2 通过注释的方式实现

修改配置文件

<?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.xsd

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

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

    <aop:aspectj-autoproxy/>

    <bean name="userService" class="com.silvan.service.UserServiceImpl"></bean>

    <bean name="log" class="com.silvan.log.Log"></bean>

</beans>

 

修改切面内容

import org.aspectj.lang.annotation.AfterReturning;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Before;

import org.aspectj.lang.annotation.Pointcut;

@Aspect

public class Log{

    @Pointcut("execution(* com.silvan.service.*.*(..))") 

    private void anyMethod(){}//定义一个切入点 

   

    @Before("anyMethod() && args(name)")

    public void before(String name){

        System.out.println("前置通知 "+name);

    }

    @AfterReturning("anyMethod()") 

    public void doAfter(){ 

        System.out.println("后置通知"); 

    } 

}

 

spring AOP