189 8069 5689

spring系列SpringAOP-创新互联

概念

原来的方法写法

创新互联是由多位在大型网络公司、广告设计公司的优秀设计人员和策划人员组成的一个具有丰富经验的团队,其中包括网站策划、网页美工、网站程序员、网页设计师、平面广告设计师、网络营销人员及形象策划。承接:成都网站建设、成都网站设计、网站改版、网页设计制作、网站建设与维护、网络推广、数据库开发,以高性价比制作企业网站、行业门户平台等全方位的服务。

public void save(){        

Long startTime = System.currentTimeMillis();        

System.out.println(“book dao save ...”);    

Long endTime = System.currentTimeMillis();        

Long totalTime = endTime-startTime;        

System.out.println("方法耗时:" + totalTime + "ms");    

}

通知

把多个方法耦合的部分抽取出来就叫做通知,设置通知的类就叫做通知类:

public void method(){        

Long startTime = System.currentTimeMillis();        

//调用原始操作     

Long endTime = System.currentTimeMillis();        

Long totalTime = endTime-startTime;        

System.out.println("方法耗时:" + totalTime + "ms");    

}

@Component
@Aspect
public class MyAdvice {

  @Pointcut("execution(void com.itheima.dao.BookDao.update())")
  private void pt(){}
  @Pointcut("execution(int com.itheima.dao.BookDao.select())")
  private void pt2(){}

  //@Before:前置通知,在原始方法运行之前执行
//    @Before("pt()")
  public void before() {
  System.out.println("before advice ...");
  }

  //@After:后置通知,在原始方法运行之后执行,原方法就算抛异常也会执行
//    @After("pt2()")
  public void after() {
  System.out.println("after advice ...");
  }

  //@Around:环绕通知,在原始方法运行的前后执行

  //借助ProceedingJoinPoint pjp可以调用原来的方法,从而达到对原来方法返回值的处理,如果原来方法有返回值而这里没有,虽然也会执行,但是如果我们想通过代码获取返回值的话就会报错。

  //这里如果ProceedingJoinPoint pjp没有,那就会跳过原始方法执行相应通知,可以用来做校验。

  //这里的规范是用Object,这样即使是个null也能接。

  //调用原始方法的时候会提示我们要抛异常throws Throwable
//    @Around("pt()")
  public Object around(ProceedingJoinPoint pjp) throws Throwable {

  //获取原方法的参数,实际值

  //Object[] args = pjp.getArgs();    

  //Object ret = pjp.proceed(args);//在拿到原方法参数值后可以加一些校验逻辑,不需要操作的话不用传参数也可以

  //System.out.println(Arrays.toString(args));

//获取执行的签名对象
  Signature signature = pjp.getSignature();
  String className = signature.getDeclaringTypeName();        //类路径和类名
  String methodName = signature.getName();        //方法名

  System.out.println("around before advice ...");
  //表示对原始操作的调用
  Object ret = pjp.proceed();
  System.out.println("around after advice ...");

System.out.println("类方法:"+ className+"."+methodName+"执行");
  return ret;
  }

//    @Around("pt2()")
  public Object aroundSelect(ProceedingJoinPoint pjp) throws Throwable {
  System.out.println("around before advice ...");
  //表示对原始操作的调用
  Integer ret = (Integer) pjp.proceed();
  System.out.println("around after advice ...");
  return ret;
  }

 //@AfterReturning:返回后通知,在原始方法执行完毕后运行,且原始方法执行过程中没有抛异常现象才会执行,执行时机在@after之前

  //如果要操作原始方法的返回值。注解值要改成这样:(value = "pt()",returning = "ret"),然后下面方法写一个形参,形参名要和上面的去接returning 值一样 ,具体类型可以自己根据情况选择。当ProceedingJoinPoint或JoinPoint和我们的返回参数一起被定义的为形参的时候,JoinPoint必须在最前面,不然会报illegalArgumentException。
//    @AfterReturning("pt2()")
  public void afterReturning() {
  System.out.println("afterReturning advice ...");
  }

