栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring AOP

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring AOP

文章目录
  • 一、AOP简介
    • 1、什么是AOP
    • 2、主要功能
    • 3、主要意图
    • 4、为什么使用它?
    • 5、AOP术语
  • 二、AOP的使用
    • 1、原生API接口
    • 2、自定义类
    • 3、注解
  • 三、遇到的问题
    • 1、配置切面
    • 2、配置切入点
    • 3、配置通知
    • 4、注解
    • 5、JointPoint类中的方法

一、AOP简介 1、什么是AOP

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。

AOP是OOP的延续,是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。

利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

2、主要功能

日志记录,性能统计,安全控制,事务处理,异常处理等等。

3、主要意图

将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。

4、为什么使用它?

在传统的业务代码中,通常都会进行事务处理等等操作,虽然使用OOP(面向对象编程)可以通过组合或者继承的方式来达到代码的重用,但是如果要实现某个功能(如日志记录),相同的代码仍然会分散到各个方法中。这样,如果想要关闭某个功能,或者要对其进行修改,就必须修改所有相关方法。
这样不但增加了工作量,而且提高了代码的出错率。

AOP采取横向抽取机制,将分散在各个方法中的重复代码提取出来,然后在程序编译或运行的时候再将这些提取的代码应用到需要执行的地方。

说的再简单一点,就是专心写自己的业务代码,当需要其他业务逻辑(类)的时候,直接引入进去就可以,不用担心它们直接存在的联系。

这不但提高了开发效率,而且增强了代码的可维护性。

5、AOP术语

二、AOP的使用

提前导入jar包

		
        
            org.aspectj
            aspectjweaver
            1.9.6
            runtime
        
        
        
            org.aspectj
            aspectjrt
            1.9.7
        
//Service接口
public interface UserService {

    public void add();

    public void delete();

    public void update();

    public void select();
    
}
//接口实现类
public class UserServiceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("增加了一个用户");
    }

    @Override
    public void delete() {
        System.out.println("删除了一个用户");
    }

    @Override
    public void update() {
        System.out.println("更新了一个用户");
    }

    @Override
    public void select() {
        System.out.println("查询了一个用户");
    }
}
public class AfterLog implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了" + method.getName() + "方法,返回结果为:" + returnValue);
    }
}
public class Log implements MethodBeforeAdvice {

    @Override
    
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行");
    }
}
1、原生API接口



	
    
    
    

    
    
    
        
        
        
        
    
   

测试:

//方法一的测试
	@Test
    public void test1() {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

        //动态代理代理的是接口
        UserService userService = (UserService) context.getBean("userService");

        userService.add();

    }

结果:

2、自定义类
public class DiyPointCut {
    public void before(){
        System.out.println("方法执行前");
    }

    public void after(){
        System.out.println("方法执行后");
    }
}
    
        
        
            
            
            
            
            
        
    
   

测试:

//方法一的测试
	@Test
    public void test2() {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

        //动态代理代理的是接口
        UserService userService = (UserService) context.getBean("userService");

        userService.add();

    }

结果:

3、注解
@Aspect
@Component  //可以使整个类不用注册Bean,但是需要开启扫描包
public class AnnotationPointCut {

    @Before("execution(* com.study.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("方法执行前");
    }

    @After("execution(* com.study.service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("方法执行后");
    }

    //在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点
    @Around("execution(* com.study.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("环绕前");

        jp.getSignature();

        Object proceed = jp.proceed();

        System.out.println("环绕后");
    }
}
	
    
    
    
    

测试:

	//方法三的测试
    @Test
    public void test1(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

        //动态代理代理的是接口
        UserService userService = (UserService) context.getBean("userService");

        userService.select();
    }

结果:

三、遇到的问题 1、配置切面

在Spring的配置文件中,配置切面使用的是< aop:aspect>元素,该元素会将一个已定义好的Spring Bean转换成切面Bean,所以要在配置文件中先定义一个普通的Spring Bean。定义完成后,通过< aop:aspect>元素的ref属性即可引用该Bean。

< aop:aspect>的两个属性:

  • id:用于定义切面的唯一标识名称
  • ref:用于引用普通的Spring Bean
2、配置切入点

在Spring的配置文件中,配置切入点是通过< aop:pointcut>元素来定义的。
当< aop:pointcut>元素作为< aop:config>元素的子元素定义时,表示该切入点是全局切入点,可以被多个切面共享;
当< aop:pointcut>元素作为< aop:aspect>元素的子元素时,表示该切入点只对当前切面有效。

< aop:pointcut>的两个属性:

  • id:用于定义切入点的唯一标识名称
  • expression:用于指定切入点关联的切入点表达式

对expression的拓展
< aop:pointcut expression=“execution(* com.study. * . * (…))” id=“pointcut”>

execution后面传入的值 就是定义的切入点表达式,该切入点表达式的意思是匹配com.study包下面的任意类方法执行。

其中execution是表达式的主体,第1个 * 表示返回返回类型,使用*代表所有类型
com.study表示的是需要拦截的包名,
后面第二个 * 表示的是类名,使用 * 表示所有的类;
第三个 * 表示的是方法名,使用 * 表示所有方法;
后面的()表示方法的参数,其中的"…"表示任意参数,第一个 * 与包名之间有一个空格

3、配置通知
  • pointcut:用于指定一个切入点表达式
  • pointcut-ref:指定一个已经存在的切入点名称
  • method:指定一个方法名
  • throwing:只对< after-throwing>元素有效,用于抛出异常
  • returning:只对< after-throwing>元素有效,用于访问目标方法的返回值
4、注解
  • @Aspect:定义切面
  • @Pointcut:定义切入点表达式
  • @Before:定义前置通知
  • @AfterReturning:定义后置通知
  • @Around:定义环绕通知
  • @AfterThrowing:定义异常通知来处理程序中未处理的异常
  • @After:定义最终final通知
  • @DeclareParents:定义引介通知
5、JointPoint类中的方法
  • joinpoint.getargs():获取带参方法的参数

  • joinpoint.getTarget():获取他们的目标对象信息

  • joinpoint.getSignature():获取被增强的方法相关信息
    * getSignature()); 修饰符 + 包名 + 组件名(类名) + 方法的名字
    * getSignature().getName()); 方法名
    * getSignature().getDeclaringTypeName()); 包名 + 组件名(类名)

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/685256.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号