之前的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. hdu 1024 Max Sum Plus Plus (动态规划)

    Max Sum Plus PlusTime Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) ...

  2. go中的关键字-defer

    1. defer的使用 defer 延迟调用.我们先来看一下,有defer关键字的代码执行顺序: func main() { defer func() { fmt.Println("1号输出 ...

  3. ES6学习 let const

    1.前言 发现网易云笔记 单纯的记笔记没什么意思,所以今天来博客园写学习感受了,毕设做了休息时间就来写写新学的知识 哈哈哈 !! 2.ES6 就是JavaScript 语言的下一代标准,2015年6月 ...

  4. 力扣(LeetCode)删除排序链表中的重复元素II 个人题解

    给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字. 思路和上一题类似(参考 力扣(LeetCode)删除排序链表中的重复元素 个人题解)) 只不过这里需要用到一个前 ...

  5. thinking in JAVA 编译记录

    编辑/编译<thinking in JAVA>源代码 一.下载源代码 首先,我阅读的是<thinking in JAVA>第四版,因此按照书中提供的链接找到了mindview主 ...

  6. think PHP 查询、更改

    最近公司没有什么新项目,故准备搞搞PHP,正好后端有一些小东西需要搞一下,我就来试试吧. PHP 基于think PHP 3 实现功能: 1.为销售绑定虚拟号码分组(查询可以绑定的分组 -> 绑 ...

  7. Install python3

    wget https://www.python.org/ftp/python/3.7.4/Python-3.7.4.tgz tar xf Python-3.7.4.tgz cd Python-3.7. ...

  8. Python3 之 类属性与实例属性

    1.类属性与实例属性 类属性就相当与全局变量,实例对象共有的属性,实例对象的属性为实例对象自己私有. 类属性就是类对象(Tool)所拥有的属性,它被所有类对象的实例对象(实例方法)所共有,在内存中只存 ...

  9. JSP内置对象详解及示例

    目录 JSP 内置对象 out request response config application session pageContext page exception JSP 内置对象 jsp一 ...

  10. tensorflow多层CNN代码分析

    tf,reshape(tensor,shape,name=None) #其中shape为一个列表形式,特殊的一点是列表中可以存在-1.-1代表的含义是不用我们自己#指定这一维的大小,函数会自动计算,但 ...