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