在Spring中默认使用JDK动态代理实现AOP编程,使用org.springframework.aop.framework.ProxyFactoryBean
创建代理是Spring AOP 实现的最基本方式。
一、通知类型
根据Spring中通知在目标类方法中的连接点位置,通知可以分为6种类型:
类型名称 | 作用 | 应用场景 |
---|---|---|
环绕通知(org.aopalliance.intercept.MethodInterceptor) | 在目标方法执行前和执行后实施增强 | 可应用于日志记录、事务处理等功能。 |
前置通知(org.springframework.aop.MethodBeforeAdvice) | 在目标方法执行前实施增强 | 可应用于权限管理等功能。 |
后置返回通知(org.springframework.aop.AfterReturningAdvice) | 在目标方法成功执行后实施增强 | 可应用于关闭流、删除临时文件等功能。 |
后置通知(org.springframework.aop.AfterAdvice) | 在目标方法执行后实施增强,与后置返回通知不同的是,不管是否发生异常都要执行该类通知 | 可应用于释放资源。 |
异常通知(org.springframework.aop.ThrowsAdvice) | 在方法抛出异常后实施增强 | 可应用于处理异常、记录日志等功能。 |
引入通知(org.springframework.aop.IntroductionInterceptor) | 在目标类中添加一些新的方法和属性 | 可应用于修改目标类(增强类)。Spring AOP 只支持方法层面的增强,所以该类型的通知只作为了解即可。 |
二、切面类型
切面类型 | 作用 |
---|---|
一般切面(Advisor) | 对目标类所有方法进行拦截 |
具有切点的切面(PointcutAdvisor) | 可以指定拦截目标类哪些方法 |
引介切面(IntroductionAdvisor) | 针对引介通知而使用切面 |
三、ProxyFactoryBean代理工厂
ProxyFactoryBean 是 org.springframework.beans.factory.FactoryBean
接口的实现类,这个类已经由spring为我们提供了,所以我们只需要在配置文件中进行配置就行了。FactoryBean 负责实例化一个Bean实例,ProxyFactoryBean 负责为其他 Bean 实例创建代理实例。ProxyFactoryBean 类的常用属性如下:
属性 | 解释 |
---|---|
target | 代理的目标对象 |
proxyInterfaces | 代理需要实现的接口列表,如果是多个接口,可以使用以下格式赋值:<list> <value></value> </list> |
InterceptorNames | 需要织入目标的Advice |
proxyTargetClass | 是否对类代理而不是接口,默认为false,使用JDK动态代理;当为true时,使用CGLIB动态代理 |
singleton | 返回的代理实例是否为单例,默认为true |
optimize | 当设置为true时强制使用CGLIB动态代理 |
四、Advisor一般切面的实现
-
在pom.xml中导入AOP联盟和Spring-aop的相关依赖
<!-- AOP联盟依赖 --> <dependency> <groupId>aopalliance</groupId> <artifactId>aopalliance</artifactId> <version>1.0</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>5.0.2.RELEASE</version> </dependency>
-
创建StuDao接口和StuDaoImpl实现类
StuDao接口:public interface StuDao { public void save(); public void modify(); public void delete(); }
StuDaoLmpl实现类:
public class StuDaoImpl implements StuDao { @Override public void save() { System.out.println("保存"); } @Override public void modify() { System.out.println("修改"); } @Override public void delete() { System.out.println("删除"); } }
-
创建切面类MyBeforeAdvice
import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; public class MyBeforeAdvice implements MethodBeforeAdvice { @Override public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println("前置增强========"); } }
-
在applicationContext.xml中配置切面并指定代理
<!--配置目标类--> <bean id="stuDao" class="aop.StuDaoImpl"></bean> <!--配置前置通知类型--> <bean id="myBeforeAdvice" class="aop.MyBeforeAdvice"></bean> <!--Spring AOP 生成代理对象--> <bean id="stuDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <!--配置目标类--> <property name="target" ref="stuDao"></property> <!--代理实现的接口--> <property name="proxyInterfaces" value="aop.StuDao"></property> <!--采取拦截的名称--> <property name="interceptorNames" value="myBeforeAdvice"></property> </bean>
-
创建测试类
@Test public void demo(){ ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); StuDao stuDao = (StuDao) app.getBean("stuDaoProxy"); stuDao.save(); stuDao.modify(); stuDao.delete(); }
五、PointcutAdvisor切点切面的实现
使用普通Advice作为切面,将对目标类所有方法进行拦截,不够灵活,在实际开发中常采用带切点的切面。
常用PointcutAdvisor实现类:
DefaultPointcutAdvisor最常用的切面类型,它可以通过任意Pointcut和Advice组合定义切面;
JdkRegexpMethodPointcut构造正则表达式切点(推荐)
- 创建StuDao接口和StuDaoImpl实现类
StuDao接口:
StuDaolmpl实现类:public interface StuDao { public void save(); public void modify(); public void delete(); }
public class StuDaoImpl implements StuDao { @Override public void save() { System.out.println("保存"); } @Override public void modify() { System.out.println("修改"); } @Override public void delete() { System.out.println("删除"); } }
- 创建一个实现环绕通知的切面类
import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class MyAspect implements MethodInterceptor { @Override public Object invoke(MethodInvocation m) throws Throwable { //前置增强方法 check(); //执行目标方法 Object obj = m.proceed(); //后置增强方法 log(); return obj; } public void check(){ System.out.println("模拟权限控制"); } public void log(){ System.out.println("模拟日志记录"); } }
- 在applicationContext.xml中配置切面并指定代理
<!--配置目标类--> <bean id="stuDao" class="aop.StuDaoImpl"></bean> <!--配置通知--> <bean id="myAspect" class="aop.MyAspect"></bean> <!--配置带切入点的切面--> <bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!--pattern中配置正则表达式:.表示任意字符 *表示任意次数--> <property name="pattern" value=".*"></property> <property name="advice" ref="myAspect"></property> </bean> <!--Spring AOP 生成代理对象--> <bean id="stuDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <!--配置目标类--> <property name="target" ref="stuDao"></property> <!--代理实现的接口--> <property name="proxyInterfaces" value="aop.StuDao"></property> <!--采取拦截的名称--> <property name="interceptorNames" value="myAdvisor"></property> </bean>
- 创建测试类
@Test public void demo(){ ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); StuDao stuDao = (StuDao) app.getBean("stuDaoProxy"); stuDao.save(); stuDao.modify(); stuDao.delete(); }