AOP(面向切面编程)
1、连接点(Joinpoint):
连接点是程序执行过程中可以插入切面的特定的点。这个点可以是调用方法时,抛出异常时,甚至修改字段时,
对于Spring而言,只支持方法连接点。
2、切点(Pointcut):
一个类有很多方法,这些方法都是连接点,但是并不是每一个方法都要插入切面,那么就要经过特定的条件过滤,这些
经过过滤后要插入切面的方法被称为切点,所以切点是由一个活多个连接点组成,是连接点集合的一个子集。
3、通知(Advice):
切面所要完成的工作称为通知,它定义的前切点前(before)还是切点后(after)做什么,可理解为”何时“、”干什么“。
Spring有5种类型的通知:
① Before:前置通知,在切点方法之前执行。
② After :后置通知,在切点方法返回后通知,无论方法是否执行成功。
③ After-returning:返回后通知,在切点方法成功执行之后调用通知。
④After-throwing:抛出后通知,在切点方法抛出异常后调用通知。
⑤Arount:环绕通知,既在方法调用前又在方法调用后调用通知。并且可以决定这个方法是否执行,什么时候执行。
4、切面(Aspect):
通知和切点的结合就是切面。
5、引入(Introduction):
引入就是为一个类添加新的方法和属性,从而在不改变现有类的情况下让他们具有新的行为和属性。
6、织入(Weaving):
织入是切面应用到目标对象的过程,在目标对象的运行周期里有多个点可以进行织入。
①编译期:在切面目标类被编译的时候织入。AspectJ就是编译期织入。
②类加载期:切面目标类被加载到JVM是织入。
③运行时:切面在运行的某个时刻织入,Spring AOP就是通过这种方式织入的,AOP容器会为目标对象动态的创建一个代理对象
7、代理(Proxy):
在实现上,Spring的AOP其实就是使用JDK的动态代理(使用接口的方式完成代理操作),也可以使用CGLIB(使用继承的方式完成代理操作)。
8、目标(Target)
业务操作的实际对象。
<aop:config> <aop:pointcut id="cut" expression="execution(**(..))"/> <aop:aspect ref="serviceFactory"> <aop:before method="before" pointcut-ref="cut"/> <aop:after method="after" pointcut-ref="cut"/> </aop:aspect> </aop:config>
Java的动态代理机制
代理模式是常用的Java设计模式。代理类主要负责为委托类预处理消息、过滤信息、把消息转发给委托类,以及事后处理信息等。
这里照前辈举了2个动态代理的例子,分别是 JDK和Cglib的代理
即通过 代理类来包装service实现AOP,ServiceFactory中相当于 AOP中的通知即要处理的事件
<span style="font-size:14px;">public class ServiceFactory {
public static void before(){
System.out.println("前置日志打印,启动事务等");
}
public static void after(){
System.out.println("后置日志打印,关闭事务等");
}
}</span>
JDK动态代理,此中涉及java的反射机制
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class LearnProxyJDK implements InvocationHandler { // 目标对象,也就是我们主要的业务,主要目的要做什么事 private Object delegate; /** * 和你额外需要做得事情,进行绑定,返回一个全新的对象 */ public Object bind(Object delegate){ this.delegate = delegate; return Proxy.newProxyInstance(this.delegate.getClass().getClassLoader(), this.delegate.getClass().getInterfaces(),this); } /** * 你刚才需要执行的方法,都需要通过该方法进行动态调用 */ @Override public Object invoke(Object proxy,Method method,Object[] args) throws Throwable { Object obj = null; ServiceFactory.before(); obj = method.invoke(this.delegate,args); ServiceFactory.after(); return obj; } }
Cglib代理还不是很明白,需要进一步理解,在此先留个印象import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class LearnProxyCglib implements MethodInterceptor { private Object target; /** * 这里和JDK 绑定目标类 类似 * 仅仅是创建了一个以继承了目标类的新对象 */ public Object bind(Object target) { this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.target.getClass()); enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object arg0, Method method, Object[] arg2, MethodProxy proxy) throws Throwable { ServiceFactory.before(); Object obj = proxy.invokeSuper(arg0, arg2); ServiceFactory.after(); return obj; } }
测试类:public class TestProxy { public static void main(String[] args) { // 这里使用 JDK代理 必须 通过接口 代理 // 直接new 接口实现类 即不启用代理 Service service = new ServiceImpl(); Service serviceProxyJDK = (Service) new LearnProxyJDK().bind(new ServiceImpl()); service.sayHello(); System.out.println("-------------"); serviceProxyJDK.sayHello(); System.out.println("-----------"); // 这里无论是使用接口 还是 实现类都可以接收 // 这里我创建了接口 和实现类,都可以实现 ServiceCglib serviceProxyCglib = (ServiceCglib) new LearnProxyCglib().bind(new ServiceCglib()); serviceProxyCglib.sayHelloToo(); // 这里顺便说说 spring aop 默认是jdk 的动态代理 // 要强制使用cglib的 ,设置 proxy-target-class="true" // 使用aop 一般在事务控制,和 业务层控制两个地方 } }