一,使用AOP
先看spring aop具體怎么使用,再分析原始碼,
1,xml配置方式
xml組態檔 spring.xml
<?xml version="1.0" encoding="utf-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
">
<bean />
<bean id="userService2" />
<bean id="beforeAdvice" />
<bean id="methodInterceptor" >
<constructor-arg ref="beforeAdvice"/>
</bean>
<bean >
<property name="expression" value="https://www.cnblogs.com/lm0903/p/execution(* bean.IUserService.*(..))"/>
<property name="advice" ref="methodInterceptor"/>
</bean>
</beans>
service介面:
package bean;
public interface IUserService {
String queryUserInfo();
}
被代理的UserService實作類 , jdk代理只會代理介面,被代理的類需要實作一個介面,使用的時候用介面
package bean;
import java.util.Random;
public class UserService2 implements IUserService {
public String queryUserInfo() {
System.out.println("這是第二個方法");
return "queryUserInfo 回傳值";
}
}
Advice增強類:
package bean;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class UserServiceBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("攔截方法:" + method.getName());
}
}
測驗結果:

2,注解方式
<?xml version="1.0" encoding="utf-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
">
<context:property-placeholder location="beans.yml" file-encoding="UTF-8"/>
<context:component-scan base-package="bean"/>
<aop:aspectj-autoproxy />
</beans>
被代理的UserService 增強類:
package bean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.util.Random;
@Service
public class UserService implements IUserService {
public String queryUserInfo() {
System.out.println("執行queryUserInfo方法");
try {
Thread.sleep(new Random(1).nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
return "回傳值";
}
}
注解式的增強類
package bean;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Component
@Aspect
public class UserServiceAnnotationAdvice {
@Around(value="https://www.cnblogs.com/lm0903/p/execution(* bean.IUserService.*(..))")
public Object adviceAround(ProceedingJoinPoint joinPoint) throws Throwable{
//獲取連接點簽名
Signature signature = joinPoint.getSignature();
//將其轉換為方法簽名
MethodSignature methodSignature = (MethodSignature) signature;
//通過方法簽名獲取被呼叫的目標方法
Method method = methodSignature.getMethod();
long startTime = System.currentTimeMillis();
//呼叫proceed方法,繼續呼叫下一個通知
Object returnVal = joinPoint.proceed();
long endTime = System.currentTimeMillis();
long costTime = endTime - startTime;
//輸出方法資訊
System.out.println(String.format("%s,耗時:%s", method.toString(), costTime));
//回傳方法的回傳值
return returnVal;
}
}
測驗效果:

二,原始碼剖析
1,AOP相關的幾個類
Joinpoint:切點定義介面
public interface Joinpoint {
/**
* Proceed to the next interceptor in the chain.
* 得到呼叫鏈的下一個攔截器
*/
Object proceed() throws Throwable;
/**
* Return the object that holds the current joinpoint's static part.
*/
Object getThis();
/**
* Return the static part of this joinpoint.
*/
AccessibleObject getStaticPart();
}
代理類介面, jdk動態代理和cglib動態代理都實作了這個介面
package org.springframework.aop.framework;
public interface AopProxy {
/**
* 創建代理物件
* Create a new proxy object.
* <p>Uses the AopProxy's default class loader (if necessary for proxy creation):
* usually, the thread context class loader.
*/
Object getProxy();
}
InvocationHandler:
package java.lang.reflect;
public interface InvocationHandler {
/**
* 觸發目標增強方法
* Processes a method invocation on a proxy instance and returns
* the result. This method will be invoked on an invocation handler
* when a method is invoked on a proxy instance that it is
* associated with.
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
jdk動態代理類 JdkDynamicAopProxy , 實作了 AopProxy 和 InvocationHandler
Interceptor攔截器介面
package org.springframework.cglib.proxy;
import java.lang.reflect.Method;
public interface MethodInterceptor extends Callback {
Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable;
}
cglib動態代理類 CglibAopProxy的 DynamicAdvisedInterceptor 實作了 MethodInterceptor
2,xml配置方式
org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator //
org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor//切面 ,是一個 Advisor
org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor//切面增強類
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator// AOP核心處理類,DefaultAdvisorAutoProxyCreator的抽象父類
DefaultAdvisorAutoProxyCreator 實作了 BeanPostProcessor,代理類在創建時,spring在初始化這個(代理)類時會呼叫 DefaultAdvisorAutoProxyCreator的postProcessAfterInitialization方法,然后根據切面 AspectJExpressionPointcutAdvisor中的切入點AspectJExpressionPointcut(expression) 去找到當前代理類 代理方法 的所有增強 Advisor,
具體來說是在AbstractAdvisorAutoProxyCreator類的findEligibleAdvisors方法,通過PointcutAdvisor(MethodBeforeAdviceInterceptor的父介面)找到在ProxyFactory創建代理類的程序中(AbstractAutoProxyCreator中的wrapIfNecessary方法),會將所有Advisor和Advice 注入給 ProxyFactory,
最后創建代理類,代理類有兩種,jdk動態代理(org.springframework.aop.framework.JdkDynamicAopProxy)或者cglib動態代理(org.springframework.aop.framework.CglibAopProxy)

AbstractAutoProxyCreator類中的createProxy方法
/**
* Create an AOP proxy for the given bean.
*/
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
//...
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
//尋找并注入被增強類的所有 Advisor,Advice
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
//...
//創建jdk動態代理或者cglib動態代理
return proxyFactory.getProxy(getProxyClassLoader());
}
如果目標類是介面 用jdk動態代理,否則用cglib動態代理

2.1 jdk動態代理
2.1.1 創建代理類:

2.1.2 為什么jdk代理類必須是介面:
因為jdk動態代理生成的類已經繼承了Proxy,而java是單繼承的,所以是基于jdk動態代理是基于介面的,
jdk代理的類必須傳他的介面:
@Test
public void test_jdk_proxy2(){
ClassLoader classLoader= Thread.currentThread().getContextClassLoader();
IUserService target=new UserService2();
IUserService proxy= (IUserService) Proxy.newProxyInstance(classLoader,target.getClass().getInterfaces() ,(obj, method, args)->{
System.out.println(obj.getClass()+",這個類被代理了,,,");
return "代理回傳值";
});
System.out.println("父類:"+proxy.getClass().getSuperclass());
for (Class<?> anInterface : proxy.getClass().getInterfaces()) {
System.out.println("介面:"+anInterface);
}
System.out.println(proxy.queryUserInfo());
byte[] bytes = ProxyGenerator.generateProxyClass("UserService", target.getClass().getInterfaces());
File file = new File("E:\\temp\\UserService.class") ;
FileOutputStream fo = null;
try {
fo = new FileOutputStream(file);
fo.write(bytes);
fo.flush();
fo.close();
} catch (Exception e) {
e.printStackTrace();
}
}
如果只傳介面,沒有實作類,那么他代理類將不實作介面:

如果不傳介面,會報錯:

查看Proxy生成代理類的方法,沒看懂:

將代理類生成出來,會發現,他繼承了Proxy,實作了自定義介面,
由于java只能是單繼承,所以jdk代理只能是介面


2.1.3 呼叫被代理的方法:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
//各種校驗,,,
Object retVal; //增強方法回傳值
// Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// 得到攔截器鏈,Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// 沒有攔截器,直接執行目標方法
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// We need to create a method invocation...
MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// 呼叫"下一個"攔截器"Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}
// 處理方法回傳值 Massage return value if necessary.
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
retVal = proxy;
}
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
}

