一、什么是代理模式?
代理模式就是給某個(gè)對(duì)象提供一個(gè)代理對(duì)象,由代理對(duì)象控制對(duì)原對(duì)象的控制,訪問(wèn)原對(duì)象的方法。
二、Spring AOP的理解
AOP:Aspect Oriented Programing,面向切面編程,是Spring的兩大核心思想之一,是一種編程的思想,也是對(duì)面向?qū)ο笏枷氩蛔愕难a(bǔ)充和擴(kuò)展,因?yàn)槿f(wàn)物皆對(duì)象的這種思想,將現(xiàn)實(shí)世界所有的事物映射為Java世界中的一個(gè)個(gè)對(duì)象,可以看作是縱向在設(shè)計(jì)一個(gè)個(gè)類,而當(dāng)這些類都需要一些公共的實(shí)現(xiàn)代碼時(shí),比如說(shuō)日志、權(quán)限,抽象類或接口的設(shè)計(jì)已經(jīng)不太能滿足這個(gè)需求了,因?yàn)镴ava是單繼承,而且使用抽象類或接口會(huì)使整個(gè)結(jié)構(gòu)變得更復(fù)雜,所以出現(xiàn)了AOP,面向切面編程,橫向去將這些類的公共代碼(系統(tǒng)代碼)提煉出來(lái),使用代理和反射的機(jī)制交由代理對(duì)象去實(shí)現(xiàn),保證了原業(yè)務(wù)代碼的純粹性,也提高了系統(tǒng)的可維護(hù)性、可擴(kuò)展性。
三、Spring AOP中常見(jiàn)通知的類型
- Before:前置通知
- After:后置通知
- AfterReturning:有返回值的后置通知,當(dāng)方法正常執(zhí)行后,執(zhí)行該通知;如果方法執(zhí)行時(shí)出現(xiàn)異常,則不執(zhí)行該通知
- AfterThrowing:異常通知,方法出現(xiàn)異常時(shí),執(zhí)行該通知
- Around:環(huán)繞通知
四、Spring AOP的實(shí)現(xiàn)
-
使用Spring AOP
?? 第一步,定義通知類,繼承相關(guān)接口,例如:MethodBeforeAdvice,AfterAdvice,AfterReturningAdvice,ThrowsAdvice,MethodInterceptor,重寫或自定義相關(guān)方法。
?? 第二步:配置XML:
<!-- 大前提 --> <!-- 通知類 --> <bean id="logAdviceBean" class="com.apesource.advice.LogAdvice"/> <!-- 目標(biāo)類 --> <bean id="homeControllerBean" class="com.apesource.web.controller.HomeController"/>形式1:手動(dòng)代理:使用ProxyFactoryBean手動(dòng)為目標(biāo)類創(chuàng)建代理類。
缺點(diǎn):每個(gè)目標(biāo)類都要寫一個(gè)Bean,很繁瑣,在獲取Bean的時(shí)候需要獲取代理類Bean。
<!-- 手動(dòng)代理 --> <bean id="homeControllerProxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 注入目標(biāo)類 --> <property name="target" ref="homeControllerBean"/> <!-- 注入通知類 --> <property name="interceptorNames" value="logAdviceBean"/> </bean>形式2:自動(dòng)代理:使用BeanNameAutoProxyCreator自動(dòng)為所匹配的Bean創(chuàng)建代理類。
優(yōu)點(diǎn):簡(jiǎn)潔,在獲取Bean的時(shí)候直接獲取目標(biāo)類的Bean,Spring會(huì)自動(dòng)地創(chuàng)建代理類對(duì)象并返回。
<!-- 自動(dòng)代理(匿名Bean) --> <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <!-- 注入符合表達(dá)式的目標(biāo)類 --> <property name="beanNames" value="*ControllerBean"/> <!-- 注入通知類 --> <property name="interceptorNames" value="logAdviceBean"/> </bean>
?? 注入的通知類類型:
-
普通的通知類:類級(jí)別的通知,會(huì)使注入該通知的類的所有方法都執(zhí)行該通知,顆粒度太大。
<bean id="logAdviceBean" class="com.apesource.advice.LogAdvice"/> -
高級(jí)的通知類:Advisor切面,方法級(jí)別的通知,可以具體到某一個(gè)符合規(guī)則的方法,顆粒度小。
實(shí)現(xiàn)方式1:
<!-- 實(shí)現(xiàn)方式1 --> <!-- 1.1切入點(diǎn)(Pointcut):通過(guò)正則表達(dá)式描述指定切入點(diǎn) --> <bean id="createMethodPointcutBean" class="org.springframework.aop.support.JdkRegexpMethodPointcut"> <!-- 描述符合表達(dá)式的方法 --> <property name="pattern" value=".*create.*"/> </bean> <!-- 和1.1類似的實(shí)現(xiàn) --> <!-- <bean id="controllerLogMethodPointcut" class="org.springframework.aop.support.NameMatchMethodPointcut">--> <!-- 描述符合表達(dá)式的方法 --> <!--<property name="mappedName" value="*create*"/>--> <!--</bean>--> <!-- 1.2 --> <!-- Advisor = Advice(通知) + Pointcut(切入點(diǎn)) --> <bean id="performanceAdvisorBean" class="org.springframework.aop.support.DefaultPointcutAdvisor"> <!--注入切入點(diǎn)--> <property name="pointcut" ref="createMethodPointcutBean"/> <!--注入通知--> <property name="advice" ref="performanceBean"/> </bean>實(shí)現(xiàn)方式2:一步到位
<!-- 實(shí)現(xiàn)方式2 --> <bean class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <!-- 通過(guò)正則表達(dá)式描述切入點(diǎn),并注入 --> <property name="mappedName" value="*create*"/> <!--注入通知--> <property name="advice" ref="logAdviceBean"/> </bean>
-
-
使用AspectJ框架:
方式1:使用POJO+XML的形式
?? 第一步,自定義通知類,自定義通知方法。
public class LogAdvice{ private Logger logger = Logger.getLogger(LogAdvice.class.getName()); public void beginLogging(JoinPoint jp){ logger.info("日志開(kāi)始"); } public void endLogging(JoinPoint jp){ logger.info("日志結(jié)束"); } }?? 第二步,進(jìn)行XML配置
1.1 首先加入aop的命名空間配置
<beans 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">1.2 定義通知類
<bean id="logAdvice" class="com.apesource.advice.LogAdvice"/>1.3 進(jìn)行aop配置
<!-- 頂層aop配置 --> <aop:config> <!-- 定義切面,注入通知類 --> <aop:aspect ref="logAdvice"> <!--定義切點(diǎn)--> <aop:pointcut id="adminControllerPointcut" expression="execution(* com.apesource.web.controller.*.handler*(..))"/> <!-- 配置前置同通知 --> <aop:before method="beginLogging" pointcut-ref="adminControllerPointcut"/> <!-- 配置后置通知 --> <aop:after method="endLogging" pointcut-ref="adminControllerPointcut"/> </aop:aspect> </aop:config>
方式2:使用純注解
?? 第一步,自定義通知類,使用相關(guān)注解,并將通知類交給容器管理
@Aspect // 聲明為一個(gè)切面類 @Component public class LogAdvice2 { private Logger logger = Logger.getLogger(LogAdvice.class.getName()); @Before("execution(* com.apesource.web.controller.*.handler*(..))") public void beginLogging(JoinPoint jp){ logger.info("日志開(kāi)始"); } @After("execution(* com.apesource.web.controller.*.handler*(..))") public void endLogging(JoinPoint jp){ logger.info("日志結(jié)束"); } }?? 第二步,開(kāi)啟AspectJ自動(dòng)代理
@ComponentScan // 默認(rèn)掃描同包之下 @EnableAspectJAutoProxy public class SystemConfig { }
相關(guān)注解:
@Aspect
@EnableAspectJAutoProxy
@Pointcut
@Before
@After
@AfterReturning
@AfterThrowing
@Around