在面试中咱们常常被问到spring的AOP,今天我也说一下AOP.java
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,经过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP能够对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度下降,提升程序的可重用性,同时提升了开发的效率。 AOP底层采用两种动态代理模式实现: 1.JDK的动态代理node
2. CGLIB动态代理
复制代码
当被代理的对象没有实现接口时候,使用的是CGLIB代理,当被代理的对象实现接口时候使用的是JDK代理,固然也能够强制使用CGLIB代理。 spring有支持3中类型实现AOP 须要实现接口,手工建立代理,最原始的实现方式python
使用XML配置。aop命名空间nginx
使用注解(@AspectJ)web
咱们今天主要是讲解最原始的方式,手工建立代理,实现AOP.这种当时几乎不用了,主要是让你们理解AOP的做用以及原理,学习以前咱们先搞清楚几个概念. 1.切面(Aspect) 交叉业务逻辑,例如事务、安全、日志等,称为切面。面试
2.目标对象(Target) 业务类的对象,称为目标对象。redis
3.织入(Weaving) 将切面插入到目标对象的目标方法的过程,称为织入。spring
4.链接点(JoinPoint) 目标对象中能够被切面织入的方法。docker
5.切入点(Pointcut) 目标对象中真正被切面织入的方法。切入点必定是链接点,但链接点不必定是切入点。被标记为final的方法是不能做用链接点与切入点的。typescript
6.通知(Advice) 通知是切面的一种,能够完成简单的织入功能。通知能够定义切面织入的时间点,切入点定义了切面织入的位置。
7.顾问(Advisor) 顾问是切面的一种,可以将通知以更为复杂的方式织入到目标对象中,是将通知包装为 更复杂的切面的装配器。
上面的通知(Advice)和顾问(Advisor)是两种织入的方式,他们两也是有区别的,通知是给目标对象的全部方法都会织入切面,而Advisor是有选择的给目标对象的方法织入切面。咱们接下来就看一下,如何基于手动建立代理方式而且使用Advisor织入来演示Spring AOP,先看一下他的关系图
咱们按照上面的图实现AOP 实现classFilter 和MethodMatcher package com.jiepi.spring.util;
import com.jiepi.spring.serviceImp.Person; import org.springframework.aop.ClassFilter;
public class MyClassFilter implements ClassFilter {
/*
* 1.一个接口下会有多个实现类
* 2.判断当前实现类是否是咱们织入方式关心的目标类
* BaseService接口咱们如今只想管理Person.
* 参数:就是当前被拦截类:可能Person,可能Gog
* */
@Override
public boolean matches(Class<?> clazz) {
if (clazz == Person.class) {
return true;
}
return false;
}
复制代码
}
package com.jiepi.spring.util;
import org.springframework.aop.MethodMatcher;
import java.lang.reflect.Method;
public class MyMethodMatcher implements MethodMatcher {
/*
* 被监控接口好比(BaseService),没有重载方法
* 每个方法名称都是以惟一
* 此时能够采用 static检测方式,只根据方法名称判断
* 参数:method: 接口中某一个方法
* targetClass: 接口中一个实现类
*
* 业务:只想为Person类中play方法提供织入
*/
@Override
public boolean matches(Method method, Class<?> targetClass) {
String methodName = method.getName();
if ("play".equals(methodName)) {
return true;
}
return false;
}
@Override
public boolean isRuntime() {
return false;
}
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
return false;
}
复制代码
} 2.实现切入点PointCut实现获取那个对象的那个方法要进行织入切面
package com.jiepi.spring.util;
import org.springframework.aop.ClassFilter; import org.springframework.aop.MethodMatcher; import org.springframework.aop.Pointcut;
public class MyPointCut implements Pointcut {
private ClassFilter classFilter;
private MethodMatcher methodMatcher;
public void setClassFilter(ClassFilter classFilter) {
this.classFilter = classFilter;
}
public void setMethodMatcher(MethodMatcher methodMatcher) {
this.methodMatcher = methodMatcher;
}
@Override
public ClassFilter getClassFilter() {
return this.classFilter;
}
@Override
public MethodMatcher getMethodMatcher() {
return this.methodMatcher;
}
复制代码
} 3.实现 切面 advice
package com.jiepi.spring.advice;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class MyAfterAdvice implements AfterReturningAdvice {
//切面:次要业务
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("玩完以后我要给洁癖洗澡");
}
复制代码
} 4.实现PointcutAdvisor实现绑定切入点和切面
package com.jiepi.spring.util;
import org.aopalliance.aop.Advice; import org.springframework.aop.Pointcut; import org.springframework.aop.PointcutAdvisor;
public class MyPointCutAdvisor implements PointcutAdvisor {
private Pointcut pointcut;//当前拦截对象和对象调用主要业务方法 person对象.play()
private Advice advice; //次要业务以及次要业务与主要业务执行顺序
public void setPointcut(Pointcut pointcut) {
this.pointcut = pointcut;
}
public void setAdvice(Advice advice) {
this.advice = advice;
}
@Override
public Pointcut getPointcut() {
return this.pointcut;
}
@Override
public Advice getAdvice() {
return this.advice;
}
@Override
public boolean isPerInstance() {
return false;
}
复制代码
} 5.目标对象
package com.jiepi.spring.service;
public interface BaseService {
void eat();
void play();
复制代码
}
package com.jiepi.spring.serviceImp;
import com.jiepi.spring.service.BaseService;
public class Person implements BaseService {
@Override
public void eat() {
System.out.println("洁癖主人吃汉堡包");
}
@Override
public void play() {
System.out.println("洁癖主人玩电脑");
}
复制代码
} 6.配置依赖注入,实现手动建立代理对象
<!-- 注册被监控实现类 -->
<bean id="person" class="com.jiepi.spring.serviceImp.Person"/>
<bean id="dog" class="com.jiepi.spring.serviceImp.Dog"/>
<!-- 注册通知实现类 -->
<bean id="after" class="com.jiepi.spring.advice.MyAfterAdvice"/>
<!-- 注册类型过滤器 -->
<bean id="classFilter" class="com.jiepi.spring.util.MyClassFilter"/>
<!-- 注册方法匹配器 -->
<bean id="methodMatcher" class="com.jiepi.spring.util.MyMethodMatcher"/>
<!-- 注册切入点 -->
<bean id="pointCut" class="com.jiepi.spring.util.MyPointCut">
<property name="classFilter" ref="classFilter"/>
<property name="methodMatcher" ref="methodMatcher"/>
</bean>
<!-- 注册顾问 -->
<bean id="myAdvisor" class="com.jiepi.spring.util.MyPointCutAdvisor">
<property name="advice" ref="after"/>
<property name="pointcut" ref="pointCut"/>
</bean>
<!-- 注册代理对象工厂 -->
<!--
此时生成代理对象,只会负责person.play方法监控
与Advice不一样,不会对BaseService全部的方法进行监控
-->
<bean id="personFactory" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="person"/>
<property name="interceptorNames" value="myAdvisor"/>
</bean>
复制代码
7.测试结果
package com.jiepi.spring;
import com.jiepi.spring.service.BaseService; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestMain {
@Test
public void test() {
ApplicationContext factory = new ClassPathXmlApplicationContext("spring_config.xml");
BaseService service = (BaseService) factory.getBean("personFactory");
service.play();
System.out.println("===========下面是没有进行代理的方法================");
service.eat();
}
复制代码
}
运行结果: 洁癖主人玩电脑 玩完以后我要给洁癖洗澡 ===========下面是没有进行代理的方法================ 洁癖主人吃汉堡包 能够看到咱们的结果,仅仅对person目标对象中的play使用了AOP,织入了其余业务。 这个例子仅仅是让你们理解SpringAOP.并不建议使用,由于写起来太繁琐了,咱们大部分工做用的都是AspectJ.其余方式的使用有机会再和你们分享.
但愿对你们有所帮助,也但愿你们持续关注转载。关注公众号获取相关资料请回复:typescript,springcloud,springboot,nodejs,nginx,mq,javaweb,java并发实战,java并发高级进阶,实战java并发,极客时间dubbo,kafka,java面试题,ES,zookeeper,java入门到精通,区块链,java优质视频,大数据,kotlin,瞬间之美,HTML与CSS,深刻体验java开发,web开发CSS系列,javaweb开发详解,springmvc,java并发编程,spring源码,python,go,redis,docker,即获取相关资料