spring框架 AOP核心详解
AOP稱為面向切面編程,在程序開發(fā)中主要用來解決一些系統(tǒng)層面上的問題,比如日志,事務(wù),權(quán)限等待,Struts2的攔截器設(shè)計(jì)就是基于AOP的思想,是個比較經(jīng)典的例子。
一 AOP的基本概念
(1)Aspect(切面):通常是一個類,里面可以定義切入點(diǎn)和通知
(2)JointPoint(連接點(diǎn)):程序執(zhí)行過程中明確的點(diǎn),一般是方法的調(diào)用
(3)Advice(通知):AOP在特定的切入點(diǎn)上執(zhí)行的增強(qiáng)處理,有before,after,afterReturning,afterThrowing,around
(4)Pointcut(切入點(diǎn)):就是帶有通知的連接點(diǎn),在程序中主要體現(xiàn)為書寫切入點(diǎn)表達(dá)式
(5)AOP代理:AOP框架創(chuàng)建的對象,代理就是目標(biāo)對象的加強(qiáng)。Spring中的AOP代理可以使JDK動態(tài)代理,也可以是CGLIB代理,前者基于接口,后者基于子類
如果想學(xué)習(xí)Java工程化、高性能及分布式、深入淺出。微服務(wù)、Spring,MyBatis,Netty源碼分析的朋友可以加我的Java高級交流:854630135,群里有阿里大牛直播講解技術(shù),以及Java大型互聯(lián)網(wǎng)技術(shù)的視頻免費(fèi)分享給大家。
二 Spring AOP
Spring中的AOP代理還是離不開Spring的IOC容器,代理的生成,管理及其依賴關(guān)系都是由IOC容器負(fù)責(zé),Spring默認(rèn)使用JDK動態(tài)代理,在需要代理類而不是代理接口的時候,Spring會自動切換為使用CGLIB代理,不過現(xiàn)在的項(xiàng)目都是面向接口編程,所以JDK動態(tài)代理相對來說用的還是多一些。
三 基于注解的AOP配置方式
1.啟用@AsjectJ支持
在applicationContext.xml中配置下面一句:
2.通知類型介紹
(1)Before:在目標(biāo)方法被調(diào)用之前做增強(qiáng)處理,@Before只需要指定切入點(diǎn)表達(dá)式即可
(2)AfterReturning:在目標(biāo)方法正常完成后做增強(qiáng),@AfterReturning除了指定切入點(diǎn)表達(dá)式后,還可以指定一個返回值形參名returning,代表目標(biāo)方法的返回值
(3)AfterThrowing:主要用來處理程序中未處理的異常,@AfterThrowing除了指定切入點(diǎn)表達(dá)式后,還可以指定一個throwing的返回值形參名,可以通過該形參名
來訪問目標(biāo)方法中所拋出的異常對象
(4)After:在目標(biāo)方法完成之后做增強(qiáng),無論目標(biāo)方法時候成功完成。@After可以指定一個切入點(diǎn)表達(dá)式
(5)Around:環(huán)繞通知,在目標(biāo)方法完成前后做增強(qiáng)處理,環(huán)繞通知是最重要的通知類型,像事務(wù),日志等都是環(huán)繞通知,注意編程中核心是一個ProceedingJoinPoint
3.例子:
spring框架 AOP核心詳解
(1)Operator.java -- 切面類
@Componentbr/@Aspect
public class Operator {
@Pointcut(execution( com.aijava.springcode.service...*(..)))
public void pointCut(){}
@Before(pointCut())
public void doBefore(JoinPoint joinPoint){
System.out.println(AOP Before Advice...);
}
@After(pointCut())
public void doAfter(JoinPoint joinPoint){
System.out.println(AOP After Advice...);
}
@AfterReturning(pointcut=pointCut(),returning=returnVal)
public void afterReturn(JoinPoint joinPoint,Object returnVal){
System.out.println(AOP AfterReturning Advice: + returnVal);
}
@AfterThrowing(pointcut=pointCut(),throwing=error)
public void afterThrowing(JoinPoint joinPoint,Throwable error){
System.out.println(AOP AfterThrowing Advice... + error);
System.out.println(AfterThrowing...);
}
@Around(pointCut())
public void around(ProceedingJoinPoint pjp){
System.out.println(AOP Aronud before...);
try {
pjp.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println(AOP Aronud after...);
}
}
(2)UserService.java -- 定義一些目標(biāo)方法
@Service
public class UserService {
public void add(){
System.out.println(UserService add());
}
public boolean delete(){
System.out.println(UserService delete());
return true;
}
public void edit(){
System.out.println(UserService edit());
int i = 5/0;
}
}
(3).applicationContext.xml
(4).Test.java
public class Test {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext(classpath:applicationContext.xml);
UserService userService = (UserService) ctx.getBean(userService);
userService.add();
}
}
上面是一個比較簡單的測試,基本涵蓋了各種增強(qiáng)定義。注意:做環(huán)繞通知的時候,調(diào)用ProceedingJoinPoint的proceed()方法才會執(zhí)行目標(biāo)方法。
4.通知執(zhí)行的優(yōu)先級
進(jìn)入目標(biāo)方法時,先織入Around,再織入Before,退出目標(biāo)方法時,先織入Around,再織入AfterReturning,最后才織入After。
注意:Spring AOP的環(huán)繞通知會影響到AfterThrowing通知的運(yùn)行,不要同時使用!同時使用也沒啥意義。
如果想學(xué)習(xí)Java工程化、高性能及分布式、深入淺出。微服務(wù)、Spring,MyBatis,Netty源碼分析的朋友可以加我的Java高級交流:854630135,群里有阿里大牛直播講解技術(shù),以及Java大型互聯(lián)網(wǎng)技術(shù)的視頻免費(fèi)分享給大家。
5.切入點(diǎn)的定義和表達(dá)式
切入點(diǎn)表達(dá)式的定義算是整個AOP中的核心,有一套自己的規(guī)范
Spring AOP支持的切入點(diǎn)指示符:
(1)execution:用來匹配執(zhí)行方法的連接點(diǎn)
A:@Pointcut(execution( com.aijava.springcode.service...*(..)))
第一個表示匹配任意的方法返回值,..(兩個點(diǎn))表示零個或多個,上面的第一個..表示service包及其子包,第二個表示所有類,第三個*表示所有方法,第二個..表示
方法的任意參數(shù)個數(shù)
B:@Pointcut(within(com.aijava.springcode.service.*))
within限定匹配方法的連接點(diǎn),上面的就是表示匹配service包下的任意連接點(diǎn)
C:@Pointcut(this(com.aijava.springcode.service.UserService))
this用來限定AOP代理必須是指定類型的實(shí)例,如上,指定了一個特定的實(shí)例,就是UserService
D:@Pointcut(bean(userService))
bean也是非常常用的,bean可以指定IOC容器中的bean的名稱
后言: spring 的環(huán)繞通知和前置通知,后置通知有著很大的區(qū)別,主要有兩個重要的區(qū)別:
1) 目標(biāo)方法的調(diào)用由環(huán)繞通知決定,即你可以決定是否調(diào)用目標(biāo)方法,而前置和后置通知 是不能決定的,他們只是在方法的調(diào)用前后執(zhí)行通知而已,即目標(biāo)方法肯定是要執(zhí)行的。
2) 環(huán)繞通知可以控制返回對象,即你可以返回一個與目標(biāo)對象完全不同的返回值,雖然這很危險(xiǎn),但是你卻可以辦到。而后置方法是無法辦到的,因?yàn)樗窃谀繕?biāo)方法返回值后調(diào)用
6.基于XML形式的配置方式
開發(fā)中如果選用XML配置方式,通常就是POJO+XML來開發(fā)AOP,大同小異,無非就是在XML文件中寫切入點(diǎn)表達(dá)式和通知類型
例子:
(1)Log.java
public class Log {
private Integer id;
//操作名稱,方法名
private String operName;
//操作人
private String operator;
//操作參數(shù)
private String operParams;
//操作結(jié)果 成功/失敗
private String operResult;
//結(jié)果消息
private String resultMsg;
//操作時間
private Date operTime = new Date();
setter,getter
}
(2).Logger.java
/**
日志記錄器 (AOP日志通知)
*/
public class Logger {
@Resource
private LogService logService;
public Object record(ProceedingJoinPoint pjp){
Log log = new Log();
try {
log.setOperator(admin);
String mname = pjp.getSignature().getName();
log.setOperName(mname);
//方法參數(shù),本例中是User user
Object[] args = pjp.getArgs();
log.setOperParams(Arrays.toString(args));
//執(zhí)行目標(biāo)方法,返回的是目標(biāo)方法的返回值,本例中 void
Object obj = pjp.proceed();
if(obj != null){
log.setResultMsg(obj.toString());
}else{
log.setResultMsg(null);
}
log.setOperResult(success);
log.setOperTime(new Date());
return obj;
} catch (Throwable e) {
log.setOperResult(failure);
log.setResultMsg(e.getMessage());
} finally{
logService.saveLog(log);
}
return null;
}
}
(3).applicationContext.xml
注意切入點(diǎn)表達(dá)式,!bean(logService) 做日志通知的時候,不要給日志本身做日志,否則會造成無限循環(huán)!
如果想學(xué)習(xí)Java工程化、高性能及分布式、深入淺出。微服務(wù)、Spring,MyBatis,Netty源碼分析的朋友可以加我的Java高級交流:854630135,群里有阿里大牛直播講解技術(shù),以及Java大型互聯(lián)網(wǎng)技術(shù)的視頻免費(fèi)分享給大家。
有關(guān)更詳細(xì)的Spring AOP知識,可以查看Spring官方文檔第9章Aspect Oriented Programming with Spring
7.JDK動態(tài)代理介紹
例子:
(1)UserService.java
public interface UserService {
public void add();
}
(2)UserServiceImpl.java
public class UserServiceImpl implements UserService{
public void add() {
System.out.println(User add()...);
}
}
(3)ProxyUtils.java
public class ProxyUtils implements InvocationHandler{
private Object target;
public ProxyUtils(Object target){
this.target = target;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(do sth before...);
method.invoke(target, args);
System.out.println(do sth after...);
return null;
}
}
(4)Test.java
public class Test {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
ProxyUtils proxyUtils = new ProxyUtils(userService);
UserService proxyObject = (UserService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),UserServiceImpl.class.getInterfaces(), proxyUtils);
proxyObject.add();
}
}
JDK動態(tài)代理核心還是一個InvocationHandler,記住這個就行了。
?
轉(zhuǎn)載于:https://juejin.im/post/5c8b6a626fb9a049e6612377
總結(jié)
以上是生活随笔為你收集整理的spring框架 AOP核心详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: UIModalPresentationS
- 下一篇: Manjaro Linux执行某些命令缺