美文网首页程序员之家
2020-03-16 Spring-Aop

2020-03-16 Spring-Aop

作者: summer96 | 来源:发表于2020-03-16 19:24 被阅读0次

    简介

    AOP:面向切面编程,是OOP的扩展和延申,解决OOP中遇到的问题
    可以进行权限校验,日志记录,性能监控,事务校验
    Spring底层的AOP实现原理:动态代理
    JDK动态代理 :只能对实现了接口的类产生代理
    Cglib动态代理(类似于Javassist第三方代理技术):对没有实现接口的类产生代理对象,生成子
    对象

    两种动态代理

    JDK动态代理实例

    1.建立一个接口:

    public interface UserDao {
        public void insert();
        public void update();
    }
    

    2.接口实现类

    public class UserDaoImpl implements UserDao {
        @Override
        public void insert() {
            System.out.println("insert方法");
        }
        @Override
        public void update() {
            System.out.println("update方法");
        }
    }
    

    3.编写代理类JdkProxy

    public class JdkProxy{
        //将被增强的对象传递到代理当中
        private UserDao userDao;
        public JdkProxy(UserDao userDao) {
            this.userDao = userDao;
        }
        public UserDao creatProxy() {
            UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(), userDao.getClass().getInterfaces(), this);
            return userDaoProxy;
        }
    }
    
    1. 实现InvocationHandler接口,并重写其中方法,用于权限管理
    public class JdkProxy implements InvocationHandler{
            ······
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if("insert".equals(method.getName())) {
                System.out.println("权限增强");
                return method.invoke(userDao, args);
            }
            return method.invoke(userDao, args);
        }
    

    5.测试类

        @Test
        public void demo1() {
            UserDao userDao=new UserDaoImpl();
            //创建代理
            UserDao proxy = new JdkProxy(userDao).creatProxy();
            proxy.insert();
            proxy.update();
        }
    

    Cglib动态代理

    第三方开源代码生成类库,动态添加类的属性和方法
    1.编写接口和测试类(同上)
    2.编写代理类CglibProxy

    public class CglibProxy {
        private UserDaoImpl userDaoImpl;
        public CglibProxy(UserDaoImpl userDaoImpl) {
            this.userDaoImpl=userDaoImpl;
        }
        public UserDaoImpl createProxy() {
            //1.创建Cglib的代理对象
            Enhancer enhancer = new Enhancer();
            //2.设置父类
            enhancer.setSuperclass(userDaoImpl.getClass());
            //3.设置回调
            enhancer.setCallback(this);
            //4.创建代理
            UserDaoImpl proxy=(UserDaoImpl) enhancer.create();
            return  proxy;
        }
    

    3.CglibProxy 继承MethodInterceptor类,并重写其中方法

    public class CglibProxy implements MethodInterceptor{
        ······
        @Override
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            // 判断是否是insert方法
            if("insert".equals(method.getName())) {
                //增强
                System.out.println("权限校验");
                return methodProxy.invokeSuper(proxy, args);
            }
            return methodProxy.invokeSuper(proxy, args);
        }
    }
    

    4.测试类

        @Test
        public void demo1() {
            UserDaoImpl userDao=new UserDaoImpl();
            //创建代理
            UserDaoImpl proxy = new CglibProxy(userDao).createProxy();
            proxy.insert();
            proxy.update();
        }
    

    Spring的AOP开发

    简介

    AOP思想最早是由AOP联盟组织提出的,Spring是使用这种思想最好的框架
    Spring的AOP有自己实现的方式(非常繁琐)AspectJ是一个AOP框架,Spring引入了AspectJ作为自身开发

    相关代码编写

    1.新建切面类,并在切面类中MethodInterceptor类(环绕通知)

    //环绕通知
    public class MyAspectXML implements MethodInterceptor{
        @Override
        public Object invoke(MethodInvocation arg0) throws Throwable {
            System.out.println("0000000000");
            Object obj=arg0.proceed();
            System.out.println("0000000000");
            return obj;
        }
     }
    

    2.在xml文件中配置

    <!-- 配置目标对象,被增强的对象-->
    <bean id="productDao" class="com.zut.aopTest.ProductDaoImpl"></bean>
    <!-- 将切面类交给Spring管理 -->
    <bean id="myAspect" class="com.zut.aopTest.MyAspectXML"></bean>
    

    3.在xml文件中配置通知

    <!-- 创建代理对象  默认JDK动态代理  如果没有proxyInterfaces 则默认cglib-->
        <bean class="org.springframework.aop.framework.ProxyFactoryBean" name="proxyBean">
            <property name="targetName" value="productDao"></property>
            <!-- 拦截器名字 -->
            <property name="interceptorNames" value="myAspect">
                <!-- 如果通知在多个类中 -->
                <!-- <array>
                    <value></value>
                </array> -->
            </property>
                <property name="proxyInterfaces" value="com.zut.aopTest.ProductDao"></property>
        </bean>
    

    4.编写测试类

    public void demo1() {
            ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
            ProductDao productDao=context.getBean("proxyBean",ProductDao.class);
            productDao.save();
    }
    

    前置通知接口:MethodBrforeAdvice
    后置通知接口:AfterReturningAdvice 注意: 有异常时后置通知不执行
    异常通知接口:ThrowsAdvice
    最终通知接口:AfterAdvice

    在此方式下,xml文件可以采用自动织入编写

    1.xml文件

    <!-- 配置目标对象,被增强的对象 -->
        <bean id="productDao" class="com.zut.aopTest.ProductDaoImpl"></bean>
        <!-- 将切面类交给Spring管理 -->
        <bean id="myAspect" class="com.zut.aopTest.MyAspectXML"></bean>
    <aop:config>
            <aop:pointcut expression="execution(* com.zut.springAOP.UserImpl.*(..))" id="pointcut"/>
            <aop:advisor advice-ref="myAspect" pointcut-ref="pointcut"/>
        </aop:config>
     //id 与 pointcut-ref 相等
    

    2.测试类

    public void demo1() {
            ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
            ProductDao productDao=context.getBean("productDao",ProductDao.class);
            productDao.save();
    }
    

    采用AspectJ的XML的方式

    相关配置文件

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.2.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>aopalliance</groupId>
        <artifactId>aopalliance</artifactId>
        <version>1.0</version>
    </dependency>
    <dependency>
        <groupId>aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.5.3</version>
    </dependency>
    

    xml文件

    clipboard.png

    相关代码编写

    1.编写接口和测试类
    2.编写切面类

    public class MyAspectXML {
        public void checkPri() {
            System.out.println("权限校验。。。。。。。。。");
        }
    }
    

    3.在xml文件里进行配置

    <!-- 配置目标对象,被增强的对象 -->
        <bean id="productDao" class="com.aopTest.ProductDaoImpl"></bean>
        <!-- 将切面类交给Spring管理 -->
        <bean id="myAspect" class="com.aopTest.MyAspectXML"></bean>
        <!-- 通过AOP的配置实现对目标产生代理 -->
        <aop:config>
            <!-- 表达式配置那些类哪些方法需要进行增强   ()里面只写参数类型  可以写成 save(int ,String) and args(a,b)  -->
            <aop:pointcut expression="execution(* com.aopTest.ProductDaoImpl.save(..))" id="pointcut1"/>
            <!-- 配置切面  myAspect指向切面 与上文要对应上  checkPri 切面类里的方法名-->
            <aop:aspect ref="myAspect">
                          <!--匹配参数  这里的名字要一致-->
                <aop:before method="checkPri" pointcut-ref="pointcut1" arg-names="a"/></aop:aspect>   
        </aop:config>
    </beans>
    

    4.测试类

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:Spring-config.xml")
    public class SpringDemo1 {
        @Resource(name="productDao")
        private ProductDao productDao;
        @Test
        public void demo1() {
            productDao.save();
            productDao.update();
            productDao.find();
            productDao.delete();
        }
    }
    

    通知类型

    前置通知

    在目标方法执行之前执行此操作
    1.xml文件

    <!-- 配置切面 -->
    <aop:aspect ref="myAspect">
    <aop:before method="checkPri" pointcut-ref="pointcut1"/>
    </aop:aspect>
    

    2.获得切入点信息(几种通知均可实现)
    在切入类中

    public void checkPri(JoinPoint joinPoint) {
            System.out.println("权限校验。。。。。。。。。"+joinPoint);
    }
    

    后置通知

    在目标方法之后执行的操作
    1.在xml文件中

    <aop:pointcut expression="execution(* com.aopTest.ProductDaoImpl.delete(..))" id="pointcut2"/>
    
    <!-- 后置通知 -->
    <aop:after-returning method="write" pointcut-ref="pointcut2" returning="result"/>
    //returning表示获取返回值
    

    2.获得方法返回值
    在切入类中

    /**
     * 后置通知
     */
        public void write(Object result) {
            System.out.println("日志通知。。。。。。。。。。"+result);
        }
     //这里的reult必须与前面配置里的returning一致
    

    环绕通知

    在目标方法执行之前和之后进行操作
    1.xml文件

    <aop:pointcut expression="execution(* com.aopTest.ProductDaoImpl.update(..))" id="pointcut3"/>
    
    <!-- 环绕通知 -->
    <aop:around method="around" pointcut-ref="pointcut3"/>
    

    2.在切入类

    /**
     * 环绕通知
     * 性能监控
     * @throws Throwable 
     */
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕通知前。。。");
        Object obj=joinPoint.proceed();
        System.out.println("环绕通知后。。。");
        return obj;
    }
    

    异常抛出通知

    在程序出现异常时,进行的操作
    1.xml文件中

    <aop:pointcut expression="execution(* com.aopTest.ProductDaoImpl.find(..))" id="pointcut4"/>
    
    <!-- 异常通知 -->
    <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="ex"/>
    //throwing:得到异常类型
    

    2.切入类

    /**
     * 异常抛出
     */
    public void afterThrowing(Throwable ex) {
        System.out.println("异常抛出。。。"+ex);
    }
     //ex必须与上文配置中的throwing一致
    

    最终通知

    无论代码是否有异常,总是会执行,相当于finally代码块
    1.xml文件里

    <!-- 最终通知 -->
    <aop:after method="after" pointcut-ref="pointcut4"/>
    

    2.切面类

    /**
    * 最终通知
    */
    public void after() {
        System.out.println("最终通知。。。...");
    }
    

    spring的切入点表达式写法

    基于execution的函数完成的
    语法:
    [访问修饰符] 方法返回值 包名.类名.方法名(参数)
    public void com.zut.aopTest.ProductDao.save(..)
    星号(*)代表任意,参数不能写 * ,可以写 ..
    *****Dao.save(..)
    *com.zut.aopTest.ProductDao+save(..)

    相关文章

      网友评论

        本文标题:2020-03-16 Spring-Aop

        本文链接:https://www.haomeiwen.com/subject/bgznehtx.html