SSM-Spirng-面向切面编程-使用@AspectJ注解开发SpringAoP

SSM-Spirng-面向切面编程-使用@AspectJ注解开发SpringAoP

​ 使用@AspectJ注解的方式已经成为了主流。


选择切点

​ spring是方法级别的AOP框架,主要是以某个类的某个方法作为切入点,用动态代理的理论就是,要拦截哪个方法植入对应的AOP通知

//创建一个接口
public interface RoleService {
    
    
    public void printRole(Role role);
}
//实现类
public class RoleServiceImpl implements RoleService {
    
    
    public void printRole(Role role) {
    
    
        System.out.println(role.getId()+role.getRoleName()+role.getNote());
    }
}

创建上面代码主要是想通过printRole方法作为AOP切点,那么动态代理需要为类RoleServiceImpl生成代理对象,然后拦截printRole方法,生成各种AOP通知方法


创建切面

​ 选着好切点就可以创建切面了,在动态代理中,他如同一个拦截器,在Spring使用注解@AspectJ注解一个类,spring IOC就会认为它是一个切面

@Aspect
public class RoleAspect {
    
    

    @Before("execution(* aop02.service.impl.RoleServiceImpl.printRole(..))")
    public void before(){
    
    
        System.out.println("before...");
    }

    @After("execution(* aop02.service.impl.RoleServiceImpl.printRole(..))")
    public void after(){
    
    
        System.out.println("after..");
    }

    @AfterReturning("execution(* aop02.service.impl.RoleServiceImpl.printRole(..))")
    public void afterReturning(){
    
    
        System.out.println("afterReturning...");
    }

    @AfterThrowing("execution(* aop02.service.impl.RoleServiceImpl.printRole(..))")
    public void afterThrowing(){
    
    
        System.out.println("afterThrowing...");
    }
}

​ 上面代码并没有环绕通知,注解表如下:

注解 通知 备注
@Before 在被代理对象的方法前先调用 前置通知
@Around 将被代理对象的方法封装起来,并用环绕通知取代它 环绕通知,它将覆盖原有的方法,但是允许你通过反射调用原有方法
@After 在被代理对象的方法后调用 后置通知
@AfterReturning 在被代理对象的方法正常返回后调用 返回通知,要求被代理对象的方法执行过程中没有发生异常
@After Throwing 在被代理对象的方法抛出异常后调用 异常通知,要求被代理对象的方法执行过程中产生异常

连接点

​ 在注解中定义execution的正则表达式,Spring通过这个正则表达式判断是否需要拦截你的方法,如:

execution(* aop02.service.impl.RoleServiceImpl.printRole(..))

分析:

  1. execution:代表执行方法的时候会触发
  2. *:代表任意返回类型的方法
  3. aop02.service.impl.RoleServiceImpl:代表类的全限定名
  4. printRole:被拦截的方法
  5. (…):任意的参数

还可以配置如下内容

AspectJ指示器 描述
arg() 限制连接点匹配参数为指定类型的方法
@args() 限制而连接点匹配参数为指定注解的执行方法
execution 用于匹配连接点的常用方法,这是最常用的匹配,可以通过类似上面的正则表达式进行匹配
this() 限制连接点匹配AOP代理的Bean,引用为指定类型的类
target 限制连接点匹配被代理对象为指定的类型
@target() 限制连接点匹配特定的执行对象,这些对象要符合指定的注解类型
within() 限制连接点指定匹配的包
@within() 限制连接点匹配指定的类型
@annotation 限制匹配带有指定注解的连接点

spring只支持上面表格的指示器,要是使用了其他会抛出异常

另外还可以使用 && || !代表 and or not

@Pointcut可以解决正则表达式需要重复书写多次的麻烦


测试AOP(*测试出问题,以后回来在解决,怀疑是AopConfig类)

​ 对Spring的Bean进行配置,采用注解方式:

//注解的方式
@Configuration
//启动AspectJ框架的自动代理
@EnableAspectJAutoProxy
@ComponentScan("aop02")
public class AopConfig {
    
    

    //生成一个切面实例
    @Bean
    public RoleAspect getRoleAspect(){
    
    
        return new RoleAspect();
    }
}

