Spring_Aop

Posted 言西早石头侠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring_Aop相关的知识,希望对你有一定的参考价值。

为什么需要AOP?

  需求加减乘除

package com.tanlei.spring.bean.Aop;

public interface AtithmeticCalculator {
    int add(int i,int j);
    int sub(int i,int j);
    int mul(int i,int j);
    int div(int i,int j);
}

 

package com.tanlei.spring.bean.Aop;

public class AtithmeticCalculatorImpl implements AtithmeticCalculator{

    @Override
    public int add(int i, int j) {
        System.out.println("the method add begin whith ["+i+","+j+"]");
        int result=i+j;
        System.out.println("the method add end whith"+result);
        return result;
    }

    @Override
    public int sub(int i, int j) {
        System.out.println("the method sub begin whith ["+i+","+j+"]");
        int result=i-j;
        System.out.println("the method sub end whith"+result);
        return result;
    }

    @Override
    public int mul(int i, int j) {
        System.out.println("the method mul begin whith ["+i+","+j+"]");
        int result=i*j;
        System.out.println("the method mul end whith"+result);
        return result;
        
    }

    @Override
    public int div(int i, int j) {
        System.out.println("the method div begin whith ["+i+","+j+"]");
        int result=i/j;
        System.out.println("the method div end whith"+result);
        return result;
    
    }

}
View Code

 

package com.tanlei.spring.bean.Aop;

public class Main {
   public static void main(String[] args) {
    AtithmeticCalculatorImpl  aImpl=new AtithmeticCalculatorImpl();
    aImpl.add(4, 8);
    aImpl.sub(4, 8);
    aImpl.mul(4, 8);
    aImpl.div(4, 8);
}
}
View Code

 

1.执行方法前后的日志

   代码混乱

 代码分散

使用动态代理解决上述问题

 复杂动态代理代码(不推荐使用)

package com.tanlei.spring.bean.Aop;

public interface AtithmeticCalculator {
    int add(int i,int j);
    int sub(int i,int j);
    int mul(int i,int j);
    int div(int i,int j);
}

 

package com.tanlei.spring.bean.Aop;

public class AtithmeticCalculatorImpl implements AtithmeticCalculator{

    @Override
    public int add(int i, int j) {
        int result=i+j;
        return result;
    }

    @Override
    public int sub(int i, int j) {
        int result=i-j;
        return result;
    }

    @Override
    public int mul(int i, int j) {
        int result=i*j;
        return result;
        
    }

    @Override
    public int div(int i, int j) {
        int result=i/j;
        return result;
    
    }

}

 

package com.tanlei.spring.bean.Aop;

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

public class AtithmeticCalculatorLoggingProxy {

    // 要代理的对象
    private AtithmeticCalculator target;

    public AtithmeticCalculatorLoggingProxy(AtithmeticCalculator target) {
        this.target = target;
    }

    
    public AtithmeticCalculator getLoggingProxy() {
        AtithmeticCalculator proxy = null;

        // 代理对象由哪一个类加载器负责
        ClassLoader loader = target.getClass().getClassLoader();
        // 代理对象的类型,即其中有哪些方法
        Class[] interfaces = new Class[] { AtithmeticCalculator.class };

        // 当调用代理对象其中的方法时,该执行的代码
        InvocationHandler h = new InvocationHandler() {
            /**
             * proxy:正在返回的那个代理对象,一般情况下,在invoke方法中都不使用该对象
             * method:正在被调用的方法
             * args:调用方法时传人的参数
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                String methodName=method.getName();
                //日志
                System.out.println("The method: "+methodName+"begins with"+Arrays.asList(args));
                //执行方法
                Object result=method.invoke(target, args);
                
                //日志
                System.out.println("The method: "+methodName+"ends with"+result);
                return result;
            }
        };
        proxy = (AtithmeticCalculator) Proxy.newProxyInstance(loader, interfaces, h);
        return proxy;
    }
}

 

package com.tanlei.spring.bean.Aop;

public class Main {
   public static void main(String[] args) {
    /*AtithmeticCalculatorLoggingImpl  aImpl=new AtithmeticCalculatorLoggingImpl();
    aImpl.add(4, 8);
    aImpl.sub(4, 8);
    aImpl.mul(4, 8);
    aImpl.div(4, 8);*/
       AtithmeticCalculator target=new AtithmeticCalculatorImpl();
       AtithmeticCalculator proxy=new AtithmeticCalculatorLoggingProxy(target).getLoggingProxy();
       int result=proxy.add(1, 2);
       System.out.println(result);
}
}

 

简单的方法实现动态代理(AOP)

AOP基本概念

   面向切面编程

在应用AOP编程时,仍然需要定义公共功能,但可以明确的定义这个功能在哪里,以什么方式应用,并且不必修改受影响的类,这样一来横切关注点就被模块化到特殊的对象(切面)里;

Aop的好处

  每个事物处理逻辑位于一个位置,代码不分散,便于维护和升级

  业务模块更简洁,只包含核心业务代码

 

Spring AOP+AspectJ注解

AspectJ:java社区里最完整最流行的AOP框架

  在Spring2.0版本上,可以使用基于AspectJ注解基于XML配置AOP

在Spring中启用AspectJ注解支持

1.导入jar包:

  aopalliance-.jar