ReflectiveMethodInvocation類的 proceed 方法:
每次呼叫 currentInterceptorIndex 加 1,最后一次就是執行 目標方法
org.springframework.aop.framework.ReflectiveMethodInvocation#invokeJoinpoint
->org.springframework.aop.support.AopUtils#invokeJoinpointUsingReflection
不是最后一次依次執行MethodInterceptor的實作類:

2.2 cglib動態代理
org.springframework.aop.framework.CglibAopProxy,核心類是Enhancer :
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
//log...
try {
Class<?> rootClass = this.advised.getTargetClass();
Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
Class<?> proxySuperClass = rootClass;
//...
// 創建cglib的 Enhancer ,Configure CGLIB Enhancer...
Enhancer enhancer = createEnhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
if (classLoader instanceof SmartClassLoader &&
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
}
}
//設定被代理的類
enhancer.setSuperclass(proxySuperClass);
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
//得到回呼方法 ,這個方法會初始化 DynamicAdvisedInterceptor,得到攔截器鏈
Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
// fixedInterceptorMap only populated at this point, after getCallbacks call above
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);
// Generate the proxy class and create a proxy instance.
return createProxyClassAndInstance(enhancer, callbacks);
}
}
org.springframework.aop.framework.CglibAopProxy#getCallbacks 方法回傳代理類所有的攔截器:
Callback是MethodInterceptor的父介面

配置了DynamicAdvisedInterceptor,在其中會創建ReflectiveMethodInvocation,會在呼叫方法時候執行


最終cglib會呼叫被增強類的構造方法,這也是為什么cglib動態代理可以代理 實作類

Enhancer#create生成代理類物件,生成類是使用 ASM 進行生成,

用如下方法可以保存生成代理類的位元組碼檔案:
@Test
public void test_scan_aop_cglib() {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\temp\\cglib");
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-scan.xml");
UserServiceCglib userService = applicationContext.getBean("userServiceCglib", UserServiceCglib.class);
System.out.println("測驗結果:" + userService.queryUserInfo());
}
可以看到生成的代理類繼承了目標類,并且方法是final的,所以cglib動態代理的類不能是final的


3,注解方式
需要在xml里配置 <aop:aspectj-autoproxy />,或者springboot用特殊的starter,
加載了 org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator,這個類是 AbstractAutoProxyCreator 的子類,處理方式和上邊一樣,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/380818.html
標籤:Java