XML方式

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       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/util  http://www.springframework.org/schema/beans/spring-util.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--开启自动代理功能-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

    <bean id="roleAspect" class="aop02.aspect.RoleAspect"></bean>
    <bean id="roleService" class="aop02.service.impl.RoleServiceImpl"></bean>


</beans>

测试

package aop02.main;
import aop02.config.AopConfig;
import aop02.pojo.Role;
import aop02.service.RoleService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    
    
    public static void main(String[] args) {
    
    
        ApplicationContext context=new ClassPathXmlApplicationContext("spring06.xml");
        //ApplicationContext context=new AnnotationConfigApplicationContext(AopConfig.class);
        RoleService roleService=(RoleService)context.getBean(RoleService.class);
        Role role=new Role();
        role.setId(1L);
        role.setRoleName("roleName01");
        role.setNote("note01");
        roleService.printRole(role);
        System.out.println("************");
        role=null;
        roleService.printRole(role);
    }
}

环绕通知

​ 是SpringAop中最强大的功能,可以同时实现前置通知和后置通知,保留了调度被代理对象原有的方法和功能。

@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...");
}

​ 通过@Aroun注解加入了切面的环绕通知


织入

​ 是生成代理对象的过程,规则:类存在接口,Spring将提供JDK动态代理,从而织入各个通知;当不存在接口,Spring将提供CGLEB来生成对象


给通知传递参数

​ 修改切点为一个多参数方法

public void printRole(Role role,int sort){
    
    
    System.out.println(role.getId+role.getRoleName+role.getNote);
    System.out.println(sort);
}

​ 存在两个参数,一个角色,一个整形排序参数,把这个方法作为切点,也就是使用切面拦截这个方法,以前置为例:

    @Before("execution(* aop02.service.impl.RoleServiceImpl.printRole(..))"+"&& args(role,sort)")
    public void before(Role role,int sort){
    
    
        System.out.println("before...");
    }

上面,在连接的定义上加入参数,就可以获取动态代理。


### 引入

​ 如果希望通过引入其他类的方法来得到更好的实现,可以引入其他方法

​ 如printRole方法要求,角色为空时不打印,那么引入一个新的检测器对其检测,定义一个接口:

public interface RoleVerifier {
    
    
    public boolean verify(Role role);
}

该方法检测role是否为空

创建实现类

public class RoleVerifierImpl implements RoleVerifier {
    
    
    public boolean verify(Role role) {
    
    
        return role != null;
    }
}

在切面类RoleAspect 加入一个新属性

    @DeclareParents(value = "aop02.service.impl.RoleServiceImpl+",defaultImpl = RoleVerifierImpl.class)
    public RoleVerifier roleVerifier;

注解@DeclareParents 使用:

  • value = “aop02.service.impl.RoleServiceImpl+”:代表对RoleServiceImpl类进行增强,也就是引入一个新接口
  • defaultImpl:代表其默认的实现类,上面是RoleVerifierImplra

然后调用测试代码

ApplicationContext context=new AnnotationConfigApplicationContext(AopConfig.class);
        RoleService roleService=(RoleService)context.getBean(RoleService.class);
        RoleVerifier roleVerifier= (RoleVerifier) roleService;
        Role role=new Role();
        role.setId(1L);
        role.setRoleName("roleName01");
        role.setNote("note01");
        if(roleVerifier.verify(role)){
    
    
            roleService.printRole(role);
        }

​ 使用强化后的roleService转换为RoleVerifier接口对象,然后就可以使用verify方法 RoleVerifer 调用的方法 verify ,显然它就是通过 RoleVerifierlmpl 来实现的

​ 原理:SpringAOP依赖于动态代理实现,生成动态代理对象是通过类型下面的代理代码

//生成代理对象,并绑定代理方法
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().get!nterfaces(), _this );

​ SpringAOP让代理对象挂到RoleService和RoleVerifier两个接口下,把对应的Bean通过强制转换,如果RoleServicelmpl没有接口,它会使用CGLIB动态代理,使用增强者类也会有一个interfaces的属性,允许代理对象挂到对应的多个接口下,然后就可以按JDK动态代理那样使得对象可以在多个接口之间相互转换

猜你喜欢

转载自blog.csdn.net/weixin_43958223/article/details/115264574