一、前言

  本文章所讲并没有基于Aspectj,而是直接通过Cglib以及ProxyFactoryBean去创建代理Bean。通过下面的例子,可以看出Cglib方式创建的代理Bean和ProxyFactoryBean创建的代理Bean的区别。

二、基本测试代码

  测试实体类,在BPP中创建BppTestDepBean类型的代理Bean。

@Component
public static class BppTestBean {
@Autowired
private BppTestDepBean depBean; public void test1() {
depBean.testDep();
} public void test2() {
depBean.testDep();
} @TestMethod
public void test3() {
depBean.testDep();
}
} @Component
public static class BppTestDepBean {
public void testDep() {
System.out.println("HEHE");
}
} @Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestMethod {
}

  测试类

@RunWith(SpringRunner.class)
@SpringBootTest
public class BppTest { @Autowired
private BppTestBean bppTestBean; @Test
public void test() {
bppTestBean.test1();
bppTestBean.test2();
bppTestBean.test3();
}
}

三、使用Cglib创建代理Bean

public class ProxyBpp1 implements BeanPostProcessor {
private static final Logger LOGGER = LoggerFactory.getLogger(ProxyBpp1.class); @Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof BppTestBean) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(bean.getClass());
//标识Spring-generated proxies
enhancer.setInterfaces(new Class[]{SpringProxy.class});
//设置增强
enhancer.setCallback((MethodInterceptor) (target, method, args, methodProxy) -> {
if ("test1".equals(method.getName())) {
LOGGER.info("ProxyBpp1 开始执行...");
Object result = methodProxy.invokeSuper(target, args);
LOGGER.info("ProxyBpp1 结束执行...");
return result;
}
return method.invoke(target, args);
}); return enhancer.create();
}
return bean;
}
}

  主要是代理 BppTestBean的test1方法。其实这种方式创建的代理Bean使用问题的,@Autowired字段没有注入进来,所以会有出现NPE。methodProxy.invokeSuper(target, args),这一行代码是有问题的,targe是代理类对象,而真实的对象是postProcessBeforeInitialization(Object bean, String beanName) 中的bean对象,此时bean对象@Autowired字段已经注入了。所以可以将methodProxy.invokeSuper(target, args) 修改为method.invoke(bean, args)解决无法注入@Autowired字段的问题。

四、使用ProxyFactoryBean创建代理Bean

public class ProxyBpp2 implements BeanPostProcessor {
private static final Logger LOGGER = LoggerFactory.getLogger(ProxyBpp2.class); @Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof BppTestBean) {
ProxyFactoryBean pfb = new ProxyFactoryBean();
pfb.setTarget(bean);
pfb.setAutodetectInterfaces(false);
NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();
advisor.addMethodName("test1");
advisor.setAdvice((MethodInterceptor) invocation -> {
LOGGER.info("ProxyBpp2 开始执行...");
Object result = invocation.getMethod().invoke(invocation.getThis(), invocation.getArguments());
LOGGER.info("ProxyBpp2 结束执行...");
return result;
});
pfb.addAdvisor(advisor); return pfb.getObject();
}
return bean;
}
}

   使用ProxyFactoryBean创建代理Bean的时候,一定要一个targe对象的。Advisor在切入的时候,会逐个执行Advice。invocation.getThis()就是在通过ProxyFactoryBean创建代理Bean的时候传入的target对象。由于target对象就是postProcessBeforeInitialization(Object bean, String beanName) 中的bean对象,所以@Autowired字段也已经注入进来了。

五、@Autowired注解何时被处理

  想必大家都知道@Autowired字段的处理也是通过一个BPP,不过这个BPP比我们平常使用的要高级一些,它就是InstantiationAwareBeanPostProcessor。这个BPP可以实现Bean的创建、属性的注入和解析(比如@Autowired、@Value、@Resource等等),大家可以参考一下CommonAnnotationBeanPostProcessor(处理JSR-250相关注解),AutowiredAnnotationBeanPostProcessor(处理@Autowired、@Value、@Inject相关注解)。

  InstantiationAwareBeanPostProcessor中有一个如下的方法,AutowiredAnnotationBeanPostProcessor就是覆盖这个方法实现了带有相关注解属性的自动注入。

@Nullable
default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
throws BeansException { return null;
}
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
}
catch (BeanCreationException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}

  InstantiationAwareBeanPostProcessor的postProcessProperties方法实在Spring AbstractAutowireCapableBeanFactory的populateBean方法中被调用。在AbstractAutowireCapableBeanFactory的doCreateBan中有如下代码。

