AOP概述
1、AOP术语
连接点:连接点好比一个类的方法,每一个方法都是一个连接点
切点:每个方法有大量的逻辑构成,可以将任何一个位置作为执行点,这个执行点作为切点。
增强:就是嵌入方法中的一段逻辑。
目标对象:织入增强的目标类。
引介:特殊的增强,为类加入方法和属性。
织入:将增强嵌入切点的过程。
代理:织入增强后产生的结果类。
切面:切入点+增强
创建增强类
前置增强
前置增强=连接点前+增强,增强只是针对所有的的连接点。前置增强的实现类为MethodBeforeAdvice。
前置增强的过程:1、创建代理工厂;2、创建目标类,也就是需要织入增强的类;3、通过MethodBeforeAdvice生成增强类;4、将增强类和目标类添加到代理工厂;5、生成目标代理。
实例:
创建目标类
package advice;
public interface Waiter {
void greetTo(String name);
void serverTo(String name);
}
package advice;
public class NaiveWaiter implements Waiter {
@Override
public void greetTo(String name) {
// TODO Auto-generated method stub
System.out.println("greet to"+name+"......");
}
@Override
public void serverTo(String name) {
// TODO Auto-generated method stub
System.out.println("serving "+name+"......");
}
}
创建前置增强
package advice;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class GreetingBeforeAdvice implements MethodBeforeAdvice{
@Override
public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
// TODO Auto-generated method stub
//arg0标识目标方法,arg1标识目标方法的参数值,arg2标识目标实例
System.out.println("====="+arg0.getName()+"=====");
String clientName = (String) arg1[0];
System.out.println("How are you!"+clientName+".");
System.out.println("===="+arg2.getClass().getName()+"====");
}
}
设置前置增强
package advice;
import org.springframework.aop.framework.ProxyFactory;
public class TestBeforeAdvice {
public static void main(String[] args){
//创建目标实例
NaiveWaiter waiter = new NaiveWaiter();
//创建增强实例
GreetingBeforeAdvice advice = new GreetingBeforeAdvice();
//创建代理工厂
ProxyFactory factory = new ProxyFactory();
//设置代理目标
factory.setTarget(waiter);
//为代理类添加增强
factory.addAdvice(advice);
//生成代理实例
Waiter proxy = (Waiter) factory.getProxy();
proxy.greetTo("JSON");
proxy.serverTo("JSON");
}
}
Spring配置前置增强实例:
application配置
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.0.xsd">
<context:component-scan base-package="beforeadvice"/>
<bean id="waiter" class="org.springframework.aop.framework.ProxyFactoryBean"
p:proxyInterfaces="beforeadvice.Waiter"
p:interceptorNames="greetingBeforeAdvice"
p:target-ref="naiveWaiter"
/>
</beans>
设置前置增强
package beforeadvice;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestSpringBefore {
public static void main(String[] args){
String path = "application.xml";
ApplicationContext ap = new ClassPathXmlApplicationContext(path);
Waiter waiter = (Waiter) ap.getBean("waiter");
waiter.greetTo("JACK");
waiter.serverTo("JACK");
}
}
后置增强
后置增强=连接点后+增强,后置增强是将在方法后织入增强,后置增强的实现类为AfterReturningAdvice。
前置增强的过程:1、创建代理工厂;2、创建目标类,也就是需要织入增强的类;3、通过AfterReturningAdvice生成增强类;4、将增强类和目标类添加到代理工厂;5、生成目标代理。
实例:
创建后置增强类
package advice.after;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class GreetingAfterAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
// TODO Auto-generated method stub
System.out.println("please enjoy yourself!");
}
}
设置后置增强
package advice.after;
import org.springframework.aop.framework.ProxyFactory;
import com.sun.org.glassfish.external.statistics.Statistic;
import advice.before.GreetingBeforeAdvice;
import advice.before.NaiveWaiter;
import advice.before.Waiter;
public class TestAfterAdvice {
public static void main(String[] args){
//创建目标实例
NaiveWaiter waiter = new NaiveWaiter();
//创建增强实例
GreetingAfterAdvice advice = new GreetingAfterAdvice();
//创建代理工厂
ProxyFactory factory = new ProxyFactory();
//设置代理目标
factory.setTarget(waiter);
//为代理类添加增强
factory.addAdvice(advice);
//生成代理实例
Waiter proxy = (Waiter) factory.getProxy();
proxy.greetTo("JSON");
proxy.serverTo("JSON");
}
}
Spring配置后置增强实例:
application配置
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.0.xsd">
<context:component-scan base-package="advice"/>
<bean id="waiter" class="org.springframework.aop.framework.ProxyFactoryBean"
p:proxyInterfaces="advice.before.Waiter"
p:target-ref="naiveWaiter"
>
<property name="interceptorNames">
<list>
<value>greetingBeforeAdvice</value>
<value>after</value>
</list>
</property>
</bean>
</beans>
设置后置增强
package advice.before;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestSpringBefore {
public static void main(String[] args){
String path = "application.xml";
ApplicationContext ap = new ClassPathXmlApplicationContext(path);
Waiter waiter = (Waiter) ap.getBean("waiter");
waiter.greetTo("JACK");
waiter.serverTo("JACK");
}
}
环绕增强
环绕增强=连接点前后+增强,在方法的前后都会织入增强,环绕增强的实现类为MethodInterceptor。
环绕增强过程:1、创建代理工厂;2、创建目标类,也就是需要织入增强的类;3、通过AfterReturningAdvice生成增强类;4、将增强类和目标类添加到代理工厂;5、生成目标代理。
Spring配置环绕增强实例:
定义增强类
package advice.interceptor;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.stereotype.Component;
@Component("interceptor")
public class GreetingInterceptor implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation arg0) throws Throwable {
// TODO Auto-generated method stub
//目标方法入参
Object[] args = arg0.getArguments();
String str = (String) args[0];
System.out.println(str+"先生,你好!");
//执行目标方法
Object object = arg0.proceed();
System.out.println(str+"先生,有什么帮忙的?");
return object;
}
}
设置Spring配置
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.0.xsd">
<context:component-scan base-package="advice"/>
<bean id="waiter" class="org.springframework.aop.framework.ProxyFactoryBean"
p:proxyInterfaces="advice.before.Waiter"
p:target-ref="naiveWaiter"
>
<property name="interceptorNames">
<list>
<value>greetingBeforeAdvice</value>
<value>after</value>
<value>interceptor</value>
</list>
</property>
</bean>
</beans>
设置环绕增强
package advice.before;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestSpringBefore {
public static void main(String[] args){
String path = "application.xml";
ApplicationContext ap = new ClassPathXmlApplicationContext(path);
Waiter waiter = (Waiter) ap.getBean("waiter");
waiter.greetTo("JACK");
waiter.serverTo("JACK");
}
}
异常抛出增强
异常抛出增强=连接点异常后+增强,在异常后会织入增强,异常抛出增强的实现类为ThrowsAdvice。
异常抛出增强过程:1、创建代理工厂;2、创建目标类,也就是需要织入增强的类;3、通过ThrowsAdvice生成增强类;4、将增强类和目标类添加到代理工厂;5、生成目标代理。
Spring配置异常增强实例:
创建目标类
package advice.throwsadvice;
import java.sql.SQLException;
import org.springframework.stereotype.Component;
@Component
public class ForumService {
public void removeForum(){
throw new RuntimeException("运行异常.........");
}
public void updateForum() throws SQLException{
throw new SQLException("数据库更新异常..........");
}
}
创建异常增强类
package advice.throwsadvice;
import java.lang.reflect.Method;
import java.sql.SQLException;
import java.util.concurrent.Executor;
import org.springframework.aop.ThrowsAdvice;
import org.springframework.stereotype.Component;
@Component
public class TransactionThrows implements ThrowsAdvice{
public void afterThrowing(Method method,Object[] objects,
Object target, RuntimeException exception){
System.out.println("操作报错========"+"RuntimeException");
}
public void afterThrowing(Method method,Object[] objects,
Object target, SQLException exception){
System.out.println("操作报错========"+"SQLException");
}
}
Spring配置异常增强
<bean id="forum" class="org.springframework.aop.framework.ProxyFactoryBean"
p:target-ref="forumService">
<property name="interceptorNames">
<value>
transactionThrows
</value>
</property>
</bean>
引介增强
引介增强只是对目标类添加属性和方法。
创建切面
增强只是对每个类中的所有方法都要织入,没有条件限制。切面可以定位到目标类和目标方法进行增强的织入。
切点类型
-
-
- 静态方法切点:StaticMethodMatcherPointcut类过滤接入点的类和方法。
- 动态方法切点:DynamicMethodMatcherPointcut类在每次运行程序时都要过滤接入点的类和方法。
- 注解切点
- 表达式切点
- 流程切点
- 复合切点
-
切面类型
-
-
- Advisor:代表一般切面。
- PoincutAdvisor:代表具有切入点的切面。
- IntroductionAdvisor:代表引介切面。
-
PointcutAdvisor
PointcutAdvisor共有6个具体实现类
-
- DefaultPointcutAdvisor:主要通过Pointcut和Advice构成一个切面,例如动态切面、流程切面、复合切面等切面的构成。
- StaticMethodMatcherPointcutAdvisor:用于设置静态切面
- NameMethodMatcherPointcutAdvisor:按照方法名定义切点的切面。
- RegexMethodMatcherPointcutAdvisor:通过正则表达式匹配方法名定义切点的切面。
- AspectJExpressionPointcutAdvisor
- AspectJPointcutAdvisor
1、静态普通方法名匹配切面
该切面会通过类名和方法名对增强进行准确的织入。StaticMethodMatcherPointcutAdvisor方法对目标类和方法进行过滤。
创建过程:1、创建目标类和方法;2、通过StaticMethodMatcherPointcutAdvisor创建切面对接入点进行过滤;3、创建增强;4、将增强注入切面;5、将目标类、切面加入代理。
Waiter.java文件
package advisor;
import org.springframework.stereotype.Component;
@Component("waiterTarget")
public class Waiter {
public void greetTo(String name){
System.out.println("waiter greet to "+name+"......");
}
public void serveTo(String name){
System.out.println("waiter serving to "+name+".....");
}
}
Seller.java文件
package advisor;
import org.springframework.stereotype.Component;
@Component("sellerTarget")
public class Seller {
public void greetTo(String name){
System.out.println("seller greet to "+name+"......");
}
public void serveTo(String name){
System.out.println("seller serving to "+name+".....");
}
}
创建增强类
package advice.before;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.stereotype.Component;
@Component
public class GreetingBeforeAdvice implements MethodBeforeAdvice{
@Override
public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
// TODO Auto-generated method stub
//arg0标识目标方法,arg1标识目标方法的参数值,arg2标识目标实例
System.out.println("====="+arg0.getName()+"=====");
String clientName = (String) arg1[0];
System.out.println("How are you!"+clientName+".");
System.out.println("===="+arg2.getClass().getName()+"====");
}
}
创建切面
package advisor.staticmethod;
import java.lang.reflect.Method;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
import advisor.Waiter;
public class GreetingAdvisor extends StaticMethodMatcherPointcutAdvisor{
@Override
public boolean matches(Method method, Class<?> arg1) {
// TODO Auto-generated method stub
System.out.println("开始过滤方法=======");
return "greetTo".equals(method.getName());
}
@Override
public ClassFilter getClassFilter(){
return new ClassFilter() {
public boolean matches(Class clazz) {
// TODO Auto-generated method stub
System.out.println("开始过滤类=======");
return Waiter.class.isAssignableFrom(clazz);
}
};
}
}
Spring配置文件
<!-- 向切面注入前置增强 -->
<bean id="greetingAdvisor" class="advisor.staticmethod.GreetingAdvisor"
p:advice-ref="greetingBeforeAdvice"/>
<!-- 定义公共配置信息 -->
<bean id="parent" abstract="true"
class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="greetingAdvisor"
p:proxyTargetClass="true"/>
<bean id="waiter1" parent="parent" p:target-ref="waiterTarget"/>
<bean id="seller" parent="parent" p:target-ref="sellerTarget"/>
测试
package advisor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestAdvisor {
public static void main(String[] args){
String path = "application.xml";
ApplicationContext ap = new ClassPathXmlApplicationContext(path);
Waiter waiter = (Waiter) ap.getBean("waiter1");
waiter.greetTo("李小龙");
}
}
2、静态正则表达式方法匹配切面
该切面会通过类名和方法名对增强进行准确的织入。RegexMethodMatcherPointcutAdvisor方法对目标类和方法进行过滤。
创建过程:1、创建目标类和方法;2、通过RegexMethodMatcherPointcutAdvisor创建切面对接入点进行过滤;3、创建增强;4、将目标类、切面和增强加入代理。
3、动态切面
动态切点在每次执行时都要进行目标类和目标方法的检查,虽然这会对服务性能造成影响,因此在进行动态检查时,首先进行静态检查,检查通过后进行动态检查。动态切面使用的类为DynamicMethodMatcherPointcut。
创建过程:1、创建目标类和方法;2、通过DynamicMethodMatcherPointcut创建动态切面;3、创建增强;4、将增强和动态切面注入默认切面;5、将目标类、切面注入代理。
创建动态切点类
package advisor.dynamic;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.support.DynamicMethodMatcherPointcut;
import advisor.Waiter;
public class GreetingDynamicPointcut extends DynamicMethodMatcherPointcut{
private static List<String> list = new ArrayList<String>();
static{
list.add("李小刚");
}
public boolean matches(Method method, java.lang.Class<?> targetClass) {
System.out.println("对方法进行静态过滤=========");
return "serveTo".equals(method.getName());
}
@Override
public boolean matches(Method arg0, Class<?> arg1, Object... arg2) {
// TODO Auto-generated method stub
System.out.println("对方法进行动态过滤=================");
String clientname = (String) arg2[0];
return list.contains(clientname);
}
public ClassFilter getClassFilter(){
System.out.println("对类进行过滤===============");
return new ClassFilter() {
@Override
public boolean matches(Class<?> arg0) {
// TODO Auto-generated method stub
return Waiter.class.isAssignableFrom(arg0);
}
};
}
}
Spring进行配置动态切面
<bean id="parent1" abstract="true"
class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="dynamicAdvisor"
p:proxyTargetClass="true"/>
<bean id="waiter2" parent="parent1" p:target-ref="waiterTarget"/>
测试
package advisor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestAdvisor {
public static void main(String[] args){
String path = "application.xml";
ApplicationContext ap = new ClassPathXmlApplicationContext(path);
Waiter waiter = (Waiter) ap.getBean("waiter2");
waiter.serveTo("李小刚");
}
}
4、流程切面
流程切面与动态切面相似,但是效率较低。流程切面主要有DefaultPointcutAdvisor和ControlFlowPointcut实现。
创建过程:1、创建目标类和方法;2、创建增强;3、ControlFolwPointcut类注入目标类和方法生成切点;4、将切点和增强注入普通切面;5、将切面和目标方法注入代理;
Spring文件配置
<!-- 设置流程切点 -->
<bean id="contrlflowpointcut" class="org.springframework.aop.support.CrontrolFolwPointcut">
<constructor-arg type="java.lang.Class" value=""/>
<constructor-arg type="java.lang.String" value="service"/>
</bean>
<!-- 设置流程切面 -->
<bean id="controlflowadvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="pointcut">
<ref bean="contrlflowpointcut"/>
</property>
<property name="advice">
<bean class="advice.before.GreetingBeforeAdvice"/>
</property>
</bean>
<!-- 设置目标代理类 -->
<bean id="parent2" abstract="true"
class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="controlflowadvisor"
p:proxyTargetClass="true"/>
<bean id="waiter3" parent="parent2" p:target-ref="waiterTarget"/>
需要织入增强的类
package advisor.controlfolw;
import advisor.Waiter;
public class WaiterDelegate {
private Waiter waiter;
public Waiter getWaiter() {
return waiter;
}
public void setWaiter(Waiter waiter) {
this.waiter = waiter;
}
public void service(String clientName){
waiter.greetTo(clientName);
waiter.serveTo(clientName);
}
}
测试
package advisor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import advisor.controlfolw.WaiterDelegate;
public class TestAdvisor {
public static void main(String[] args){
String path = "application.xml";
ApplicationContext ap = new ClassPathXmlApplicationContext(path);
Waiter waiter = (Waiter) ap.getBean("waiter3");
WaiterDelegate delegate = new WaiterDelegate();
delegate.setWaiter(waiter);
delegate.service("小刚");
}
}
5、复合切点切面
复合切点切面是将多个切点添加到复合切点对象中,生成复合切点的类为ComposablePointcut类。复合切点可以由两种方法实现,1、insertsection方法实现切点交集;2、union方法实现切点并集。
创建过程:1、通过ComposablePointcut创建复合切点,将其他类型的切点加入其中。2、将复合切点和增强注入切面Bean中;3、将切面注入代理Bean。
创建复合切点类
package advisor.composable;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.ControlFlowPointcut;
import org.springframework.aop.support.NameMatchMethodPointcut;
import org.springframework.stereotype.Component;
import advisor.controlfolw.WaiterDelegate;
@Component("componsable")
public class GreetingComposablePointcut {
public ComposablePointcut getComposablePointcut(){
ComposablePointcut pointcut = new ComposablePointcut();
Pointcut pc = new ControlFlowPointcut(WaiterDelegate.class, "service");
NameMatchMethodPointcut mPointcut = new NameMatchMethodPointcut();
mPointcut.addMethodName("greetTo");
return pointcut.intersection(pc).intersection(mPointcut);
}
}
Spring配置
<!-- 创建复合切面 -->
<bean id="composableadvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="pointcut">
<ref bean="#componsable.composablePointcut"/>
</property>
<property name="advice">
<bean class="advice.before.GreetingBeforeAdvice"/>
</property>
</bean>
<!-- 创建目标代理 -->
<bean id="parent3" abstract="true"
class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="composableadvisor"
p:proxyTargetClass="true"/>
<bean id="waiter4" parent="parent3" p:target-ref="waiterTarget"/>