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();

    }
    }
    }

  • 相关阅读:
    记忆的永恒
    放弃我是你的错
    献给我逝去的长辈们清明
    思维的局限,穷人为什么会穷?
    借我一生
    陪你到老
    风雨路途
    人生的十二大财富
    怀才不遇
    javascript变量
  • 原文地址:https://www.cnblogs.com/chuangzhijian/p/8477266.html
Copyright © 2011-2022 走看看