  //@AfterThrowing:抛出异常后通知,在原始方法执行过程中如果出现异常后就会运行

  //要获取异常对象的话,(value = "pt()",throwing = "t"),下面的参数改成Throwable t
  @AfterThrowing("pt2()")
  public void afterThrowing() {
  System.out.println("afterThrowing advice ...");
  }
}

连接点

这种实际的方法就是一个个的连接点:

public void update(){    

System.out.println("book dao update ...");

}

public void delete(){    

System.out.println("book dao delete ...");

}

public void select(){    

System.out.println("book dao select ...");

}

切入点

指定要具体操作连接点的就是切入点。

代码中指要进行增强的方法。

切入点表达式

切入点表达式标准格式:

动作关键字(访问修饰符  返回值  包名.类/接口名.方法名(参数)异常名)

动作关键字:描述切入点的行为动作,例如execution表示执行到指定切入点 访问

修饰符:public,private等,可以省略

异常名:方法定义中抛出指定异常,可以省略

下面两种都行:

execution(void com.xxz.dao.BookDao.update())

execution(void com.xxz.dao.impl.BookDaoImpl.update())

表达式使用通配符:

* :指代文件目录的一层,或一个元素,也可以用来匹配前后缀。

需要注意的是方法参数中要是写了*,那就是匹配的任意参数的,没参数不行

execution(public * com.xx.*.UserService.find*(*))

.. :多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写

execution(public User com..UserService.findById(..))

+:用来匹配子类类型

execution(* *..*Service+.*(..))

切入点通常描述接口,而不描述实现类,因为实现类更可能会随需求改变,耦合度更小。

返回值类型对于增删改类使用精准类型匹配,而查询方法的返回值往往有多种数据结构的,对于查询类使用*通配快速描述。

包名书写使用..匹配时,效率过低,尽量不使用,常用*做单个包描述匹配,或精准匹配。

接口名/类名书写名称与模块相关的采用*匹配,例如UserService书写成*Service,绑定业务层接口名。

方法名书写以动词进行精准匹配,具体名词可以采用*匹配,例如getById书写成getBy*,而selectAll可以直接书写成selectAll。

通常不使用异常作为匹配规则,异常有相应的处理手段。。

切面

就是将通知和切入点联系起来的部分。

案例 导包

 
org.springframework
spring-context
5.2.10.RELEASE
 

 
org.aspectj
aspectjweaver
1.9.4
 

配置类:

@EnableAspectJAutoProxy,开启注解开发AOP功能

@Configuration
@ComponentScan("com.xxz")
@EnableAspectJAutoProxy
public class SpringConfig {
}

要被代理的方法save和update:

@Repository
public class BookDaoImpl implements BookDao {

  public void save() {
  System.out.println("book dao save ...");
  }

  public void update(){
  System.out.println("book dao update ...");
  }
}

通知类:

//通知类必须配置成Spring管理的bean
@Component
//设置当前类为切面类
@Aspect
public class MyAdvice {
  //设置切入点,要求配置在方法上方,这个方法要求无参无返回值无逻辑
  @Pointcut("execution(void com.xxz.dao.BookDao.update())")
  private void pt(){}

  //设置在切入点pt()的前面运行当前操作(前置通知)
  @Before("pt()")
  public void method(){
  System.out.println(System.currentTimeMillis());
  }
}

启动类:

public class App {
  public static void main(String[] args) {
  ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
  BookDao bookDao = ctx.getBean(BookDao.class);
//        bookDao.update();
  System.out.println(bookDao);
  System.out.println(bookDao.getClass());//当方法名不对应目标时,比如void com.xxz.dao.BookDao.update()写成void com.xxz.dao.BookDao.updateD(),对象还是自身。而经过代理的由于重写了toString方法会打出一个proxy对象地址。
  }
}

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


名称栏目:spring系列SpringAOP-创新互联
文章URL:http://cdxtjz.cn/article/gocei.html

其他资讯