理解Spring AOP并使用XML及注解两种方式实现SpringAOP
Spring AOP
概述
aop(面向切面编程) 是oop的扩展和延伸,是为了解决OOP中产生的开发问题。
应用场景
权限校验、日志记录、性能监控、事务控制
以权限校验举例AOP的优势
需求:在每个数据持久层dao层saveDB()方法前,统一验证,必须拥有管理员权限才可以save。此时就需要在每次saveDB前验证权限,假设为check()方法
解决方案:
- 在dao层的每一个方法都增加一个check()方法
劣势:check方法中的代码重复,当要修改时,多个类需要修改 - 纵向继承: 让dao层的所有类都继承一个BaseDao,在BaseDao中实现check方法,这样dao层中的所有类都可以直接调用的方法。
劣势: 当不需要check方法了,需要在basedao中做修改 - 横向抽取: 采用代理机制,在save方法前面增加一个check方法。这就是aop的实现原理,这样处理的好处就是当我需要增加这个check方法的时候我就使用代理类,不需要了我就不用。
同理,日志记录,性能监控,事务控制都是需要在原有的处理方法的前面或者后面增加一段代码,我们都可以采用代理机制来实现。
spring aop底层实现使用的代理模式实现
代理模式
代理类和被代理类实现共同的接口(或继承),代理类中存有指向被代理类的索引,实际执行时通过调用代理类的方法、实际执行的是被代理类的方法。
为什么AOP可以通过代理模式实现
从上文所述,实际上AOP的需求就是要一个已经实现好的类里面去增加一些操作。而代理模式中的代理类可以在被代理的类的基础上增加一些方法。
代理模式可以分为静态代理和动态代理,而AOP是通过动态代理来实现的。
Spring Aop采用的两种代理模式
- JDK动态代理
- CGLIB
JDK动态代理
JDK动态代理只能对实现了接口的类生成代理,而不能针对类
JDK动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
CGLIB
CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以该类或方法最好不要声明成final
CGLIB动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
Spring在选择用JDK还是CGLiB的依据:
-
当Bean实现接口时,Spring就会用JDK的动态代理
-
当Bean没有实现接口时,Spring使用CGlib是实现
-
可以强制使用CGlib
在spring配置中加入如下代码
<aop:aspectj-autoproxy proxy-target-class="true"/>
AOP的开发中的相关术语
图片
连接点: 可以被拦截的点
切入点: 真正被拦截的点
通知: 增强方法
引介: 类的增强
目标: 被增强的对象
织入: 将增强应用到目标的过程
代理: 织入增强后产生的对象
切面: 切入点和通知的组合
Spring中使用AOP
Spring 中使用AOP的方式有使用传统方式和AspectJ两种方式,本文只介绍使用AspectJ的方式,并分别通过xml和注解来实现
spring的aop的开发(AspectJ的XML的方式)
在spring的配置文件中配置目标类和切面类都交给spring管理,之后利用标签声明好pointcut、aspect
spring的配置文件------application2.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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 在配置文件中开启注解的AOP的开发 -->
<aop:aspectj-autoproxy />
<!-- 配置目标类 -->
<bean id="studentDao1" class="com.demo.spring_aop.aopXml.StudentDao">
</bean>
<!-- 配置切面类 -->
<bean id="studentAspect1" class="com.demo.spring_aop.aopXml.StudentAspect">
</bean>
<!-- 配置 AOP -->
<aop:config>
<!-- 配置切点表达式 -->
<!--
整个表达式可以分为五个部分:
1、execution()::表达式主体。
2、第一个*号:表示返回类型, *号表示所有的类型。
3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service包、子孙包下所有类的方法。
4、第二个*号:表示类名,*号表示所有的类。
5、*(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数
-->
<aop:pointcut
expression="execution(* com.demo.spring_aop.aopXml.StudentDao.saveStudent(..))"
id="pointcut1" />
<aop:pointcut
expression="execution(* com.demo.spring_aop.aopXml.StudentDao.updateStudent(..))"
id="pointcut2" />
<aop:pointcut
expression="execution(* com.demo.spring_aop.aopXml.StudentDao.deleteStudent(..))"
id="pointcut3" />
<aop:pointcut
expression="execution(* com.demo.spring_aop.aopXml.StudentDao.findStudent(..))"
id="pointcut4" />
<!-- 配置切面及通知 -->
<aop:aspect ref="studentAspect1">
<!-- 前置增强 -->
<aop:before method="before" pointcut-ref="pointcut1" />
<!-- 异常抛出增强 -->
<aop:after-throwing method="afterThrowing"
pointcut-ref="pointcut3" throwing="e" />
<!-- 后置增强 -->
<aop:after-returning method="afterReturning"
pointcut-ref="pointcut4" returning="result" />
<!-- 环绕增强 -->
<aop:around method="around" pointcut-ref="pointcut2" />
<!-- 后置增强 -->
<!-- <aop:after method="afterMethod" pointcut-ref="pointcut2" /> -->
</aop:aspect>
</aop:config>
</beans>
需要被增强的类,该类里面所有的方法都是连接点,但是只有真正被增强的方法才是切入点,从我们上文的配置中我们把所有的方法都做了增加,即:该类中的所有方法都是切入点。
StudentDao.java
package com.demo.spring_aop.aopXml;
public class StudentDao {
public void saveStudent(){
System.out.println("==========增加学生========");
System.out.println("save方法结束");
}
public void updateStudent(String name){
System.out.println("=========更新学生" + name + "=========");
System.out.println("update方法结束");
}
public void deleteStudent(){
System.out.println("=======删除学生========");
System.out.println("delete方法结束");
}
public String findStudent(){
System.out.println("=======查找学生========");
System.out.println("find方法结束");
return "张小明";
}
}
进行增强的切面类,可以设置切面中五种通知中需要做的不同的方法。
StudentAspect.java
package com.demo.spring_aop.aopXml;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* 切面类:注解的切面类
*
*/
public class StudentAspect {
// 前置通知
public void before() {
System.out.println("前置增强===========");
}
// 后置通知:
public void afterReturning(Object result) {
System.out.println("后置增强===========" + result);
}
// 环绕通知:
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前增强==========");
Object obj = joinPoint.proceed();
System.out.println("环绕后增强==========");
return obj;
}
// 异常抛出通知:
public void afterThrowing(Throwable e) {
System.out.println("异常抛出增强=========" + e.getMessage());
}
// 最终通知
public void after() {
System.out.println("最终增强============");
}
}
测试类
DemoTest.java
package com.demo.spring_aop.aopXml;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* Spring的AOP的注解开发
* @author jt
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext2.xml")
public class DemoTest {
@SuppressWarnings("restriction")
@Resource(name="studentDao1")
private StudentDao studentDao;
@Test
public void demo1(){
// studentDao.saveStudent();
studentDao.updateStudent("小花");
// studentDao.deleteStudent();
// studentDao.findStudent();
}
}
测试结果:
Spring AOP的注解方式
将切面类和需要被增强的类交给spring管理
spring的配置文件-----application.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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 在配置文件中开启注解的AOP的开发 -->
<aop:aspectj-autoproxy/>
<!-- 配置目标类 -->
<bean id="studentDao" class="com.demo.spring_aop.aop.StudentDao">
</bean>
<!-- 配置切面类 -->
<bean id="studentAspect" class="com.demo.spring_aop.aop.StudentAspect">
</bean>
</beans>
需要被增强的类
同xml方式的StudentDao.java
进行增强的切面类
@Aspect声明这是一个切面类
@Before:前置增强
@AfterReturning:在方法返回值之后增强
@Around:在方法前后都增强
@After:在方法结束时增强
@AfterThrowing: 抛出异常增强
package com.demo.spring_aop.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
/**
* 切面类:注解的切面类
*
*/
@Aspect
public class StudentAspect {
@Before(value = "StudentAspect.pointcut2()")
public void before() {
System.out.println("前置增强===========");
}
// 后置通知:
@AfterReturning(value = "StudentAspect.pointcut1()", returning = "result")
public void afterReturning(Object result) {
System.out.println("后置增强===========" + result);
}
// 环绕通知:
@Around(value = "StudentAspect.pointcut3()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前增强==========");
Object obj = joinPoint.proceed();
System.out.println("环绕后增强==========");
return obj;
}
// 异常抛出通知:
@AfterThrowing(value = "StudentAspect.pointcut1()", throwing = "e")
public void afterThrowing(Throwable e) {
System.out.println("异常抛出增强=========" + e.getMessage());
}
// 最终通知
@After(value = "StudentAspect.pointcut1()")
public void after() {
System.out.println("最终增强============");
}
// 切入点注解:
@Pointcut(value = "execution(* com.demo.spring_aop.aop.StudentDao.find*(..))")
private void pointcut1() {
}
@Pointcut(value = "execution(* com.demo.spring_aop.aop.StudentDao.save*(..))")
private void pointcut2() {
}
@Pointcut(value = "execution(* com.demo.spring_aop.aop.StudentDao.update*(..))")
private void pointcut3() {
}
@Pointcut(value = "execution(* com.demo.spring_aop.aop.StudentDao..delete*(..))")
private void pointcut4() {
}
}
测试类
package com.demo.spring_aop.aop;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* Spring的AOP的注解开发
* @author jt
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath*:applicationContext.xml")
public class DemoTest {
@SuppressWarnings("restriction")
@Resource(name="studentDao")
private StudentDao studentDao;
@Test
public void demo1(){
// studentDao.saveStudent();
studentDao.updateStudent("小红");
// studentDao.deleteStudent();
// studentDao.findStudent();
}
}
测试结果: