zoukankan      html  css  js  c++  java
  • 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");
  • 相关阅读:
    jar 常用操作
    linux 加载新的磁盘(卷组)
    apache 代理配置
    spring boot datasource 参数设置
    svn 常用命令
    最详细的maven教程
    centos 用户组操作
    ubuntu命令行操作mysql常用操作
    Ruby-Clamp
    maven使用备忘
  • 原文地址:https://www.cnblogs.com/tanlei-sxs/p/10140920.html
Copyright © 2011-2022 走看看