SpringAOP在web应用中的使用
程序员文章站
2022-07-04 22:49:24
之前的aop是通过手动创建代理类来进行通知的,但是在日常开发中,我们并不愿意在代码中硬编码这些代理类,我们更愿意使用DI和IOC来管理aop代理类。Spring为我们提供了以下方式来使用aop框架 一、以声明的方式配置AOP(就是使用xml配置文件) 1.使用ProxyFactoryBean的方式: ......
之前的aop是通过手动创建代理类来进行通知的,但是在日常开发中,我们并不愿意在代码中硬编码这些代理类,我们更愿意使用di和ioc来管理aop代理类。spring为我们提供了以下方式来使用aop框架
一、以声明的方式配置aop(就是使用xml配置文件)
1.使用proxyfactorybean的方式:
proxyfactorybean类是factorybean的一个实现类,它允许指定一个bean作为目标,并且为该bean提供一组通知和顾问(这些通知和顾问最终会被合并到一个aop代理中)它和我们之前的proxyfactory都是advised的实现。
以下是一个简单的例子:一个学生和一个老师,老师会告诉学生应该做什么。
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; } }
- 我们创建一个通知类,这个和之前是一样的springaop中的通知类型以及创建
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> <!--注入我们创建的通知类--> <bean id="advice" class="cn.lyn4ever.aop.aopconfig.auditadvice"></bean> <!--创建代理类,使用前边写的通知进行通知,这样会使这个类上的所有方法都被通知--> <bean name="proxyone" class="org.springframework.aop.framework.proxyfactorybean" p:target-ref="student" p:interceptornames-ref="interceptornames"> <!--因为interceptornames的属性是一个可变参数,也就是一个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(); }
运行结果没有问题
- 以上是通过直接创建通知的方式,接下来我们试一个创建一个切入点(因为以上是对类中所有方法都进行通知,这时我们使用切入点只对其中部分方法进行通知),在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> <!--注入我们创建的通知类--> <bean id="advice" class="cn.lyn4ever.aop.aopconfig.auditadvice"></bean> <!--创建代理类,使用前边写的通知进行通知,这样会使这个类上的所有方法都被通知--> <bean name="proxyone" class="org.springframework.aop.framework.proxyfactorybean" p:target-ref="student" p:interceptornames-ref="interceptornames"> <!--因为interceptornames的属性是一个可变参数,也就是一个list--> </bean> <!--在上边引入了util的名称空间,简化了书写--> <util:list id="interceptornames"> <value>advice</value> </util:list> <!--以下是使用切入点的方式来进行通知,上边的代码和上一个配置文件一样,没有修改--> <!--sutdent基本bean,我们继续使用--> <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的表达式,当然也可以用其他的类型切入点,这个在上边链接中能看到--> <bean class="org.springframework.aop.aspectj.aspectjexpressionpointcut" p:expression="execution(* talk*(..))"/> </property> </bean> </beans>
- 上图中的那个aspectj表达式写错了,在代码中有正确的
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> <!--注入我们创建的通知类--> <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"/> <!--当然,还可以配置其他通知类型--> </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) 意思是参数类型是string,也可是自定义的类,这个后边有例子 bean(stu*) 意思是bean的id是以stu开头的,常用的就是用bean(*service*)来表示服务层的bean -->
3.使用@aspectj样式注解方式
虽然是通过注解的方式来声明注解类,但是还是需要在xml中配置一点点内容(通过注解的方式也可以配置,但是在springboot中要使用的话有更方便的方式)
- 为了方便,就只写了一个highstudent,而且直接调用它的方法,不依赖于外部的teacher实例来调用
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来做为参数,为了配置后边切入点中的args() * @param teacher */ public void sleep(teacher teacher) { system.out.println("i want to sleep"); } }
- 创建切面类
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 { /* 创建切入点,当然也可以是多个 */ @pointcut("execution(* talk*(..))") public void talkexecution(){} @pointcut("bean(high*)")//这里为什么是high,因为我们这回测试bean是highstudent public void beanpoint(){} @pointcut("args(value)") public void argspoint(teacher value){} /* 创建通知,当然也可以是多个 这个注解的参数就是上边的切入点方法名,注意有的还带参数 这个通知方法的参数和之前一样,榀加joinpoint,也可不加 */ @before("talkexecution()") public void dosomethingbefore(joinpoint joinpoint){ system.out.println("before: do something"+joinpoint.getsignature().getname()+"()"); } /** * 环绕通知请加上proceedingjoinpoint参数 ,它是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)//相当于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());//应该被环绕通知 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());//应该被环绕通知 system.out.println(); student.talk();//前置通知 system.out.println(); student.walk();//不会被通知 system.out.println(); } }
上一篇: JAVA笔记 -- 访问权限控制