技术标签: spring java 后端 # Spring源码
AOP(Aspect-Oriented Programming:面向切面编程),是Spring中一个重要内容,在AOP思想中例如权限控制
、日志管理
、事务控制
等被定义为切面,它实际做的就是将核心功能与切面功能拆分,降低核心功能与切面功能的耦合度,然后在把核心功能和切面功能编织在一起。Spring是基于动态代理实现AOP的。Spring默认情况下在目标类实现接口时是通过JDK代理实现的,只有非接口的是通过Cglib代理实现的。
AOP除了Spring AOP实现外,还有比较出名的AspectJAOP,下面我们说说AspectJ AOP和Spring AOP的区别:
为了更好的演示源码,我们首先简单创建一个示例,首先是目标类代码,新建AopDemoServiceImpl类
@Service("demoService")
public class AopDemoServiceImpl {
public void doMethod1(){
System.out.println("调用AopDemoServiceImpl.doMethod1()");
}
public String doMethod2(){
System.out.println("调用AopDemoServiceImpl.doMethod2() 返回Hello World");
return "Hello World";
}
public String doMethod3() throws Exception {
System.out.println("调用AopDemoServiceImpl.doMethod3() 抛出Exception");
throw new Exception("some exception");
}
}
添加LogAspect切面
@Aspect
@Component
public class LogAspect {
@Pointcut("execution(* com.example.springboot.spring.aop.*.*(..))")
private void pointCutMethod() {
}
/**
* 环绕通知
*
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("pointCutMethod()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("-----------------------");
System.out.println("环绕通知开始,方法名" + joinPoint.getSignature());
Object o = joinPoint.proceed();
System.out.println("环绕通知得到结果:" + String.valueOf(o));
return o;
}
/**
* 前置通知
*/
@Before("pointCutMethod()")
public void doBefore() {
System.out.println("前置通知");
}
/**
* 后置通知
*
* @param result return val
*/
@AfterReturning(value = "pointCutMethod()", returning = "result")
public void doAfterReturning(String result) {
System.out.println("后置通知, 返回值: " + result);
}
/**
* 异常通知
*
* @param e exception
*/
@AfterThrowing(value = "pointCutMethod()", throwing = "e")
public void doAfterThrowing(Exception e) {
System.out.println("异常通知, 异常: " + e.getMessage());
}
/**
* 最终通知
*/
@After("pointCutMethod()")
public void doAfter() {
System.out.println("最终通知");
}
}
spring-config.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--aop相关配置-->
<context:annotation-config/>
<context:component-scan base-package="com.example.springboot.spring"/>
<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
程序入口:
public class Entrance {
/**
* 基于配置文件的依赖注入测试
*
* @param args
*/
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
// retrieve configured instance
AopDemoServiceImpl demoService = context.getBean("demoService", AopDemoServiceImpl.class);
// use configured instance
demoService.doMethod1();
demoService.doMethod2();
try {
demoService.doMethod3();
} catch (Exception e) {
// e.printStackTrace();
}
}
}
在结合上面代码进行源码分析前,先介绍下整个执行流程,整体分为三步。
我们从AbstractApplicationContext#refresh()
进入直奔finishBeanFactoryInitialization()
方法,这个方法初始化所有的singleton beans(除了lazy-init)
从beanFactory.preInstantiateSingletons()
进入,开始前置处理。
我们在这里打个断点,设置下Condition:beanName.equals("demoService")
,跳过其他beanName,只看demoService。
进入到getBean()方法中,点进doGetBean()
后直奔创建bean的逻辑代码,如下图:
接下来,我们往下说说前置处理的工作流程,主要是遍历切面,将切面放入缓存中。
进入到create后,我们径直来到前置处理的入口resolveBeforeInstantiation(beanName, mbdToUse)
然后我们进入到resolveBeforeInstantiation(beanName, mbdToUse);
,在 applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
开始前置处理,我们直接来到核心方法postProcessBeforeInstantiation,在这里面就是bean实例化前做的处理。
笔者贴出了bean初始化前置处理器的postProcessBeforeInstantiation的核心逻辑,可以看到AbstractAutoProxyCreator里面会判断当前传入的bean是否是AOP类,如果是则将其生成通知器类然后放入缓存advisedBeans中。
判断是否跳过的逻辑也很简单,如下图所示,它会获取所有切面信息,判断当前这个bean是否跟里面某个切面名字一样,如果一样就返回true。
我们不妨看看findCandidateAdvisors的逻辑,可以看到它又会调用父类的findCandidateAdvisors查找所有的advisor
核心逻辑来了buildAspectJAdvisors,在这一步它主要用DCL双重锁的单例实现方式,拿到切面类里的切面方法,将其转换成advisor(并放入缓存中)。
在this.advisorFactory.getAdvisors
中返回切面列表,我们进去方法里面看看详细
@Override
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
validate(aspectClass);
//忽略细节....
List<Advisor> advisors = new ArrayList<>();
//遍历方法列表
for (Method method : getAdvisorMethods(aspectClass)) {
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
if (advisor != null) {
advisors.add(advisor);
}
}
//忽略细节....
return advisors;
}
我们主要看getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName)
里面内容,代码如下:
@Override
@Nullable
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName) {
validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
AspectJExpressionPointcut expressionPointcut = getPointcut(
candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
if (expressionPointcut == null) {
return null;
}
// 封装成advisor
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}
在InstantiationModelAwarePointcutAdvisorImpl
中封装Advisor
public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut,
Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory,
MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
this.declaredPointcut = declaredPointcut;
this.declaringClass = aspectJAdviceMethod.getDeclaringClass();
this.methodName = aspectJAdviceMethod.getName();
this.parameterTypes = aspectJAdviceMethod.getParameterTypes();
this.aspectJAdviceMethod = aspectJAdviceMethod;
this.aspectJAdvisorFactory = aspectJAdvisorFactory;
this.aspectInstanceFactory = aspectInstanceFactory;
this.declarationOrder = declarationOrder;
this.aspectName = aspectName;
if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
// Static part of the pointcut is a lazy type.
Pointcut preInstantiationPointcut = Pointcuts.union(
aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);
// Make it dynamic: must mutate from pre-instantiation to post-instantiation state.
// If it's not a dynamic pointcut, it may be optimized out
// by the Spring AOP infrastructure after the first evaluation.
this.pointcut = new PerTargetInstantiationModelPointcut(
this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);
this.lazy = true;
}
else {
// A singleton aspect.
this.pointcut = this.declaredPointcut;
this.lazy = false;
this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
}
}
通过pointcut获取advice
private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {
Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut,
this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
return (advice != null ? advice : EMPTY_ADVICE);
}
交由aspectJAdvisorFactory生成切面信息
@Override
@Nullable
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
// 获取切面类
Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
validate(candidateAspectClass);
// 获取切面注解
AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
}
// If we get here, we know we have an AspectJ method.
// Check that it's an AspectJ-annotated class
if (!isAspect(candidateAspectClass)) {
throw new AopConfigException("Advice must be declared inside an aspect type: " +
"Offending method '" + candidateAdviceMethod + "' in class [" +
candidateAspectClass.getName() + "]");
}
if (logger.isDebugEnabled()) {
logger.debug("Found AspectJ method: " + candidateAdviceMethod);
}
// 切面注解转换成advice
AbstractAspectJAdvice springAdvice;
switch (aspectJAnnotation.getAnnotationType()) {
case AtPointcut: // AtPointcut忽略
if (logger.isDebugEnabled()) {
logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
}
return null;
case AtAround:
springAdvice = new AspectJAroundAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtBefore:
springAdvice = new AspectJMethodBeforeAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfter:
springAdvice = new AspectJAfterAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfterReturning:
springAdvice = new AspectJAfterReturningAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterReturningAnnotation.returning())) {
springAdvice.setReturningName(afterReturningAnnotation.returning());
}
break;
case AtAfterThrowing:
springAdvice = new AspectJAfterThrowingAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
}
break;
default:
throw new UnsupportedOperationException(
"Unsupported advice type on method: " + candidateAdviceMethod);
}
// 最后将其它切面信息配置到advice
springAdvice.setAspectName(aspectName);
springAdvice.setDeclarationOrder(declarationOrder);
String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
if (argNames != null) {
springAdvice.setArgumentNamesFromStringArray(argNames);
}
springAdvice.calculateArgumentBindings();
return springAdvice;
}
从上文切面信息处理完后,我们接着分析代理对象的创建,这一步主要是从缓存中拿切面,和demoService的方法匹配,并创建AOP代理对象。回到AbstractAutowireCapableBeanFactory#createBean
,进入doCreateBean()
方法。
径直来到初始化实例bean这段代码
进入applyBeanPostProcessorsAfterInitialization(bean, beanName);
后,我们会看到本次的核心方法postProcessAfterInitialization(result, beanName);
,这个方法总的来说主要负责将Advisor注入到合适的位置,然后以Cglib或者JDK形式创建代理,为后面给代理进行增强实现做准备。
在AbstractAutoProxyCreator#postProcessAfterInitialization
中如果bean被子类标识为代理,则使用配置的拦截器创建一个代理
/**
* Create a proxy with the configured interceptors if the bean is
* identified as one to proxy by the subclass.
* @see #getAdvicesAndAdvisorsForBean
*/
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
//如果不是提前暴露的代理
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
wrapIfNecessary方法主要用于判断是否需要创建代理,如果Bean能够获取到advisor才需要创建代理
我们看下getAdvicesAndAdvisorsForBean方法是如何获取 demoService的切面列表。
通过findEligibleAdvisors方法获取advisor
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 和上文一样,获取所有切面类的切面方法生成Advisor
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 找到这些Advisor中能够应用于beanClass的Advisor
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
// 如果需要,交给子类拓展
extendAdvisors(eligibleAdvisors);
// 对Advisor排序
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
上文从findCandidateAdvisors中进入到buildAspectJAdvisors()
方法中是为了将切面信息存入到缓存中,而本次则是从缓存中获取domeService切面列表
回到findEligibleAdvisors()
,获取完切面列表来到findAdvisorsThatCanApply()
,找到这些Advisor中能够增强demoService的Advisor
protected List<Advisor> findAdvisorsThatCanApply(
List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
ProxyCreationContext.setCurrentProxiedBeanName(beanName);
try {
return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
}
finally {
ProxyCreationContext.setCurrentProxiedBeanName(null);
}
}
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new ArrayList<>();
for (Advisor candidate : candidateAdvisors) {
// 通过Introduction实现的advice
if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
eligibleAdvisors.add(candidate);
}
}
boolean hasIntroductions = !eligibleAdvisors.isEmpty();
//遍历每个增强器
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor) {
// already processed
continue;
}
// 看看是否能够在demoSerce上增强
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
在canApply中遍历demoSerivce所有方法,然后与增强器进行匹配,符合则返回true
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
if (advisor instanceof IntroductionAdvisor) {
return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
}
else if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pca = (PointcutAdvisor) advisor;
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
}
else {
// It doesn't have a pointcut so we assume it applies.
return true;
}
}
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
//忽略细节....
for (Class<?> clazz : classes) {
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
//遍历demoSerivce所有方法
for (Method method : methods) {
//与增强器进行匹配
if (introductionAwareMethodMatcher != null ?
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
获取demoService相关切面列表后,就可以开始创建代理对象
createProxy方法主要是在内部创建要给ProxyFactory的实例,然后设置一些内容,最后通过ProxyFactory创建代理。
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
// 创建 ProxyFactory 实例
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
// 判断是否代理目标类,默认是false
if (proxyFactory.isProxyTargetClass()) {
// Explicit handling of JDK proxy targets (for introduction advice scenarios)
// 显式处理 JDK 代理目标
if (Proxy.isProxyClass(beanClass)) {
// Must allow for introductions; can't just set interfaces to the proxy's interfaces only.
// 必须允许引介;不能只将接口设置为代理的接口。
for (Class<?> ifc : beanClass.getInterfaces()) {
proxyFactory.addInterface(ifc);
}
}
}
else {
// 判断是否应该使用cglib代理
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
// 构建增强器
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
// 添加增强器
proxyFactory.addAdvisors(advisors);
// 设置要代理的目标类
proxyFactory.setTargetSource(targetSource);
// 定制代理工厂
customizeProxyFactory(proxyFactory);
// 控制代理工厂被配置后 是否允许修改通知。
// 默认false
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
// 初始化工作准备完毕,通过ProxyFactory创建代理
return proxyFactory.getProxy(getProxyClassLoader());
}
最后我们直接来到DefaultAopProxyFactory#createAopProxy方法,这里会根据条件判断创建代理(jdk或cglib)。
代理对象创建完成后,终于来到最后一步,切面的执行;上文从创建完代理后调用CglibAopProxy#intercept
方法
核心代码proceed()
proceed()是执行的核心,执行方法和执行通知都是在此处搞定的。 这里是递归调用的方式,执行所有的拦截器链。执行完递归后依次退出递归,整个流程结束。
结果输出
上文介绍了SpringAOP的切面实现和创建动态代理的过程,那么动态代理是如何工作的呢?
代理模式(Proxy pattern):通过代理对象访问目标对象,在目标对象的基础上,增加额外的操作,扩展目标对象的功能。
说白了就是在运行期间,为目标对象创建代理对象,目标对象不变,然后通过对方法进行动态拦截,进行前置或者后置功能执行等等增强操作,上文说的Cglib动态代理和JDK动态代理
就是动态代理。
Cglib是一个强大的、高性能的代码生成包。它使用ASM操作字节码,动态生成代理,对目标对象的方法进行增强操作,它广泛被许多AOP框架使用,为他们提供方法的拦截。
引入cglib依赖包
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
创建实体类User
public class User {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public User() {
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
目标类,Service类
public class UserServiceImpl {
public List<User> findUserList() {
return Collections.singletonList(new User("zayton", 24));
}
}
cglib代理类
public class CglibProxy<T> implements MethodInterceptor {
private static final Logger logger = LoggerFactory.getLogger(CglibProxy.class);
private Object target;
public T getTargetClass(Object target) {
//设置被代理的目标类
this.target = target;
// 创建加强器设置代理类以及回调,当代理类被调用时,callback就会去调用intercept
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback(this);
//返回代理类
return (T) enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
logger.info("调用被代理对象的方法,代理对象:[{}],代理方法:[{}]", o.getClass().getName(), method.getName());
Object result = methodProxy.invokeSuper(o, args);
logger.info("代理调用结束,返回结果:[{}]", String.valueOf(result));
return null;
}
}
测试代码
public class CglibProxyDemo {
public static void main(String[] args) {
UserServiceImpl targetClass = new CglibProxy<UserServiceImpl>().getTargetClass(new UserServiceImpl());
targetClass.findUserList();
}
}
如下图所示,通过Enhancer去配置各种代理类的参数,然后生成代理类;其中final类不能被代理,因为它无法被子类覆盖。
源码如下,大致流程跟我们写的示例代码差不多。
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
//忽略....
try {
//忽略....
// 重点看这里,通过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 ClassLoaderAwareGeneratorStrategy(classLoader));
// 获取目标代理类中的方法
Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);
// 生成代理对象(重点)
return createProxyClassAndInstance(enhancer, callbacks);
}
catch (CodeGenerationException | IllegalArgumentException ex) {
//忽略....
}
catch (Throwable ex) {
//忽略....
}
}
JDK动态代理是JDK自带的一种代理,我们通过继承InvocationHandler就可以实现。前提是目标类需要实现接口才能使用JDK代理。
JDK动态代理不需要额外引用其他依赖,首先我们定义接口,实体类则沿用上文的User类
public interface UserService {
List<User> findUserList();
}
创建实现类UserServiceImpl
public class UserServiceImpl implements UserService{
@Override
public List<User> findUserList() {
return Collections.singletonList(new User("zayton", 24));
}
}
JDK代理类
public class JDKProxy<T> {
private static final Logger logger = LoggerFactory.getLogger(JDKProxy.class);
private Object target;
public JDKProxy(Object target) {
this.target = target;
}
public T getTargetProxy() {
UserService proxy;
ClassLoader loader = target.getClass().getClassLoader();
Class[] interfaces = new Class[]{
UserService.class};
//objProxy: 代理对象。 一般不使用该对象 method: 正在被调用的方法 args: 调用方法传入的参数
InvocationHandler handler = (objProxy, method, args) -> {
logger.info("代理方法被调用前,方法名称[{}]", method.getName());
Object result = null;
try {
result = method.invoke(target, args);
} catch (NullPointerException e) {
e.printStackTrace();
}
logger.info("代理方法调用结束,方法名称[{}],返回结果:[{}]", method.getName(), String.valueOf(result));
return result;
};
/**
* loader: 代理对象使用的类加载器.
* interfaces: 指定代理对象的类型. 即代理代理对象中可以有哪些方法.
* h: 当具体调用代理对象的方法时, 应该如何进行响应, 实际上就是调用 InvocationHandler 的 invoke 方法
*/
proxy = (UserService) Proxy.newProxyInstance(loader, interfaces, handler);
return (T) proxy;
}
}
测试代码
public class JDKProxyDemo {
public static void main(String[] args) {
JDKProxy<UserService> jdkProxy=new JDKProxy<>(new UserServiceImpl());
UserService userService = jdkProxy.getTargetProxy();
System.out.println(userService.findUserList());
//将JDK动态代理生成的类保存本地
System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
}
}
通过debug
可以发现上述代码执行到这段代码,它会通过generateClassFile()
方法生成一个ClassFile
public static byte[] generateProxyClass(final String name,
Class<?>[] interfaces,
int accessFlags)
{
ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
final byte[] classFile = gen.generateClassFile();
...
}
generateClassFile方法如下,可以看到它将代理类的所有方法都封装成ProxyMethod
对象,然后写入class文件:
private byte[] generateClassFile() {
/* 第一步:将所有方法包装成ProxyMethod对象 */
// 将Object类中hashCode、equals、toString方法包装成ProxyMethod对象
addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class);
// 将代理类接口方法包装成ProxyMethod对象
for (Class<?> intf : interfaces) {
for (Method m : intf.getMethods()) {
addProxyMethod(m, intf);
}
}
// 校验返回类型
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
checkReturnTypes(sigmethods);
}
/* 第二步:为代理类组装字段,构造函数,方法,static初始化块等 */
try {
// 添加构造函数,参数是InvocationHandler
methods.add(generateConstructor());
// 代理方法
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
for (ProxyMethod pm : sigmethods) {
// 字段
fields.add(new FieldInfo(pm.methodFieldName,
"Ljava/lang/reflect/Method;",
ACC_PRIVATE | ACC_STATIC));
// 上述ProxyMethod中的方法
methods.add(pm.generateMethod());
}
}
// static初始化块
methods.add(generateStaticInitializer());
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception", e);
}
if (methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
}
if (fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
}
/* 第三步:写入class文件 */
/*
* Make sure that constant pool indexes are reserved for the
* following items before starting to write the final class file.
*/
cp.getClass(dotToSlash(className));
cp.getClass(superclassName);
for (Class<?> intf: interfaces) {
cp.getClass(dotToSlash(intf.getName()));
}
/*
* Disallow new constant pool additions beyond this point, since
* we are about to write the final constant pool table.
*/
cp.setReadOnly();
ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);
try {
/*
* Write all the items of the "ClassFile" structure.
* See JVMS section 4.1.
*/
// u4 magic;
dout.writeInt(0xCAFEBABE);
// u2 minor_version;
dout.writeShort(CLASSFILE_MINOR_VERSION);
// u2 major_version;
dout.writeShort(CLASSFILE_MAJOR_VERSION);
cp.write(dout); // (write constant pool)
// u2 access_flags;
dout.writeShort(accessFlags);
// u2 this_class;
dout.writeShort(cp.getClass(dotToSlash(className)));
// u2 super_class;
dout.writeShort(cp.getClass(superclassName));
// u2 interfaces_count;
dout.writeShort(interfaces.length);
// u2 interfaces[interfaces_count];
for (Class<?> intf : interfaces) {
dout.writeShort(cp.getClass(
dotToSlash(intf.getName())));
}
// u2 fields_count;
dout.writeShort(fields.size());
// field_info fields[fields_count];
for (FieldInfo f : fields) {
f.write(dout);
}
// u2 methods_count;
dout.writeShort(methods.size());
// method_info methods[methods_count];
for (MethodInfo m : methods) {
m.write(dout);
}
// u2 attributes_count;
dout.writeShort(0); // (no ClassFile attributes for proxy classes)
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception", e);
}
return bout.toByteArray();
}
然后我们来看下通过 System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
命令生成的代码,可以看它实现了UserSerivce,然后通过创建的Proxy调用InvocationHandler的invoke方法,执行我们自定义的invoke方法。
public final class $Proxy0 extends Proxy implements UserService {
private static final Method m0;
private static final Method m1;
private static final Method m2;
private static final Method m3;
public $Proxy0(InvocationHandler var1) {
super(var1);
}
public final int hashCode() {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final boolean equals(Object var1) {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{
var1});
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final List findUserList() {
try {
return (List)super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.example.springboot.spring.aop.jdk.UserService").getMethod("findUserList");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
private static MethodHandles.Lookup proxyClassLookup(MethodHandles.Lookup var0) throws IllegalAccessException {
if (var0.lookupClass() == Proxy.class && var0.hasFullPrivilegeAccess()) {
return MethodHandles.lookup();
} else {
throw new IllegalAccessException(var0.toString());
}
}
}
执行源码如下:
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
}
// 调用 JDK 的 Proxy#newProxyInstance(..) 方法创建代理对象
// 传入的参数就是当前 ClassLoader 类加载器、需要代理的接口、InvocationHandler 实现类
return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
}
@Override
@Nullable
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 {
// 执行的是equal方法
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
// 执行的是hashcode方法
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
// 如果是包装类,则dispatch to proxy config
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
// 用反射方式来执行切点
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// 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);
// 获取拦截链
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())) {
// Special case: it returned "this" and the return type of the method
// is type-compatible. Note that we can't help if the target sets
// a reference to itself in another returned object.
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;
}
finally {
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
https://www.pdai.tech/md/spring/spring-x-framework-aop-source-3.html
https://www.pdai.tech/md/spring/spring-x-framework-aop-source-4.html
https://javadoop.com/post/spring-aop-source
https://juejin.cn/post/7153214385236738055
文章浏览阅读645次。这个肯定是末尾的IDAT了,因为IDAT必须要满了才会开始一下个IDAT,这个明显就是末尾的IDAT了。,对应下面的create_head()代码。,对应下面的create_tail()代码。不要考虑爆破,我已经试了一下,太多情况了。题目来源:UNCTF。_攻防世界困难模式攻略图文
文章浏览阅读2.9k次,点赞3次,收藏10次。偶尔会用到,记录、分享。1. 数据库导出1.1 切换到dmdba用户su - dmdba1.2 进入达梦数据库安装路径的bin目录,执行导库操作 导出语句:./dexp cwy_init/[email protected]:5236 file=cwy_init.dmp log=cwy_init_exp.log 注释: cwy_init/init_123..._达梦数据库导入导出
文章浏览阅读1.9k次。1. 在官网上下载KindEditor文件,可以删掉不需要要到的jsp,asp,asp.net和php文件夹。接着把文件夹放到项目文件目录下。2. 修改html文件,在页面引入js文件:<script type="text/javascript" src="./kindeditor/kindeditor-all.js"></script><script type="text/javascript" src="./kindeditor/lang/zh-CN.js"_kindeditor.js
文章浏览阅读2.3k次,点赞6次,收藏14次。SPI的详情简介不必赘述。假设我们通过SPI发送0xAA,我们的数据线就会变为10101010,通过修改不同的内容,即可修改SPI中0和1的持续时间。比如0xF0即为前半周期为高电平,后半周期为低电平的状态。在SPI的通信模式中,CPHA配置会影响该实验,下图展示了不同采样位置的SPI时序图[1]。CPOL = 0,CPHA = 1:CLK空闲状态 = 低电平,数据在下降沿采样,并在上升沿移出CPOL = 0,CPHA = 0:CLK空闲状态 = 低电平,数据在上升沿采样,并在下降沿移出。_stm32g431cbu6
文章浏览阅读1.2k次,点赞2次,收藏8次。数据链路层习题自测问题1.数据链路(即逻辑链路)与链路(即物理链路)有何区别?“电路接通了”与”数据链路接通了”的区别何在?2.数据链路层中的链路控制包括哪些功能?试讨论数据链路层做成可靠的链路层有哪些优点和缺点。3.网络适配器的作用是什么?网络适配器工作在哪一层?4.数据链路层的三个基本问题(帧定界、透明传输和差错检测)为什么都必须加以解决?5.如果在数据链路层不进行帧定界,会发生什么问题?6.PPP协议的主要特点是什么?为什么PPP不使用帧的编号?PPP适用于什么情况?为什么PPP协议不_接收方收到链路层数据后,使用crc检验后,余数为0,说明链路层的传输时可靠传输
文章浏览阅读587次。软件测试工程师移民加拿大 无证移民,未受过软件工程师的教育(第1部分) (Undocumented Immigrant With No Education to Software Engineer(Part 1))Before I start, I want you to please bear with me on the way I write, I have very little gen...
文章浏览阅读304次。Thinkpad X250笔记本电脑,装的是FreeBSD,进入BIOS修改虚拟化配置(其后可能是误设置了安全开机),保存退出后系统无法启动,显示:secure boot failed ,把自己惊出一身冷汗,因为这台笔记本刚好还没开始做备份.....根据错误提示,到bios里面去找相关配置,在Security里面找到了Secure Boot选项,发现果然被设置为Enabled,将其修改为Disabled ,再开机,终于正常启动了。_安装完系统提示secureboot failure
文章浏览阅读10w+次,点赞93次,收藏352次。1、用strtok函数进行字符串分割原型: char *strtok(char *str, const char *delim);功能:分解字符串为一组字符串。参数说明:str为要分解的字符串,delim为分隔符字符串。返回值:从str开头开始的一个个被分割的串。当没有被分割的串时则返回NULL。其它:strtok函数线程不安全,可以使用strtok_r替代。示例://借助strtok实现split#include <string.h>#include <stdio.h&_c++ 字符串分割
文章浏览阅读2.3k次。1 .高斯日记 大数学家高斯有个好习惯:无论如何都要记日记。他的日记有个与众不同的地方,他从不注明年月日,而是用一个整数代替,比如:4210后来人们知道,那个整数就是日期,它表示那一天是高斯出生后的第几天。这或许也是个好习惯,它时时刻刻提醒着主人:日子又过去一天,还有多少时光可以用于浪费呢?高斯出生于:1777年4月30日。在高斯发现的一个重要定理的日记_2013年第四届c a组蓝桥杯省赛真题解答
文章浏览阅读851次,点赞17次,收藏22次。摘要:本文利用供需算法对核极限学习机(KELM)进行优化,并用于分类。
文章浏览阅读1.1k次。一、系统弱密码登录1、在kali上执行命令行telnet 192.168.26.1292、Login和password都输入msfadmin3、登录成功,进入系统4、测试如下:二、MySQL弱密码登录:1、在kali上执行mysql –h 192.168.26.129 –u root2、登录成功,进入MySQL系统3、测试效果:三、PostgreSQL弱密码登录1、在Kali上执行psql -h 192.168.26.129 –U post..._metasploitable2怎么进入
文章浏览阅读257次。本文将为初学者提供Python学习的详细指南,从Python的历史、基础语法和数据类型到面向对象编程、模块和库的使用。通过本文,您将能够掌握Python编程的核心概念,为今后的编程学习和实践打下坚实基础。_python人工智能开发从入门到精通pdf