Spring AOP在web應(yīng)用中的使用方法實例
前言
之前的aop是通過手動創(chuàng)建代理類來進(jìn)行通知的,但是在日常開發(fā)中,我們并不愿意在代碼中硬編碼這些代理類,我們更愿意使用DI和IOC來管理aop代理類。Spring為我們提供了以下方式來使用aop框架
一、以聲明的方式配置AOP(就是使用xml配置文件)
1.使用ProxyFactoryBean的方式:
ProxyFactoryBean類是FactoryBean的一個實現(xiàn)類,它允許指定一個bean作為目標(biāo),并且為該bean提供一組通知和顧問(這些通知和顧問最終會被合并到一個AOP代理中)它和我們之前的ProxyFactory都是Advised的實現(xiàn)。
以下是一個簡單的例子:一個學(xué)生和一個老師,老師會告訴學(xué)生應(yīng)該做什么。
public class Student {
public void talk() {
System.out.println("I am a boy");
}
public void walk() {
System.out.println("I am walking");
}
public void sleep() {
System.out.println("I want to sleep");
}
}
老師類
public class Teacher {
private Student student;
public void tellStudent(){
student.sleep();
student.talk();
}
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
}
我們創(chuàng)建一個通知類,這個和之前是一樣的SpringAOP中的通知類型以及創(chuàng)建
package cn.lyn4ever.aop;
import org.aspectj.lang.JoinPoint;
public class AuditAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, @Nullable Object o) throws Throwable {
System.out.println("這個方法被通知了" + method.getName());
}
}
然后就使用spring的IOC來管理這個通知類,在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:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd"> <!--注入student--> <bean name="student" class="cn.lyn4ever.aop.aopconfig.Student"> </bean> <!--注入teacher--> <bean name="teacher" class="cn.lyn4ever.aop.aopconfig.Teacher"> <!--注意,這個student的屬性要是上邊的代理類,而不是能student--> <!--<property name="student" ref="student"/>--> <property name="student" ref="proxyOne"/> </bean> <!--注入我們創(chuàng)建的通知類--> <bean id="advice" class="cn.lyn4ever.aop.aopconfig.AuditAdvice"></bean> <!--創(chuàng)建代理類,使用前邊寫的通知進(jìn)行通知,這樣會使這個類上的所有方法都被通知--> <bean name="proxyOne" class="org.springframework.aop.framework.ProxyFactoryBean" p:target-ref="student" p:interceptorNames-ref="interceptorNames"> <!--因為interceptorNames的屬性是一個可變參數(shù),也就是一個list--> </bean> <!--在上邊引入了util的名稱空間,簡化了書寫--> <util:list id="interceptorNames"> <value>advice</value> </util:list> </beans>
測試類
public static void main(String[] args) {
GenericXmlApplicationContext context = new GenericXmlApplicationContext();
context.load("application1.xml");
context.refresh();
Teacher teacher = (Teacher) context.getBean("teacherOne");
teacher.tellStudent();
}
運行結(jié)果沒有問題

以上是通過直接創(chuàng)建通知的方式,接下來我們試一個創(chuàng)建一個切入點(因為以上是對類中所有方法都進(jìn)行通知,這時我們使用切入點只對其中部分方法進(jìn)行通知),在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:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
https://www.springframework.org/schema/util/spring-util.xsd">
<!--注入student-->
<bean name="student" class="cn.lyn4ever.aop.aopconfig.Student">
</bean>
<!--注入teacher-->
<bean name="teacherOne" class="cn.lyn4ever.aop.aopconfig.Teacher">
<!--注意,這個student的屬性要是上邊的代理類,而不是能student-->
<!--<property name="student" ref="student"/>-->
<property name="student" ref="proxyOne"/>
</bean>
<!--注入我們創(chuàng)建的通知類-->
<bean id="advice" class="cn.lyn4ever.aop.aopconfig.AuditAdvice"></bean>
<!--創(chuàng)建代理類,使用前邊寫的通知進(jìn)行通知,這樣會使這個類上的所有方法都被通知-->
<bean name="proxyOne" class="org.springframework.aop.framework.ProxyFactoryBean" p:target-ref="student"
p:interceptorNames-ref="interceptorNames">
<!--因為interceptorNames的屬性是一個可變參數(shù),也就是一個list-->
</bean>
<!--在上邊引入了util的名稱空間,簡化了書寫-->
<util:list id="interceptorNames">
<value>advice</value>
</util:list>
<!--以下是使用切入點的方式來進(jìn)行通知,上邊的代碼和上一個配置文件一樣,沒有修改-->
<!--sutdent基本bean,我們繼續(xù)使用-->
<bean name="teacherTwo" p:student-ref="proxyTwo" class="cn.lyn4ever.aop.aopconfig.Teacher"/>
<bean id="proxyTwo" class="org.springframework.aop.framework.ProxyFactoryBean"
p:target-ref="student" p:interceptorNames-ref="interceptorAdvisorNames">
</bean>
<util:list id="interceptorAdvisorNames">
<value>advisor</value>
</util:list>
<!--配置切入點bean-->
<bean id="advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
p:advice-ref="advice">
<property name="pointcut">
<!--這個切入點我們用了一個匿名bean來寫aspectJ的表達(dá)式,當(dāng)然也可以用其他的類型切入點,這個在上邊鏈接中能看到-->
<bean class="org.springframework.aop.aspectj.AspectJExpressionPointcut"
p:expression="execution(* talk*(..))"/>
</property>
</bean>
</beans>

