Spring AOP实现:使用@AspectJ注解

     AOP在Spring框架中并不是特有的,Spring只是支持AOP编程的框架之一。每个框架对AOP的支持都各有各的特点,有些AOP能够对方法的参数进行拦截,有些AOP对方法进行拦截。其中Spring AOP是一种基于方法拦截的AOP,也就是Spring只能支持方法拦截的AOP,在Spring中有4种方式实现AOP的拦截功能。

  • 使用ProxyFactoryBean和对应的接口实现AOP
  • 使用XML配置AOP
  • 使用@AspectJ注解驱动切面
  • 使用AspectJ注入切面

    在4种方式中,真正常用的也就两种就是@AspectJ注解和XML配置,而且是用@AspectJ注解的方式实现的切面,有时候XML配置也起到一定的辅助作用。本篇就是使用第3种方式@AspectJ注解来实现Spring AOP。

    使用@AspectJ注解测试Spring AOP,我们可以以一个用户类的例子来测试(为了更好的使用注解,都是使用注解的方式来创建):

  1. 创建个用户信息类
  2. 创建一个用户接口
  3. 提供一个用户接口实现类
  4. 创建个切面类
  5. 配置XML
  6. 测试

1.用户信息类创建:

package soft.demo6_2;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;

@Repository("user")
public class User {
    private Integer id;
    private String name;
    private Integer age;

    public Integer getId() {
        return id;
    }

    @Value("1")
    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    @Value("张三")
    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    @Value("22")
    public void setAge(Integer age) {
        this.age = age;
    }
}

@Value注解推荐放在set方法上使用,因为放在定义变量上时是通过反射的Filed赋值,破坏了封装性。

2.用户接口的创建:

package soft.demo6_2;

public interface UserInterface {
    void getInfo(User user);
}

3.接口实现类的创建:

package soft.demo6_2;

import org.springframework.stereotype.Repository;

@Repository("userImpl")
public class UserImpl implements UserInterface {
    @Override
    public void getInfo(User user) {
        System.out.println(" id:"+user.getId()+",\n name:"+user.getName()+",\n age:"+user.getAge());
    }
}

可在此处使用注解,也可以在XML中配置该目标对象,由于我使用注解,所有在此处用注解代替XML的配置。

4.创建切面类--使用@注解Aspect:

package soft.demo6_2;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

@Aspect
public class UserAspect {

    @Pointcut("execution(* soft.demo6_2.*Impl.*(..))")
    public void pc(){}

    //前置
    @Before("UserAspect.pc()")
    public void before(){
        System.out.println("------前置------");
    }
    //后置
    @AfterReturning("UserAspect.pc()")
    public void afterReturning(){
        System.out.println("------后置(如果出现异常不会被调用)------");
    }
    //环绕
    @Around("UserAspect.pc()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("------环绕(之前部分)------");
        Object proceed = pjp.proceed();//调用目标方法
        System.out.println("------环绕(之后部分)------");
        return proceed;
    }
    //异常
    @AfterThrowing("UserAspect.pc()")
    public void afterException(){
        System.out.println("-------异常(异常出现了!!!)------");
    }
    //后置
    @After("UserAspect.pc()")
    public void after(){
        System.out.println("------后置(出现异常也会被调用)------");
    }
}

需要定义的切点使用注解在该类中定义切入点,为了便于修改切点,可定义一个切入点方法,

@Pointcut("execution(* soft.demo6_2.*Impl.*(..))")
    public void pc(){}

然后在后面直接调用该切入点方法即可。Spring就是通过这个切入点的正则表达式判断是否需要拦截你的方法,表达式:

execution(* soft.demo6_2.*Impl.*(..))
  • execution:代表执行方法的时候会触发
  • *:代表任意返回类型的方法
  • soft.demo6_2.*Impl.*(..)):代表在该包下类的限定名
  • *(..):星号代表被拦截任意方法名称
  • (..):任意的参数

代码中注解部分就是AspectJ的注解,其中:

  • @Before:在被代理对象的方法前先调用,属于前置通知。
  • @AfterReturning:在被代理对象的方法正常返回后调用,属于返回通知(也等于后置通知)如果发生异常则不会被调用
  • @Around:在被代理对象的方法封装起来,并用环绕通知取代它,属于环绕通知,它将覆盖原有的方法,但是允许你通过反射调用原有方法。环绕通知是Spring AOP中最强大的通知,它可以同时实现前置和后置的通知,它保留了调度被代理对象原有方法的功能,所以它强大,又灵活。这个通知里有一个参数ProceedingJoinPoint,是Spring提供的一个参数,使它可以反射连接点。当环绕通知使用pjp.proceed()方法后会先调用前置通知,然后反射切点方法,最后就是后置通知和返回(或者异常)通知。
  • @AfterThrowing:在被调用对象的方法抛出异常后调用,属于异常通知,要求被代理对象的方法执行过程中产生异常
  • @After:在被代理对象的方法后调用,属于后置通知

5.配置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:context="http://www.springframework.org/schema/context"
       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/context
                           http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 值定扫描soft.demo6_2包下的所有类中的注解。
		 注意:扫描包时,会扫描值定包下的所有子包
	 -->
    <context:component-scan base-package="soft.demo6_2"></context:component-scan>
    <!-- 配置目标对象 -->
    <!--<bean name="userImpl" class="soft.demo6_2.UserImpl"></bean>-->
    <!-- 配置切面对象 -->
    <bean name="userAspect" class="soft.demo6_2.UserAspect"></bean>
    <!-- 开始使用注解 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>

使用了注解配置后,XML文件将会变的简单方便。

6.创建测试类测试代码:

package soft.demo6_2;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath*:soft/demo6_2/applicationContext.xml")
public class DemoTest {

    @Resource(name = "user")
    private User user;
    @Resource(name = "userImpl")
    private UserInterface uf;

    @Test
    public void test(){
        uf.getInfo(user);
        System.out.println("--------------------------------");
        user=null;
        uf.getInfo(user);
    }

}

注意:获取UserImpl实现类应该用UserInterface接口来接收,不然将会出现一下类型不匹配的异常:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'soft.demo6_2.DemoTest': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'userImpl' must be of type [soft.demo6_2.UserImpl], but was actually of type [com.sun.proxy.$Proxy22]

最后测试结果为:

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值