zoukankan      html  css  js  c++  java
  • spring aop

    AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子。
    一 AOP的基本概念
    (1)Aspect(切面):通常是一个类,里面可以定义切入点和通知
    (2)JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用
    (3)Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around
    (4)Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式
    (5)AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类
    二 Spring AOP
    Spring中的AOP代理还是离不开Spring的IOC容器,代理的生成,管理及其依赖关系都是由IOC容器负责,Spring默认使用JDK动态代理,在需要代理类而不是代理接口的时候,Spring会自动切换为使用CGLIB代理,不过现在的项目都是面向接口编程,所以JDK动态代理相对来说用的还是多一些。
    基于注解的AOP配置方式
    1.启用@AsjectJ支持
    在applicationContext.xml中配置下面一句:
    <aop:aspectj-autoproxy />
    2.通知类型介绍
    (1)Before:在目标方法被调用之前做增强处理,@Before只需要指定切入点表达式即可
    (2)AfterReturning:在目标方法正常完成后做增强,@AfterReturning除了指定切入点表达式后,还可以指定一个返回值形参名returning,代表目标方法的返回值
    (3)AfterThrowing:主要用来处理程序中未处理的异常,@AfterThrowing除了指定切入点表达式后,还可以指定一个throwing的返回值形参名,可以通过该形参名
    来访问目标方法中所抛出的异常对象
    (4)After:在目标方法完成之后做增强,无论目标方法时候成功完成。@After可以指定一个切入点表达式
    (5)Around:环绕通知,在目标方法完成前后做增强处理,环绕通知是最重要的通知类型,像事务,日志等都是环绕通知,注意编程中核心是一个ProceedingJoinPoint
    3.例子:

    (1)Operator.java --> 切面类

    @Component

    @Aspect

    public class Operator {

    @Pointcut("execution(* com.aijava.springcode.service..*.*(..))")

    public void pointCut(){}

    @Before("pointCut()")

    public void doBefore(JoinPoint joinPoint){

    System.out.println("AOP Before Advice...");

    }

    @After("pointCut()")

    public void doAfter(JoinPoint joinPoint){

    System.out.println("AOP After Advice...");

    }

    @AfterReturning(pointcut="pointCut()",returning="returnVal")

    public void afterReturn(JoinPoint joinPoint,Object returnVal){

    System.out.println("AOP AfterReturning Advice:" + returnVal);

    }

    @AfterThrowing(pointcut="pointCut()",throwing="error")

    public void afterThrowing(JoinPoint joinPoint,Throwable error){

    System.out.println("AOP AfterThrowing Advice..." + error);

    System.out.println("AfterThrowing...");

    }

    @Around("pointCut()")

    public void around(ProceedingJoinPoint pjp){

    System.out.println("AOP Aronud before...");

    try {

    pjp.proceed();

    } catch (Throwable e) {

    e.printStackTrace();

    }

    System.out.println("AOP Aronud after...");

    }


    }
    }

    (2)UserService.java --> 定义一些目标方法

    @Service

    public class UserService {

    public void add(){

    System.out.println("UserService add()");

    }

    public boolean delete(){

    System.out.println("UserService delete()");

    return true;

    }

    public void edit(){

    System.out.println("UserService edit()");

    int i = 5/0;

    }


    }
    }

    (3).applicationContext.xml
    <context:component-scan base-package="com.aijava.springcode"/>

    <aop:aspectj-autoproxy />
    (4).Test.java

    public class Test {

    public static void main(String[] args) {

    ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");

    UserService userService = (UserService) ctx.getBean("userService");

    userService.add();

    }
    }
    }

    上面是一个比较简单的测试,基本涵盖了各种增强定义。注意:做环绕通知的时候,调用ProceedingJoinPoint的proceed()方法才会执行目标方法。
    4.通知执行的优先级
    进入目标方法时,先织入Around,再织入Before,退出目标方法时,先织入Around,再织入AfterReturning,最后才织入After。
    注意:Spring AOP的环绕通知会影响到AfterThrowing通知的运行,不要同时使用!同时使用也没啥意义。
    5.切入点的定义和表达式
    切入点表达式的定义算是整个AOP中的核心,有一套自己的规范
    Spring AOP支持的切入点指示符:
    (1)execution:用来匹配执行方法的连接点
    A:@Pointcut("execution(* com.aijava.springcode.service..*.*(..))")
    第一个*表示匹配任意的方法返回值,..(两个点)表示零个或多个,上面的第一个..表示service包及其子包,第二个*表示所有类,第三个*表示所有方法,第二个..表示
    方法的任意参数个数
    B:@Pointcut("within(com.aijava.springcode.service.*)")
    within限定匹配方法的连接点,上面的就是表示匹配service包下的任意连接点
    C:@Pointcut("this(com.aijava.springcode.service.UserService)")
    this用来限定AOP代理必须是指定类型的实例,如上,指定了一个特定的实例,就是UserService
    D:@Pointcut("bean(userService)")
    bean也是非常常用的,bean可以指定IOC容器中的bean的名称
    6.基于XML形式的配置方式
    开发中如果选用XML配置方式,通常就是POJO+XML来开发AOP,大同小异,无非就是在XML文件中写切入点表达式和通知类型
    例子:
    (1)Log.java

    public class Log {

    private Integer id;

    //操作名称,方法名
    private String operName;

    //操作人
    private String operator;

    //操作参数
    private String operParams;

    //操作结果 成功/失败
    private String operResult;

    //结果消息
    private String resultMsg;

    //操作时间
    private Date operTime = new Date();

    setter,getter

    }
    }

    (2).Logger.java

    /**
    * 日志记录器 (AOP日志通知)

    */
    public class Logger {

    @Resource

    private LogService logService;

    public Object record(ProceedingJoinPoint pjp){

    Log log = new Log();

    try {

    log.setOperator("admin");
    String mname = pjp.getSignature().getName();

    log.setOperName(mname);

    //方法参数,本例中是User user
    Object[] args = pjp.getArgs();

    log.setOperParams(Arrays.toString(args));

    //执行目标方法,返回的是目标方法的返回值,本例中 void
    Object obj = pjp.proceed();

    if(obj != null){

    log.setResultMsg(obj.toString());

    }else{

    log.setResultMsg(null);

    }

    log.setOperResult("success");

    log.setOperTime(new Date());

    return obj;

    } catch (Throwable e) {

    log.setOperResult("failure");

    log.setResultMsg(e.getMessage());

    } finally{

    logService.saveLog(log);

    }

    return null;

    }
    }
    }

    (3).applicationContext.xml

    <aop:config>
    <aop:aspect id="loggerAspect" ref="logger">
    <aop:around method="record" pointcut="(execution(* com.aijava.distributed.ssh.service..*.add*(..))

    or execution(* com.aijava.distributed.ssh.service..*.update*(..))

    or execution(* com.aijava.distributed.ssh.service..*.delete*(..)))

    and !bean(logService)"/>
    </aop:aspect>
    </aop:config>

    注意切入点表达式,!bean(logService) 做日志通知的时候,不要给日志本身做日志,否则会造成无限循环!
    关更详细的Spring AOP知识,可以查看Spring官方文档第9章Aspect Oriented Programming with Spring 
    7.JDK动态代理介绍
    例子:
    (1)UserService.java
    public interface UserService {

    public void add();
    }
    }
    (2)UserServiceImpl.java

    public class UserServiceImpl implements UserService{

    public void add() {

    System.out.println("User add()...");

    }


    }
    }

    (3)ProxyUtils.java

    public class ProxyUtils implements InvocationHandler{

    private Object target;

    public ProxyUtils(Object target){

    this.target = target;

    }

    public Object getTarget() {

    return target;

    }

    public void setTarget(Object target) {

    this.target = target;

    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

    System.out.println("do sth before...");

    method.invoke(target, args);

    System.out.println("do sth after...");

    return null;

    }

    }
    }

    (4)Test.java

    public class Test {

    public static void main(String[] args) {

    UserService userService = new UserServiceImpl();

    ProxyUtils proxyUtils = new ProxyUtils(userService);

    UserService proxyObject = (UserService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),UserServiceImpl.class.getInterfaces(), proxyUtils);

    proxyObject.add();

    }
    }
    }

  • 相关阅读:
    年末反思
    Flink运行时架构
    Phoenix 启动报错:Error: ERROR 726 (43M10): Inconsistent namespace mapping properties. Cannot initiate connection as SYSTEM:CATALOG is found but client does not have phoenix.schema.
    Clickhouse学习
    Flink简单认识
    IDEA无法pull代码到本地,Can't Update No tracked branch configured for branch master or the branch doesn't exist.
    第1章 计算机系统漫游
    简单的 Shell 脚本入门教程
    开源≠免费 常见开源协议介绍
    MySQL 视图
  • 原文地址:https://www.cnblogs.com/chuangzhijian/p/8477266.html
Copyright © 2011-2022 走看看