Spring笔记07(Spring AOP的通知advice和顾问advisor)

Posted 迷茫王子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring笔记07(Spring AOP的通知advice和顾问advisor)相关的知识,希望对你有一定的参考价值。

1.Spring AOP的通知advice

01.接口代码:

package cn.pb.dao;

public interface UserDao {
    //主业务
    String add();
    //主业务
    void del();
}

 

02.实现类代码:

package cn.pb.dao;

public class UserDaoImpl implements UserDao{
//主业务
public String add() {
//模拟异常
//int a=8/0;
System.out.println("add ok!");
return "新增成功!";
}
//主业务
public void del() {
System.out.println("del ok!");
}

03.增强通知类:

001.前置增强类:

package cn.pb.advices;
/**
 * 前置增强类 实现MethodBeforeAdvice接口
 * 在目标对象方法执行之前执行
 */

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class BeforeAdvice implements MethodBeforeAdvice {
    /**
     *
     * @param method :目标方法
     * @param args  :目标方法参数
     * @param target :目标对象
     * @throws Throwable
     */
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("前置增强====================》");
    }
}

 

002.后置增强类:

package cn.pb.advices;
/**
 * 后置增强类 实现AfterReturningAdvice接口
 * 在目标对象方法执行之后执行
 */
import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

public class AfterAdvice implements AfterReturningAdvice {
    /**
     *
     * @param target:目标对象
     * @param method :目标方法
     * @param args :目标方法参数
     * @param returnValue :目标方法返回值
     * @throws Throwable
     */
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("后置增强==================》");
    }
}

 

003.环绕增强类:

package cn.pb.advices;
/**
 * 环绕增强 :
 * 在前置通知 之后,
 * 后置通知之前执行环绕通知!
 */

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class AroundAdvice implements MethodInterceptor {
    /**
     * 在前置通知 之后,后置通知之前执行环绕通知!
     * 可以获取方法的返回值,并且改变!
     * @param methodInvocation 方法的执行器, getMethod 包含了方法中的所有方法
     * @return
     */
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("执行方法之前的  环绕通知");
        //执行目标方法
        Object result= methodInvocation.proceed();
        if (result!=null){
            //对方法的返回值进行修改
            result="xiaoheihei";
        }
        System.out.println("执行方法之后的  环绕通知");
        return result;
    }
}

 

004.异常增强类:

package cn.pb.advices;

/**
 * 异常增强类:在目标方法出现异常的时候执行
 * 实现ThrowsAdvice接口
 */

import org.springframework.aop.ThrowsAdvice;



public class ExceptionAdvice implements ThrowsAdvice{
    public void throwsAdvice(){
        System.out.println("方法在执行过程中出现了异常!");
    }
}

 

04.applicationContext.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"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--01.配置目标对象-->
    <bean id="userDao" class="cn.pb.dao.UserDaoImpl"/>
    <!--02.配置通知-->
    <bean id="beforeAdvice" class="cn.pb.advices.BeforeAdvice"/>
    <bean id="afterAdvice" class="cn.pb.advices.AfterAdvice"/>
    <bean id="aroundAdvice" class="cn.pb.advices.AroundAdvice"/>

    <!--03.通过配置代理工厂bean,生成代理类,来把通知织入到目标对象
      问题:只能管理  通知!
         01.只能将切面织入到目标类的所有方法中!
         02.只能配置一个 目标对象
   -->
    <bean id="userProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--注册目标对象  如果nam为target 那么ref="userDao" -->
        <property name="targetName" value="userDao"/>
        <!--注册通知-->
        <property name="interceptorNames" value="beforeAdvice,afterAdvice,aroundAdvice"/>

    </bean>



    <!--配置异常目标对象-->
    <bean id="userException" class="cn.pb.exceptionPackage.UserServiceImpl"/>
    <!--配置异常通知-->
    <bean id="myException" class="cn.pb.advices.ExceptionAdvice"/>

    <!--现在是一个service对应一个ProxyFactoryBean  这样不可以!-->
    <bean id="exceptionProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--注册目标对象 -->
        <property name="targetName" value="userException"/>
        <!--注册通知-->
        <property name="interceptorNames">
            <array>
                <value>myException</value> <!--异常通知-->
            </array>
        </property>
        <!--代理类的优化  设置之后程序就会自动选择是使用JDK动态代理还是使用cglib动态代理-->
        <property name="optimize" value="true"/>
        <!-- <property name="proxyTargetClass" value="true"/>
            proxyTargetClass:默认是false  ,默认执行jdk动态代理!
                              设置成true,强制执行cglib!
            optimize :  代理类的优化
                         有接口就是用jdk,没有接口使用cglib动态代理-->
    </bean>
