文章目录
Spring AOP 目标
- 将分散在程序各处的横切关注点剥离出来并以集中的方式进行表达
- 使得开发人员能够专注于业务逻辑的实现而非繁杂的非功能代码, 简化了程序编写与单元测试
- 应用场景
- 日志
- 安全
- 事务
AOP 核心概念
- Advice(通知):定义在连接点处的行为,围绕方法调用而进行注入
- Pointcut(切点):确定在哪些连接点处应用通知
- Advisor(通知器):组合Advice与Pointcut
Spring AOP实现
- ProxyFactoryBean
- FactoryBean implementation that builds an AOP proxy based on beans in Spring BeanFactory.(From Spring doc)
- FactoryBean实现,基于Spring BeanFactory中的Bean构建AOP代理
- Spring AOP的底层实现与源头
ProxyFactoryBean介绍
BeanFactory是spring Ioc的工厂,它里面管理着Spring所创建出来的各种Bean对象,当我们在配置文件(注解)中声明了某个bean的id后,通过 这个id就可以获取到与该id所对应的class对象的实例(可能新建,也可能从缓存中获取)
FactoryBean本质上也是一个Bean, 它同其他Bean一样,也是由BeanFactory所管理和维护的,当然它的实例也会缓存到spring的工厂中(如果是单例) 它与普通的Bean的唯一区别在于,当spring创建一个FactoryBean的实例后,它接下来会判断当前所创建的Bean是否是一个FactoryBean实例,如果 不是,那么就直接将创建出来的Bean返回给客户端;如果是,那么它会对其进行进一步的处理,根据配置文件所配置的target, advisor与interfaces等 信息,在运行期动态构建出一个类,并生成该类的一个实例,最后将该实例返回给客户端;因此,我们在声明一个FactoryBean时,通过id获取到的并非 这个FactoryBean的实例,而是它动态生成出来的一个代理对象(通过三种方式来进行生成:JDK动态代理、CGLIB、ObjenesisCglibAopProxy)
ProxyFactoryBean的典型配置
<?xml version="1.0" encoding="UTF-8"?>
<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="myService" class="com.kernel.spring.aop.service.impl.MyServiceImpl"/>
<!-- 通知器 : 对目标类里的方法增强(调用目标方法前后做一些额外的逻辑) -->
<bean id="myAdvisor" class="com.kernel.spring.aop.advisor.MyAdvisor"/>
<!-- AOP ProxyFactoryBean 目标对象 代理对象所实现的接口 通知器-->
<bean id="myAop" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理对象所实现的接口 -->
<property name="proxyInterfaces" value="com.kernel.spring.aop.service.MyService"/>
<!-- 通知器列表 -->
<property name="interceptorNames">
<list>
<value>myAdvisor</value>
</list>
</property>
<!-- 目标对象 -->
<property name="target" ref="myService"/>
</bean>
</beans>
ProxyFactoryBean的构成
- target:目标对象,需要对其进行切面增强
- proxylnterfaces:代理对象所实现的接口
- interceptorNames:通知器(Advisor)列表,通知器中包含了通知 (Advice) 与切点(Pointcut)
ProxyFactoryBean的作用
针对目标对象来创建代理对象,将对目标对象方法的调用转到对相应代理对象方法的调用,并且可以在代理对象方法调用前后执行与之匹配的 各个通知器中定义好的方法
未使用AOP的情形:直接调用目标对象的特定方法
使用AOP的情形:目标对象执行前后可以增加自定义逻辑
目标代理对象的创建
- Spring通过3种方式来创建目标代理对象
- JDK动态代理
- CGLIB
- ObjenesisCglibAopProxy(Spring4.0引入)
DefaultAopProxyFactory.java
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (NativeDetector.inNativeImage() || !config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
return new JdkDynamicAopProxy(config);
} else {
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.");
} else {
return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));
}
}
}
代理模式详解
设计模式之代理模式:https://blog.csdn.net/ZSA222/article/details/122674562
- 代理模式的作用是:为其他对象提供一种代理以控制对这个对象 的访问。
- 在某些情况下,一个客户不想或者不能直接引用另一个对象,而 代理对象可以在客户端和目标对象之间起到中介的作用。
JDK动态代理
如果目标对象实现了接口,那么Spring就会通过JDK动态代理为目 标对象生成代理对象
在运行时生成class,在生成它时你必须提供一组interface给它,然后该 class就会声明它实现了这些 interface。 因此我们可以将该class的实例当 作这些interface中的任何一个来用。当然,这个动态代理其实就是一个 Proxy, 它不会替你作实质性的工作,在生成它的实例时你必须提供一个 handler, 由它接管实际的工作
JDK动态代理实现步骤
- 创建一个实现接口InvocationHandler的类,它必须实现invoke方 法
- 创建被代理的类以及接口
- 通过Proxy的静态方法
static Object newProxylnstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
- 通过代理调用方法
代码举例
/**
* @ClassName ITeacher
* @author: shouanzh
* @Description ITeacher
* @date 2022/1/24 16:44
*/
public interface ITeacherDao {
void teach();
}
/**
* @ClassName TeacherDao
* @author: shouanzh
* @Description TeacherDao 目标对象
* @date 2022/1/24 16:45
*/
public class TeacherDao implements ITeacherDao{
@Override
public void teach() {
System.out.println("老师正在讲课。。。");
}
}
/**
* @ClassName ProxyFactory
* @author: shouanzh
* @Description ProxyFactory 代理工厂
* @date 2022/1/24 16:47
*/
public class ProxyFactory {
// 维护一个目标对象
private Object target;
// 构造器 对 target 初始化
public ProxyFactory(Object target) {
this.target = target;
}
// 给目标对象生成一个代理对象
public Object getProxyInstance() {
// ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法固定
// Class<?>[] interfaces:目标对象实现的接口类型,使用泛型方法确认类型
// InvocationHandler h:事情处理,执行目标对象的方法时触发事情处理器方法,把当前执行的目标对象方法作为参数传入
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK代理开始");
Object invoke = method.invoke(target, args);
System.out.println("JDK代理结束");
return invoke;
}
});
}
}
/**
* @ClassName Client
* @author: shouanzh
* @Description Client
* @date 2022/1/24 17:04
*/
public class Client {
public static void main(String[] args) {
// 创建目标对象
ITeacherDao target = new TeacherDao();
// 给目标对象创建代理对象
ProxyFactory proxyFactory = new ProxyFactory(target);
// 可以转成ITeacherDao
ITeacherDao teacherDao = (ITeacherDao) proxyFactory.getProxyInstance();
teacherDao.teach();
}
}
Spring源码分析:JdkDynamicAopProxy.java
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
}
return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
}
CGLIB代理
如果目标类并未实现接口,那么Spring就会使用CGLIB库为其创建 代理。
CGLIB代码举例
/**
* @ClassName TeacherDao
* @author: shouanzh
* @Description TeacherDao 被代理的对象
* @date 2022/1/24 20:27
*/
public class TeacherDao {
public String teach() {
System.out.println("老师授课中...");
return "Good";
}
}
/**
* @ClassName ProxyFactory
* @author: shouanzh
* @Description ProxyFactory 代理工厂
* @date 2022/1/24 20:27
*/
public class ProxyFactory implements MethodInterceptor {
/**
* 目标对象
*/
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
/**
* 返回代理对象
* @return
*/
public Object getProxyInstance() {
// 1、创建工具类
Enhancer enhancer = new Enhancer();
// 2、设置父类
enhancer.setSuperclass(target.getClass());
// 3、设置回调函数
enhancer.setCallback(this);
// 4、创建子类对象,即代理对象
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib代理开始...");
Object retVal = method.invoke(target, objects);
System.out.println("cglib代理结束...");
return retVal;
}
}
/**
* @ClassName Client
* @author: shouanzh
* @Description Client
* @date 2022/1/24 20:30
*/
public class Client {
public static void main(String[] args) {
// 创建目标对象
TeacherDao teacherDao = new TeacherDao();
// 通过代理工厂创建代理对象
TeacherDao proxyInstance = (TeacherDao) new ProxyFactory(teacherDao).getProxyInstance();
// 通过代理对象调用目标对象方法 触发intercept 从而实现对目标对象的调用
String retVal = proxyInstance.teach();
System.out.println("retVal=" + retVal);
}
}
小结
- Spring核心IOC与AOP是整个Spring的基石
- Spring AOP实现方式有3种
- 动态代理
- CGLIB库
- ObjenesisCglibAopProxy
- 事务是Spring AOP应用的最佳范例