// Initialize the bean instance.
Object exposedObject = bean;#
try {
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}

  也就是先进行了Bean的属性填充,然后进行Bean的初始化工作。initializeBean方法中主要做了四件事。

  1、invokeAwareMethods
  2、applyBeanPostProcessorsBeforeInitialization
  3、invokeInitMethods
  4、applyBeanPostProcessorsAfterInitialization

  其中2和4就是分别调用的普通的BPP中的postProcessBeforeInitialization方法和postProcessAfterInitialization方法。

  这就是为什么在BPP中创建代理Bean的时候,对应的目标Bean相关的@Autowired字段已经注入的原因了。

六、InstantiationAwareBeanPostProcessor方式创建动态代理Bean

  InstantiationAwareBeanPostProcessor接口中有个postProcessBeforeInstantiation方法,可以让我们自己去实例化Bean。通过查看AbstractAutowireCapableBeanFactory,方法调用:createBean方法 -> resolveBeforeInstantiation方法 -> applyBeanPostProcessorsBeforeInstantiation方法 ->InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation方法,如果最终返回一个非null的实例,那么就不会再执行doCreateBean方法。这就意味着不会有Bean属性的填充和初始化的流程了,但是可以借助AbstractAutowireCapableBeanFactory帮助我们实现。

public <T> T postProcess(T object) {
if (object == null) {
return null;
}
T result;
try {
// 使用容器autowireBeanFactory标准依赖注入方法autowireBean()处理 object对象的依赖注入
this.autowireBeanFactory.autowireBean(object);
// 使用容器autowireBeanFactory标准初始化方法initializeBean()初始化对象 object
result = (T) this.autowireBeanFactory.initializeBean(object,
object.toString());
} catch (RuntimeException e) {
Class<?> type = object.getClass();
throw new RuntimeException(
"Could not postProcess " + object + " of type " + type, e);
}
return result;
}

  上图代码,可以帮组我们实现非Spring容器Bean自动注入和初始化的功能。使用过Spring security同学都知道,内部也是用了这个方式解决对象中的属性注入问题。如果你阅读了Spring security的源码,你会发现很多对象,比如WebSecurity、ProviderManager、各个安全Filter等,这些对象的创建并不是通过bean定义的形式被容器发现和注册进入spring容器的,而是直接new出来的。Spring security提供的AutowireBeanFactoryObjectPostProcessor这个工具类可以使这些对象具有容器bean同样的生命周期,也能注入相应的依赖,从而进入准备好被使用的状态。

  使用Cglib在InstantiationAwareBeanPostProcessor 中创建动态代理Bean。

public class ProxyBpp3 implements InstantiationAwareBeanPostProcessor {
private static final Logger LOGGER = LoggerFactory.getLogger(ProxyBpp3.class); private final AutowireCapableBeanFactory autowireBeanFactory; ProxyBpp3(AutowireCapableBeanFactory autowireBeanFactory) {
this.autowireBeanFactory = autowireBeanFactory;
} @Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if (beanClass.equals(BppConfig.BppTestBean.class)) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(beanClass);
//标识Spring-generated proxies
enhancer.setInterfaces(new Class[]{SpringProxy.class});
//设置增强
enhancer.setCallback((MethodInterceptor) (target, method, args, methodProxy) -> {
if ("test1".equals(method.getName())) {
LOGGER.info("ProxyBpp3 开始执行...");
Object result = methodProxy.invokeSuper(target, args);
LOGGER.info("ProxyBpp3 结束执行...");
return result;
}
return methodProxy.invokeSuper(target, args);
}); return this.postProcess(enhancer.create());
}
return null;
} ...
}

  使用ProxyFactoryBean在InstantiationAwareBeanPostProcessor 中创建动态代理Bean。

public class ProxyBpp4 implements InstantiationAwareBeanPostProcessor {
private static final Logger LOGGER = LoggerFactory.getLogger(ProxyBpp4.class); private final AutowireCapableBeanFactory autowireBeanFactory; ProxyBpp4(AutowireCapableBeanFactory autowireBeanFactory) {
this.autowireBeanFactory = autowireBeanFactory;
} @Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if (beanClass.equals(BppConfig.BppTestBean.class)) {
ProxyFactoryBean pfb = new ProxyFactoryBean();
pfb.setTarget(this.postProcess(BeanUtils.instantiateClass(beanClass)));
pfb.setAutodetectInterfaces(false);
NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();
advisor.addMethodName("test1");
advisor.setAdvice((MethodInterceptor) invocation -> {
LOGGER.info("ProxyBpp4 开始执行...");
Object result = invocation.getMethod().invoke(invocation.getThis(), invocation.getArguments());
LOGGER.info("ProxyBpp4 结束执行...");
return result;
});
pfb.addAdvisor(advisor); return pfb.getObject();
}
return null;
}
...
}

  上述向两种方式,注意,实例化bean后主动通过postProcess方法借助AbstractAutowireCapableBeanFactory完成对象相关属性的注入以及对象的初始化流程。