<!--
      我们的动态代理 (在程序运行期间,动态生成的代理类) 分为两种方式:
         01.jdk     只能应用于实现接口的情况
         02.cglib   应用于实现接口和类的情况
         如果我们是接口的情况,使用jdk效率高!
         如果我们是类的情况,必须使用cglib!
        问题?
           程序  spring容器怎么知道我们是用的类还是接口??
          public class ProxyConfig implements Serializable
            private boolean proxyTargetClass = false;
            private boolean optimize = false;
            spring底层默认使用cglib! 现在我们的项目中使用的是接口!
            用spring默认的性能不高!
           proxyTargetClass 和optimize都是用来设置 我们使用的代理模式是jdk还是cglib!
           @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
       根据我们配置文件中 proxyTargetClass 和 optimize的配置
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }
           根据目标对象返回对应的动态代理
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }
-->

</beans>

 

05.测试代码:

package cn.pb;

import cn.pb.dao.UserDao;
import cn.pb.exceptionPackage.ServiceException;
import cn.pb.exceptionPackage.UserException;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestAdvice {

    /**
     *  前置  后置 通知测试
     */

    @Test
    public    void   testBefore(){
        ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao service= (UserDao) context.getBean("userProxy");
        service.add();
        System.out.println("*************");
        service.del();
    }


    /**
     * 环绕 通知测试
     */

    @Test
    public    void   testAround(){
        ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao service= (UserDao) context.getBean("userProxy");
        String result= service.add();
        System.out.println(result);
        System.out.println("*************");
        service.del();
    }


    /**
     *  异常通知测试
     */

    @Test
    public    void   testException(){
        ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
        ServiceException service= (ServiceException) context.getBean("exceptionProxy");
        try {
            service.chechUser("admins",25);
        } catch (UserException e) {
            e.printStackTrace();
        }

    }

}

 

 

2.Spring AOP的顾问advisor

01.readMe

顾问:在通知的基础之上,在细化我们的aop切面!

通知和顾问都是切面的实现方式!
通知是顾问的一个属性!

顾问会通过我们的设置,将不同的通知,在不通过的时间点,把切面
织入到不同的切入点!

PointCutAdvisor接口!
比较常用的两个实现类:
NameMatchMethodPointcutAdvisor :根据切入点(主业务方法)名称织入切面!
RegexpMethodPointcutAdvisor :根据自定义的正则表达式织入切面!


正则表达式中常用的三个运算符
.   任意单个字符
+   表示字符出现一次或者多次
*   表示字符出现0次或者多次

 

02.接口代码:

package cn.pb.dao;

public interface UserDao {
    //主业务
    void  add();
    //主业务
    void  del();
}

 

03.实现类代码:

package cn.pb.dao.impl;

import cn.pb.dao.UserDao;

public class UserDaoImpl implements UserDao{

    //主业务
    public void add() {
        System.out.println("add ok!");
    }
    //主业务
    public void del() {
        System.out.println("del ok!");
    }
}

 

04.增强类代码:

package cn.pb.advices;
/**
 * 前置增强类 在目标方法执行之前 执行
 * 要实现MethodBeforeAdvice接口
 */

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class BeforeAdvice implements MethodBeforeAdvice {
    /**
     * 在目标方法执行之前
     * @param method   目标方法
     * @param args    目标方法的参数列表
     * @param target    目标对象
     * @throws Throwable
     */
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("前置增强==============》");
    }
}

 

