SpringBoot项目的代理机制【一】
这是了解Spring代理机制的第一篇,尝试了解Spring如何实现Bean的注册和代理。这篇文章会抛出问题:Spring注册Bean,都会用Jdk代理或cglib创建代理对象吗?
1 项目准备
1.1 创建 Spring Boot 项目
创建一个使用 jpa 访问数据库的 Spring Boot 项目。
1.1.1 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/>
</parent>
<groupId>tech.codestory.research</groupId>
<artifactId>research-spring-boot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>research-spring-boot</name> <properties>
<java.version>1.8</java.version>
<fastjson.version>1.2.62</fastjson.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency> <dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
1.1.2 application.yml
src/main/resources/application.yml
server:
port: 9080
spring:
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:h2test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
username: sa
password:
platform: h2
jpa:
database-platform: org.hibernate.dialect.H2Dialect
hibernate:
ddl-auto: update
properties:
hibernate:
show_sql: true
use_sql_comments: tru
h2:
console:
enabled: true
path: /console
settings:
trace: false
web-allow-others: false
logging:
level:
root: INFO
1.1.3 ResearchSpringBootApplication.java
主程序
package tech.codestory.research.boot; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; /**
* Research Spring Boot Demo Application
*
* @author javacodestory@gmail.com
*/
@SpringBootApplication
public class ResearchSpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(ResearchSpringBootApplication.class, args);
}
}
1.2 监控 Spring 注册的 Bean
为了方便了解 Spring 启动过程,先创建一个类用于在日志中输出生成的 Bean。可以使用 BeanPostProcessor 接口。它设计的作用,如果需要在 Spring 容器完成 Bean 的实例化、配置和其他的初始化前后添加一些自己的逻辑处理,就可以定义一个或者多个 BeanPostProcessor 接口的实现,然后注册到容器中。
我们实现一个接口,只是打印一下注册的 Bean 信息,代码如下:
package tech.codestory.research.boot.config; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component; /**
* 创建一个 BeanPostProcessor , 为了方便查看Spring 注册的 Bean
*
* @author javacodestory@gmail.com
*/
@Component
@Slf4j
public class SpringBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;
} @Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
log.info("完成 初始化Bean {} : {}", beanName, bean.getClass().getName());
return bean;
}
}
启动项目,在控制台就可以看到一些日志输出(对输出做了一些调整):
完成 初始化Bean dataSource : com.zaxxer.hikari.HikariDataSource
完成 初始化Bean entityManagerFactoryBuilder : org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder
完成 初始化Bean researchSpringBootApplication : tech.codestory.research.boot.ResearchSpringBootApplication$$EnhancerBySpringCGLIB$$e4d04c1b
完成 初始化Bean transactionManager : org.springframework.orm.jpa.JpaTransactionManager
完成 初始化Bean jdbcTemplate : org.springframework.jdbc.core.JdbcTemplate
完成 初始化Bean namedParameterJdbcTemplate : org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate
注意看bean:researchSpringBootApplication,实例类名中有字符串$$EnhancerBySpringCGLIB$$,这表示 Spring 使用 cglib 实现其代理类。
2 创建一个基本的 Service
在代码中创建一个 service,观察 Spring 注册 Bean 的信息。
2.1 数据对象 model
package tech.codestory.research.boot.model; import lombok.Data; /**
* 用户实体
*
* @author javacodestory@gmail.com
*/
@Data
public class UserInfo {
/**
* 账号
*/
private String account;
/**
* 密码
*/
private String password;
/**
* 姓名
*/
private String name;
}
2.2 service 接口
package tech.codestory.research.boot.service; import tech.codestory.research.boot.model.UserInfo; /**
* 定义 Service 接口
*
* @author javacodestory@gmail.com
*/
public interface UserInfoFirstService {
/**
* 获取一个用户信息
*
* @param account
* @return
*/
UserInfo getUserInfo(String account);
}
2.3 无其他注解的 service 实现
package tech.codestory.research.boot.service.impl; import org.springframework.stereotype.Service;
import tech.codestory.research.boot.model.UserInfo;
import tech.codestory.research.boot.service.UserInfoFirstService; /**
* 没有添加其他注解的实现类
*
* @author javacodestory@gmail.com
*/
@Service
public class UserInfoFirstServiceImpl implements UserInfoFirstService {
/**
* 获取一个用户信息
*
* @param account
* @return
*/
@Override
public UserInfo getUserInfo(String account) {
return null;
}
}
2.4 查看 Bean 注册信息
重新启动项目,再日志中查看 Bean 注册信息,可以看到注册的 beanName 是 userInfoFirstServiceImpl
完成 初始化Bean userInfoFirstServiceImpl : tech.codestory.research.boot.service.impl.UserInfoFirstServiceImpl
2.5 测试 Bean 引用
2.5.1 测试类 UserInfoFirstServiceTest
注意,我在测试代码中同时注入了 UserInfoFirstService 和 UserInfoFirstServiceImpl
package tech.codestory.research.boot.service; import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import tech.codestory.research.boot.service.impl.UserInfoFirstServiceImpl; /**
* 测试UserInfoFirstService
*
* @author javacodestory@gmail.com
*/
@SpringBootTest
@Slf4j
public class UserInfoFirstServiceTest {
@Autowired
UserInfoFirstService firstService;
@Autowired
UserInfoFirstServiceImpl firstServiceImpl; @Test
public void testServiceInstances() {
log.info("firstService = {}", firstService);
assert firstService != null;
log.info("firstServiceImpl = {}", firstServiceImpl);
assert firstServiceImpl != null; // 是同一个实例
log.info("firstService 和 firstServiceImpl 是同一个Bean = {}", firstService == firstServiceImpl);
assert firstService == firstServiceImpl;
}
}
2.5.2 测试结果
在项目目录执行 maven 命令
mvn clean test
关键测试输出
t.c.r.b.s.UserInfoFirstServiceTest : firstService = tech.codestory.research.boot.service.impl.UserInfoFirstServiceImpl@4899799b
t.c.r.b.s.UserInfoFirstServiceTest : firstServiceImpl = tech.codestory.research.boot.service.impl.UserInfoFirstServiceImpl@4899799b
t.c.r.b.s.UserInfoFirstServiceTest : firstService 和 firstServiceImpl 是同一个Bean = true
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.01 s - in tech.codestory.research.boot.service.UserInfoFirstServiceTest
从测试结果看,两个依赖注入都正常引用了同一个对象。
2.6 问题来了
2.6.1 问题 1
通常我们理解Spring 注册Bean,会使用JDK代理或cglib。但在本例中,注册UserInfoFirstServiceImpl 时,为什么没有创建代理对象?
2.6.2 问题 2
注册 beanName 是userInfoFirstServiceImpl,为什么用接口和实现类定义变量却都能正常注入?
【未完待续】续篇不知要到猴年马月
敬请关注公众号 《程序猿讲故事》 codestory

