zoukankan      html  css  js  c++  java
  • Spring框架的AOP

    Spring学习笔记(四)

    本文目录

    1 AOP的介绍

    2 Spring的AspectJ实现AOP(annotation)

    3 Spring的AspectJ实现AOP (XML)

    Spring文档http://docs.spring.io/spring/docs/4.0.0.BUILD-SNAPSHOT/spring-framework-reference/htmlsingle/

    一 AOP介绍

    spect Oriented Programming(AOP),面向切面编程。AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间降低耦合的隔离效果

    简单来说就是在方法的前后加一层过滤的方法

    如果类实现了interface接口,通过使用动态代理(Proxy),通过拦截一个对象的行为并添加我们需要的功能来完成

    如果没有实现接口的类,可以使用Cglib代理实现AOP 

     

     AOP用途比较广,适合切面的功能都可以使用AOP,比如打日志,声明式的事务的管理,性能测试,权限检查等

    通过invocationhandle和Proxy实现

    测试代码

    接口DO

    package proxy.service;
    
    public interface Do {
        public void doSomething();
    
    }

    DO接口的实现

    package proxy.service;
    
    public class DoImpl implements Do {
    
        @Override
        public void doSomething() {
            System.out.println("doimpl...");
            
        }
    
    }

    动态代理的实现,invocationhandle和Proxy

    package proxy.service;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class dynamicProxy implements InvocationHandler {
        private Object target;
    
        public dynamicProxy(Object target) {
            this.target = target;
        }
    
        public void before(Method method) {
            System.out.println(method.getName() + "调用开始");
        }
    
        public void after(Method method) {
            System.out.println(method.getName() + "调用结束");
        }
    
        public static Object newInstance(Object target) {
            return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(), new dynamicProxy(target));
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) {
            Object result = null;
            try {
                before(method);
                result = method.invoke(target, args);
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } finally {
                after(method);
            }
            return result;
        }
    }

    测试类

    package proxy.service.test;
    
    import static org.junit.Assert.*;
    
    import org.junit.Before;
    import org.junit.Test;
    
    import proxy.service.Do;
    import proxy.service.DoImpl;
    import proxy.service.dynamicProxy;
    
    public class dynamicProxyTest {
    
        @Before
        public void setUp() throws Exception {
        }
    
        @Test
        public void testDynamicProxy() {
                Do doSth = (Do) dynamicProxy.newInstance(new DoImpl());
                doSth.doSomething();
            }
    }

    执行结果

    doSomething调用开始
    doimpl...
    doSomething调用结束

    在不改动实现类和接口的情况下,通过invocationhandleProxy实现动态代理在方法的前后插入了业务逻辑

    二、Spring的AspectJ实现AOP(annotation)

    1 添加JAR包

    aspectjrt.jar    aspectjweaver.jar

    2 XML的配置

    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation = "http://www.springframework.org/schema/aop
                         http://www.springframework.org/schema/aop/spring-aop.xsd"
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <context:annotation-config/>
        <context:component-scan base-package="com"></context:component-scan>
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
        
    
    </beans>

     
    3 新建切面类

    package com.aop;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Component;
    
    @Aspect
    @Component
    public class logInterceptor {
        @Before("execution(public void com.daoImpl.UserDaoImpl.*(..))")
        public void beforMethod(){
            System.out.println("aspectJ before method");
        }
        @AfterReturning("execution(public void com.daoImpl.UserDaoImpl.*(..))")
        public void afterMethod(){
            System.out.println("aspectJ after method");
        }
    
    }

    @Aspect在类名上方注解表示实现Aspect的类

    @Before表示方法运行前

    @AfterReturning表示在方法执行之后

    他们的语法建议记住 execution即可

    execution(<修饰符模式>? <返回类型模式> <方法名模式>(<参数模式>) <异常模式>?)  除了返回类型模式、方法名模式和参数模式外,其它项都是可选的,可使用通配符
    例如
    execution( public void com.daoImpl.UserDaoImpl.* (com.entity.User) )//这里没有异常
             (<修饰符模式>? <返回类型模式>         <方法名模式>            (<参数模式>)         <异常模式>?)

    具体详见传送门http://dylanxu.iteye.com/blog/1312454

    测试类
    package com.serviceImpl.test;
    import org.junit.Before;
    import org.junit.Test;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import com.entity.User;
    import com.serviceImpl.UserServiceImpl;
    
    public class UserServiceImplTest {
        User user;
    
        @Before
        public void setUp() throws Exception {
            user = new User();
            user.setName("testName");
            user.setRemark("testRemark");
        }
    
        @Test
        public void testAdd() {
            ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("beans.xml");
             UserServiceImpl UserServiceImpl = (UserServiceImpl)app.getBean("userServiceImpl");
             UserServiceImpl.add(user);//调用方法
             UserServiceImpl.update(user);//调用方法
        }
    }

    执行结果

    aspectJ before method
    testName-->testRemark save --调用UserDaoImpl!
    aspectJ after method
    aspectJ before method
    testName-->testRemark update --调用UserDaoImpl!
    aspectJ after method

    通过@Aspect,@Before,@AfterReturning 成功的在方法的前后加入了切面逻辑


    介绍另外几个常用的annotation

    @Pointcut

    @Pointcut("execution(public void com.daoImpl.UserDaoImpl.*(com.entity.User))")

    Pointcut提供了一个切面的切入点切必须写在一个空方法上面

    public class logInterceptor {
        @Pointcut("execution(public void com.daoImpl.UserDaoImpl.*(com.entity.User))")
        public void myAop(){};
        
        @Before("myAop()")
        public void beforMethod(){
            System.out.println("aspectJ before method");
        }
        @AfterReturning("myAop()")
        public void afterMethod(){
            System.out.println("aspectJ after method");
        }
    }
    @Before("myAop()") 效果等于 @Before("execution(public void com.daoImpl.UserDaoImpl.*(com.entity.User))")

    执行效果与之前的执行效果一致

    @Around

    @Around("execution(public void com.daoImpl.UserDaoImpl.*(com.entity.User))")

    Around标签,可循环执行方法  注意---> pjp.proceed();重复了三次 

    package com.aop;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Component;
    
    @Aspect
    @Component
    public class logInterceptor {
        @Pointcut("execution(public void com.daoImpl.UserDaoImpl.*(com.entity.User))")
        public void myAop(){};
    @Around(
    "myAop()") public void around(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("aspectJ before method"); pjp.proceed(); pjp.proceed(); pjp.proceed(); System.out.println("aspectJ after method"); } }

    测试类

    package com.serviceImpl.test;
    import org.junit.Before;
    import org.junit.Test;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import com.entity.User;
    import com.serviceImpl.UserServiceImpl;
    
    public class UserServiceImplTest {
        User user;
    
        @Before
        public void setUp() throws Exception {
            user = new User();
            user.setName("testName");
            user.setRemark("testRemark");
        }
    
        @Test
        public void testAdd() {
            ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("beans.xml");
             UserServiceImpl UserServiceImpl = (UserServiceImpl)app.getBean("userServiceImpl");
             UserServiceImpl.add(user);//调用方法
             UserServiceImpl.update(user);//调用方法
        }
    }

    执行结果

    aspectJ before method
    testName-->testRemark save --调用UserDaoImpl!
    testName-->testRemark save --调用UserDaoImpl!
    testName-->testRemark save --调用UserDaoImpl!
    aspectJ after method
    aspectJ before method
    testName-->testRemark update --调用UserDaoImpl!
    testName-->testRemark update --调用UserDaoImpl!
    testName-->testRemark update --调用UserDaoImpl!
    aspectJ after method

    pjp.proceed();重复了三次 

    从结果中得到方法也确实执行了三次

    通过around可以有效控制方法在这个切面里的执行次数,甚至不执行

    annotation介绍到这里

    通过上述资料可以掌握 @Aspect @Pointcut @Before @AfterReturning @Around 以及 execution语法

    3 Spring的AspectJ实现AOP (XML)

    XML配置

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <context:annotation-config/>
        <context:component-scan base-package="com"></context:component-scan>
        <aop:config>
            <aop:aspect id="logInterceptor" ref="logInterceptor">
                <aop:pointcut expression="execution(public void com.daoImpl.UserDaoImpl.*(..))" id="myAop" />
                <aop:before pointcut-ref="myAop" method="beforMethod" /><aop:after pointcut-ref="myAop" method="afterMethod" />  
            </aop:aspect>
        </aop:config>
        
    
    </beans>

    由于我使用的是annotation的component组件bean配置

    定义<aop:config>配置

    aspect的ref表示Spring管理的bean,id应该是随意的

    pointcut +表达式 + id(方法名)

    before/after在与method方法之间需要+表达式或者是pointcut的id来明确切入点

    切面类

    package com.aop;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Component;
    
    @Component
    public class logInterceptor {
        public void myAop(){};
        public void beforMethod(){
            System.out.println("aspectJ before method");
        }
        public void afterMethod(){
            System.out.println("aspectJ after method");
        }
    //    @Around("myAop()")
    //    public void around(ProceedingJoinPoint pjp) throws Throwable{
    //        System.out.println("aspectJ before method");
    //        pjp.proceed();
    //        System.out.println(pjp.getStaticPart());
    //        System.out.println("aspectJ after method");
    //    }
    
    }

    测试类

    package com.serviceImpl.test;
    import org.junit.Before;
    import org.junit.Test;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import com.entity.User;
    import com.serviceImpl.UserServiceImpl;
    
    public class UserServiceImplTest {
        User user;
    
        @Before
        public void setUp() throws Exception {
            user = new User();
            user.setName("testName");
            user.setRemark("testRemark");
        }
    
        @Test
        public void testAdd() {
            ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("beans.xml");
             UserServiceImpl UserServiceImpl = (UserServiceImpl)app.getBean("userServiceImpl");
             UserServiceImpl.add(user);//调用方法
             UserServiceImpl.update(user);//调用方法
        }
    }

    执行结果与annotation的一致

    aspectJ before method
    testName-->testRemark save --调用UserDaoImpl!
    aspectJ after method
    aspectJ before method
    testName-->testRemark update --调用UserDaoImpl!
    aspectJ after method

    around的XML配置

            <aop:aspect id="logInterceptor" ref="logInterceptor">
                <aop:pointcut expression="execution(public void com.daoImpl.UserDaoImpl.*(..))" id="myAop" />
                <aop:before pointcut-ref="myAop" method="beforMethod" /><aop:after pointcut-ref="myAop" method="afterMethod" />  
                <aop:around pointcut-ref="myAop" method="around"/>
            </aop:aspect>

    切面类

    package com.aop;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Component;
    
    @Component
    public class logInterceptor {
        public void myAop(){};
        public void beforMethod(){
            System.out.println("aspectJ before method");
        }
        public void afterMethod(){
            System.out.println("aspectJ after method");
        }
        public void around(ProceedingJoinPoint pjp) throws Throwable{
            System.out.println("aspectJ before method");
            pjp.proceed();
            System.out.println(pjp.getStaticPart());
            System.out.println("aspectJ after method");
        }
    
    }

    执行结果

    aspectJ before method
    aspectJ before method
    testName-->testRemark save --调用UserDaoImpl!
    aspectJ after method
    execution(void com.dao.UserDao.save(User))
    aspectJ after method
    aspectJ before method
    aspectJ before method
    testName-->testRemark update --调用UserDaoImpl!
    aspectJ after method
    execution(void com.dao.UserDao.update(User))
    aspectJ after method

    个人建议使用XML来完成AOP的切面配置,这样代码的可读性会比较强,而且配置较为灵活。

    另外如果非实现接口的类需要做切面处理的话,需要引入JAR包,这里没继续研究。

    通过上述资料可以掌握 @Aspect @Pointcut @Before @AfterReturning @Around注解和XML配置 以及 execution语法

     
  • 相关阅读:
    [转化率预估-1]引言
    MATLAB 的输入输出命令
    MATLAB R2018a 输入中文却显示方框问号的问题
    支持向量机-SVM 学习
    MATLAB R2018a 安装教程
    【DeepLearning】用于几何匹配的卷积神经网络体系结构
    Alias Method for Sampling 采样方法
    关于机器学习的充分统计量
    java.lang.NullPointerException 空指针异常问题
    Eclipse导入项目时出错提示 project is missing required library
  • 原文地址:https://www.cnblogs.com/sunfan1988/p/3477539.html
Copyright © 2011-2022 走看看