项目中的一个AOP的编写案例(配置+案例)
AOP和Spring事務處理2008年05月01日 星期四 00:40二. AOP
1. AOP是什么?
AOP是OOP的延續,是Aspect Oriented Programming的縮寫,意思是面向方面編程。AOP實際是GoF設計模式的延續,設計模式孜孜不倦追求的是調用者和被調用者之間的解耦,AOP可以說也是這種目標的一種實現。
2. 切面意義何在?
就可以在這層切面上進行統一的集中式權限管理。而業務邏輯組件則無需關心權限方面的問題。也就是說,通過切面,我們可以將系統中各個不同層次上的問題隔離開來,實現統一集約式處理。各切面只需集中于自己領域內的邏輯實現。這一方面使得開發邏輯更加清晰,專業化分工更加易于進行;另一方面,由于切面的隔離,降低了耦合性,我們就可以在不同的應用中將各個切面組合使用,從而使得代碼可重用性大大增強。
3. AOP應用范圍
Authentication 權限
Caching 緩存
Context passing 內容傳遞
Error handling 錯誤處理
Lazy loading 懶加載
Debugging 調試
logging, tracing, profiling and monitoring 記錄跟蹤 優化 校準
Performance optimization 性能優化
Persistence 持久化
Resource pooling 資源池
Synchronization 同步
Transactions 事務
?
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" xmlns:context="http://www.springframework.org/schema/context"
?xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
?xsi:schemaLocation="http://www.springframework.org/schema/beans
??????http://www.springframework.org/schema/beans/spring-beans-3.0.xsd?????
??????http://www.springframework.org/schema/context
??????http://www.springframework.org/schema/context/spring-context-3.0.xsd?????
??????http://www.springframework.org/schema/aop
?????????? ????http://www.springframework.org/schema/aop/spring-aop-3.0.xsd?????
??????http://www.springframework.org/schema/tx
??????http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
?<!-- setting annotation -->
?<context:annotation-config />
?<!-- auto proxy configuration -->
?<!-- 啟動對@Aspect-->
?<aop:aspectj-autoproxy />
?<!--
??scan annotation for @Controller(action), @Service(service),
??@Repository(dao)
?-->
?<context:component-scan base-package="cn.com" />
?<!-- 屬性文件讀入 -->
?<bean id="propertyConfigurer"
??class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
??<property name="locations">
???<list>
????<value>classpath:jdbc.properties</value>
???</list>
??</property>
?</bean>
?<!-- pls config the parameter for c3p0 setting -->
?<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
??destroy-method="close">
??<!-- c3p0 type -->
??<property name="driverClass" value="${jdbc.driverClass}" />
??<property name="jdbcUrl" value="${jdbc.jdbcUrl}" />
??<property name="user" value="${jdbc.user}" />
??<property name="password" value="${jdbc.password}" />
??<!-- 連接池中保留的最小連接數 -->
??<property name="minPoolSize" value="${c3p0.minPoolSize}" />
??<!--連接池中保留的最大連接數。Default: 15 -->
??<property name="maxPoolSize" value="${c3p0.maxPoolSize}" />
??<!--初始化時獲取的連接數,取值應在minPoolSize與maxPoolSize之間。Default: 3 -->
??<property name="initialPoolSize" value="${c3p0.initialPoolSize}" />
??<!--最大空閑時間,60秒內未使用則連接被丟棄。若為0則永不丟棄。Default: 0 -->
??<property name="maxIdleTime" value="${c3p0.maxIdleTime}" />
??<!--當連接池中的連接耗盡的時候c3p0一次同時獲取的連接數。Default: 3 -->
??<property name="acquireIncrement" value="${c3p0.acquireIncrement}" />
??<!--
???JDBC的標準參數,用以控制數據源內加載的PreparedStatements數量。但由于預緩存的statements
???屬于單個connection而不是整個連接池。所以設置這個參數需要考慮到多方面的因素。
???如果maxStatements與maxStatementsPerConnection均為0,則緩存被關閉。Default: 0
??-->
??<property name="maxStatements" value="${c3p0.maxStatements}" />
??<!--每60秒檢查所有連接池中的空閑連接。Default: 0 -->
??<property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}" />
??<!--定義在從數據庫獲取新連接失敗后重復嘗試的次數。Default: 30 -->
??<property name="acquireRetryAttempts" value="${c3p0.acquireRetryAttempts}" />
??<!--
???獲取連接失敗將會引起所有等待連接池來獲取連接的線程拋出異常。但是數據源仍有效
???保留,并在下次調用getConnection()的時候繼續嘗試獲取連接。如果設為true,那么在嘗試
???獲取連接失敗后該數據源將申明已斷開并永久關閉。Default: false
??-->
??<property name="breakAfterAcquireFailure" value="${c3p0.breakAfterAcquireFailure}" />
??<!--
???因性能消耗大請只在需要的時候使用它。如果設為true那么在每個connection提交的
???時候都將校驗其有效性。建議使用idleConnectionTestPeriod或automaticTestTable
???等方法來提升連接測試的性能。Default: false
??-->
??<property name="testConnectionOnCheckout" value="${c3p0.testConnectionOnCheckout}" />
?</bean>
?<bean id="sessionFactory"
??class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
??<property name="dataSource" ref="dataSource">
??</property>
??<property name="hibernateProperties">
???<props>
????<!--
?????<prop
?????key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
????-->
????<prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop>
????<prop key="hibernate.show_sql">true</prop>
????<prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop>
????<prop key="hibernate.hbm2ddl.auto">update</prop>
????<prop key="javax.persistence.validation.mode">none</prop>
????<!-- default 10 -->
????<prop key="hibernate.jdbc.fetch_size">50</prop>
????<prop key="hibernate.jdbc.batch_size">30</prop>
????<prop key="hibernate.connection.autocommit">true</prop>
????<!--
?????ON_CLOSE:Hibernate session在第一次需要進行JDBC操作的時候獲取連接,然后
?????持有他,直到session關閉。不鼓勵使用
?????AFTER_TRANSACTION:在org.hibernate.Transaction結束后釋放連接。這一個不應該在JTA環境下使用。
?????AFTER_STATEMENT:(也被稱做積極釋放),在每一條語句被執行后就釋放連接。
????-->
????<prop key="hibernate.connection.release_mode">after_transaction</prop>
???</props>
??</property>
??<property name="packagesToScan">
???<list>
????<value>cn.com.css.misps.domain</value>
???</list>
??</property>
?</bean>
?<!-- Transaction configuration-->
?<bean id="txManager"
??class="org.springframework.orm.hibernate4.HibernateTransactionManager">
??<property name="sessionFactory" ref="sessionFactory" />
?</bean>
?<!-- auto transaction manager configuration -->
?<tx:annotation-driven transaction-manager="txManager" />
?
?<!-- 引入web Service的定義xml -->
?<import resource="graph.xml" />
?
</beans>
這里是要做aop的一個類:
package cn.com.css.misps.graph.service.impl;
import java.util.Date;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import cn.com.css.misps.domain.TCoGraph;
import cn.com.css.misps.graph.dao.ITCoGraphDao;
import cn.com.css.misps.graph.dao.ITProductLDao;
import cn.com.css.misps.graph.dao.ITTaskStateDao;
import cn.com.css.misps.graph.dao.impl.TCoGraphDaoImpl;
import cn.com.css.misps.graph.service.ITCoGraphService;
import cn.com.css.misps.graph.util.ProductLUtils;
/**
?* @brief ITCoGraphServiceImpl.java 操作流程配置的接口
?* @attention
?* @author 涂作權
?* @date 2013-9-23
?* @note begin modify by null
?*/
@Transactional
@Service("iTCoGraphService")
public class TCoGraphServiceImpl extends TCoGraphDaoImpl implements ITCoGraphService {
?@Resource
?private ITCoGraphDao tCoGraphDao;
?@Resource(name = "iTTaskStateDao")
?private ITTaskStateDao taskStateDao;
?@Resource(name = "iTProductLDao")
?private ITProductLDao productLDao;
?
?/**
? * \brief 獲得圖形制作的參數
? *
? * @return
? * @attention 通過
? * @author 涂作權
? * @date 2013-9-23
? * @note begin modify by null
? */
?public Object[] getServiceParametersByAccountIdAndDataType(Long accountId,String code) {
??TCoGraph tCoGraph = tCoGraphDao.getConfigByCondition(accountId, code);
??String[] parameters = tCoGraph.getCserviceParameters()
????.split("##");
??
??String[] serviceParameters = new String[parameters.length + 1];
??
??//將服務參數保存到數組中
??for (int i = 0; i < parameters.length; i++) {
???serviceParameters[i] = parameters[i].split("=")[1];
??}
??// 通過這個serviceId來判斷到底是使用哪個服務
??String serviceId = tCoGraph.getVserviceId().toString();
??serviceParameters[parameters.length] = serviceId;
??//這個地方表示獲取數據(包括MICAPS日志數據和服務配置數據)完成,服務參數為20
??new ProductLUtils().saveOrUpdateTaskStateLAndProductL(
????taskStateDao, //操作任務表的Dao
????productLDao,? //操作產品日志庫的Dao
????accountId,??? //用戶賬號
????code,???????? //產品CODE
????"20",???????? //狀態標識
????new Date(),?? //最后制作時間
????"GRAPH",????? //產品類別
????"數據獲取完成" //操作類型
???);
??return serviceParameters;
?}
}
對上面的一個里做AOP操作
package cn.com.css.misps.graph.aop;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
?* @brief GraphAop.java 這是出圖過程中的切面
?* @attention
?* @author 涂作權
?* @date 2013-9-21
?* @note begin modify by null
?*/
@Aspect
@Component(value = "generateGraphAop")
@SuppressWarnings("unused")
public class GraphAop {
?/**
? * \brief 將micaps發布日志中信息進行拼裝整合的過程
? *
? * @attention
? * @author 涂作權
? * @date 2013-9-22
? * @note begin modify by null
? */
?@Pointcut(value = "execution(* cn.com.css.misps.graph.service.impl.TCoGraphServiceImpl.getServiceParametersByAccountIdAndDataType(long,String))&&args(accountId,code)", argNames = "accountId,code")
?private void micapsPathAndDatatypePointcut(Long accountId, String code) {
??System.out.println("操作日志表");
?}
?/**
? * \brief 只有發現有發布日志的時候才會接著執行下面的代碼,否則不執行。所以這里就是就是數據獲取的部分,
? * 并且是在服務參數也獲取到了之后才算是數據獲取階段
? *
? * @param accountId
? *??????????? :表示最后配置的系統管理員角色的用戶的用戶id
? * @param code
? *??????????? :這里代表的時候是micaps日志文件中用來區分產品標識,這里希望是類似11節的產品CODE
? * @attention 這里的CODE代表的意義可能在MICAPS日志規范定下來了之后會變
? * @author 涂作權
? * @date 2013-9-23
? * @note begin modify by null
? */
?@AfterReturning(value = "micapsPathAndDatatypePointcut(accountId,code)", argNames = "accountId,code")
?private void serviceParametersAdvice(Long accountId, String code) {
??System.out.println("數據獲取中");
?}
?// /**
?// * \brief 獲取圖形流程配置表中的數據信息
?// *
?// * @attention 這里面的參數可變
?// * @author 涂作權
?// * @date 2013-9-22
?// * @note begin modify by null
?// *
?// @Pointcut("execution(* cn.com.css.misps.graph.dao.impl.TCoGraphDaoImpl.getConfigByCondition(..))")
?// private void tCoGraphPointcut() {
?// }
?//
?// /**
?// * \brief 圖形生成的過程
?// *
?// * @attention
?// * @author 涂作權
?// * @date 2013-9-22
?// * @note begin modify by null
?// */
?// @Pointcut("execution(* cn.com.css.misps.graph.service.impl.GraphServiceImpl.generateGraphs(..))")
?// private void generateGraphsPointcut() {
?// }
?//
?// /**
?// * \brief 生成獲取micaps文件的路徑和數據類型的日志
?// *
?// * @attention 將micaps文件的路徑和數據類型保存到數據庫中
?// * @author 涂作權
?// * @date 2013-9-22
?// * @note begin modify by null
?// */
?// @Before("micapsPathAndDatatypePointcut()")
?// public void micapsPathAndDatatypeAdvice() {
?// System.out.println("獲取micaps文件路徑和數據類型的拼接字符串......");
?// }
?//
?// /**
?// * \brief 生成圖形前做日志
?// *
?// * @attention 將日志信息保存到制作圖形產品的日志表中
?// * @author 涂作權
?// * @date 2013-9-22
?// * @note begin modify by null
?// */
?// @Before("generateGraphsPointcut()")
?// public void generateGraphsAdvice() {
?// System.out.println("生成圖形產品.....");
?// }
}
這里是另外一個AOP的寫法:
package cn.com.css.misps.graph.aop;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.tempuri.IMPService;
/**
?* @brief GraphUtilsAop.java 這個圖形制作工具類的切面
?* @attention
?* @author 涂作權
?* @date 2013-9-26
?* @note begin modify by null
?*/
@SuppressWarnings("unused")
@Aspect
@Component("GraphUtilsAop")
public class GraphUtilsAop {
?/**
? * \brief 圖形制作的切入點
? *
? * @attention 這里是圖形制作切入點
? * @author 涂作權
? * @date 2013-9-26
? * @note begin modify by null
? */
?@Pointcut(value = "execution(* cn.com.css.misps.graph.util.GraphUtils.getGraphBytesByMpServiceFile(Long,String))&&args(accountId,code)",
???argNames = "accountId,code")
?private void getGraphBytesByMpServiceFilePointcut(Long accountId,
???String code) {
?}
?@Before("getGraphBytesByMpServiceFilePointcut(accountId,code)")
?private void getGraphBytesByMpServiceFileAdvice(Long accountId,
???String code) {
??System.out.println("accountId = " + accountId);
??System.out.println("code = " + code);
?}
}
這里是另外一個AOP的寫法:
?
?
package cn.com.css.misps.graph.aop;
import javax.annotation.Resource;
import org.aopalliance.intercept.Joinpoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import cn.com.css.misps.domain.TProductL;
import cn.com.css.misps.graph.dao.ITMicapsLDao;
import cn.com.css.misps.graph.dao.ITProductLDao;
/**
?* @brief MicapsAOP.java 這個類是關于MICAPS日志相關的aop
?* @attention
?* @author 涂作權
?* @date 2013-9-22
?* @note begin modify by null
?*/
@Aspect
@Component(value = "micapsAOP")
@SuppressWarnings("unused")
public class MicapsAOP {
?@Resource(name = "iTProductLDao")
?private ITProductLDao productLDao;?
?@Resource(name = "iTMicapsLDao")
?private ITMicapsLDao tMicapsLDao;
?/**
? * \brief 查找狀態是"發布"的,并且掃描狀態為0的MICAPS日志,
? * 0表示未掃描過的,也就是說是新產生的MICAPS制作日志文件
? *
? * @attention 掃描MICAPS制作日志文件
? * @author 涂作權
? * @date 2013-9-22
? * @note begin modify by null
? */
?@Pointcut(value = "execution(* cn.com.css.misps.graph.dao.impl.TMicapsLDaoImpl.findTMicapsLByState(String,String))&&args(state,ifScanned)", argNames = "state,ifScanned")
?private void dataSourcePointcut(String state, String ifScanned) {}
?/**
? * \brief 說明一直在進行
? *
? * @attention 注意這里的參數是cn.com.css.misps.graph.dao.impl.TMicapsLDaoImpl.
? *??????????? findTMicapsLByState(..)方法中的參數
? *??????????? 還可以直接通過:@Before("dataSourcePointcut()&&args(state,ifScanned)"
? *??????????? )方式, 在上面的切入點部分直接寫execution表達式,也就是: execution(*
? *??????????? cn.com.css.misps
? *??????????? .graph.dao.impl.TMicapsLDaoImpl.findTMicapsLByState(..))<br/>
? *
? *??????????? 這里不用再寫:args(state,ifScanned)
? *
? * @author 涂作權
? * @date 2013-9-22
? * @note begin modify by null
? */
//?@Before(value = "dataSourcePointcut(state,ifScanned)", argNames = "state,ifScanned")
//?public void dataSourceBeforeAdvice(String state, String ifScanned) {
//??System.out.println("state=======" + state);
//??System.out.println("ifScanned===" + ifScanned);
//??System.out.println("按照條件查找micaps日志的dao.impl....");
//?}
?/**
? * \brief 這里是后置通知
? *
? * @param state
? *??????????? 標識狀態的參數
? * @param ifScanned
? *??????????? 表示是否掃描過,如果為0表示掃描過,如果為1表示掃描過
? * @attention 通過后置通知將查詢過T_MICAPS_L表中的ifScanned參數變為1,表示掃描過
? * @author 涂作權
? * @date 2013-9-22
? * @note begin modify by null
? */
?@AfterReturning(value = "dataSourcePointcut(state,ifScanned)", argNames = "state,ifScanned")
?public void dataSourceAfterReturningAdvice(String state, String ifScanned) {
??//更新狀態
??tMicapsLDao.updateFieldOfIfScanned();
?}
?/**
? * \brief 這里是一個最終通知
? *
? * @param state
? *??????????? 標識狀態的參數
? * @param ifScanned
? *??????????? 表示是否掃描過,如果為0表示掃描過,如果為1表示掃描過
? * @attention
? * @author 涂作權
? * @date 2013-9-22
? * @note begin modify by null
? */
//?@After(value = "dataSourcePointcut(state,ifScanned)", argNames = "state,ifScanned")
//?public void dataSourceAfterAdvice(String state, String ifScanned) {
//??System.out.println("這里是MICAPS相關的最終通知,暫時不做任何動作..............");
//?}
?/**
? * \brief 這里是一個異常通知
? *
? * @attention 如果調用中出現了異常,則執行這一句
? * @author 涂作權
? * @date 2013-9-22
? * @note begin modify by null
? */
//?@AfterThrowing(value = "dataSourcePointcut(state,ifScanned)", throwing = "ex")
//?public void dataSourceThrowMethod(String state, String ifScanned,
//???Throwable ex) {
//??System.out.println(ex.getMessage());
//??System.out.println("如果在查詢MICAPS日志信息的時候出現了異常,那么將執行這一句......");
//?}
}
這里是另外一個AOP的寫法:
package cn.com.css.misps.graph.aop;
import java.util.Date;
import javax.annotation.Resource;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import cn.com.css.misps.graph.dao.ITProductLDao;
import cn.com.css.misps.graph.dao.ITTaskStateDao;
import cn.com.css.misps.graph.util.ProductLUtils;
/**
?* @brief TCoGraphAOP.java 類或者接口的簡要說明
?* @attention 如果沒有找到這個CODE對應的產品提示:這種產品沒有配置, 或者沒有對產品進行管理,請管理員添加圖形產品的配置
?*
?* @author 圖形產品相關的日志信息
?* @date 2013-9-25
?* @note begin modify by null
?*/
@Aspect
@Component("tCoGraphAOP")
public class TCoGraphAOP {
?@Resource(name = "iTTaskStateDao")
?private ITTaskStateDao taskStateDao;
?@Resource(name = "iTProductLDao")
?private ITProductLDao productLDao;
?
?@SuppressWarnings("unused")
?@Pointcut(value = "execution(* cn.com.css.misps.graph.service.impl.TCoGraphServiceImpl.getServiceParametersByAccountIdAndDataType(*,Long,String))" +
???"&&args(accountId,code)",
???argNames = "accountId,code")
?private void queryGraphConfig(Long accountId, String code) {
?}
?/**
? * \brief 獲取流程配置參數方法的前置通知,這個時候一定發現了MICAPS日志發布信息
? *
? * @param accountId
? * @param code
? * @attention 如果在BaseInfo表中沒有找到這種產品,做出提示,告訴系統管理員進行圖形產品配置
? * @author 涂作權
? * @date 2013-9-25
? * @note begin modify by null
? */
?@AfterReturning(value = "queryGraphConfig(accountId,code)",
???argNames = "accountId,code")
?public void queryGraphConfigAdvice(Long accountId, String code) {
??new ProductLUtils().saveOrUpdateTaskStateLAndProductL(
???taskStateDao, //操作任務表的Dao
???productLDao,? //操作產品日志庫的Dao
???accountId,??? //用戶賬號
???code,???????? //產品CODE
???"20",???????? //狀態標識
???new Date(),?? //最后制作時間
???"GRAPH",????? //產品類別
???"數據獲取完成" //操作類型
??);
?}
}
AOP的單元測試類:
package cn.com.css.misps.graph.aop;
import java.io.File;
import java.util.List;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import cn.com.css.misps.domain.TMicapsL;
import cn.com.css.misps.graph.dao.ITMicapsLDao;
import cn.com.css.misps.graph.service.impl.TCoGraphServiceImpl;
/**
?* @brief PersonProxyTest.java 類或者接口的簡要說明
?* @attention 使用注意事項
?* @author Administrator
?* @date 2013-9-22
?* @note begin modify by 修改人 修改時間 修改內容摘要說明
?*/
public class ProxyTest {
?@Test
?public void test() {
??ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
????"applicationContext.xml");
??PersonDao personDao = (PersonDao) applicationContext
????.getBean("personDao");
??personDao.deletePerson();
?}
?@Test
?public void test1() {
??ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
??"applicationContext.xml");
??TCoGraphServiceImpl coGraphService = (TCoGraphServiceImpl) applicationContext
??.getBean("iTCoGraphService");
??
??coGraphService.getServiceParametersByAccountIdAndDataType(1L, "SEVP_PWSC_SJCF_PRO_EQ2_TRD_L88_PB_001_01_PNG");
?}
?
?
?@Test
?public void test2() {
??ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
????"applicationContext.xml");
??ITMicapsLDao tMicapsLDao = (ITMicapsLDao) applicationContext
????.getBean("iTMicapsLDao");
??List<TMicapsL> tMicapsLByStateList = tMicapsLDao
????.findTMicapsLByState("發布","0");
??if (tMicapsLByStateList != null) {
???for (int i = 0; i < tMicapsLByStateList.size(); i++) {
????TMicapsL tMicapsL = tMicapsLByStateList.get(i);
????System.out.println(tMicapsL.getCoutputfolder() + File.separator
??????+ tMicapsL.getCoutputdata() + "##"
??????+ tMicapsL.getCcode());
???}
??} else {
???return;
??}
?}
}
?
?開發AOP時可能遇到的常見問題:http://blog.csdn.net/tototuzuoquan/article/details/11889239
?
總結
以上是生活随笔為你收集整理的项目中的一个AOP的编写案例(配置+案例)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Hibernate主键生成策略总结(这里
- 下一篇: 根据我国宪法的明确规定全国人民代表大会由