上圖中的那個aspectj表達(dá)式寫錯了,在代碼中有正確的

2.使用aop名稱空間
在xml中引入如下的名稱空間,為了不被影響,我冊了其他多余的名稱空間。然后很普通地注入我們之前那三個bean
<?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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd "> <!--通過普通的方式來注入三個bean--> <!--注入student--> <bean name="student" class="cn.lyn4ever.aop.aopconfig.Student"/> <!--注入teacher--> <bean name="teacherOne" class="cn.lyn4ever.aop.aopconfig.Teacher"> <property name="student" ref="student"/> </bean> <!--注入我們創(chuàng)建的通知類--> <bean id="advice" class="cn.lyn4ever.aop.proxyfactory.BeforeAdvice"/> <aop:config> <aop:pointcut id="talkExecution" expression="execution(* talk*(..))"/> <aop:aspect ref="advice"> <!--這個方法就是我們在自定義通知類中之寫的方法--> <aop:before method="beforeSaySomething" pointcut-ref="talkExecution"/> <!--當(dāng)然,還可以配置其他通知類型--> </aop:aspect> </aop:config> </beans>
在這個配置中,我們還可以配置其他類型的通知,但是這個method屬性一定要寫我們自定義的那個通知類中的方法

在aop:pointcut中寫expression時還支持如下語法:
<aop:pointcut id="talkExecution" expression="execution(* talk*(..)) and args(String) and bean(stu*)"/> <!-- 中間的這個and表示和,也可以用or來表示或 args(String) 意思是參數(shù)類型是string,也可是自定義的類,這個后邊有例子 bean(stu*) 意思是bean的id是以stu開頭的,常用的就是用bean(*Service*)來表示服務(wù)層的bean -->
3.使用@AspectJ樣式注解方式
雖然是通過注解的方式來聲明注解類,但是還是需要在xml中配置一點點內(nèi)容(通過注解的方式也可以配置,但是在springboot中要使用的話有更方便的方式)
為了方便,就只寫了一個HighStudent,而且直接調(diào)用它的方法,不依賴于外部的teacher實例來調(diào)用
package cn.lyn4ever.aop.aspectj;
import cn.lyn4ever.aop.aopconfig.Teacher;
import org.springframework.stereotype.Component;
/**
* 聲明這是一個SpringBean,由Spring來管理它
*/
@Component
public class HighStudent {
public void talk() {
System.out.println("I am a boy");
}
public void walk() {
System.out.println("I am walking");
}
/**
* 這個方法添加一個teacher來做為參數(shù),為了配置后邊切入點中的args()
* @param teacher
*/
public void sleep(Teacher teacher) {
System.out.println("I want to sleep");
}
}
創(chuàng)建切面類
package cn.lyn4ever.aop.aspectj;
import cn.lyn4ever.aop.aopconfig.Teacher;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* 聲明切面類,也就是包括切點和通知
*/
@Component //聲明交由spring管理
@Aspect //表示這是一個切面類
public class AnnotatedAdvice {
/*
創(chuàng)建切入點,當(dāng)然也可以是多個
*/
@Pointcut("execution(* talk*(..))")
public void talkExecution(){}
@Pointcut("bean(high*)")//這里為什么是high,因為我們這回測試bean是highStudent
public void beanPoint(){}
@Pointcut("args(value)")
public void argsPoint(Teacher value){}
/*
創(chuàng)建通知,當(dāng)然也可以是多個
這個注解的參數(shù)就是上邊的切入點方法名,注意有的還帶參數(shù)
這個通知方法的參數(shù)和之前一樣,榀加JoinPoint,也可不加
*/
@Before("talkExecution()")
public void doSomethingBefore(JoinPoint joinPoint){
System.out.println("before: Do Something"+joinPoint.getSignature().getName()+"()");
}
/**
* 環(huán)繞通知請加上ProceedingJoinPoint參數(shù) ,它是joinPoint的子類
* 因為你要放行方法的話,必須要加這個
* @param joinPoint
* @param teacher
*/
@Around("argsPoint(teacher) && beanPoint()")
public Object doSomethindAround(ProceedingJoinPoint joinPoint, Teacher teacher) throws Throwable {
System.out.println("Around: Before Do Something"+joinPoint.getSignature().getName()+"()");
Object proceed = joinPoint.proceed();
System.out.println("Around: After Do Something"+joinPoint.getSignature().getName()+"()");
return proceed;
}
}
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:aop="http://www.springframework.org/schema/aop" 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/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!--通知Spring掃描@Aspect注解--> <aop:aspectj-autoproxy/> <!--配置掃描包,掃描@Component--> <context:component-scan base-package="cn.lyn4ever.aop.aspectj"/> </beans>
使用Java注解配置的方式配置掃描注解
@Configuration //聲明這是一個配置類
@ComponentScan("cn.lyn4ever.aop.aspectj")
@EnableAspectJAutoProxy(proxyTargetClass = true)//相當(dāng)于xml中的<aop:aspectj-autoproxy/>
public class BeanConfig {
}
測試方法
package cn.lyn4ever.aop.aspectj;
import cn.lyn4ever.aop.aopconfig.Teacher;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class AspectMain {
public static void main(String[] args) {
// xmlConfig();
javaConfig();
}
private static void javaConfig() {
GenericApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
HighStudent student = (HighStudent) context.getBean("highStudent");
student.sleep(new Teacher());//應(yīng)該被環(huán)繞通知
System.out.println();
student.talk();//前置通知
System.out.println();
student.walk();//不會被通知
System.out.println();
}
private static void xmlConfig(){
GenericXmlApplicationContext context = new GenericXmlApplicationContext();
context.load("application_aspect.xml");
context.refresh();
HighStudent student = (HighStudent) context.getBean("highStudent");
student.sleep(new Teacher());//應(yīng)該被環(huán)繞通知
System.out.println();
student.talk();//前置通知
System.out.println();
student.walk();//不會被通知
System.out.println();
}
}

