Spring——AOP

Spring——AOP

AOP:

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方
式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个
热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑
的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高
了开发的效率。

 

首先我们先创建一个简单的例子,使用多种方式实现下面功能,最后用AOP的方式再实现它:

例子:编写一个加法器,要使在调用前和调用后打印相关信息:

   常规方法

   CalculatorImpl:

package com.chenx.spring.beans;

public class CalculatorImpl implements Calculator{

    @Override
    public int add(int a, int b) {
        System.out.println("begin");
        int result=a+b;
        System.out.println("end");
        return result;
    }
}

 Main:

package com.chenx.spring.beans;

public class Main {
    public static void main(String[] args) {
        Calculator calculator=new CalculatorImpl();
        calculator.add(1,2);
    }
}

从以上代码可以想象,如果有加减乘除等多个方法,修改和编写的量会大大加深,因此将使用动态代理将对象包装起来,使用该代理对象取代原先的对象,从而方便。

 

动态代理:

创建一个代理类,传入Calculator,接着为其代理: 

   LoggingProxy:

package com.chenx.spring.beans;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class LoggingProxy {

    private Calculator target;

    public LoggingProxy(Calculator target) {
        this.target = target;
    }

    public Calculator getLoggingProxy(){

        Calculator proxy=null;
        //指明代理对象由哪个类加载器加载
        ClassLoader loader=target.getClass().getClassLoader();
        //代理对象类型
        Class [] interfaces=new Class[]{Calculator.class};
        //当调用代理对象其中方法时候,执行的方法,在此编写
        InvocationHandler h=new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //proxy:返回的对象
                //method:正在调用的方法
                //args:调用方法时,传入的参数
                System.out.println("begin");
                Object result=method.invoke(target,args);
                System.out.println("end");
                
                return result;
            }
        };
        proxy= (Calculator) Proxy.newProxyInstance(loader,interfaces,h);

        return proxy;
    }


}

Main:

    package com.chenx.spring.beans;

    public class Main {
        public static void main(String[] args) {

            Calculator calculator=new CalculatorImpl();
            Calculator proxy=(Calculator) new LoggingProxy(calculator).getLoggingProxy();
            proxy.add(1,2);
        }
    }

Spring AOP

 

AOP是面向切面编程,OOP是面向对象编程

Aspect(切面):横切关注点被模块化的对象

Advice(通知):切面必须完成的工作(切面中的每一个方法)

Target(目标 ):被通知的对象

Proxy(代理) :向目标对象应用通知之后创建的对象

Joinpoint(连接点):程序执行的某个特殊的位置

pointcut (切点) :连接点是Calculator中的add方法,通过切点定位到特定的连接点

 

Advice(通知)类型:

1.@Before 前置通知:在目标方法前执行

2.@After 后置通知:在目标方法执行后执行(无论是否报错)

3.@AfterReturning 返回通知:在目标方法正常执行后执行

4.@AfterThrowing 异常通知:在目标方法执行后出现异常后执行

5.@Around 环绕通知:围绕着目标方法执行

 

 

接下来在之前的Add基础上使用AOP的方式,我们使用 aspectj ,加入相关的包:

创建日志的切面文件:

1.@Component 使其加入IOC容器

2.@Aspect 声明为一个切面

3.@Before 声明为前置通知 并在后说明在什么方法前调用  

1.@Before 前置通知

package com.chenx.spring.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {
    @Before("execution(* com.chenx.spring.aop.Calculator.add(int,int))")
    public void beforeMethod(JoinPoint joinPoint){

        System.out.println("before");
    }


}

   还需在配置文件中配置:

<context:component-scan base-package="com.chenx.spring.aop"></context:component-scan>

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

   上方只是简单的输出,我们可以通过JoinPoint 做一些操作:  

方法名功能
Signature getSignature();获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息
Object[] getArgs();获取传入目标方法的参数对象
Object getTarget();获取被代理的对象
Object getThis();获取代理对象