  aspectj.weaver.jar

  spring-aspects.jar

2.要在Spring Ioc容器中启用AspectJ注解支持,只要在bean配置文件中定义一个空的XML元素<aop:aspectj-autoproxy>

3.当Spring IOC容器侦测到Bean配置文件中的<aop:aspectj-autoproxy>元素时,会自动为AspectJ切面匹配的Bean创建代理

常见AspectJ的注解:
  1. @Before – 方法执行前运行
  2. @After – 运行在方法返回结果后
  3. @AfterReturning – 运行在方法返回一个结果后,在拦截器返回结果。
  4. @AfterThrowing – 运行方法在抛出异常后,
  5. @Around – 围绕方法执行运行,结合以上这三个通知。 

小结: 

1.Spring AOP

1).加入jar包
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar

commons-logging-1.1.1.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar

2).配置文件中加入Aop的命名空间

3)基于注解的方式
1.在配置文件中加入如下配置
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

2.把横切关注点的代码抽象到切面的类中
    切面首先是一个IOC的bean,即加入@Componet注解
    切面还需要加入@Aspect 注解

3.类中声明各种通知
@Before – 方法执行前运行
@After – 运行在方法返回结果后
@AfterReturning – 运行在方法返回一个结果后,在拦截器返回结果。
@AfterThrowing – 运行方法在抛出异常后,
@Around – 围绕方法执行运行,结合以上这三个通知。
1.声明一个方法  
2.在方法前加入@Before
3.在通知的方法里面可以加入连接点JoinPoint  访问方法的参数  如方法的签名和参数

 

Spring AOP 通知

package com.tanlei.spring.bean.AspectJ;

import java.util.Arrays;
import java.util.List;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

//把这个类声明一个切面:需要把该类放入到IOC容器中,再声明为一个切面
@Aspect
@Component
public class LoggingAspectj {

    // 声明该方法是一个前置通知,在目标方法开始之前执行

    @Before("execution(public int com.tanlei.spring.bean.AspectJ.AtithmeticCalculator.add(int, int))")
    // JoinPoint 连接点
    public void beforeMethod(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        List<Object> lists = Arrays.asList(joinPoint.getArgs());
        System.out.println("The method" + methodName + " begins with " + lists);
    }

    // 声明该方法是一个 后置通知,在目标结束之后执行,无论是否发送异常
    // 在后置通知中还不能访问目标方法执行的结果
    @After("execution(*  com.tanlei.spring.bean.AspectJ.AtithmeticCalculator.*(int ,int))")
    public void afterMethod(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        List<Object> lists = Arrays.asList(joinPoint.getArgs());
        System.out.println("The method" + methodName + " ends with" + lists);

    }

    // 返回通知 在方法正常结束执行的通知 是可以访问到方法的返回值得
    @AfterReturning(pointcut = "execution(public int com.tanlei.spring.bean.AspectJ.AtithmeticCalculator.add(int ,int))", returning = "result")
    public void afterReturningMethod(JoinPoint joinPoint, Object result) {
        System.out.println("hijacked : " + joinPoint.getSignature().getName());
        System.out.println("Method returned value is : " + result);

    }

    // 异常通知 在方法出现异常时会执行的代码 而且可以访问到异常对象,且可以指定出现特点异常时出现
    @AfterThrowing(pointcut = "execution(* com.tanlei.spring.bean.AspectJ.AtithmeticCalculator.*(int ,int))", throwing = "error")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable error) {
        System.out.println("hijacked : " + joinPoint.getSignature().getName());
        System.out.println("Exception : " + error);

    }

    // 环绕通知 围绕着方法执行
    // 需要携带ProceedingJoinPoint类型的参数
    // 环绕通知类似于动态代理的全过程 ProceedingJoinPoint类型的参数可以决定是否执行目标方法
    // 且环绕通知必须有返回值,返回值为目标方法的返回值
    @Around("execution(* com.tanlei.spring.bean.AspectJ.AtithmeticCalculator.*(int ,int))")
    public void logAround(ProceedingJoinPoint joinPoint) throws Throwable {

        System.out.println("hijacked method : " + joinPoint.getSignature().getName());
        System.out.println("hijacked arguments : " + Arrays.toString(joinPoint.getArgs()));

        System.out.println("Around before is running!");
        joinPoint.proceed(); // 执行目标方法
        System.out.println("Around after is running!");

    }

}

 

注意点:

对于Spring AOP 采用两种代理方法,一种是常规JDK,一种是CGLIB,我的UserDaoImpl实现了一个接口IUserDao,当代理对象实现了至少一个接口时,默认使用JDK动态创建代理对象,当代理对象没有实现任何接口时,就会使用CGLIB方法。由于UserDAOImpl实现了UserDAO接口,所以强制转换必须用父类UserDAO来定义

如果你的代理对象没有实现接口的方法,就将代理对象转换成接口。 
  获取代理类的代码该为: 

ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDaoImpl)ctx.getBean("userDaoImpl");

 

以上是关于Spring_Aop的主要内容,如果未能解决你的问题,请参考以下文章

Spring_AOP 记录系统关键操作日志用法

Spring_AOP的实现机制-动态代理

Spring_Aop的xml和注解的使用

spring_aop注解

Spring_Aop_

Spring_Aop