總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對腳本之家的支持。
相關(guān)文章
Java詳細(xì)講解IO流的Writer與Reader操作
Writer與Reader類不能直接調(diào)用,需要使用多帶的方法調(diào)用它們的子類,在他們的前邊加上一個File即可如(FileWriter或FileReader)的多態(tài)方法進(jìn)行其調(diào)用,并且他們也是抽象類調(diào)用需要連接接口Exception,它們的優(yōu)點在于可以直接寫入或讀出內(nèi)容,不需要使用byte轉(zhuǎn)八進(jìn)制2022-05-05
詳解使用Spring Security進(jìn)行自動登錄驗證
本篇文章主要介紹了詳解使用Spring Security進(jìn)行自動登錄驗證,非常具有實用價值,需要的朋友可以參考下2017-09-09
JDBC連接SQL?Server數(shù)據(jù)庫實現(xiàn)增刪改查的全過程
實際開發(fā)中手動的輸入SQL語句是少之又少,大多數(shù)情況下是通過編譯代碼進(jìn)行來控制自動執(zhí)行,下面這篇文章主要給大家介紹了關(guān)于JDBC連接SQL?Server數(shù)據(jù)庫實現(xiàn)增刪改查的相關(guān)資料,需要的朋友可以參考下2023-04-04
關(guān)于Java單個TCP(Socket)連接發(fā)送多個文件的問題
這篇文章主要介紹了關(guān)于Java單個TCP(Socket)連接發(fā)送多個文件的問題,每次我只能使用一個 Socket 發(fā)送一個文件,沒有辦法做到連續(xù)發(fā)送文件,本文來解決這個問題,需要的朋友可以參考下2023-04-04
如何保證RabbitMQ全鏈路數(shù)據(jù)100%不丟失問題
這篇文章主要介紹了如何保證RabbitMQ全鏈路數(shù)據(jù)100%不丟失問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05
MyBatis中的表關(guān)聯(lián)查詢實現(xiàn)示例
這篇文章主要介紹了MyBatis中的表關(guān)聯(lián)查詢實現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01
Java 通過JDBC連接Mysql數(shù)據(jù)庫
本文給大家詳細(xì)介紹了java如何使用JDBC連接Mysql的方法以及驅(qū)動包的安裝,最后給大家附上了java通過JDBC連接其他各種數(shù)據(jù)庫的方法,有需要的小伙伴可以參考下。2015-11-11