测试如下:

@Aspect
@Component
public class LoggingAspect {
    @Before("execution(public int com.chenx.spring.aop.Calculator.add(int,int))")
    public void beforeMethod(JoinPoint joinPoint){
        //获取目标方法名
        String name=joinPoint.getSignature().getName();
        System.out.println(name);
        //获取目标方法传入的参数
        List<Object> args= Arrays.asList(joinPoint.getArgs());
        System.out.println(args);
        //获取被代理的对象(类型是一个类)
        System.out.println(joinPoint.getTarget().getClass());
        //获取代理对象本身(类型是一个代理)
        System.out.println(joinPoint.getThis().getClass());
        
        System.out.println("before");
    }
}

 

    如果切点需要匹配多个连接点,则可以使用占位符:(这样就会匹配所有参数为二个的连接点)

@Before("execution(* com.chenx.spring.aop.Calculator.*(*,*))")

 

2.@After 后置通知

   @After("execution(public int com.chenx.spring.aop.Calculator.add(int,int))")
    public void afterMethod(){
        System.out.println("end");
    }

3.@AfterReturning 返回通知

    @AfterReturning("execution(public int com.chenx.spring.aop.Calculator.add(int,int))")
    public void afterReturningMethod(){
        System.out.println("end no error");
    }

4.@AfterThrowing 异常通知

     可以加入throwing来访问异常,在方法中定义异常的类型,只有符合此类型的异常才可以执行


    @AfterThrowing(value = "execution(public int com.chenx.spring.aop.Calculator.add(int,int))",throwing = "ex")
    public void afterThrowingMethod(Exception ex){
        System.out.println("end with error");
    }

5.@Around 环绕通知 

这是功能最强大的通知,其类似于我们之前写的Proxy动态代理,用此就可以模拟所有的通知:

 @Around("execution(public int com.chenx.spring.aop.Calculator.add(int,int))")
    public Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint){
        Object result=null;

        try{
            System.out.println("begin");
            result=proceedingJoinPoint.proceed();
            System.out.println("end no error");
        }catch (Throwable e){
            System.out.println("end with error");
            throw new RuntimeException(e);
        }

        System.out.println("end");

        return result;
    }

 

6.@Pointcut 

从上面那么多代码可以看到,我们每次使用切点的时候都是使用的同一个,但是写了很多遍,这样导致编写代码的不便,现在我们使用重用代码:

    @Pointcut("execution(public int com.chenx.spring.aop.Calculator.add(int,int))")
    public void declareJoinPointExpression(){}

    @After("declareJoinPointExpression()")
    public void afterMethod(){
        System.out.println("end");
    }

 在别的切面中可以这样调用:

    @After("LoggingAspect.declareJoinPointExpression()")
    public void afterMethod(){
        System.out.println("end");
    }

 

  切面的优先级

   如果有多个切面,就会有哪个切面先后的问题,可以使用order进行优先级的设定:

   参数越小,优先级越高

@Order(1)
@Aspect
@Component
public class LoggingAspect {

2.使用XML配置文件的方式配置SpringAOP

    

    <bean id="calculator" class="com.chenx.spring.aop.CalculatorImpl"></bean>

    <bean id="loggingAspect" class="com.chenx.spring.aop.LoggingAspect"></bean>

<!--    配置Aop-->
    <aop:config>
        
        <aop:pointcut id="addcut" expression="execution(public int com.chenx.spring.aop.Calculator.add(int,int ))"/>
        <aop:aspect ref="loggingAspect">
            <aop:before method="beforeMethod" pointcut-ref="addcut"></aop:before>
            <aop:after method="afterMethod" pointcut-ref="addcut"></aop:after>
            <aop:after-returning method="afterReturningMethod" pointcut-ref="addcut"></aop:after-returning>
            <aop:after-throwing throwing="ex" method="afterThrowingMethod" pointcut-ref="addcut"></aop:after-throwing>
        </aop:aspect>

    </aop:config>

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值