之前的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;
}
}
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();
}
}

项目代码地址,如果觉得还不错的话,给个star吧

SpringAOP在web应用中的使用的更多相关文章

  1. SpringMVC(十六):如何使用编程方式替代/WEB-INF/web.xml中的配置信息

    在构建springmvc+mybatis项目时,更常用的方式是采用web.xml来配置,而且一般情况下会在web.xml中使用ContextLoaderListener加载applicationCon ...

  2. 在web项目中使用shiro(认证、授权)

    一.在web项目中实现认证 第一步,在web项目中导入shiro依赖的包 第二步,在web.xml中声明shiro拦截权限的过滤器 <filter> <filter-name> ...

  3. java: web应用中不经意的内存泄露

    前面有一篇讲解如何在spring mvc web应用中一启动就执行某些逻辑,今天无意发现如果使用不当,很容易引起内存泄露,测试代码如下: 1.定义一个类App package com.cnblogs. ...

  4. 【初码干货】使用阿里云对Web开发中的资源文件进行CDN加速的深入研究和实践

    提示:阅读本文需提前了解的相关知识 1.阿里云(https://www.aliyun.com) 2.阿里云CDN(https://www.aliyun.com/product/cdn) 3.阿里云OS ...

  5. web.xml中welcome-file-list的作用

    今天尝试使用struts2+ urlrewrite+sitemesh部署项目,结果发现welcome-file-list中定义的欢迎页不起作用: <welcome-file-list> & ...

  6. web.xml中load-on-startup的作用

    如下一段配置,熟悉DWR的再熟悉不过了:<servlet>   <servlet-name>dwr-invoker</servlet-name>   <ser ...

  7. Entity Framework 6 Recipes 2nd Edition(9-3)译->找出Web API中发生了什么变化

    9-3. 找出Web API中发生了什么变化 问题 想通过基于REST的Web API服务对数据库进行插入,删除和修改对象图,而不必为每个实体类编写单独的更新方法. 此外, 用EF6的Code Fri ...

  8. 在基于MVC的Web项目中使用Web API和直接连接两种方式混合式接入

    在我之前介绍的混合式开发框架中,其界面是基于Winform的实现方式,后台使用Web API.WCF服务以及直接连接数据库的几种方式混合式接入,在Web项目中我们也可以采用这种方式实现混合式的接入方式 ...

  9. 如何在ASP.NET Web站点中统一页面布局[Creating a Consistent Layout in ASP.NET Web Pages(Razor) Sites]

    如何在ASP.NET Web站点中统一页面布局[Creating a Consistent Layout in ASP.NET Web Pages(Razor) Sites] 一.布局页面介绍[Abo ...

随机推荐

  1. salesforce lightning零基础学习(十五) 公用组件之 获取表字段的Picklist(多语言)

    此篇参考:salesforce 零基础学习(六十二)获取sObject中类型为Picklist的field values(含record type) 我们在lightning中在前台会经常碰到获取pi ...

  2. 【RocketMQ源码学习】- 5. 消息存储机制

    前言 面试官:你了解RocketMQ是如何存储消息的吗?我:额,,,你等下,我看下这篇文字, (逃 由于这部分内容优点多,所以请哥哥姐姐们自备茶水,欢迎留言! RocketMQ存储设计是高可用和高性能 ...

  3. js对象的直接赋值、浅拷贝与深拷贝

    最近Vue项目中写到一个业务,就是需要把对话框的表单中的数据,每次点击提交之后,就存进一个el-table表格中,待多次需要的表单数据都提交进表格之后,再将这个表格提交,实现多个表单数据的同时提交,期 ...

  4. 【Android - 组件】之Activity的启动模式

    Activity的启动模式目前有四种:standard.singleTop.singleTask 和 singleInstance. 1.standard standard 是标准模式,也是系统的默认 ...

  5. NIO-Buffeer

    目录 NIO-Buffeer 目录 什么是Buffer 缓冲区类型 缓冲区存储类型 字节存放顺序 Buffer使用 Buffer ByteBuffer 总结 相关文献 NIO-Buffeer 目录 N ...

  6. c 程序之 最大公约数 和 最小公倍数

    1.最大公约数     30  10  -> 10 #include<stdio.h> /* 求最大公约数 辗转相减法 36 --16 4 */ int main(){ int a, ...

  7. elastic search(es)安装

    全文搜索属于最常见的需求,开源的 Elasticsearch (以下简称 Elastic)是目前全文搜索引擎的首选. 它可以快速地储存.搜索和分析海量数据.维基百科.Stack Overflow.Gi ...

  8. springboot打印SQL及多环境配置

    1-在控制台打印sql语句 在springBoot+Mybatis中,要想在控制台日志显示SQL的执行情况,简单设置即可:在properties新增: logging.level.com.anson. ...

  9. 在开发Thinkphp5.0智慧软文个人微信个人支付宝企业支付宝接口时遇到的坑

    在开发Thinkphp5.0智慧软文个人微信个人支付宝企业支付宝接口时遇到回调后提示成功但是不能自动充值的情况,现在记录一下: 两种情况 1.个人支付宝  个人微信遇到的情况 因为个人支付宝 个人微信 ...

  10. Codeforces Round #451 (Div. 2) A B C D E

    Codeforces Round #451 (Div. 2) A Rounding 题目链接: http://codeforces.com/contest/898/problem/A 思路: 小于等于 ...