使用@AspectJ注解开发Spring AOP

1.选择连接点(join point)

spring是方法级别的AOP框架,而我们主要也是以某个类的某个方法作为连接点,用动态代理的理论来说,就是要拦截某个方法织入对应AOP通知。

public interface RoleService {
    
    public void printRole(Role role);
}

@Component
public class RoleServiceImpl implements RoleService {

    @Override
    public void printRole(Role role) {
        // TODO Auto-generated method stub
        System.out.println("{id="+role.getId()+",roleName="+role.getRoleName()+",note="+role.getNote()+"}");
    }

}


printRole方法作为AOP的连接点。用动态代理来说就是要为类RoleServiceImpl生成代理对象,然后拦截printRole方法,于是可以产生各种AOP通知方法。

2.创建切面

选择好连接点就可以创建切面了,用动态代理来说,它就如同一个拦截器。在Spring中用@Aspect注解一个类,IOC容器就会认为这是一个切面了。

@Aspect
public class RoleAspect {
    @Pointcut("execution(* test1.RoleServiceImpl.printRole(..))")
    public void print() {
        
    }
    @Before("print()")
    public void before() {
        System.out.println("before....");
    }
    @After("print()")
    public void after() {
        System.out.println("after....");
    }
    @AfterReturning("print()")
    public void afterReturning() {
        System.out.println("afterReturning....");
    }
    @AfterThrowing("print()")
    public void afterThrowing() {
        System.out.println("afterThrowing....");
    }
}

@Pointcut定义一个切点。

4.测试AOP

//这个类是对Spring的Bean进行配置,采用的是注解方式
//表明这是个配置类,相当于xml文件
@Configuration
//开启动态代理,开启AOP
@EnableAspectJAutoProxy
//扫描这个包下的所有bean
@ComponentScan("test1")
public class AopConfig {
    //表示创建一个bean,返回值类型表示bean的类型,方法名表示bean的id
    @Bean
    //生成一个切面实例
    public RoleAspect getRoleAspect() {
        return new RoleAspect();
    }
}
//这个类是对Spring的Bean进行配置,采用的是注解方式
//表明这是个配置类,相当于xml文件
@Configuration
//开启动态代理,开启AOP
@EnableAspectJAutoProxy
//扫描这个包下的所有bean
@ComponentScan("test1")
public class AopConfig {
    //表示创建一个bean,返回值类型表示bean的类型,方法名表示bean的id
    @Bean
    //生成一个切面实例
    public RoleAspect getRoleAspect() {
        return new RoleAspect();
    }
}

结果:

before....
{id=1,roleName=role_name_1,note=note_1}
after....
afterReturning....
############
before....
after....
afterThrowing....
Exception in thread "main" java.lang.NullPointerException

显然切面的通知已经通过AOP织入约定的流程中了,这是可以使用AOP来处理一切需要切面的场景了。

环绕通知

是spring AOP中最强大的通知,它可以同时实现前置通知和后置通知,还保留了调度被代理对象原有方法的功能,强大而灵活,但如果不需要大量改变业务的逻辑,一般而言并不需要使用它。

@Around("print()")
    public void around(ProceedingJoinPoint jp) {
        System.out.println("around before....");
        try {
        //反射连接点/切点方法
            jp.proceed();
        }catch(Throwable e) {
            e.printStackTrace();
        }
        System.out.println("around after....");
    }

给通知传递参数

上面的通知除了around()方法之外都是无参的,其实是可以传递参数来使用的。比如:

@Before("execution(* test1.RoleServiceImpl.printRole(..))"+"&&args(role)")
    public void before(Role role) {
        System.out.println("before....");
    }

引入

Spring AOP只是通过动态代理技术,把各类通知织入到它所约定的流程中,而事实上有时我们希望通过引入其他类的方法来得到更好的实现,这时就可以引入其他的方法了。

比如说引入一个方法检测printRole角色为空时不再打印:

public interface RoleVerifier {
    public boolean verify(Role role);
}
public class RoleVerifierImpl implements RoleVerifier {
    @Override
    public boolean verify(Role role) {
        return role!=null;
    }
}
加入RoleVerifier到切面中:
//value="test1.RoleServiceImpl+":表示对RoleServiceImpl类进行增强,也就是在RoleServiceImpl中引入一个新的接口
    //defaultImpl=RoleVerifierImpl.class:代表其默认的实现类
    @DeclareParents(value="test1.RoleServiceImpl+",defaultImpl=RoleVerifierImpl.class)
    public RoleVerifier roleVerifier;
ApplicationContext ctx=new AnnotationConfigApplicationContext(AopConfig.class);
        RoleService roleService=(RoleService)ctx.getBean(RoleService.class);
        RoleVerifier roleVerifier=(RoleVerifier)roleService;
        Role role=new Role();
        role.setId(1);
        role.setRoleName("role_name_1");
        role.setNote("note_1");
        if(roleVerifier.verify(role)) {
        roleService.printRole(role);
        }
        System.out.println("############");
        //测试异常通知
        role=null;
        if(roleVerifier.verify(role)) {
            roleService.printRole(role);
            }else {
                System.out.println("空的");
            }

只要Spring AOP让代理对象(RoleServiceImpl)挂到RoleService和RoleVerifier两个接口之下,那么就可以把对应的bean通过强制转换,让其在RoleService和RoleVerifier之间相互转换,调用各自的方法。