aop(面向切面编程) 是oop的扩展和延伸,是为了解决OOP中产生的开发问题。
权限校验、日志记录、性能监控、事务控制
以权限校验举例AOP的优势
需求:在每个数据持久层dao层saveDB()方法前,统一验证,必须拥有管理员权限才可以save。此时就需要在每次saveDB前验证权限,假设为check()方法
解决方案:
代理类和被代理类实现共同的接口(或继承),代理类中存有指向被代理类的索引,实际执行时通过调用代理类的方法、实际执行的是被代理类的方法。
从上文所述,实际上AOP的需求就是要一个已经实现好的类里面去增加一些操作。而代理模式中的代理类可以在被代理的类的基础上增加一些方法。
代理模式可以分为静态代理和动态代理,而AOP是通过动态代理来实现的。
JDK动态代理只能对实现了接口的类生成代理,而不能针对类
JDK动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以该类或方法最好不要声明成final
CGLIB动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
当Bean实现接口时,Spring就会用JDK的动态代理
当Bean没有实现接口时,Spring使用CGlib是实现
可以强制使用CGlib
在spring配置中加入如下代码
<aop:aspectj-autoproxy proxy-target-class="true"/>
图片
连接点: 可以被拦截的点
切入点: 真正被拦截的点
通知: 增强方法
引介: 类的增强
目标: 被增强的对象
织入: 将增强应用到目标的过程
代理: 织入增强后产生的对象
切面: 切入点和通知的组合
Spring 中使用AOP的方式有使用传统方式和AspectJ两种方式,本文只介绍使用AspectJ的方式,并分别通过xml和注解来实现
在spring的配置文件中配置目标类和切面类都交给spring管理,之后利用标签声明好pointcut、aspect
spring的配置文件------application2.xml
<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: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:config>
<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: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管理
spring的配置文件-----application.xml
<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: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();
}
}
github代码传送门