SpringBoot项目的代理机制【一】的更多相关文章
- nginx反向代理部署springboot项目报404无法加载静态资源
问题:nginx反向代理部署springboot项目报404无法加载静态资源(css,js,jpg,png...) springboot默认启动端口为8080,如果需要通过域名(不加端口号)直接访问s ...
- 从头捋了一遍 Java 代理机制,收获颇丰
尽人事,听天命.博主东南大学硕士在读,热爱健身和篮球,乐于分享技术相关的所见所得,关注公众号 @ 飞天小牛肉,第一时间获取文章更新,成长的路上我们一起进步 本文已收录于 「CS-Wiki」Gitee ...
- Java进阶 | Proxy动态代理机制详解
一.Jvm加载对象 在说Java动态代理之前,还是要说一下Jvm加载对象的过程,这个依旧是理解动态代理的基础性原理: Java类即源代码程序.java类型文件,经过编译器编译之后就被转换成字节代码.c ...
- 黑马程序猿_Java 代理机制学习总结
-------<a href="http://www.itheima.com/"">android培训</a>.<a href=" ...
- 深度剖析JDK动态代理机制
摘要 相比于静态代理,动态代理避免了开发人员编写各个繁锁的静态代理类,只需简单地指定一组接口及目标类对象就能动态的获得代理对象. 代理模式 使用代理模式必须要让代理类和目标类实现相同的接口,客户端通过 ...
- Java 反射 设计模式 动态代理机制详解 [ 转载 ]
Java 反射 设计模式 动态代理机制详解 [ 转载 ] @author 亦山 原文链接:http://blog.csdn.net/luanlouis/article/details/24589193 ...
- springboot项目部署云服务器
Springboot项目部署云服务器 springboot项目部署云服务器还是挺简单的 首先你要有java运行环境,就是jdk的安装,如果还没有装没有参考安装:阿里云ECS建网站(建站)超详细全套完整 ...
- Java代理和动态代理机制分析和应用
本博文中项目代码已开源下载地址:GitHub Java代理和动态代理机制分析和应用 概述 代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问.代理类负责为委托类预处理消息 ...
- Java动态代理机制研读
java动态加载类(反射机制) /*MyClass.java*/ public class MyClass { public int id; public String name; public in ...
随机推荐
- 4 文件操作 支持图片 视频 mp3 文本等
#文件操作:send_file,支持图片 视频 mp3 文本等@app.route("/img")def img(): return send_file("1.jpg&q ...
- 2019南昌网络赛-I. Yukino With Subinterval 线段树套树状数组,CDQ分治
TMD...这题卡内存卡的真优秀... 所以以后还是别用主席树的写法...不然怎么死的都不知道... 树套树中,主席树方法开权值线段树...会造成空间的浪费...这道题内存卡的很紧... 由于树套树已 ...
- Activity学习(二):Activity的启动模式(转载)
在Android中每个界面都是一个Activity,切换界面操作其实是多个不同Activity之间的实例化操作.在Android中Activity的启动模式决定了Activity的启动运行方式. An ...
- 2005年NOIP普及组复赛题解
题目涉及算法: 陶陶摘苹果:入门题: 校门外的树:简单模拟: 采药:01背包: 循环:模拟.高精度. 陶陶摘苹果 题目链接:https://www.luogu.org/problem/P1046 循环 ...
- 2018-8-10-win10-uwp-读取保存WriteableBitmap-、BitmapImage
title author date CreateTime categories win10 uwp 读取保存WriteableBitmap .BitmapImage lindexi 2018-08-1 ...
- 2018-8-10-使用-RetroShare-分享资源
title author date CreateTime categories 使用 RetroShare 分享资源 lindexi 2018-08-10 19:16:51 +0800 2018-02 ...
- 慎用curl测网站速度
curl的计时功能也许不够准确. curl -s -o /dev/null -w %{time_total} g.cn 耗时(秒) 0.1350.1340.1350.1350.1340.132 ab ...
- 【a602】最大乘积
Time Limit: 1 second Memory Limit: 32 MB [问题描述] 一个正整数一般可以分为几个互不相同的自然数的,如3=1+2,4=1+3,5=1+4=2+3,6=1+5= ...
- 几种常见排序算法的基本介绍,性能分析,和c语言实现
本文介绍6种常见的排序算法,以及他们的原理,性能分析和c语言实现: 为了能够条理清楚,本文所有的算法和解释全部按照升序排序进行 首先准备一个元素无序的数组arr[],数组的长度为length,一个交换 ...
- 最短路算法(floyed+Dijkstra+bellman-ford+SPFA)
最短路算法简单模板 一.floyed算法 首先对于floyed算法来说就是最短路径的动态规划解法,时间复杂度为O(n^3) 适用于图中所有点与点之间的最短路径的算法,一般适用于点n较小的情况. Flo ...