七、源码分享

  点我查看源码,如果有任何疑问请关注公众号后进行咨询。

Spring BPP中优雅的创建动态代理Bean的更多相关文章

  1. spring框架中JDK和CGLIB动态代理区别

    转载:https://blog.csdn.net/yhl_jxy/article/details/80635012 前言JDK动态代理实现原理(jdk8):https://blog.csdn.net/ ...

  2. 七 MyBatis整合Spring,DAO开发(传统DAO&动态代理DAO)

    整合思路: 1.SQLSessionFactory对象应该放到Spring中作为单例存在 2.传统dao开发方式中,应该从Spring容器中获得SqlSession对象 3.Mapper代理行驶中,应 ...

  3. Spring AOP详解 、 JDK动态代理、CGLib动态代理

    AOP是Aspect Oriented Programing的简称,面向切面编程.AOP适合于那些具有横切逻辑的应用:如性能监测,访问控制,事务管理以及日志记录.AOP将这些分散在各个业务逻辑中的代码 ...

  4. 【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理

    Spring AOP详解 . JDK动态代理.CGLib动态代理  原文地址:https://www.cnblogs.com/kukudelaomao/p/5897893.html AOP是Aspec ...

  5. spring AOP底层原理实现——jdk动态代理

    spring AOP底层原理实现——jdk动态代理

  6. 18.5.1使用Proxy和InvocationHandler创建动态代理

    package d18_5_1; public interface Person { void walk(); void sayHello(String name); } package d18_5_ ...

  7. Spring5源码解析-Spring框架中的单例和原型bean

    Spring5源码解析-Spring框架中的单例和原型bean 最近一直有问我单例和原型bean的一些原理性问题,这里就开一篇来说说的 通过Spring中的依赖注入极大方便了我们的开发.在xml通过& ...

  8. Spring配置文件中使用ref local与ref bean的区别

    Spring配置文件中使用ref local与ref bean的区别.在ApplicationResources.properties文件中,使用<ref bean>与<ref lo ...

  9. 【spring基础】AOP概念与动态代理详解

    一.代理模式 代理模式的英文叫做Proxy或Surrogate,中文都可译为”代理“,所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动.在一些情况下,一个客户不想或者不能够直接引用一 ...

随机推荐

  1. javaScript遍历对象、数组总结

        javaScript遍历对象总结 1.使用Object.keys()遍历 返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性). var obj = {'0':'a ...

  2. 打包谷歌浏览器 Chrome 已安装的插件

    环境: OS - win7 64bit 旗舰版 Chrome - 37.0.2062.120 m 以 Smooth Gestures (一款鼠标手势插件)为例,在扩展程序面板 chrome://ext ...

  3. JSON数据写入和解析

    如何写入JSON 需要第三方jar包,JSON包 //写入json数据 public static String sendJson() { JSONObject json = new JSONObje ...

  4. WBXML 1.3协议摘要

    协议地址:WAP195   网络字节顺序:big-endian.   为什么要加0x40? 参考:Compressing XML When an element contains content (t ...

  5. Python关键字及其用法

    Python有哪些关键字 -Python常用的关键字  and, del, from, not, while, as, elif, global, or, with, assert, else, if ...

  6. 回到未来123Back To The Future

    或许,决定着现在的过去已经无法改变,但决定着未来的现在,却在我们每个人的手里. 路?我们要去的地方不需要路.(Roads? Where we're going we don't need roads) ...

  7. spring boot引入json,jsonobject,需要指定jdk15

    spring boot引入json,需要指定jdk15 <dependency> <groupId>net.sf.json-lib</groupId> <ar ...

  8. office web apps搭建与解决方案

    微软office在线预览解决方案https://view.officeapps.live.com/op/view.aspx?src=http://storage.xuetangx.com/public ...

  9. C# 操作windows服务[启动、停止、卸载、安装]

    主要宗旨:不已命令形式操作windows服务 static void Main(string[] args) { var path = @"E:\开发辅助项目\WCF\WCF.Test\WC ...

  10. [转] js实现对图片的二进制流md5计算

    //计算图片md5 function img_MD5(img_path,callback) { plus.io.resolveLocalFileSystemURL(img_path, function ...