SpringAop

为什么使用AOP?

1:代码混乱:越来越多的非业务需求(比如日志和验证等)加入后,原有的业务方法急剧膨胀,每个方法在处理自己的业务逻辑的同时还要兼顾其它多个关注点。
2:代码分散:假如就单单的满足加入日志需求,就不得不在多个模块中重复相同的日志代码,如果日志需求发送了改变,还要去修改所有的模块。

解决方案:

现在有一个业务类:

package com.shiro.springbootshiro.aop;

/**
 * 作用:spring  aop 中的业务类
 */
public interface ArithmeticCalculator {

    int add(int i,int j);
}

实现这个业务类:

package com.shiro.springbootshiro.aop;

/**
 * 作用:实现这个业务类
 */
public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
    @Override
    public int add(int i, int j) {
        return i+j;
    }
}

给这个业务加上日志信息:使用动态代理

package com.shiro.springbootshiro.aop;

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

/**
 * 作用:给spring aop中业务添加日志需求,使用动态代理
 */
public class ArithmeticCalculatorLoggingProxy {
    //要代理的对象
    private ArithmeticCalculator target;

    public ArithmeticCalculatorLoggingProxy(ArithmeticCalculator target) {
        this.target = target;
    }
    public ArithmeticCalculator getLoggingProxy(){
        ArithmeticCalculator proxy = null;
        //代理对象由哪一个类加载器负责加载。
        ClassLoader loader = target.getClass().getClassLoader();
        //代理对象的类型,即其中有哪些方法。
        Class[] interfaces = new Class[]{ArithmeticCalculator.class};
        //当调用代理对象中的方法的时候,要先执行什么代码。
        InvocationHandler h = new InvocationHandler() {

            /**
             *
             * @param proxy : 正在返回的那个代理对象,一般情况下,在invoke都不使用该对象。
             * @param method : 正在调用的那个方法
             * @param args :调用方法时,传入的参数
             * @return
             * @throws Throwable
             */

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                String name = method.getName();
                //在执行方法前,先打印出方法的名称
                System.out.println("先打印出方法的名字:"+name+"开始:"+ Arrays.asList(args));
                //执行方法
                Object result = method.invoke(target, args);
                //执行方法后,打印日志,输出结果
                System.out.println("打印"+name+"后的结果:"+result);
                return result;
            }
        };
        proxy= (ArithmeticCalculator) Proxy.newProxyInstance(loader,interfaces,h);
        return proxy;
    }
}

测试:

package com.shiro.springbootshiro.aop;

/**
 * 作用:实现spring aop中的业务代码
 */
public class Main {
    public static void main(String[] args) {
        //目标方法
        ArithmeticCalculator target = new ArithmeticCalculatorImpl();
        //代理类
        ArithmeticCalculator proxy= new ArithmeticCalculatorLoggingProxy(target).getLoggingProxy();
        int i = proxy.add(3, 4);
        System.out.println(i);
    }
}

输出:

使用AOP

aop是面向切面编程。切面模块化横切关注点就是要添加的日志功能。

好处:每个业务的逻辑不用修改。便于维护和升级。业务模块更加的简洁。

验证是一个切面,日志是一个切面。

概念:

切面(Aspect):横切关注点。就是要添加的功能验证,日志。

通知(Advice):切面必须要完成的工作。

目标(Target):被通知的对象,就是原有的业务。

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

连接点(Joinpoint):程序执行的某个特点位置。比较类的某个方法执行前,或者执行后。比如上面的add方法,就是连接点。

切点(pointcut):每个类拥有多个连接点。AOP通过特点的切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点就是查询条件。一个切点可以匹配多个连接点。使用的是类和方法作为连接点的查询条件。

在springboot中使用AOP

1:加入依赖:

<!--使用spring的AOP-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

2:写切面类

package com.example.aop.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * 作用: 测试spring 的AOP
 */
@Aspect     //说明这是一个切面类
@Component  // 添加到IOC容器中
public class UserServiceAspect {

    private static final Logger log = LoggerFactory.getLogger(UserServiceAspect.class);

    /*@PointCut注解表示表示横切点,哪些方法需要被横切*/
    /*切点表达式*/
    @Pointcut("execution(public * com.example.aop.service.*.*(..))")
    /*切点签名*/
    public void print() {
    }

    @Before("print()")
    public void before(JoinPoint joinPoint){
        log.info("前置切面before……");
        Object[] args = joinPoint.getArgs();
        String methodName = joinPoint.getSignature().getName();
        System.out.println("参数:"+args);
        System.out.println("方法名:"+methodName);
    }
}

 

posted @ 2019-04-13 14:14  陆伟  阅读(113)  评论(0编辑  收藏  举报