05.applicationContext.xml文件:

001.NameMatchMethodPointcutAdvisor :根据切入点(主业务方法)名称织入切面!

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--01.配置目标对象-->
    <bean id="userDao" class="cn.pb.dao.impl.UserDaoImpl"/>
    <!--02.配置增强 通知-->
    <bean id="beforeAdvice" class="cn.pb.advices.BeforeAdvice"/>


    <!---配置顾问   实现了 在指定的主业务方法中 增强-->
    <bean id="myAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
        <!--通知就是顾问中的一个属性-->
        <property name="advice" ref="beforeAdvice"/>
        <!--配置切入点   这里的切入点指的是 方法的简写!-->
        <property name="mappedNames" value="add,del"/>
    </bean>

    <!--03.配置代理对象-->
    <bean id="userProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--注册目标对象-->
        <property name="target" ref="userDao"/>
        <!--注册顾问-->
        <property name="interceptorNames" value="myAdvisor"/>
    </bean>

</beans>

 

002.RegexpMethodPointcutAdvisor :根据自定义的正则表达式织入切面!

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--01.配置目标对象-->
    <bean id="userDao" class="cn.pb.dao.impl.UserDaoImpl"/>
    <!--02.配置增强 通知-->
    <bean id="beforeAdvice" class="cn.pb.advices.BeforeAdvice"/>


    <!---配置顾问   实现了 在指定的主业务方法中 增强-->
    <bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <!--通知就是顾问中的一个属性-->
        <property name="advice" ref="beforeAdvice"/>
        <!--配置切入点   这里的切入点指的是 方法的全限定方法名
          cn.pb.dao.impl.UserServiceImpl.add
           cn.pb.dao.impl.UserServiceImpl.del-->
        <!-- <property name="pattern" value=".*add.*"/>  匹配单个方法-->
        <!-- <property name="pattern" value=".*mpl.*"/>匹配多个方法-->
        <!--<property name="patterns" value=".*add.*,.*del.*"/>  匹配多个方法-->
        <property name="pattern" value=".*add.*|.*del.*"/>


</bean>

<!--03.配置代理对象-->
    <bean id="userProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--注册目标对象-->
        <property name="target" ref="userDao"/>
        <!--注册顾问-->
        <property name="interceptorNames" value="myAdvisor"/>
    </bean>

</beans>

 

06.测试代码:

package cn.pb;

import cn.pb.dao.UserDao;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestUser {
    /**
     * 测试NameMatchMethodPointcutAdvisor
     */
    @Test
    public void testNameMethod(){
        ApplicationContext context=new
                ClassPathXmlApplicationContext("applicationContext.xml");
       UserDao proxy= (UserDao) context.getBean("userProxy");
       proxy.add();
        System.out.println("***************");
        proxy.del();
    }

    /**
     * 测试RegexpMethodPointcutAdvisor
     */
    @Test
    public void testRegexpMethod(){
        ApplicationContext context=new
                ClassPathXmlApplicationContext("regexp.xml");
        UserDao proxy= (UserDao) context.getBean("userProxy");
        proxy.add();
        System.out.println("***************");
        proxy.del();
    }
}

 
















以上是关于Spring笔记07(Spring AOP的通知advice和顾问advisor)的主要内容,如果未能解决你的问题,请参考以下文章

Spring 注解版 学习笔记面向AOP切面

Spring-AOP学习笔记-03通知

Spring框架学习笔记 --- [在spring中初步上手实现AOP,以及对事务的初步配置使用]

#展望我的2022Flag#Spring框架使用AspectJ实现AOP前置通知学习笔记

[原创]java WEB学习笔记106:Spring学习---AOP的通知 :前置通知,后置通知,返回通知,异常通知,环绕通知

Spring AOP官方文档学习笔记之AOP概述