前言
到目前為止贞奋,已經(jīng)簡單學(xué)習(xí)了Spring的Core模塊内狸、....于是我們就開啟了Spring的AOP模塊了...在講解AOP模塊之前欠肾,首先我們來講解一下cglib代理忱详、以及怎么手動實現(xiàn)AOP編程
cglib代理
在講解cglib之前虚汛,首先我們來回顧一下靜態(tài)代理和動態(tài)代理....我之前就寫過了靜態(tài)代理匾浪、動態(tài)代理的博文:http://blog.csdn.net/hon_3y/article/details/70655966
由于靜態(tài)代理需要實現(xiàn)目標(biāo)對象的相同接口,那么可能會導(dǎo)致代理類會非常非常多....不好維護(hù)---->因此出現(xiàn)了動態(tài)代理
動態(tài)代理也有個約束:目標(biāo)對象一定是要有接口的卷哩,沒有接口就不能實現(xiàn)動態(tài)代理.....----->因此出現(xiàn)了cglib代理
cglib代理也叫子類代理蛋辈,從內(nèi)存中構(gòu)建出一個子類來擴(kuò)展目標(biāo)對象的功能!
- CGLIB是一個強(qiáng)大的高性能的代碼生成包将谊,它可以在運(yùn)行期擴(kuò)展Java類與實現(xiàn)Java接口冷溶。它廣泛的被許多AOP的框架使用,例如Spring AOP和dynaop尊浓,為他們提供方法的interception(攔截)逞频。
編寫cglib代理
接下來我們就講講怎么寫cglib代理:
- 需要引入cglib – jar文件, 但是spring的核心包中已經(jīng)包括了cglib功能栋齿,所以直接引入spring-core-3.2.5.jar即可苗胀。
- 引入功能包后,就可以在內(nèi)存中動態(tài)構(gòu)建子類
- 代理的類不能為final瓦堵,否則報錯【在內(nèi)存中構(gòu)建子類來做擴(kuò)展基协,當(dāng)然不能為final,有final就不能繼承了】
- 目標(biāo)對象的方法如果為final/static, 那么就不會被攔截菇用,即不會執(zhí)行目標(biāo)對象額外的業(yè)務(wù)方法澜驮。
//需要實現(xiàn)MethodInterceptor接口
public class ProxyFactory implements MethodInterceptor{
// 維護(hù)目標(biāo)對象
private Object target;
public ProxyFactory(Object target){
this.target = target;
}
// 給目標(biāo)對象創(chuàng)建代理對象
public Object getProxyInstance(){
//1\. 工具類
Enhancer en = new Enhancer();
//2\. 設(shè)置父類
en.setSuperclass(target.getClass());
//3\. 設(shè)置回調(diào)函數(shù)
en.setCallback(this);
//4\. 創(chuàng)建子類(代理對象)
return en.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("開始事務(wù).....");
// 執(zhí)行目標(biāo)對象的方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事務(wù).....");
return returnValue;
}
}
- 測試:
public class App {
public static void main(String[] args) {
UserDao userDao = new UserDao();
UserDao factory = (UserDao) new ProxyFactory(userDao).getProxyInstance();
factory.save();
}
}
使用cglib就是為了彌補(bǔ)動態(tài)代理的不足【動態(tài)代理的目標(biāo)對象一定要實現(xiàn)接口】
手動實現(xiàn)AOP編程
AOP 面向切面的編程:
- AOP可以實現(xiàn)“業(yè)務(wù)代碼”與“關(guān)注點代碼”分離
下面我們來看一段代碼:
// 保存一個用戶
public void add(User user) {
Session session = null;
Transaction trans = null;
try {
session = HibernateSessionFactoryUtils.getSession(); // 【關(guān)注點代碼】
trans = session.beginTransaction(); // 【關(guān)注點代碼】
session.save(user); // 核心業(yè)務(wù)代碼
trans.commit(); //…【關(guān)注點代碼】
} catch (Exception e) {
e.printStackTrace();
if(trans != null){
trans.rollback(); //..【關(guān)注點代碼】
}
} finally{
HibernateSessionFactoryUtils.closeSession(session); ////..【關(guān)注點代碼】
}
}
- 關(guān)注點代碼,就是指重復(fù)執(zhí)行的代碼惋鸥。
- 業(yè)務(wù)代碼與關(guān)注點代碼分離泉唁,好處?
- 關(guān)注點代碼寫一次即可揩慕;
- 開發(fā)者只需要關(guān)注核心業(yè)務(wù)亭畜;
- 運(yùn)行時期,執(zhí)行核心業(yè)務(wù)代碼時候動態(tài)植入關(guān)注點代碼迎卤; 【代理】
案例分析:
- IUser接口
public interface IUser {
void save();
}
我們一步一步來分析拴鸵,首先我們的UserDao有一個save()方法,每次都要開啟事務(wù)和關(guān)閉事務(wù)
//@Component -->任何地方都能用這個
@Repository //-->這個在Dao層中使用
public class UserDao {
public void save() {
System.out.println("開始事務(wù)");
System.out.println("DB:保存用戶");
System.out.println("關(guān)閉事務(wù)");
}
}
- 在剛學(xué)習(xí)java基礎(chǔ)的時候,我們知道:如果某些功能經(jīng)常需要用到就封裝成方法:
//@Component -->任何地方都能用這個
@Repository //-->這個在Dao層中使用
public class UserDao {
public void save() {
begin();
System.out.println("DB:保存用戶");
close();
}
public void begin() {
System.out.println("開始事務(wù)");
}
public void close() {
System.out.println("關(guān)閉事務(wù)");
}
}
- 現(xiàn)在呢劲藐,我們可能有多個Dao八堡,都需要有開啟事務(wù)和關(guān)閉事務(wù)的功能,現(xiàn)在只有UserDao中有這兩個方法聘芜,重用性還是不夠高兄渺。因此我們抽取出一個類出來
public class AOP {
public void begin() {
System.out.println("開始事務(wù)");
}
public void close() {
System.out.println("關(guān)閉事務(wù)");
}
}
- 在UserDao維護(hù)這個變量,要用的時候汰现,調(diào)用方法就行了挂谍。
@Repository //-->這個在Dao層中使用
public class UserDao {
AOP aop;
public void save() {
aop.begin();
System.out.println("DB:保存用戶");
aop.close();
}
}
- 現(xiàn)在的開啟事務(wù)、關(guān)閉事務(wù)還是需要我在userDao中手動調(diào)用瞎饲。還是不夠優(yōu)雅口叙。。我想要的效果:當(dāng)我在調(diào)用userDao的save()方法時嗅战,動態(tài)地開啟事務(wù)妄田、關(guān)閉事務(wù)。因此驮捍,我們就用到了代理疟呐。當(dāng)然了,真正執(zhí)行方法的都是userDao东且、要干事的是AOP萨醒,因此在代理中需要維護(hù)他們的引用。
public class ProxyFactory {
//維護(hù)目標(biāo)對象
private static Object target;
//維護(hù)關(guān)鍵點代碼的類
private static AOP aop;
public static Object getProxyInstance(Object target_, AOP aop_) {
//目標(biāo)對象和關(guān)鍵點代碼的類都是通過外界傳遞進(jìn)來
target = target_;
aop = aop_;
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
aop.begin();
Object returnValue = method.invoke(target, args);
aop.close();
return returnValue;
}
}
);
}
}
工廠靜態(tài)方法:
- 把AOP加入IOC容器中
//把該對象加入到容器中
@Component
public class AOP {
public void begin() {
System.out.println("開始事務(wù)");
}
public void close() {
System.out.println("關(guān)閉事務(wù)");
}
}
- 把UserDao放入容器中
@Component
public class UserDao {
public void save() {
System.out.println("DB:保存用戶");
}
}
- 在配置文件中開啟注解掃描,使用工廠靜態(tài)方法創(chuàng)建代理對象
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="proxy" class="aa.ProxyFactory" factory-method="getProxyInstance">
<constructor-arg index="0" ref="userDao"/>
<constructor-arg index="1" ref="AOP"/>
</bean>
<context:component-scan base-package="aa"/>
</beans>
- 測試苇倡,得到UserDao對象富纸,調(diào)用方法
public class App {
public static void main(String[] args) {
ApplicationContext ac =
new ClassPathXmlApplicationContext("aa/applicationContext.xml");
IUser iUser = (IUser) ac.getBean("proxy");
iUser.save();
}
}
工廠非靜態(tài)方法
上面使用的是工廠靜態(tài)方法來創(chuàng)建代理類對象。我們也使用一下非靜態(tài)的工廠方法創(chuàng)建對象旨椒。
package aa;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* Created by ozc on 2017/5/11.
*/
public class ProxyFactory {
public Object getProxyInstance(final Object target_, final AOP aop_) {
//目標(biāo)對象和關(guān)鍵點代碼的類都是通過外界傳遞進(jìn)來
return Proxy.newProxyInstance(
target_.getClass().getClassLoader(),
target_.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
aop_.begin();
Object returnValue = method.invoke(target_, args);
aop_.close();
return returnValue;
}
}
);
}
}
配置文件:先創(chuàng)建工廠晓褪,再創(chuàng)建代理類對象
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--創(chuàng)建工廠-->
<bean id="factory" class="aa.ProxyFactory"/>
<!--通過工廠創(chuàng)建代理-->
<bean id="IUser" class="aa.IUser" factory-bean="factory" factory-method="getProxyInstance">
<constructor-arg index="0" ref="userDao"/>
<constructor-arg index="1" ref="AOP"/>
</bean>
<context:component-scan base-package="aa"/>
</beans>
AOP的概述
Aop: aspect object programming 面向切面編程
- 功能: 讓關(guān)注點代碼與業(yè)務(wù)代碼分離!
- 面向切面編程就是指: 對很多功能都有的重復(fù)的代碼抽取综慎,再在運(yùn)行的時候往業(yè)務(wù)方法上動態(tài)植入“切面類代碼”涣仿。
關(guān)注點:
- 重復(fù)代碼就叫做關(guān)注點。
// 保存一個用戶
public void add(User user) {
Session session = null;
Transaction trans = null;
try {
session = HibernateSessionFactoryUtils.getSession(); // 【關(guān)注點代碼】
trans = session.beginTransaction(); // 【關(guān)注點代碼】
session.save(user); // 核心業(yè)務(wù)代碼
trans.commit(); //…【關(guān)注點代碼】
} catch (Exception e) {
e.printStackTrace();
if(trans != null){
trans.rollback(); //..【關(guān)注點代碼】
}
} finally{
HibernateSessionFactoryUtils.closeSession(session); ////..【關(guān)注點代碼】
}
}
切面:
- 關(guān)注點形成的類示惊,就叫切面(類)好港!
public class AOP {
public void begin() {
System.out.println("開始事務(wù)");
}
public void close() {
System.out.println("關(guān)閉事務(wù)");
}
}
切入點:
- 執(zhí)行目標(biāo)對象方法,動態(tài)植入切面代碼米罚。
- 可以通過切入點表達(dá)式钧汹,指定攔截哪些類的哪些方法; 給指定的類在運(yùn)行的時候植入切面類代碼录择。
切入點表達(dá)式:
- 指定哪些類的哪些方法被攔截
使用Spring AOP開發(fā)步驟
1) 先引入aop相關(guān)jar文件 (aspectj aop優(yōu)秀組件)
- spring-aop-3.2.5.RELEASE.jar 【spring3.2源碼】
- aopalliance.jar 【spring2.5源碼/lib/aopalliance】
- aspectjweaver.jar 【spring2.5源碼/lib/aspectj】或【aspectj-1.8.2\lib】
- aspectjrt.jar 【spring2.5源碼/lib/aspectj】或【aspectj-1.8.2\lib】
注意: 用到spring2.5版本的jar文件拔莱,如果用jdk1.7可能會有問題碗降。
- 需要升級aspectj組件,即使用aspectj-1.8.2版本中提供jar文件提供塘秦。
2) bean.xml中引入aop名稱空間
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
引入jar包
引入4個jar包:
引入名稱空間
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
注解方式實現(xiàn)AOP編程
我們之前手動的實現(xiàn)AOP編程是需要自己來編寫代理工廠的讼渊,現(xiàn)在有了Spring,就不需要我們自己寫代理工廠了尊剔。Spring內(nèi)部會幫我們創(chuàng)建代理工廠爪幻。
- 也就是說,不用我們自己寫代理對象了须误。
因此挨稿,我們只要關(guān)心切面類、切入點霹期、編寫切入表達(dá)式指定攔截什么方法就可以了叶组!
還是以上一個例子為案例拯田,使用Spring的注解方式來實現(xiàn)AOP編程
在配置文件中開啟AOP注解方式
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="aa"/>
<!-- 開啟aop注解方式 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
代碼:
- 切面類
@Component
@Aspect//指定為切面類
public class AOP {
//里面的值為切入點表達(dá)式
@Before("execution(* aa.*.*(..))")
public void begin() {
System.out.println("開始事務(wù)");
}
@After("execution(* aa.*.*(..))")
public void close() {
System.out.println("關(guān)閉事務(wù)");
}
}
- UserDao實現(xiàn)了IUser接口
@Component
public class UserDao implements IUser {
@Override
public void save() {
System.out.println("DB:保存用戶");
}
}
- IUser接口
public interface IUser {
void save();
}
- 測試代碼:
public class App {
public static void main(String[] args) {
ApplicationContext ac =
new ClassPathXmlApplicationContext("aa/applicationContext.xml");
//這里得到的是代理對象....
IUser iUser = (IUser) ac.getBean("userDao");
System.out.println(iUser.getClass());
iUser.save();
}
}
目標(biāo)對象沒有接口
上面我們測試的是UserDao有IUser接口历造,內(nèi)部使用的是動態(tài)代理...那么我們這次測試的是目標(biāo)對象沒有接口
- OrderDao沒有實現(xiàn)接口
@Component
public class OrderDao {
public void save() {
System.out.println("我已經(jīng)進(jìn)貨了!4印吭产!");
}
}
- 測試代碼:
public class App {
public static void main(String[] args) {
ApplicationContext ac =
new ClassPathXmlApplicationContext("aa/applicationContext.xml");
OrderDao orderDao = (OrderDao) ac.getBean("orderDao");
System.out.println(orderDao.getClass());
orderDao.save();
}
}
優(yōu)化和AOP注解API
API:
@Aspect
指定一個類為切面類@Pointcut("execution(* cn.itcast.e_aop_anno.*.*(..))")
指定切入點表達(dá)式@Before("pointCut_()")
前置通知: 目標(biāo)方法之前執(zhí)行@After("pointCut_()")
后置通知:目標(biāo)方法之后執(zhí)行(始終執(zhí)行)@AfterReturning("pointCut_()")
返回后通知: 執(zhí)行方法結(jié)束前執(zhí)行(異常不執(zhí)行)@AfterThrowing("pointCut_()")
異常通知: 出現(xiàn)異常時候執(zhí)行@Around("pointCut_()")
環(huán)繞通知: 環(huán)繞目標(biāo)方法執(zhí)行測試:
// 前置通知 : 在執(zhí)行目標(biāo)方法之前執(zhí)行
@Before("pointCut_()")
public void begin(){
System.out.println("開始事務(wù)/異常");
}
// 后置/最終通知:在執(zhí)行目標(biāo)方法之后執(zhí)行 【無論是否出現(xiàn)異常最終都會執(zhí)行】
@After("pointCut_()")
public void after(){
System.out.println("提交事務(wù)/關(guān)閉");
}
// 返回后通知: 在調(diào)用目標(biāo)方法結(jié)束后執(zhí)行 【出現(xiàn)異常不執(zhí)行】
@AfterReturning("pointCut_()")
public void afterReturning() {
System.out.println("afterReturning()");
}
// 異常通知: 當(dāng)目標(biāo)方法執(zhí)行異常時候執(zhí)行此關(guān)注點代碼
@AfterThrowing("pointCut_()")
public void afterThrowing(){
System.out.println("afterThrowing()");
}
// 環(huán)繞通知:環(huán)繞目標(biāo)方式執(zhí)行
@Around("pointCut_()")
public void around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("環(huán)繞前....");
pjp.proceed(); // 執(zhí)行目標(biāo)方法
System.out.println("環(huán)繞后....");
}
優(yōu)化
我們的代碼是這樣的:每次寫B(tài)efore、After等鸭轮,都要重寫一次切入點表達(dá)式臣淤,這樣就不優(yōu)雅了。
@Before("execution(* aa.*.*(..))")
public void begin() {
System.out.println("開始事務(wù)");
}
@After("execution(* aa.*.*(..))")
public void close() {
System.out.println("關(guān)閉事務(wù)");
}
于是乎窃爷,我們要**使用@Pointcut這個注解邑蒋,來指定切入點表達(dá)式,在用到的地方中按厘,直接引用就行了医吊!**
- 那么我們的代碼就可以改造成這樣了:
@Component
@Aspect//指定為切面類
public class AOP {
// 指定切入點表達(dá)式,攔截哪個類的哪些方法
@Pointcut("execution(* aa.*.*(..))")
public void pt() {
}
@Before("pt()")
public void begin() {
System.out.println("開始事務(wù)");
}
@After("pt()")
public void close() {
System.out.println("關(guān)閉事務(wù)");
}
}
XML方式實現(xiàn)AOP編程
首先逮京,我們把所有的注解都去掉...
- 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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--對象實例-->
<bean id="userDao" class="aa.UserDao"/>
<bean id="orderDao" class="aa.OrderDao"/>
<!--切面類-->
<bean id="aop" class="aa.AOP"/>
<!--AOP配置-->
<aop:config >
<!--定義切入表達(dá)式卿堂,攔截哪些方法-->
<aop:pointcut id="pointCut" expression="execution(* aa.*.*(..))"/>
<!--指定切面類是哪個-->
<aop:aspect ref="aop">
<!--指定來攔截的時候執(zhí)行切面類的哪些方法-->
<aop:before method="begin" pointcut-ref="pointCut"/>
<aop:after method="close" pointcut-ref="pointCut"/>
</aop:aspect>
</aop:config>
</beans>
- 測試:
public class App {
@Test
public void test1() {
ApplicationContext ac =
new ClassPathXmlApplicationContext("aa/applicationContext.xml");
OrderDao orderDao = (OrderDao) ac.getBean("orderDao");
System.out.println(orderDao.getClass());
orderDao.save();
}
@Test
public void test2() {
ApplicationContext ac =
new ClassPathXmlApplicationContext("aa/applicationContext.xml");
IUser userDao = (IUser) ac.getBean("userDao");
System.out.println(userDao.getClass());
userDao.save();
}
}
測試OrderDao
測試UserDao
切入點表達(dá)式
切入點表達(dá)式主要就是來配置攔截哪些類的哪些方法
查官方文檔
..我們?nèi)ノ臋n中找找它的語法...
在文檔中搜索:execution(
語法解析
那么它的語法是這樣子的:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
符號講解:
- ?號代表0或1,可以不寫
- “”號代表任意類型懒棉,0或多*
- 方法參數(shù)為..表示為可變參數(shù)
參數(shù)講解:
- modifiers-pattern?【修飾的類型草描,可以不寫】
- ret-type-pattern【方法返回值類型,必寫】
- declaring-type-pattern?【方法聲明的類型策严,可以不寫】
- name-pattern(param-pattern)【要匹配的名稱穗慕,括號里面是方法的參數(shù)】
- throws-pattern?【方法拋出的異常類型,可以不寫】
官方也有給出一些例子給我們理解:
測試代碼
<!-- 【攔截所有public方法】 -->
<!--<aop:pointcut expression="execution(public * *(..))" id="pt"/>-->
<!-- 【攔截所有save開頭的方法 】 -->
<!--<aop:pointcut expression="execution(* save*(..))" id="pt"/>-->
<!-- 【攔截指定類的指定方法, 攔截時候一定要定位到方法】 -->
<!--<aop:pointcut expression="execution(public * cn.itcast.g_pointcut.OrderDao.save(..))" id="pt"/>-->
<!-- 【攔截指定類的所有方法】 -->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.*(..))" id="pt"/>-->
<!-- 【攔截指定包妻导,以及其自包下所有類的所有方法】 -->
<!--<aop:pointcut expression="execution(* cn..*.*(..))" id="pt"/>-->
<!-- 【多個表達(dá)式】 -->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) || execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) or execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
<!-- 下面2個且關(guān)系的揍诽,沒有意義 -->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) && execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) and execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
<!-- 【取非值】 -->
<!--<aop:pointcut expression="!execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
如果文章有錯的地方歡迎指正诀蓉,大家互相交流。習(xí)慣在微信看技術(shù)文章暑脆,想要獲取更多的Java資源的同學(xué)渠啤,可以關(guān)注微信公眾號:Java3y