博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
spring--AOP
阅读量:3713 次
发布时间:2019-05-21

本文共 5030 字,大约阅读时间需要 16 分钟。

作用

基于开闭原则,在不改变原有系统核心业务代码的基础上,动态增加一些扩张功能

应用场景

日志处理:例如之前的智能柜系统,在登录接口或开箱接口调用时需要打印并存储请求信息,例如ip等

事务处理:spring的事务处理就是基于aop开发的

权限处理:某些接口在执行前需要进行token权限校验,可以使用拦截器实现权限校验,也可以使用aop实现

底层实现:

JDK动态代理模式

如果目标对象(被代理对象)有实现接口,则底层默认采用“JDK动态代理机制”为目标对象创建“代理对象”  

Subject.java

//类似  目标接口abstract class Subject {    public abstract void Request();}

RealSubject .java

//类似  目标对象public class RealSubject extends Subject {    @Override    public void Request() {        System.out.println("七个葫芦娃");    }}

Proxy .java

//类似 代理对象 -- 保存一个代理对象实体,使代理对象可以调用目标对象;//在执行目标对象之前或之后执行其他逻辑,这样就实现了AOPpublic class Proxy extends Subject {    RealSubject realSubject;    @Override    public void Request() {        if(realSubject == null){            realSubject = new RealSubject();        }        //执行扩展逻辑        realSubject.Request();    }}

测试

public class TestMain {    public static void main(String[] args) {        Proxy proxy = new Proxy();        proxy.Request();    }}

CGLIB代理模式:

如果目标对象没有实现接口,则底层采用CGLIB代理机制为目标对象创建代理对象(默认创建的代理类$B会继承目标对象类型---A$$B)

CGLIB设计模式:

原理:主要是对指定的类生成一个子类,覆盖其中的方法

目标对象UserDao.java

public class UserDao {    public void save(){        System.out.println("数据已经保存");    }    public void update(){        System.out.println("数据已经更新");    }}

cglib子类代理工厂  CglibProxyFactory.java

/** * cglib子类代理工厂 对UserDao在内存中动态构建一个子类对象 * * @author wangchaoyouziying * */public class CglibProxyFactory implements MethodInterceptor {    private Object target;// 被代理对象    public CglibProxyFactory(Object target) {        this.target = target;    }    /**     * 创建代理对象     *     * @return     */    public Object getProxyInstance() {        Enhancer enhancer = new Enhancer();        // 设置父类        enhancer.setSuperclass(target.getClass());        // 设置回调函数        enhancer.setCallback(this);        // 创建子类(代理对象)        return enhancer.create();    }    /**     * obj    代理对象     * method  被代理对象的方法     * args       被代理对象的参数     */    @Override    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {        if ("save".equals(method.getName())) {//这里只改造被代理对象的save方法            System.out.println("开始事务");            // 执行被代理对象的方法            Object invoke = method.invoke(target, args);            System.out.println("提交事务");            return invoke;        }else{//其余方法按原样执行            return method.invoke(target, args);        }    }}

测试  

UserDao proxy

= (UserDao) new CglibProxyFactory(userDao).    //创建cglib代理工厂对象

getProxyInstance();  //创建代理对象 --- 目标对象的子类

proxy.save();   // 调用代理对象的方法,其重写目标对象的方法(在interrcept)

public class Test {    public static void main(String[] args) {        //被代理对象        UserDao userDao = new UserDao();        //class cn.chao.cglibProxy.UserDao        System.out.println(userDao.getClass());        //代理对象        UserDao proxy = (UserDao) new CglibProxyFactory(userDao).getProxyInstance();        //UserDao的子类   class cn.chao.cglibProxy.UserDao$$EnhancerByCGLIB$$e6c21b2c        System.out.println(proxy.getClass());        //执行代理对象的方法        proxy.save();        proxy.update();    }}

JDK动态代理实践:

目标接口HelloService.java

public interface HelloService {    void sayHello(String msg);}

目标类HelloServiceImpl.java

public class HelloServiceImpl implements HelloService{   public void sayHello(String msg) {      System.out.println(msg);   };}

代理类LoggingAspect.java   -- 切面对象

/**切面对象:封装扩展功能,本类对象封装日志处理功能*/public class LoggingAspect {      /*** 通知:(可以将其理解为前置通知),切入点的方法执行之前,执行此方法(功能)*/      public void beforeMethod(){         System.out.println("method start");      }      /** * 通知:(可以将其理解为最终通知),切入点的方法执行之后,执行此方法(功能) */      public void afterMethod(){         System.out.println("method end");      }}

配置类

术语:

切入点: within(com.beans.impl.*)   指定对那些目标对象进行扩展功能 ,即目标对象的集合

切面:   logAspect   即代理对象

连接点: 程序执行过程中某个特定的点,一般指拦截的方法 ,例如sayHello

通知:在切面的某个动作,例如beforeMethod

基于注解实现:

@Aspect  指定为切面对象,即代理对象

@Order(1)

@Before("bean(orderServiceImpl)")      通知

切面表达式:

bean         用于匹配指定bean id的的方法执行;类级别

within       用于匹配指定包名下类型内的方法执行;类级别     within(“aop.service.*”)

execution          用于进行细粒度方法匹配执行具体业务;方法级别

切面通知:

前置通知@Before:

后置通知@AfterReturning:   ---   类似return

异常通知@AfterThrow:  --- 类似 catch(){throw ... }

最终通知@After :   -----      类似 finally()

环绕通知@Around : --- 绕通知围绕在连接点前后,比如一个方法调用的前后

环绕通知的使用:

//@Component//Aspect注解声明 Audience是一个切面@Aspectpublic class Audience {     //@Pointcut注解声明切点    @Pointcut("execution(* com.example.aroundaspectdemo.concert.Performance.perform(..))")    public void performance() {    }     //@Around注解声明环绕通知的方法    @Around("performance()")    public void watchPerformance(ProceedingJoinPoint jointPoint) {        try {            System.out.println("Silencing cell phones");            System.out.println("Task seats");//jointPoint.proceed()方法时调用Performance接口的实现类中的perform()方法//如果不写这一句,就不会调用,如果写多次就会调用多次              jointPoint.proceed();//            jointPoint.proceed();            System.out.println("CLAP CLAP CLAP");        } catch (Throwable e) {            System.out.println("Demanding a refund");        }    }}

输出:

转载地址:http://bjijn.baihongyu.com/

你可能感兴趣的文章
Redis
查看>>
ElasticSearch
查看>>
HBase原理
查看>>
Spring面试题
查看>>
Kafka详解
查看>>
Scala编程及基础
查看>>
计算机操作系统第三章处理机调度与死锁习题及答案
查看>>
SparkCore 第二部分
查看>>
Scala求平均值
查看>>
Spark SQL函数API
查看>>
Exception in thread "main" java.lang.AbstractMethodError
查看>>
SparkStreaming 第一部分
查看>>
每日任务
查看>>
HashSet如何保障元素不重复?
查看>>
ArrayList为什么是线程不安全的?
查看>>
2019大数据面试题
查看>>
hadoop作业调度策略
查看>>
Spark基础
查看>>
Spark集群三种部署模式的区别
查看>>
SparkCore 第一部分
查看>>