AOP (Aspect-Oriented Programming,面向切面编程) 能够将那些与业务无关,却被业务共同调用的逻辑 (如:事务处理、日志管理、权限控制等) 封装起来,降低模块间的耦合程度,利于扩展和维护
AOP 是基于 动态代理 实现,说白了就是在原有代码基础上进行一定的包装,如:在方法执行前、方法返回后、方法抛出异常后等地方进行一定的拦截处理或者叫增强处理,和动态代理的思想一模一样
更具体点,AOP 的实现是有一个代理类,实际执行的其实是生成的代理类对象,并非真正的目标类对象,由代理类对象真正的调用目标类对象的方法,在调用前后可以进行一些特殊处理,称之为增强处理
在动态代理中介绍有两种实现方式,JDK 动态代理适用于实现了接口的目标类,CGLIB 动态代理适用于没有实现接口的目标类,所以 AOP 也是如此,因为它底层的实现就是动态代理
日志记录:日志可以用于系统审计、调试、性能检测等,在方法执行前后或异常抛出时自动记录日志,而不需要在每个方法中都编写记录日志的逻辑
事务管理:在方法执行前开始事务,在方法执行后根据执行结果提交或回滚事务
安全控制:在方法执行前进行权限检查,验证用户是否具有执行该方法的权限
性能监控:在方法执行前后计算方法执行时间,统计方法调用次数,用于性能监测和优化
缓存管理:在方法执行前检查缓存中是否存在所需数据,如果存在直接返回,不需要执行方法
异常处理:在方法抛出异常后统一处理,进行异常的记录、转换、重试等操作
总结:这些应用场景无非就是将多个方法中重复的代码逻辑抽离出来,放到代理类中统一处理,这样就可以不需要在每个方法中都编写相同逻辑的代码,减少冗余,增强扩展性
下面给五个从不同角度实现 Spring AOP 的小 Demo,都是基于 XML 配置文件实现滴~~
先给出这五个例子中使用到的依赖包:
xxxxxxxxxx<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>5.0.0.RELEASE</version></dependency><dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.0.RELEASE</version></dependency><dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.28</version></dependency>再给出这五个例子都会用到的类:
xxxxxxxxxx// package com.lfool.entitypublic class User { private String firstName; private String lastName; private int age; private String address;}public class Order { private String username; private String product;}// package com.lfool.servicepublic interface UserService { User createUser(String firstName, String lastName, int age); User queryUser();}public interface OrderService { Order createOrder(String username, String product); Order queryOrder(String username);}// package com.lfool.service.implpublic class UserServiceImpl implements UserService { private static User user = null; public User createUser(String firstName, String lastName, int age) { user = new User(); user.setFirstName(firstName); user.setLastName(lastName); user.setAge(age); return user; }
public User queryUser() { return user; }}public class OrderServiceImpl implements OrderService { private static Order order = null; public Order createOrder(String username, String product) { order = new Order(); order.setUsername(username); order.setProduct(product); return order; }
public Order queryOrder(String username) { return order; }}// package com.lfool.interceptor// 拦截方法执行前,输出执行方法的参数public class LogArgsAdvice implements MethodBeforeAdvice { public void before(Method method, Object[] args, Object o) throws Throwable { System.out.println("准备执行方法:" + method.getName() + ", 参数列表:" + Arrays.toString(args)); }}// 拦截方法执行后,输出执行方法的结果public class LogResultAdvice implements AfterReturningAdvice { public void afterReturning(Object returnValue, Method method, Object[] args, Object o1) throws Throwable { System.out.println("方法返回:" + returnValue); }}先给出 XML 配置文件spring_1.2_advice.xml:
xxxxxxxxxx <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userServiceImpl" class="com.lfool.service.impl.UserServiceImpl" /> <bean id="orderServiceImpl" class="com.lfool.service.impl.OrderServiceImpl" />
<!-- 定义两个 advice --> <bean id="logArgsAdvice" class="com.lfool.interceptor.LogArgsAdvice" /> <bean id="logResultAdvice" class="com.lfool.interceptor.LogResultAdvice" />
<bean id="useServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 代理的接口 --> <property name="proxyInterfaces"> <list> <value>com.lfool.service.UserService</value> </list> </property> <!-- 代理的具体实现 --> <property name="target" ref="userServiceImpl" /> <!-- 配置拦截器,这里可以配置 advice、advisor、interceptor --> <property name="interceptorNames"> <list> <value>logArgsAdvice</value> <value>logResultAdvice</value> </list> </property> </bean>
<!-- =========================================== --> <!-- 同理,我们也可以配置一个 orderServiceProxy... --> <!-- =========================================== -->
</beans>然后给出测试类:
xxxxxxxxxxpublic class Main { public static void main(String[] args) { // 启动 Spring IoC 容器 ApplicationContext context = new ClassPathXmlApplicationContext("spring_1.2_advice.xml"); UserService userService = (UserService) context.getBean("useServiceProxy");
userService.createUser("Tom", "Cruise", 55); userService.queryUser(); }}输出结果如下:
xxxxxxxxxx准备执行方法:createUser, 参数列表:[Tom, Cruise, 55]方法返回:User(firstName=Tom, lastName=Cruise, age=55, address=null)准备执行方法:queryUser, 参数列表:[]方法返回:User(firstName=Tom, lastName=Cruise, age=55, address=null)
从这个例子中可以看出 Advice 实现的两点局限性:
Advice 实现的拦截器粒度是类级别,也就是会拦截目标类所有方法
需要在 XML 配置文件中为每个类配置一个代理类
为了解决 Advice 实现的第一点局限性,增加了 Advisor 实现,它可以匹配到方法,也就是可以只拦截指定方法,而非目标类的所有方法
先给出 XML 配置文件spring_1.2_advisor.xml:
xxxxxxxxxx <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userServiceImpl" class="com.lfool.service.impl.UserServiceImpl" /> <bean id="orderServiceImpl" class="com.lfool.service.impl.OrderServiceImpl" />
<!-- 定义两个 advice --> <bean id="logArgsAdvice" class="com.lfool.interceptor.LogArgsAdvice" /> <bean id="logResultAdvice" class="com.lfool.interceptor.LogResultAdvice" />
<!-- 定义一个只拦截 createUser 方法的 advisor --> <bean id="logCreateAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <!-- advisor 实例的内部会有一个 advice --> <property name="advice" ref="logArgsAdvice"/> <!-- 只有下面两个方法才会被拦截 --> <property name="mappedNames" value="createUser, createOrder" /> </bean>
<bean id="useServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 代理的接口 --> <property name="proxyInterfaces"> <list> <value>com.lfool.service.UserService</value> </list> </property> <!-- 代理的具体实现 --> <property name="target" ref="userServiceImpl" /> <!-- 配置拦截器,这里可以配置 advice、advisor、interceptor --> <property name="interceptorNames"> <list> <value>logCreateAdvisor</value> </list> </property> </bean>
<!-- =========================================== --> <!-- 同理,我们也可以配置一个 orderServiceProxy... --> <!-- =========================================== -->
</beans>然后给出测试类:
xxxxxxxxxxpublic class Main { public static void main(String[] args) { // 启动 Spring IoC 容器 ApplicationContext context = new ClassPathXmlApplicationContext("spring_1.2_advisor.xml"); UserService userService = (UserService) context.getBean("useServiceProxy");
userService.createUser("Tom", "Cruise", 55); userService.queryUser(); }}输出结果如下:
xxxxxxxxxx准备执行方法:createUser, 参数列表:[Tom, Cruise, 55]
从结果中可以看出,它只拦截了createUser()方法,且只进行了方法执行前的处理,因为我们在 XML 配置文件中只配置了logArgsAdvice
先给出 XML 配置文件spring_1.2_interceptor.xml:
xxxxxxxxxx <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userServiceImpl" class="com.lfool.service.impl.UserServiceImpl" /> <bean id="orderServiceImpl" class="com.lfool.service.impl.OrderServiceImpl" />
<!-- 定义两个 advice --> <bean id="logArgsAdvice" class="com.lfool.interceptor.LogArgsAdvice" /> <bean id="logResultAdvice" class="com.lfool.interceptor.LogResultAdvice" />
<!-- 定义一个 interceptor --> <bean id="logCreateInterceptor" class="com.lfool.aop_interceptor.DebugInterceptor" />
<bean id="useServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 代理的接口 --> <property name="proxyInterfaces"> <list> <value>com.lfool.service.UserService</value> </list> </property> <!-- 代理的具体实现 --> <property name="target" ref="userServiceImpl" /> <!-- 配置拦截器,这里可以配置 advice、advisor、interceptor --> <property name="interceptorNames"> <list> <value>logCreateInterceptor</value> </list> </property> </bean>
<!-- =========================================== --> <!-- 同理,我们也可以配置一个 orderServiceProxy... --> <!-- =========================================== -->
</beans>然后实现MethodInterceptor接口:
xxxxxxxxxxpublic class DebugInterceptor implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("Before: invocation=[" + invocation + "]"); // 执行 真实实现类 的方法 Object rval = invocation.proceed(); System.out.println("Invocation returned"); return rval; }}最后给出测试类:
xxxxxxxxxxpublic class Main { public static void main(String[] args) { // 启动 Spring IoC 容器 ApplicationContext context = new ClassPathXmlApplicationContext("spring_1.2_interceptor.xml"); UserService userService = (UserService) context.getBean("useServiceProxy");
userService.createUser("Tom", "Cruise", 55); userService.queryUser(); }}输出结果如下:
xxxxxxxxxxBefore: invocation=[ReflectiveMethodInvocation: public abstract com.lfool.entity.User com.lfool.service.UserService.createUser(java.lang.String,java.lang.String,int); target is of class [com.lfool.service.impl.UserServiceImpl]]Invocation returnedBefore: invocation=[ReflectiveMethodInvocation: public abstract com.lfool.entity.User com.lfool.service.UserService.queryUser(); target is of class [com.lfool.service.impl.UserServiceImpl]]Invocation returned
可以看到当我们调用目标对象的方法时,其实调用的是invoke()方法
上面三种方式都是基于 ProxyFactoryBean 实现,需要在 XML 配置文件中为每个目标类设置一个ProxyFactoryBean,十分的不灵活;而 BeanNameAutoProxyCreator 实现就对这一局限进行了改进
先给出 XML 配置文件spring_1.2_BeanNameAutoProxy.xml:
xxxxxxxxxx <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userServiceImpl" class="com.lfool.service.impl.UserServiceImpl" /> <bean id="orderServiceImpl" class="com.lfool.service.impl.OrderServiceImpl" />
<!-- 定义两个 advice --> <bean id="logArgsAdvice" class="com.lfool.interceptor.LogArgsAdvice" /> <bean id="logResultAdvice" class="com.lfool.interceptor.LogResultAdvice" />
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <!-- 配置拦截器,这里可以配置 advice、advisor、interceptor --> <property name="interceptorNames"> <list> <value>logArgsAdvice</value> <value>logResultAdvice</value> </list> </property> <!-- 会为 *ServiceImpl 自动创建代理类 --> <property name="beanNames" value="*ServiceImpl" /> </bean>
</beans>然后给出测试类:
xxxxxxxxxxpublic class Main { public static void main(String[] args) { // 启动 Spring IoC 容器 ApplicationContext context = new ClassPathXmlApplicationContext("spring_1.2_BeanNameAutoProxy.xml");
// 这里不再需要根据 BeanName 获取 Bean // userService 和 orderService 是一个披着接口外套的 JdkDynamicAopProxy 代理对象 UserService userService = context.getBean(UserService.class); OrderService orderService = context.getBean(OrderService.class);
userService.createUser("Tom", "Cruise", 55); userService.queryUser();
orderService.createOrder("Leo", "随便买点什么"); orderService.queryOrder("Leo"); }}输出结果如下:
xxxxxxxxxx准备执行方法:createUser, 参数列表:[Tom, Cruise, 55]方法返回:User(firstName=Tom, lastName=Cruise, age=55, address=null)准备执行方法:queryUser, 参数列表:[]方法返回:User(firstName=Tom, lastName=Cruise, age=55, address=null)准备执行方法:createOrder, 参数列表:[Leo, 随便买点什么]方法返回:Order(username=Leo, product=随便买点什么)准备执行方法:queryOrder, 参数列表:[Leo]方法返回:Order(username=Leo, product=随便买点什么)
从结果中可以看出,BeanNameAutoProxyCreator 实现和 Advice 实现很像,拦截粒度都是类级别,不同之处在于它会自动生成符合正则表达式规则的所有代理类
DefaultAdvisorAutoProxyCreator 实现可以匹配到方法,也就是可以只拦截指定方法,而非目标类的所有方法
先给出 XML 配置文件spring_1.2_DefaultAdvisorAutoProxy.xml:
xxxxxxxxxx <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userServiceImpl" class="com.lfool.service.impl.UserServiceImpl" /> <bean id="orderServiceImpl" class="com.lfool.service.impl.OrderServiceImpl" />
<!-- 定义两个 advice --> <bean id="logArgsAdvice" class="com.lfool.interceptor.LogArgsAdvice" /> <bean id="logResultAdvice" class="com.lfool.interceptor.LogResultAdvice" />
<!-- 记录 create* 方法的参数 --> <bean id="logArgsAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice" ref="logArgsAdvice" /> <property name="pattern" value="com.lfool.service.*.create.*" /> </bean> <!-- 记录 create* 方法的返回值 --> <bean id="logResultAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice" ref="logResultAdvice" /> <property name="pattern" value="com.lfool.service.*.create.*" /> </bean>
<!-- 定义 DefaultAdvisorAutoProxyCreator --> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />
</beans>然后给出测试类:
xxxxxxxxxxpublic class Main { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("spring_1.2_DefaultAdvisorAutoProxy.xml"); UserService userService = context.getBean(UserService.class); OrderService orderService = context.getBean(OrderService.class);
userService.createUser("Tom", "Cruise", 55); userService.queryUser();
orderService.createOrder("Leo", "随便买点什么"); orderService.queryOrder("Leo"); }}输出结果如下:
xxxxxxxxxx准备执行方法:createUser, 参数列表:[Tom, Cruise, 55]方法返回:User(firstName=Tom, lastName=Cruise, age=55, address=null)准备执行方法:createOrder, 参数列表:[Leo, 随便买点什么]方法返回:Order(username=Leo, product=随便买点什么)
从结果中可以看出,只拦截了create*()方法,并没有拦截query*()方法
下面主要分析 BeanNameAutoProxyCreator 实现的源码,关注点在于 Spring 如何为目标对象自动创建代理对象~~
在 XML 配置文件中会配置BeanNameAutoProxyCreator,它继承AbstractAutoProxyCreator类
AbstractAutoProxyCreator类实现了BeanPostProcessor接口的postProcessBeforeInitialization方法和postProcessAfterInitialization方法
在 Spring IoC 容器启动自动注入对象时,会在初始化阶段前执行postProcessBeforeInitialization方法,初始化后执行postProcessAfterInitialization方法。关于 Spring IoC 相关内容可见 Spring IoC
为目标对象自动创建代理对象就在postProcessAfterInitialization方法中完成,这也是 AOP 基于后处理器实现的原因
先来看看AbstractAutoProxyCreator类:
x
// AbstractAutoProxyCreator.javapublic Object postProcessAfterInitialization( Object bean, String beanName) throws BeansException { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (!this.earlyProxyReferences.contains(cacheKey)) { // 创建代理对象,具体见下方 return wrapIfNecessary(bean, beanName, cacheKey); } } return bean;}protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { // 检查参数,跳过已经执行过代理对象生成,或者已知的不需要生成代理对象的 Bean if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) { return bean; } if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; }
// Create proxy if we have advice. // 查看当前 Bean 所有 AOP 增强设置,最终通过 AOPUtils 工具类实现 Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); // 创建代理对象,具体见下方 Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; }
this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean;}protected Object createProxy(Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) { AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass); } // 实例化代理工厂类 ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.copyFrom(this); // 当全局使用动态代理时,设置是否需要对目标 Bean 强制使用 CGLIB 动态代理 if (!proxyFactory.isProxyTargetClass()) { if (shouldProxyTargetClass(beanClass, beanName)) { proxyFactory.setProxyTargetClass(true); } else { evaluateProxyInterfaces(beanClass, proxyFactory); } } // 构建 AOP Advisor Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); // 设置 proxyFactory 属性 proxyFactory.addAdvisors(advisors); proxyFactory.setTargetSource(targetSource); customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy); if (advisorsPreFiltered()) { proxyFactory.setPreFiltered(true); } // 创建代理对象,具体见下方 return proxyFactory.getProxy(getProxyClassLoader());}继续看ProxyFactory类:
x
// ProxyFactory.javapublic Object getProxy( ClassLoader classLoader) { return createAopProxy().getProxy(classLoader);}// ProxyCreatorSupport.java,是 ProxyFactory 的父类protected final synchronized AopProxy createAopProxy() { if (!this.active) { activate(); } // 创建代理对象,具体见下方 return getAopProxyFactory().createAopProxy(this);}最后看DefaultAopProxyFactory类:
x
// DefaultAopProxyFactory.javapublic AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { // 如果 optimize 或 proxyTargetClass 属性设置为 true,或者未指定代理接口,则使用 CGLIB 动态代理 if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { Class<?> targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation."); } // 目标类本身是接口或者代理对象,仍然使用 JDK 动态代理 if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { // 使用 JDK 动态代理 return new JdkDynamicAopProxy(config); } // 使用 CGLIB 动态代理 return new ObjenesisCglibAopProxy(config); } else { // 使用 JDK 动态代理 return new JdkDynamicAopProxy(config); }}总结:Spring AOP 的实现基于 动态代理。如果目标类实现了接口,Spring 会使用 JDK 动态代理;如果目标类没有实现接口,Spring 会使用 CGLIB 动态代理
这里给出个人的一个猜想,不知对错,有懂的可以在首页联系作者微信!!
在写 Spring 项目时,为什么建议在 Service 层先定义接口,然后再写对应实现类??
我认为有一个原因可能是,目标类实现了接口就可以使用 JDK 动态代理,而 JDK 动态代理效率相比于 CGLIB 动态代理更加优秀,而且随着 JDK 版本的升级,这个优势更加明显