Flink
发表于|更新于|Java
|总字数:2.4k|阅读时长:14分钟|浏览量:
Flink
尝试flink
本地安装
步骤一:下载
为了能够运行Flink,唯一的要求是安装有效的Java 8或11。您可以通过发出以下命令来检查Java的正确安装:
1 | # 要安装java环境 |
步骤二:启动本地集群
1 | $ ./bin/start-cluster.sh |
步骤三:提交一个job
1 | $ ./bin/flink run examples/streaming/WordCount.jar |
步骤四:停止集群
1 | $ ./bin/stop-cluster.sh |
使用DataStream API进行欺诈检测
Java环境
1 | <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
输入
File输入
1 | public static void main(String[] args) throws Exception { |
Kafka
需要引入包
1 | <dependency> |
1 | public static void main(String[] args) throws Exception { |
集合获取
1 | public static void main(String[] args) throws Exception { |
输出
自定义数据源
模拟从kafka中获取
1 | public static void main(String[] args) throws Exception { |
自定义数据源
1 | public class MySensorce implements SourceFunction<SensorReading> { |
算子
基本算子
map
1 | final SingleOutputStreamOperator<Integer> map = inputStream.map(new MapFunction<String, Integer>() { |
flatmap
1 | final SingleOutputStreamOperator<String> stringSingleOutputStreamOperator = inputStream.flatMap(new FlatMapFunction<String, String>() { |
filter
1 | final SingleOutputStreamOperator<String> filter = inputStream.filter(new FilterFunction<String>() { |
分流,合流
1 | public static void main(String[] args) throws Exception { |
富函数
1 | public static void main(String[] args) throws Exception { |
分组,聚合
1 | public static void main(String[] args) throws Exception { |
相关推荐

2023-12-20
Tron
波场官方trident-java文档 m1 mac Android protobuf 遇到的问题 tron中文文档 trc20离线签名的demo,有trxdent-java,就不需要这个了,使用http的方式可以看看 trc20离线签名的demo,有trxdent-java,就不需要这个了,使用http的方式可以看看,另外一个demo 要继续学习tron可以看看 打包依赖 下载源文件trident-java 这里选择main分支 进入trident-java文件夹,可以看到有build.gradle,如果是mac系统,修改一下依赖 1234567891011121314protoc { // 原来的依赖 artifact = 'com.google.protobuf:protoc:3.12.0' // 修改之后的依赖 artifact = 'com.google.protobuf:protoc:3.12.0:osx-x86_64' } plugins { grpc { // 原来的依赖 artifact = 'io.grpc:protoc-gen-grpc-java:1.31.0' // 修改之后的依赖 artifact = 'io.grpc:protoc-gen-grpc-java:1.31.0:osx-x86_64' } } IDEA打包 打包之后,在每个子模块的build/libs/xxx-jar 下面的操作就可以跟着开发文档了,创建项目 pom.xml 123456789101112131415161718192021222324252627<dependency> <groupId>org.tron.trident</groupId> <artifactId>abi</artifactId> <version>0.8.0</version> <scope>system</scope> <systemPath>${project.basedir}/src/main/resources/jar/abi-0.8.0.jar</systemPath></dependency><dependency> <groupId>org.tron.trident</groupId> <artifactId>utils</artifactId> <version>0.8.0</version> <scope>system</scope> <systemPath>${project.basedir}/src/main/resources/jar/utils-0.8.0.jar</systemPath></dependency><dependency> <groupId>org.tron.trident</groupId> <artifactId>core</artifactId> <version>0.8.0</version> <scope>system</scope> <systemPath>${project.basedir}/src/main/resources/jar/core-0.8.0.jar</systemPath></dependency><dependency> <groupId>io.grpc</groupId> <artifactId>grpc-all</artifactId> <version>1.48.1</version></dependency> 查询余额的代码 123456789101112131415public static String privateKey = "私钥"; public static String apiKey = "apiKey";//波场申请 @Test public void getCount() { //生成密钥对 KeyPair keyPair = new KeyPair(privateKey); //地址转换 System.out.println(keyPair.toHexAddress()); ApiWrapper wrapper = ApiWrapper.ofMainnet(privateKey,apiKey); //账号查询 Account account = wrapper.getAccount("账户地址"); //查询余额 System.out.println(account.getBalance()); System.out.println(account.getCreateTime()); } 转账示例 123456789101112131415161718192021public static String transferTrc(String fromAddress, String toAddress, String amount, String contractAddress, String privateKey) { ApiWrapper wrapper = ApiWrapper.ofNile(privateKey); String ret = ""; try { //根据合约地址获取封装好的合约 Contract contract = wrapper.getContract(contractAddress); //创建一个TRC20合约对象 Trc20Contract token = new Trc20Contract(contract, fromAddress, wrapper); //获取想要转移的数量 BigInteger sunAmountValue = Convert.toSun(amount, Convert.Unit.TRX).toBigInteger(); //设置最大手续费 long maxTrx = Convert.toSun("17", Convert.Unit.TRX).longValue(); ret = token.transfer(toAddress, sunAmountValue.longValue(), 0, "", maxTrx); System.out.println("哈希:" + ret); } catch (Exception e) { System.out.println("转移失败:" + e.getMessage().toString()); } finally { wrapper.close(); } return ret;}

2022-11-19
Spring Security
Spring Security1234<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId></dependency> 登录业务 1234567891011121314151617181920212223242526272829303132333435363738@Servicepublic class LoginService { public static Map<String, RedisUser> map = new HashMap<>(); @Resource AuthenticationManager authenticationManager; public String login(SysUser sysUser) { // AuthenticationManager 进行用户认证 UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(sysUser.getUsername(), sysUser.getPassword()); Authentication authenticate = authenticationManager.authenticate(usernamePasswordAuthenticationToken); // 如果认证不通过,给出对应的提示 if (Objects.isNull(authenticate)) { throw new RuntimeException("登录失败"); } // 如果认证通过,使用username给jwt生成一个 RedisUser principal = (RedisUser) authenticate.getPrincipal(); map.put(principal.getUsername(), principal); return createJWT("1234567", 9990000, principal.getUsername()); } public void logout() { UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); RedisUser principal = (RedisUser) authentication.getPrincipal(); String username = principal.getUsername(); // 从redis里删除 map.remove(username); }} 123456789101112131415161718192021222324@RestController@RequestMapping("/")public class LoginController { @Resource LoginService loginService; @PostMapping("/user/login") public ResponseEntity<HashMap> login(@RequestParam("username") String username,@RequestParam("password") String password){ SysUser sysUser = new SysUser(); sysUser.setUsername(username); sysUser.setPassword(password); HashMap<String, Object> stringObjectHashMap = new HashMap<>(); stringObjectHashMap.put("token", loginService.login(sysUser)); return ResponseEntity.status(200).body(stringObjectHashMap); } @PostMapping("/user/logout") public ResponseEntity<String> logout(){ loginService.logout(); return ResponseEntity.ok("success"); }} 验证账户 12345678910111213141516171819202122232425262728293031323334353637383940414243import com.example.spirngsecutirylearn.pojo.RedisUser;import com.example.spirngsecutirylearn.pojo.SysUser;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.core.userdetails.UserDetailsService;import org.springframework.security.core.userdetails.UsernameNotFoundException;import org.springframework.stereotype.Service;import java.util.ArrayList;@Servicepublic class TestService implements UserDetailsService { static String user = "anthony"; static String password = "$2a$10$yyR9WuT9JY/bpe1VPU0yguqlv0lWpgzTD9NEetf2.n8y7NXIa1rfm"; /** * DaoAuthenticationProvier 会调用这个方法查询用户,并且返回UserDetails对象 * @param username * @return * @throws UsernameNotFoundException */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 从数据库查询用户信息 SysUser sysUser = new SysUser(); if (username.equals(user)) { sysUser.setUsername(user); sysUser.setPassword(password); }else { // 没有查到用户 throw new RuntimeException("没有查到用户信息"); } // 查询对应的权限信息 ArrayList<String> strings = new ArrayList<>();// strings.add("test"); strings.add("admin"); // 返回封装的信息 return new RedisUser(sysUser,strings); }} 缓存的用户对象 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263package com.example.spirngsecutirylearn.pojo;import lombok.AllArgsConstructor;import lombok.Data;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.core.authority.SimpleGrantedAuthority;import org.springframework.security.core.userdetails.UserDetails;import java.util.Collection;import java.util.List;import java.util.stream.Collectors;@Datapublic class RedisUser implements UserDetails { SysUser sysUser; List<String> list; public RedisUser(SysUser sysUser, List<String> list) { this.sysUser = sysUser; this.list = list; } /** * 权限 * @return */ @Override public Collection<? extends GrantedAuthority> getAuthorities() { return list.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()); } @Override public String getPassword() { return sysUser.getPassword(); } @Override public String getUsername() { return sysUser.getUsername(); } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; }} 认证失败的回调 123456789101112131415161718192021222324252627282930313233package com.example.spirngsecutirylearn.handler;import cn.hutool.json.JSONUtil;import org.springframework.http.ResponseEntity;import org.springframework.security.core.AuthenticationException;import org.springframework.security.web.AuthenticationEntryPoint;import org.springframework.stereotype.Component;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;@Componentpublic class AuthException implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { ResponseEntity<String> responseEntity = ResponseEntity.ok().body("认证失败,请重新登录"); String str = JSONUtil.toJsonStr(responseEntity); /// 处理异常 extracted(response,str); } private static void extracted(HttpServletResponse response,String string) throws IOException { response.setStatus(200); response.setContentType("application/json"); response.setCharacterEncoding("utf-8"); response.getWriter().println(string); }} 授权失败的回调 123456789101112131415161718192021222324252627282930313233package com.example.spirngsecutirylearn.handler;import cn.hutool.json.JSONUtil;import org.springframework.http.ResponseEntity;import org.springframework.security.access.AccessDeniedException;import org.springframework.security.core.AuthenticationException;import org.springframework.security.web.AuthenticationEntryPoint;import org.springframework.security.web.access.AccessDeniedHandler;import org.springframework.stereotype.Component;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;@Componentpublic class AccessException implements AccessDeniedHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { ResponseEntity<String> responseEntity = ResponseEntity.ok().body("权限不租"); String str = JSONUtil.toJsonStr(responseEntity); /// 处理异常 extracted(response,str); } private static void extracted(HttpServletResponse response,String string) throws IOException { response.setStatus(200); response.setContentType("application/json"); response.setCharacterEncoding("utf-8"); response.getWriter().println(string); }} 开启注解权限控制和自定义注解 1234567891011121314151617181920@RestController@RequestMapping("/test")public class TestController { @GetMapping("/hello") @PreAuthorize("hasAnyAuthority('test')") public ResponseEntity<String> hello(){ return ResponseEntity.ok("hello server"); } /** * 自定义校验权限 * @return */ @GetMapping("/hello2") @PreAuthorize("@anthony.hasAnyAuthority('test')") public ResponseEntity<String> hello2(){ return ResponseEntity.ok("hello server2"); }} 配置 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100package com.example.spirngsecutirylearn.cofig;import com.example.spirngsecutirylearn.filter.JwtFilter;import com.example.spirngsecutirylearn.handler.AccessException;import com.example.spirngsecutirylearn.handler.AuthException;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.http.HttpMethod;import org.springframework.security.authentication.AuthenticationManager;import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication;import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import org.springframework.security.config.http.SessionCreationPolicy;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import org.springframework.security.crypto.password.PasswordEncoder;import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;import org.springframework.security.web.authentication.logout.LogoutFilter;import javax.annotation.Resource;@Configuration@EnableGlobalMethodSecurity(prePostEnabled = true)public class SecurityConfig extends WebSecurityConfigurerAdapter { @Resource JwtFilter jwtFilter; @Resource AuthException authException; @Resource AccessException accessException; @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override protected void configure(HttpSecurity httpSecurity) throws Exception { httpSecurity // CSRF禁用,因为不使用session .csrf().disable() // 基于token,所以不需要session .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() // 过滤请求 .authorizeRequests() // 对于登录login 注册register 验证码captchaImage 允许匿名访问 .antMatchers("/user/login").anonymous() // 静态资源,permitAll 有没有登录都访问// .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()// .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll() // 除上面外的所有请求全部需要鉴权认证 .anyRequest().authenticated();// .and()// .headers().frameOptions().disable(); // 添加Logout filter// httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);// // 添加JWT filter httpSecurity.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);// // 添加CORS filter// httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);// httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class); // 添加自定义的认证和授权的自定义失败处理 httpSecurity.exceptionHandling().authenticationEntryPoint(authException).accessDeniedHandler(accessException); httpSecurity.cors(); } /** * 密码加密的规则 * @return */ @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } /** * BCryptPasswordEncoder 的测试方法 * @param args */ public static void main(String[] args) { BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); // 加密 String encode = bCryptPasswordEncoder.encode("123456"); System.out.println(encode); // 解密 boolean matches = bCryptPasswordEncoder.matches("123456", encode); System.out.println(matches); }} 自定义注解 123456789101112131415161718@Component("anthony")public class TestExpress { public boolean hasAnyAuthority(String authority){ Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); RedisUser principal = (RedisUser) authentication.getPrincipal();// List<String> list = (List<String>)principal.getAuthorities(); List<SimpleGrantedAuthority> authorities = (List<SimpleGrantedAuthority>) principal.getAuthorities(); for (SimpleGrantedAuthority simpleGrantedAuthority : authorities) { if (simpleGrantedAuthority.getAuthority().equals(authority)) { return true; } } return false; }} 找Java包的路径 12# mac/usr/libexec/java_home -V

2023-12-10
Netty
Server 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162// 把 NettyServer 的创建交给 Spring 管理@Componentpublic class NettyServer { private Logger logger = LoggerFactory.getLogger(getClass()); @Value("${netty.port}") private Integer port; @Resource private NettyServerHandlerInitializer nettyServerHandlerInitializer; // boss 线程组,用于服务端接受客户端的连接 private EventLoopGroup bossGroup = new NioEventLoopGroup(); // worker 线程组,用于服务端接受客户端的数据读写 private EventLoopGroup workerGroup = new NioEventLoopGroup(); // Netty Server Channel private Channel channel; // 启动 Netty Server @PostConstruct public void start() throws InterruptedException { // 创建 ServerBootstrap 对象,用于 Netty Server 启动 ServerBootstrap bootstrap = new ServerBootstrap(); // 设置 ServerBootstrap 的各种属性 // 设置两个 EventLoopGroup 对象 bootstrap.group(bossGroup, workerGroup) // 指定 Channel 为服务端 NioServerSocketChannel .channel(NioServerSocketChannel.class) // 设置 Netty Server 的端口 .localAddress(new InetSocketAddress(port)) // 服务端 accept 队列的大小 .option(ChannelOption.SO_BACKLOG, 1024) // TCP Keepalive 机制,实现 TCP 层级的心跳保活功能 .childOption(ChannelOption.SO_KEEPALIVE, true) // 允许较小的数据包的发送,降低延迟 .childOption(ChannelOption.TCP_NODELAY, true) // 处理器 .childHandler(nettyServerHandlerInitializer); // 绑定端口,并同步等待成功,即启动服务端 ChannelFuture future = bootstrap.bind().sync(); if (future.isSuccess()) { channel = future.channel(); logger.info("[start][Netty Server 启动在 {} 端口]", port); } } // 关闭 Netty Server @PreDestroy public void shutdown() { // 关闭 Netty Server if (channel != null) { channel.close(); } // <3.2> 优雅关闭两个 EventLoopGroup 对象 bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); }} NettyServerHandlerInitializer 12345678910111213141516171819202122232425262728293031@Componentpublic class NettyServerHandlerInitializer extends ChannelInitializer<Channel> { /** * 心跳超时时间 */ private static final Integer READ_TIMEOUT_SECONDS = 3 * 60; @Resource private NettyServerHandler nettyServerHandler; @Override protected void initChannel(Channel ch) { // 获得 Channel 对应的 ChannelPipeline ChannelPipeline channelPipeline = ch.pipeline(); // 添加一堆 NettyServerHandler 到 ChannelPipeline 中 channelPipeline // 空闲检测 .addLast(new ReadTimeoutHandler(READ_TIMEOUT_SECONDS, TimeUnit.SECONDS)) // 编码器 .addLast(new InvocationEncoder()) // 解码器 .addLast(new InvocationDecoder()) // 消息分发器 .addLast(messageDispatcher) // 服务端处理器 .addLast(nettyServerHandler) ; }} 模拟启动Netty123ChannelFuture future = bootstrap.bind(port).sync();System.out.println("Netty 代理服务器启动,监听端口:" + port);future.channel().closeFuture().sync(); // 阻塞直到关闭 1234567891011121314151617181920212223242526272829303132333435363738394041424344public class NettyTest { public static void main(String[] args) throws InterruptedException { RiceCooker cooker = new RiceCooker(); // 模拟 bootstrap.bind().sync() cooker.start(); // 启动(阻塞直到准备好) // 模拟 future.channel().closeFuture().sync() new Thread(() -> { try { Thread.sleep(5000); // 模拟5秒后煮好饭 cooker.close(); // 自动关闭 } catch (InterruptedException ignored) {} }).start(); cooker.waitUntilClosed(); // 阻塞,直到关闭 }}class RiceCooker { boolean isStarted = false; boolean isClosed = false; // 启动电饭煲 public void start() throws InterruptedException { System.out.println("启动电饭煲..."); Thread.sleep(1000); // 模拟准备时间 isStarted = true; System.out.println("电饭煲开始煮饭!"); } // 等待“煮饭完成” public void waitUntilClosed() throws InterruptedException { while (!isClosed) { Thread.sleep(500); // 等待状态变化 } System.out.println("煮饭完成,电饭煲关闭。"); } public void close() { isClosed = true; }} 粘包模拟粘包问题123456789101112131415161718192021222324252627282930public class NettyServer { public static void main(String[] args) throws Exception { EventLoopGroup boss = new NioEventLoopGroup(); EventLoopGroup worker = new NioEventLoopGroup(); ServerBootstrap b = new ServerBootstrap(); b.group(boss, worker) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) { ChannelPipeline pipeline = ch.pipeline(); // 👇 没有拆包处理器,粘包将出现! pipeline.addLast(new ServerHandler()); } }); b.bind(8080).sync(); System.out.println("Server started on port 8080"); }}class ServerHandler extends SimpleChannelInboundHandler<ByteBuf> { @Override protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) { String received = msg.toString(CharsetUtil.UTF_8); System.out.println("数据长度:" + msg.readableBytes()); System.out.println("原始数据: [" + received + "]"); }} 1234567891011121314151617181920212223242526public class NettyClient { public static void main(String[] args) throws Exception { EventLoopGroup group = new NioEventLoopGroup(); Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) { ch.pipeline().addLast(new ChannelInboundHandlerAdapter() { @Override public void channelActive(ChannelHandlerContext ctx) { // 连续发多条消息,粘包一定会出现 for (int i = 0; i < 5; i++) { String msg = "msg" + i + "\n"; ctx.writeAndFlush(Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8)); } } }); } }); bootstrap.connect("127.0.0.1", 8080).sync(); }} 服务端打印 12345678Server started on port 8080数据长度:25原始数据: [msg0msg1msg2msg3msg4] 这里错误情况,正常情况应该是 1234原始数据:[msg0]原始数据:[msg1]原始数据:[msg2]..... 方案1:换行符或自定义分隔符在服务端配置 1234// 按 \n 拆new LineBasedFrameDecoder(1024); // 按 "--" 拆new DelimiterBasedFrameDecoder(1024, Unpooled.wrappedBuffer("--".getBytes())); 1234567891011121314151617181920212223242526272829303132public class NettyServer { public static void main(String[] args) throws Exception { EventLoopGroup boss = new NioEventLoopGroup(); EventLoopGroup worker = new NioEventLoopGroup(); ServerBootstrap b = new ServerBootstrap(); b.group(boss, worker) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new LineBasedFrameDecoder(1024));// pipeline.addLast(new DelimiterBasedFrameDecoder(1024, Unpooled.wrappedBuffer("--".getBytes()))); // 👇 没有拆包处理器,粘包将出现! pipeline.addLast(new ServerHandler()); } }); b.bind(8080).sync(); System.out.println("Server started on port 8080"); }}class ServerHandler extends SimpleChannelInboundHandler<ByteBuf> { @Override protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) { String received = msg.toString(CharsetUtil.UTF_8); System.out.println("数据长度:" + msg.readableBytes()); System.out.println("原始数据: [" + received + "]"); }} 控制台打印,这是正常的 1234567891011Server started on port 8080数据长度:4原始数据: [msg0]数据长度:4原始数据: [msg1]数据长度:4原始数据: [msg2]数据长度:4原始数据: [msg3]数据长度:4原始数据: [msg4] 方案2:固定长度12// 客户端每条消息必须正好 8 字节,否则解析错。pipeline.addLast(new FixedLengthFrameDecoder(8)); 方案三:LengthFieldBasedFrameDecoder

2022-11-19
Mycat
主从复制启动1 .下载jre包 2 下载mycat包 3 打包镜像 Dockerfile 1234567891011121314FROM centos# 解压这个包到home下面ADD server-jre-8u251-linux-x64.tar.gz /home/ADD ./Mycat-server-1.6.7.4-release-20200105164103-linux.tar.gz /homeENV WORKPATH /home/mycat/WORKDIR $WORKPATHENV JAVA_HOME /home/jdk1.8.0_251ENV CLASSPATH $JAVA_HOME/dt.jar:$JAVA_HOME/lib/tools.jarENV PATH $PATH:$JAVA_HOME/BIN:$CATALINA_HOME/lib:$CATALINA_HOME/bin# 暴露8066端口EXPOSE 8066CMD /home/mycat/bin/mycat console 把mycat里的conf 和 logs 从容器中拷贝出来 再挂在两个目录,并启动 123456789101112131415docker run -it \ --name mycat \ -p 8066:8066 \ -v /home/anthony/mycat/conf:/home/mycat/conf/ \ -v /home/anthony/mycat/logs:/home/mycat/logs/ \ java1234/mycat:1.0Running Mycat-server...Removed stale pid file: /home/mycat/logs/mycat.pidwrapper | --> Wrapper Started as Consolewrapper | Launching a JVM...jvm 1 | Wrapper (Version 3.2.3) http://wrapper.tanukisoftware.orgjvm 1 | Copyright 1999-2006 Tanuki Software, Inc. All Rights Reserved.jvm 1 |jvm 1 | MyCAT Server startup successfully. see logs in logs/mycat.log 配置 schems.xml 定义逻辑库,表,分片节点等内容 rule.xml 定义分片规则 server.xml 定义用户以及系统相关变量 安装mysql5.7123# 从容器中拷贝这些文件docker cp xxx:/etc/mysql/mysql.conf.d/ /home/anthony/mysqldocker cp xxx:/var/log/ /home/anthony/mysql 启动的时候映射这些 创建网络12345678910# 可以查看容器的虚拟ipdocker inspect ac7# 查看网络docker network ls# 自定义一个网络模式# extnetwork 是网络模式的名字# 172.20.0.1 是网关docker network create --subnet=172.20.0.0/16 extnetwork mysql,mycat指定网路,重建容器12345678910111213141516171819202122232425262728# 指定网络模式和ip地址--net extnetwork --ip 172.20.0.2docker run -itd \ --name mysql-master \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=123456 \ -v /home/anthony/mysql/mysql.conf.d/:/etc/mysql/conf.d/ \ -v /home/anthony/mysql/log/:/var/log/ \ --net extnetwork --ip 172.20.0.2 \ mysql:5.7docker run -itd \ --name mysql-slave \ -p 3307:3306 \ -e MYSQL_ROOT_PASSWORD=123456 \ -v /home/anthony/mysql2/mysql.conf.d/:/etc/mysql/conf.d/ \ -v /home/anthony/mysql2/log/:/var/log/ \ --net extnetwork --ip 172.20.0.3 \ mysql:5.7docker run -it \ --name mycat \ -p 8066:8066 \ -v /home/anthony/mycat/conf:/home/mycat/conf/ \ -v /home/anthony/mycat/logs:/home/mycat/logs/ \ --net extnetwork --ip 172.20.0.4 \ java1234/mycat:1.0 主从配置主,在[mysqld] 12345678910111213# 主服务器ID,必须唯一server-id=2# 开启和设置二进制日志文件名称log_bin=mysql-bin# 要同步的数据库binlog-do-db=db_java1234# 不需要同步的数据库binlog-ignore_db=mysqlbinlog-ignore_db=information_schemabinlog-ignore_db=performation_schemabinlog_ignore_db=sys# 设置logbin格式,mysql默认采用statement,建议使用mixedbinlog_format=MIXED 从,在[mysqld] 12345678910111213141516# 主服务器ID,必须唯一server-id=3# 这行是从库需要添加的relay-log=mysql-relay# 开启和设置二进制日志文件名称log_bin=mysql-bin# 要同步的数据库binlog-do-db=db_java1234# 不需要同步的数据库binlog-ignore_db=mysqlbinlog-ignore_db=information_schemabinlog-ignore_db=performation_schemabinlog_ignore_db=sys# 设置logbin格式,mysql默认采用statement,建议使用mixedbinlog_format=MIXED 测试主从还没有生效 12345678910mysql> show master status;+------------------+----------+--------------+--------------------------------------------------+-------------------+| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |+------------------+----------+--------------+--------------------------------------------------+-------------------+| mysql-bin.000003 | 357 | db_java1234 | mysql,information_schema,performation_schema,sys | |+------------------+----------+--------------+--------------------------------------------------+-------------------+1 row in set (0.01 sec)mysql> show slave status;Empty set 设置用户名在master执行 12345678910111213# 创建用户名为slave1# 172.20.0.3从库ip# 123456 密码CREATE USER 'slave1'@'172.20.0.3' IDENTIFIED BY '123456';# 语法:GRANT privileges ON databasename.tablename TO 'username'@'host'# 172.20.0.3 从库# 两个权限:REPLICATION和SLAVE# *.* 所有的库和表GRANT REPLICATION SLAVE ON *.* TO 'slave1'@'172.20.0.3';# 刷新权限FLUSH PRIVILEGES; 在slave执行 1234CHANGE MASTER TO MASTER_HOST='172.20.0.2',MASTER_USER='slave1',MASTER_PASSWORD='123456',MASTER_LOG_FILE='mysql-bin.000003',MASTER_LOG_POS=964;# 开启主从start slave; 在从库执行 1show slave status; 主要看Slave_io_running =yes 主要看Slave_sql_running =yes 读写分离scheml.xmlschema标签1234567<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1"> <!-- auto sharding by id (long) --> <!--splitTableNames 启用<table name 属性使用逗号分割配置多个表,即多个表使用这个配置--> <table name="travelrecord,address" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" splitTableNames ="true"/> <!-- <table name="oc_call" primaryKey="ID" dataNode="dn1$0-743" rule="latest-month-calldate" /> --></schema> name: 配置逻辑库的名称 checkSQLschema: 当执行select * from T_Account.user 为true时,mycat会把sql转成select * from user false 会报错 sqlMaxLimit:最大查询每页条数,如果sql指定了limit,就以sql为准,没有,就以配置为准 randomDataNode:这个先不用,先改成datanote='dn1' datanote标签123<dataNode name="dn1" dataHost="localhost1" database="db1" /><dataNode name="dn2" dataHost="localhost1" database="db2" /><dataNode name="dn3" dataHost="localhost1" database="db3" /> name:数据分片节点名称 dataHost:定义该数据分片节点属于哪台数据库主机 database:指定哪个数据库,db_java1234 datahost标签123456789<dataHost name="localhost1" maxCon="1000" minCon="10" balance="3" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100"> <heartbeat>select user()</heartbeat> <!-- can have multi write hosts --> <writeHost host="hostM1" url="localhost:3306" user="root" password="123456"> </writeHost> <!-- <writeHost host="hostM2" url="localhost:3316" user="root" password="123456"/> --></dataHost> name:跟datanote标签的datahost对应 maxCon 指定每个读写实例连接池的的最大链接,write合readhost标签都会使用这个属性的值来实例化出连接池的最大连接数 min 最小的连接 balance: 0 不开启读写分离,所有读操作都发送到当前可用的writehost上 1 2 所有读操作都随机在writehost合readehost上分发 3 所有读请求随机分发到writehost合readhos 双组双从第二套主从1.在mysql添加中添加 123log-slave-update=1auto-increment-increment=2auto-increment-offset=1 2.把mysql文件复制一份mysql3 3.把mysql3的配置文件修修改这些配置 123# 主服务器ID,必须唯一server-id=5auto-increment-offset=2 4.再创建两个容器,m2 和 s2 master 和 master同步就是相互slave 测试读写分离插入语句 1insert into test valus(@@hostname)

2022-11-19
设计模式
创建型模式简单工厂Pay.java 123public interface Pay { void pay(Integer money);} SimpleFactory.java 12345678910111213public class SimpleFactory { public static Pay create(String payType) { if (payType.equals("ZFB")) { return new ZFBPay(); } else if (payType.equals("WX")) { return new WXPay(); }else { return null; } }} WXPay.java 微信支付 12345public class WXPay implements Pay { public void pay(Integer money) { System.out.println("微信支付:"+money+"元"); }} ZFBPay.java 支付宝支付 12345public class ZFBPay implements Pay { public void pay(Integer money) { System.out.println("支付宝支付:"+money+"元"); }} Test.java 1234567891011121314/** * 简单工厂设计模式 */public class Test { public static void main(String[] args) { Pay wx = SimpleFactory.create("WX"); wx.pay(2); Pay zfb = SimpleFactory.create("ZFB"); zfb.pay(1); }} 行为模式策略模式基础 Strategy 1234567public interface Strategy { /** * 策略方法 */ public void strategyInterface();} ConcreteStrategyA 1234567public class ConcreteStrategyA implements Strategy { @Override public void strategyInterface() { System.out.println("ConcreteStrategyA"); }} ConcreteStrategyB 1234567public class ConcreteStrategyB implements Strategy { @Override public void strategyInterface() { System.out.println("ConcreteStrategyB"); }} ConcreteStrategyC 1234567public class ConcreteStrategyC implements Strategy { @Override public void strategyInterface() { System.out.println("ConcreteStrategyC"); }} Context 1234567891011121314151617181920public class Context { //持有一个具体策略的对象 private Strategy strategy; /** * 构造函数,传入一个具体策略对象 */ public Context(Strategy strategy){ this.strategy = strategy; } /** * 策略方法 */ public void contextInterface(){ strategy.strategyInterface(); }} Test.java 1234567public class Test { public static void main(String[] args) { Context context = new Context(new ConcreteStrategyA()); context.contextInterface(); }} 应用MemberStrategy.java 12345678910111213/** * 会员策略 */public interface MemberStrategy { /** * 计算图书的价格,根绝会员等级 * @param booksPrice 图书的原价 * @return 计算出打折后的价格 */ public double calcPrice(double booksPrice);} PrimaryMemberStrategy.java 初级会员 1234567public class PrimaryMemberStrategy implements MemberStrategy { @Override public double calcPrice(double booksPrice) { System.out.println("初级会员,没有折扣"); return 0; }} IntermediateMemberStrategy.java 中级会员 1234567public class IntermediateMemberStrategy implements MemberStrategy { @Override public double calcPrice(double booksPrice) { System.out.println("对于中级会员的折扣为10%"); return booksPrice * 0.9; }} AdvancedMemberStrategy.java 高级会员 1234567public class AdvancedMemberStrategy implements MemberStrategy { @Override public double calcPrice(double booksPrice) { System.out.println("对于高级会员的折扣为20%"); return booksPrice * 0.8; }} Price.java 1234567891011121314151617public class Price { private MemberStrategy strategy; public Price(MemberStrategy strategy) { this.strategy = strategy; } /** * 计算图书的价格 * @param booksPrice 图书的原价 * @return 计算出打折后的价格 */ public double quote(double booksPrice){ return this.strategy.calcPrice(booksPrice); }} Test.java 123456789101112public class Test { public static void main(String[] args) { //选择并创建需要使用的策略对象 MemberStrategy strategy = new AdvancedMemberStrategy(); //创建环境 Price price = new Price(strategy); //计算价格 double quote = price.quote(300); System.out.println("图书的最终价格为:" + quote); }} 结构模式装饰模式mybatis的执行期就用的是装饰模式 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162// 抽象构件角色// 比如这是一期开发的接口public interface Shape { void draw();}// 具体构件角色// 比如这是一期开发好的实现类public class Rectangle implements Shape { @Override public void draw() { System.out.println("Shape: Rectangle"); }}// 到二期开发了,发现这个Rectangle的实现不太好用了,需要添加别的功能// 抽象装饰器public abstract class ShapeDecorator implements Shape { protected Shape decoratedShape; public ShapeDecorator(Shape decoratedShape){ this.decoratedShape = decoratedShape; } public void draw(){ decoratedShape.draw(); } }// 具体装饰器角色// 这里就是开发的二期的功能public class RedShapeDecorator extends ShapeDecorator { public RedShapeDecorator(Shape decoratedShape) { super(decoratedShape); } @Override public void draw() { // 调用一期的代码原本的功能 decoratedShape.draw(); // 调用二期的新代码的功能 setRedBorder(decoratedShape); } private void setRedBorder(Shape decoratedShape){ System.out.println("Border Color: Red"); }}// 比如一个人冷了,多套了一件毛衣,发现还是冷,又多穿了一件羽绒服,一层一层的,装饰public class DecoratorPatternDemo { public static void main(String[] args) { // 一期的代码 Shape circle = new Circle(); // 二期的代码 ShapeDecorator redCircle = new RedShapeDecorator(new Circle()); redCircle.draw(); }} 模板模式12345678910111213141516171819202122232425262728293031323334353637383940414243444546public interface Lifecycle { void init();}abstract class AbstractLifecycle implements Lifecycle{ abstract void run(); @Override public void init() { System.out.println("这里是公共方法"); run(); System.out.println("这里也是公共方法"); }}public class ServerStandard extends AbstractLifecycle { @Override void run() { System.out.println("我是实现,父类将调用我"); }}public class ServerStandard2 extends AbstractLifecycle { @Override void run() { System.out.println("我是实现2,父类将调用我"); }}public class Test { public static void main(String[] args) { ServerStandard serverStandard = new ServerStandard(); serverStandard.init(); System.out.println("============================================"); ServerStandard2 serverStandard2 = new ServerStandard2(); serverStandard2.init(); }} 责任链模式123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110public abstract class Handler { protected String name; // 处理者姓名 @Setter protected Handler nextHandler; // 下一个处理者 public Handler(String name) { this.name = name; } public abstract boolean process(LeaveRequest leaveRequest); // 处理请假}public class Director extends Handler { public Director(String name) { super(name); } @Override public boolean process(LeaveRequest leaveRequest) { // 随机数大于3则为批准,否则不批准 boolean result = (new Random().nextInt(10)) > 3; String log = "主管<%s> 审批 <%s> 的请假申请,请假天数: <%d> ,审批结果:<%s> "; System.out.println(String.format(log, this.name, leaveRequest.getName(), leaveRequest.getNumOfDays(), result == true ? "批准" : "不批准")); if (!result) { // 不批准 return false; } else if (leaveRequest.getNumOfDays() < 3) { // 批准且天数小于3,返回true return true; } return nextHandler.process(leaveRequest); // 批准且天数大于等于3,提交给下一个处理者处理 }}public class Manager extends Handler { public Manager(String name) { super(name); } @Override public boolean process(LeaveRequest leaveRequest) { boolean result = (new Random().nextInt(10)) > 3; // 随机数大于3则为批准,否则不批准 String log = "经理<%s> 审批 <%s> 的请假申请,请假天数: <%d> ,审批结果:<%s> "; System.out.println(String.format(log, this.name, leaveRequest.getName(), leaveRequest.getNumOfDays(), result == true ? "批准" : "不批准")); if (!result) { // 不批准 return false; } else if (leaveRequest.getNumOfDays() < 7) { // 批准且天数小于7 return true; } return nextHandler.process(leaveRequest); // 批准且天数大于等于7,提交给下一个处理者处理 }}public class TopManager extends Handler { public TopManager(String name) { super(name); } @Override public boolean process(LeaveRequest leaveRequest) { boolean result = (new Random().nextInt(10)) > 3; // 随机数大于3则为批准,否则不批准 String log = "总经理<%s> 审批 <%s> 的请假申请,请假天数: <%d> ,审批结果:<%s> "; System.out.println(String.format(log, this.name, leaveRequest.getName(), leaveRequest.getNumOfDays(), result == true ? "批准" : "不批准")); if (!result) { // 总经理不批准 return false; } return true; // 总经理最后批准 }}@Data@AllArgsConstructorpublic class LeaveRequest { private String name; // 请假人姓名 private int numOfDays; // 请假天数}public class Test { public static void main(String[] args) { // 创建审批人 Handler zhangsan = new Director("张三"); Handler lisi = new Manager("李四"); Handler wangwu = new TopManager("王五"); // 创建责任链 zhangsan.setNextHandler(lisi); lisi.setNextHandler(wangwu); // 发起请假申请第一次 boolean result1 = zhangsan.process(new LeaveRequest("小旋锋", 1)); System.out.println("最终结果:" + result1 + "\n"); // 发起请假申请第二次 boolean result2 = zhangsan.process(new LeaveRequest("小旋锋", 4)); System.out.println("最终结果:" + result2 + "\n"); // 发起请假申请第三次 boolean result3 = zhangsan.process(new LeaveRequest("小旋锋", 8)); System.out.println("最终结果:" + result3 + "\n"); }} Tomcat的过滤器也使用到了责任链ApplicationFilterChain,具体的还没有读,先写完这个模式,比模板模式感觉有点复杂 参考的是:https://juejin.im/post/6844903702260629512#heading-11, 感觉举的例子的业务显示还不是特别的好理解 也参考下这个:https://www.cnblogs.com/tanshaoshenghao/p/10741160.html 适配器模式SpringMVC的DispatchServlet的例子 具体实现12345678910111213141516171819202122232425262728293031323334/** * Description: 控制器接口 */public interface Controller {}/** * Controller 实现之1:HttpController */public class HttpController implements Controller { public void doHttpHandler() { System.out.println("HttpController:httpMethod()"); }}/** * Controller 实现之2:SimpleController */public class SimpleController implements Controller { public void doSimplerHandler() { System.out.println("SimpleController:simpleMethod()"); }}/** * Controller 实现之3:HttpController */public class AnnotationController implements Controller { public void doAnnotationHandler() { System.out.println("AnnotationController:annotationMethod()"); }} 处理器123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263/** * 定义一个Adapter接口 */public interface HandlerAdapter { /** * 是否支持 * * @param handler * @return */ boolean supports(Object handler); /** * 处理 * * @param handler */ void handle(Object handler);}/** * HttpController 的适配器 */public class HttpHandlerAdapter implements HandlerAdapter { public boolean supports(Object handler) { return (handler instanceof HttpController); } public void handle(Object handler) { ((HttpController) handler).doHttpHandler(); }}/** * SimpleController的适配器 */public class SimpleHandlerAdapter implements HandlerAdapter { public boolean supports(Object handler) { return (handler instanceof SimpleController); } public void handle(Object handler) { ((SimpleController) handler).doSimplerHandler(); }}/** * AnnotationController 的适配器 */public class AnnotationHandlerAdapter implements HandlerAdapter { public boolean supports(Object handler) { return (handler instanceof AnnotationController); } public void handle(Object handler) { ((AnnotationController) handler).doAnnotationHandler(); }} main方法1234567891011121314151617181920212223242526272829303132333435363738394041424344454647/** * 模拟一个DispatcherServlet,适配器(适配者模式) * HandlerAdapter(适配器类) 作为适配器来适配各种 Handler(适配者类)(如Controller) */public class DispatchServlet { public static List<HandlerAdapter> handlerAdapters = new ArrayList<HandlerAdapter>(); /** * 注册所有 HandlerAdapter */ public DispatchServlet() { handlerAdapters.add(new AnnotationHandlerAdapter()); handlerAdapters.add(new HttpHandlerAdapter()); handlerAdapters.add(new SimpleHandlerAdapter()); } /** * 模拟DispatchServlet 中的 doDispatch()方法 */ public void doDispatch() { Controller handler = new AnnotationController(); // 通过handler来找到对应适配器 HandlerAdapter handlerAdapter = getHandler(handler); // 通过执行适配器的handle,来执行对应的controller对应方法 handlerAdapter.handle(handler); } /** * 找到与handler适配的适配器:通过handler 遍历 HandlerAdapter 适配器来实现 */ public HandlerAdapter getHandler(Controller handler) { for (HandlerAdapter adapter : handlerAdapters) { if (adapter.supports(handler)) { return adapter; } } return null; } /** * 模拟运行 */ public static void main(String[] args) { new DispatchServlet().doDispatch(); }} 参考 https://refactoringguru.cn/design-patterns 装饰器模式

2023-08-25
Spring
SpringMVC 总结: 首先请求进入DispatcherServlet 由DispatcherServlet 从HandlerMappings中提取对应的Handler。 2.此时只是获取到了对应的Handle,然后得去寻找对应的适配器,即:HandlerAdapter。 拿到对应HandlerAdapter时,这时候开始调用对应的Handler处理业务逻辑了。 (这时候实际上已经执行完了我们的Controller) 执行完成之后返回一个ModeAndView 这时候交给我们的ViewResolver通过视图名称查找出对应的视图然后返回。 最后 渲染视图 返回渲染后的视图 –>响应请求。 初始化过程version 5.3.8 1234567// org.springframework.web.servlet.HttpServletBean#init@Overridepublic final void init() throws ServletException { // 子类实现,初始化web环境 // Let subclasses do whatever initialization they like. initServletBean();} 12345678// org.springframework.web.servlet.FrameworkServlet#initServletBean@Overrideprotected final void initServletBean() throws ServletException { // 初始化spring上下文 this.webApplicationContext = initWebApplicationContext(); // 子类实现 initFrameworkServlet();} 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657// org.springframework.web.servlet.FrameworkServlet#initWebApplicationContextprotected WebApplicationContext initWebApplicationContext() { WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { // A context instance was injected at construction time -> use it wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> set // the root application context (if any; may be null) as the parent cwac.setParent(rootContext); } //配置和刷新spring容器(重要) //这个无非就是初始化spring ioc的环境,创建bean和实例化bean等操作 //这个方法最终也是调用refresh()方法,已在spring源码解析中解析过了 configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { // No context instance was injected at construction time -> see if one // has been registered in the servlet context. If one exists, it is assumed // that the parent context (if any) has already been set and that the // user has performed any initialization such as setting the context id wac = findWebApplicationContext(); } if (wac == null) { // No context instance is defined for this servlet -> create a local one wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { // Either the context is not a ConfigurableApplicationContext with refresh // support or the context injected at construction time had already been // refreshed -> trigger initial onRefresh manually here. synchronized (this.onRefreshMonitor) { // 初始化DispatcherServlet的配置initStrategies() onRefresh(wac); } } if (this.publishContext) { // Publish the context as a servlet context attribute. String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); } return wac; } 12345// org.springframework.web.servlet.DispatcherServlet#onRefresh protected void onRefresh(ApplicationContext context) { // //初始化springmvc的配置 initStrategies(context); } 总体流程: 执行DispatcherServlet的init()方法, 会执行父类的HttpServletBean的init()方法 然后调用了FrameworkServlet的initServletBean()方法 没看懂,执行initWebApplicationContext()方法,就是对spring ioc环境的初始化。那么这里就衍生出了一个面试题:spring容器和spring mvc的容器的区别?通过源码的分析,spring和spring mvc底层,都是调用了同一个refresh()方法,所以spring容器和spring mvc容器是没有区别的,都是指的是同一个容器。 (3)执行到onRefresh()方法,就是开始初始化DispatcherServlet了,也就是开始初始化spring mvc。 12345678910111213141516171819// org.springframework.web.servlet.DispatcherServlet#initStrategies protected void initStrategies(ApplicationContext context) { //上传文件 initMultipartResolver(context); //国际化 initLocaleResolver(context); //前段的主题样式 initThemeResolver(context); //初始化HandlerMappings(请求映射器)重点 initHandlerMappings(context); // 初始化HandlerAdapters(处理适配器) initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); //视图转换器 initViewResolvers(context); //重定向数据管理器 initFlashMapManager(context); } 1234567891011121314// org.springframework.web.servlet.DispatcherServlet#initHandlerMappingsprivate void initHandlerMappings(ApplicationContext context) { // Ensure we have at least one HandlerMapping, by registering // a default HandlerMapping if no other mappings are found. // 通过配置文件中的配置信息,得到handlerMappings if (this.handlerMappings == null) { this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isTraceEnabled()) { logger.trace("No HandlerMappings declared for servlet '" + getServletName() + "': using default strategies from DispatcherServlet.properties"); } }} 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657// org.springframework.web.servlet.DispatcherServlet#getDefaultStrategiesprivate static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) { if (defaultStrategies == null) { try { // Load default strategy implementations from properties file. // This is currently strictly internal and not meant to be customized // by application developers. /** * 从属性文件加载默认策略实现 * 说白了这里的意思就是从DEFAULT_STRATEGIES_PATH这个文件当中拿出所有的配置 * 可以去数一下一共有8个: DispatcherServlet.properties == DEFAULT_STRATEGIES_PATH */ ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class); defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); } catch (IOException ex) { throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage()); } } String key = strategyInterface.getName(); // defaultStrategies 是DispatcherServlet.properties 配置文件,在static静态代码块初始化 // 版本变了,不是从静态方法中获取到的 String value = defaultStrategies.getProperty(key); if (value != null) { String[] classNames = StringUtils.commaDelimitedListToStringArray(value); List<T> strategies = new ArrayList<>(classNames.length); for (String className : classNames) { try { // 获取class字节码文件 Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader()); // 底层是通过调用spring的getBean的方式创建该对象(可以进行bean的属性装配) // 请求映射就是在这个方法实现装配的 Object strategy = createDefaultStrategy(context, clazz); strategies.add((T) strategy); } catch (ClassNotFoundException ex) { throw new BeanInitializationException( "Could not find DispatcherServlet's default strategy class [" + className + "] for interface [" + key + "]", ex); } catch (LinkageError err) { throw new BeanInitializationException( "Unresolvable class definition for DispatcherServlet's default strategy class [" + className + "] for interface [" + key + "]", err); } } return strategies; } else { return Collections.emptyList(); } } DispatcherServlet.properties 从DispatcherServlet.properties配置文件,可以看出handlerMapping默认是有两个: 1.BeanNameUrlHandlerMapping (主要处理object) 2.RequestMappingHandlerMapping(主要处理method) 123456789101112131415161718192021222324252627282930# Default implementation classes for DispatcherServlet's strategy interfaces.# Used as fallback when no matching beans are found in the DispatcherServlet context.# Not meant to be customized by application developers.org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolverorg.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver// HandlerMappingorg.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\ org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\ org.springframework.web.servlet.function.support.RouterFunctionMapping// HandlerAdapterorg.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\ org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\ org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\ org.springframework.web.servlet.function.support.HandlerFunctionAdapterorg.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\ org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\ org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolverorg.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslatororg.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolverorg.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager (1) initHandlerMappings方法,就是初始化我们的handlerMapping(请求映射器)。 (2) handlerMapping的主要作用是,找到请求路径对应的controller的方法。 例如:请求的路径 “/index”,然后这个handlerMapping,在初始化的时候,已经将所有controller的请求路径映射保存在一个map集合,当请求过来的时候,就将”/index”作为一个key,从map集合中找到对应的controller的index方法。 (3) 这里初始化handlerMappings ,默认是有两个handlerMappings ,是直接在defaultStrategies配置文件中获取。 (4) 那么defaultStrategies的值是什么时候初始化的呢? 通过查看源码,defaultStrategies这个值,是DispatcherServlet类的静态代码块初始化的。 全世界都知道,当一个类被初始化的时候,会执行该类的static静态代码块的。 请求阶段分析用户的一个请求过来,会由servlet接收到,然后一步一步调用到DispatcherServlet的doService方法。 123456// org.springframework.web.servlet.DispatcherServlet#doService@Overrideprotected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { // 核心方法(重点) doDispatch(request, response);} 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879// org.springframework.web.servlet.DispatcherServlet#doDispatchprotected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; // 异步编程 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { // 定义变量,哈哈哈,好熟悉呀 ModelAndView mv = null; Exception dispatchException = null; try { //检查请求中是否有文件上传操作 processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // Determine handler for the current request. // 确定当前请求的处理程序(重点),推断controller和handler的类型, // 进到这里的getHandler方法 mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } // Determine handler adapter for the current request. //推断适配器,不同的controller类型,交给不同的适配器去处理 //如果是一个bean,mappedHandler.getHandler()返回的是一个对象 //如果是一个method,mappedHandler.getHandler()返回的是一个方法 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); //到这里,spring才确定我要怎么反射调用 // Process last-modified header, if supported by the handler. String method = request.getMethod(); boolean isGet = HttpMethod.GET.matches(method); if (isGet || HttpMethod.HEAD.matches(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } // 前置拦截器处理 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } //通过适配器,处理请求(可以理解为,反射调用方法)(重点) // Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } applyDefaultViewName(processedRequest, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } catch (Throwable err) { // As of 4.3, we're processing Errors thrown from handler methods as well, // making them available for @ExceptionHandler methods and other scenarios. dispatchException = new NestedServletException("Handler dispatch failed", err); } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err)); } } 通过对DispatcherServlet的分析,得到请求的核心处理方法是doDispatch(), 主要是分了几步: (1) 检查请求中是否有文件上传操作 (2) 确定当前请求的处理的handler(重点) (3) 推断适配器,不同的controller类型,交给不同的适配器去处理 (4) 执行前置拦截器处理interceptor (5) 通过找到的HandlerAdapter ,反射执行相关的业务代码controller的方法。 (6) 返回结果。 123456789101112131415161718// org.springframework.web.servlet.DispatcherServlet#getHandler@Nullableprotected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { //循环所有的HandlerMappings //this.handlerMappings这个是什么时候初始化的?(重点) //在handlerMappings初始化的时候 for (HandlerMapping mapping : this.handlerMappings) { //把请求传过去看能不能得到一个handler //注意:怎么得到handler和handlerMapping自己实现的逻辑有关系 HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } return null;} 12345678910111213141516171819202122232425262728293031323334353637383940414243444546// org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler@Override@Nullablepublic final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { //获取handler(重点) Object handler = getHandlerInternal(request); if (handler == null) { handler = getDefaultHandler(); } if (handler == null) { return null; } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } // Ensure presence of cached lookupPath for interceptors and others if (!ServletRequestPathUtils.hasCachedPath(request)) { initLookupPath(request); } HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); if (logger.isTraceEnabled()) { logger.trace("Mapped to " + handler); } else if (logger.isDebugEnabled() && !DispatcherType.ASYNC.equals(request.getDispatcherType())) { logger.debug("Mapped to " + executionChain.getHandler()); } if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) { CorsConfiguration config = getCorsConfiguration(handler, request); if (getCorsConfigurationSource() != null) { CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request); config = (globalConfig != null ? globalConfig.combine(config) : config); } if (config != null) { config.validateAllowCredentials(); } executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain;} (1) getHandler()方法,主要是遍历在DispatcherServlet初始化是,初始化的handlerMappings。 (2) 这个方法的主要思想是,通过request的路径,去匹配对应的controller去处理。 (3) SpringMVC自己自带了2个HandlerMapping 来供我们选择 至于 为什么要有2个呢? 两种注册Controller的方式我们用2种方式来注册Controller 分别是: (1) 作为Bean的形式:实现Controller接口,重写handleRequest方法,请求路径为”/test” 123456789@Component("/test")public class TesrController implements org.springframework.web.servlet.mvc.Controller{ @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { System.out.println("1"); return null; }} (2) 以Annotation形式: 12345678@Controllerpublic class AnnotationController { @RequestMapping("/test2") public Object test(){ System.out.println("test"); return null; }} 经过测试: (1)可以得到以Bean方式的controller,是通过BeanNameUrlHandlerMapping去匹配 (2)以注解方法的controller,是通过RequestMappingHandlerMapping去匹配 BeanNameUrlHandlerMappingBeanNameUrlHandlerMapping处理bean方式的源码分析: 12345678910111213141516171819202122232425262728293031323334353637// org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#getHandlerInternal @Override @Nullable protected Object getHandlerInternal(HttpServletRequest request) throws Exception { // 获取请求的路径 String lookupPath = initLookupPath(request); // 到对应的handler(重点)调用 lookupHandler() Object handler; if (usesPathPatterns()) { RequestPath path = ServletRequestPathUtils.getParsedRequestPath(request); handler = lookupHandler(path, lookupPath, request); } else { handler = lookupHandler(lookupPath, request); } if (handler == null) { // We need to care for the default handler directly, since we need to // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well. Object rawHandler = null; if (StringUtils.matchesCharacter(lookupPath, '/')) { rawHandler = getRootHandler(); } if (rawHandler == null) { rawHandler = getDefaultHandler(); } if (rawHandler != null) { // Bean name or resolved handler? if (rawHandler instanceof String) { String handlerName = (String) rawHandler; rawHandler = obtainApplicationContext().getBean(handlerName); } validateHandler(rawHandler, request); handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null); } } return handler; } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869// org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#lookupHandler(java.lang.String, javax.servlet.http.HttpServletRequest)@Nullable protected Object lookupHandler(String lookupPath, HttpServletRequest request) throws Exception { // 查看这里的方法 Object handler = getDirectMatch(lookupPath, request); if (handler != null) { return handler; } // Pattern match? List<String> matchingPatterns = new ArrayList<>(); for (String registeredPattern : this.handlerMap.keySet()) { if (getPathMatcher().match(registeredPattern, lookupPath)) { matchingPatterns.add(registeredPattern); } else if (useTrailingSlashMatch()) { if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", lookupPath)) { matchingPatterns.add(registeredPattern + "/"); } } } String bestMatch = null; Comparator<String> patternComparator = getPathMatcher().getPatternComparator(lookupPath); if (!matchingPatterns.isEmpty()) { matchingPatterns.sort(patternComparator); if (logger.isTraceEnabled() && matchingPatterns.size() > 1) { logger.trace("Matching patterns " + matchingPatterns); } bestMatch = matchingPatterns.get(0); } if (bestMatch != null) { handler = this.handlerMap.get(bestMatch); if (handler == null) { if (bestMatch.endsWith("/")) { handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1)); } if (handler == null) { throw new IllegalStateException( "Could not find handler for best pattern match [" + bestMatch + "]"); } } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } validateHandler(handler, request); String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, lookupPath); // There might be multiple 'best patterns', let's make sure we have the correct URI template variables // for all of them Map<String, String> uriTemplateVariables = new LinkedHashMap<>(); for (String matchingPattern : matchingPatterns) { if (patternComparator.compare(bestMatch, matchingPattern) == 0) { Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, lookupPath); Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars); uriTemplateVariables.putAll(decodedVars); } } if (logger.isTraceEnabled() && uriTemplateVariables.size() > 0) { logger.trace("URI variables " + uriTemplateVariables); } return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables); } // No handler found... return null; } 1234567891011121314151617// org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#getDirectMatch@Nullable private Object getDirectMatch(String urlPath, HttpServletRequest request) throws Exception { // 通过请求的路径,在handlerMap中去匹配。 // handlerMap这个值,什么时候填充值?在init初始化的时候,就已经存放在这个handlerMap种 Object handler = this.handlerMap.get(urlPath); if (handler != null) { // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } validateHandler(handler, request); return buildPathExposingHandler(handler, urlPath, urlPath, null); } return null; } (1) 以Bean方式的controller,匹配请求的路径,是通过一个handlerMap去匹配,比较简单。 (2) 这里的问题是,这个handlerMap的值,是什么时候放进去的? 通过源码分析,BeanNameUrlHandlerMapping是实现了ApplicationContextAware接口。 如果你精通spring的源码,就知道spring的实例bean的时候,会回调这些类的setApplicationContext()方法。 12345678910111213141516171819202122232425262728// org.springframework.context.support.ApplicationObjectSupport#setApplicationContext@Override public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException { if (context == null && !isContextRequired()) { // Reset internal context state. this.applicationContext = null; this.messageSourceAccessor = null; } else if (this.applicationContext == null) { // Initialize with passed-in context. if (!requiredContextClass().isInstance(context)) { throw new ApplicationContextException( "Invalid application context: needs to be of type [" + requiredContextClass().getName() + "]"); } this.applicationContext = context; this.messageSourceAccessor = new MessageSourceAccessor(context); // 初始化ApplicationContext,就会执行到子类的方法(重点) initApplicationContext(context); } else { // Ignore reinitialization if same context passed in. if (this.applicationContext != context) { throw new ApplicationContextException( "Cannot reinitialize with different application context: current one is [" + this.applicationContext + "], passed-in one is [" + context + "]"); } } } 123456789// 没看懂怎么走到这里来呢// org.springframework.web.servlet.handler.AbstractDetectingUrlHandlerMapping#initApplicationContext @Override public void initApplicationContext() throws ApplicationContextException { super.initApplicationContext(); // 检测出handler detectHandlers(); } 12345678910111213141516171819// org.springframework.web.servlet.handler.AbstractDetectingUrlHandlerMapping#detectHandlers protected void detectHandlers() throws BeansException { // 获取spring ioc所有的beanName,然后判断beanName,那些是以 "/" 开头 ApplicationContext applicationContext = obtainApplicationContext(); String[] beanNames = (this.detectHandlersInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) : applicationContext.getBeanNamesForType(Object.class)); // Take any bean name that we can determine URLs for. for (String beanName : beanNames) { // 然后判断beanName,那些是以 "/" 开头 String[] urls = determineUrlsForHandler(beanName); if (!ObjectUtils.isEmpty(urls)) { // 注册handler(重点) // URL paths found: Let's consider it a handler. registerHandler(urls, beanName); } } } 1234567// org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#registerHandler(java.lang.String[], java.lang.String) protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException { Assert.notNull(urlPaths, "URL path array must not be null"); for (String urlPath : urlPaths) { registerHandler(urlPath, beanName); } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748// org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#registerHandler(java.lang.String, java.lang.Object)protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException { Assert.notNull(urlPath, "URL path must not be null"); Assert.notNull(handler, "Handler object must not be null"); Object resolvedHandler = handler; // Eagerly resolve handler if referencing singleton via name. if (!this.lazyInitHandlers && handler instanceof String) { String handlerName = (String) handler; ApplicationContext applicationContext = obtainApplicationContext(); if (applicationContext.isSingleton(handlerName)) { resolvedHandler = applicationContext.getBean(handlerName); } } Object mappedHandler = this.handlerMap.get(urlPath); if (mappedHandler != null) { if (mappedHandler != resolvedHandler) { throw new IllegalStateException( "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath + "]: There is already " + getHandlerDescription(mappedHandler) + " mapped."); } } else { if (urlPath.equals("/")) { if (logger.isTraceEnabled()) { logger.trace("Root mapping to " + getHandlerDescription(handler)); } setRootHandler(resolvedHandler); } else if (urlPath.equals("/*")) { if (logger.isTraceEnabled()) { logger.trace("Default mapping to " + getHandlerDescription(handler)); } setDefaultHandler(resolvedHandler); } else { // 最终put到map集合中(省略其他无关代码) this.handlerMap.put(urlPath, resolvedHandler); if (getPatternParser() != null) { this.pathPatternHandlerMap.put(getPatternParser().parse(urlPath), resolvedHandler); } if (logger.isTraceEnabled()) { logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler)); } } } } BeanNameUrlHandlerMapping处理bean方式的源码分析,其实是很简单: (1) 在类初始化的时候,就已经将所有实现了Controller接口的controller类,拿到他们的@Componet(‘/test’) (2) 然后将’/test’这个作为key,controller类作为value,放入到一个map集合。 (3) 当一个请求过来的时候,拿到这个请求的uri,在map里面找,找到了即表示匹配上 RequestMappingHandlerMapping处理注解方式的源码分析: 1234567891011121314151617// org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal// 对于RequestMappingHandlerMapping,indexController.index(),方法的请求路径映射 @Override @Nullable protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { // 获取请求路径 String lookupPath = initLookupPath(request); this.mappingRegistry.acquireReadLock(); try { // 通过请求路径,获取handler HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { this.mappingRegistry.releaseReadLock(); } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748// org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethod@Nullable protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<>(); // 从mappingRegistry的urlLookup,匹配请求路径 List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath); if (directPathMatches != null) { addMatchingMappings(directPathMatches, matches, request); } if (matches.isEmpty()) { addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request); } if (!matches.isEmpty()) { Match bestMatch = matches.get(0); if (matches.size() > 1) { Comparator<Match> comparator = new MatchComparator(getMappingComparator(request)); matches.sort(comparator); bestMatch = matches.get(0); if (logger.isTraceEnabled()) { logger.trace(matches.size() + " matching mappings: " + matches); } if (CorsUtils.isPreFlightRequest(request)) { for (Match match : matches) { if (match.hasCorsConfig()) { return PREFLIGHT_AMBIGUOUS_MATCH; } } } else { Match secondBestMatch = matches.get(1); if (comparator.compare(bestMatch, secondBestMatch) == 0) { Method m1 = bestMatch.getHandlerMethod().getMethod(); Method m2 = secondBestMatch.getHandlerMethod().getMethod(); String uri = request.getRequestURI(); throw new IllegalStateException( "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}"); } } } request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod()); handleMatch(bestMatch.mapping, lookupPath, request); // 返回handler return bestMatch.getHandlerMethod(); } else { return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request); } } 12345// 3.AbstractHandlerMethodMapping.MappingRegistry#getMappingsByDirectPath@Nullablepublic List<T> getMappingsByDirectPath(String urlPath) { return this.pathLookup.get(urlPath);} RequestMappingHandlerMapping处理注解方式的源码分析,比较复杂,用一个MappingRegistry维护所有的请求路径映射。 MappingRegistry的初始化,也是在该bean实例化的时候,就已经做好的了。 原理也是和上一个差不多,都是从一个map集合里面匹配。所以这里就不再做解析了 总结:getHandler() 找适配器 123456789101112// org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { if (this.handlerAdapters != null) { for (HandlerAdapter adapter : this.handlerAdapters) { if (adapter.supports(handler)) { return adapter; } } } throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler"); } 其实能看见他是从一个handlerAdapters属性里面遍历了我们的适配器 这个handlerAdapters哪来的呢? 跟我们的HandlerMappings一样 在他的配置文件里面有写,就是我们刚刚所说的 。 至于什么是适配器,我们结合Handler来讲, 就如我们在最开始的总结时所说的, 一开始只是找到了Handler 现在要执行了,但是有个问题,Handler不止一个, 自然而然对应的执行方式就不同了, 这时候适配器的概念就出来了:对应不同的Handler的执行方案。当找到合适的适配器的时候, 基本上就已经收尾了,因为后面在做了一些判断之后(判断请求类型之类的),就开始执行了你的Handler了,上代码: mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 这个mv就是我们的ModlAndView 其实执行完这一行 我们的Controller的逻辑已经执行完了, 剩下的就是寻找视图 渲染图的事情了。 总结: 其实我们的SpringMVC关键的概念就在于Handler(处理器) 和Adapter(适配器) 通过一个关键的HandlerMappings 找到合适处理你的Controller的Handler 然后再通过HandlerAdapters找到一个合适的HandlerAdapter 来执行Handler即Controller里面的逻辑。 最后再返回ModlAndView… 参考:https://juejin.cn/post/6991290858880368676 Spring事务的传播 参考:https://segmentfault.com/a/1190000013341344 传播等级 描述 理解 REQUIRED 默认的事务传播级别表示如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。 A有事务,B就跟着用A没有事务,B就开启自己的事务,只B方法用 SUPPORTS 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。 MANDATORY 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。 REQUIRES_NEW 表示创建一个新的事务如果当前存在事务,则把当前事务挂起。也就是说不管外部方法是否开启事务,Propagation.REQUIRES_NEW 修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。 NOT_SUPPORTED 以非事务方式运行,如果当前存在事务,则把当前事务挂起。 NEVER 以非事务方式运行,如果当前存在事务,则抛出异常。 NESTED 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于REQUIRED。 默认数据是anthony和0 REQUIRED A方法有事务,A方法报错,有一个报错都会回滚,结果是:anthony和0 1234567891011121314151617@PostMapping("/test1")@Transactionalpublic void methodA(){ Test byId = testService.getById(1); byId.setUsername("anthony2"); byId.updateById(); transactionalController.methodB(); int i = 1 / 0;}@Transactional(propagation= Propagation.REQUIRED)public void methodB(){ Test byId = testService.getById(1); byId.setParentId(1); byId.updateById();} A方法没有事务,A方法有报错,结果是:anthony2和1 B方法自己开启事务,就不管A事务了,所以A方法,就算报错了,也成功写入数据库,B事务没有报错,也成功写入数据库 12345678910111213141516@PostMapping("/test1")public void methodA(){ Test byId = testService.getById(1); byId.setUsername("anthony2"); byId.updateById(); transactionalController.methodB(); int i = 1 / 0;}@Transactional(propagation= Propagation.REQUIRED)public void methodB(){ Test byId = testService.getById(1); byId.setParentId(1); byId.updateById();} A方法没有事务,A方法和B方法都报错,,结果是:anthony2和0 A方法没有事务,所以A方法插入数据库成功,就算报错,也没有回滚 B方法自己开始事务,B方法报错,所以回滚 1234567891011121314151617@PostMapping("/test1")public void methodA(){ Test byId = testService.getById(1); byId.setUsername("anthony2"); byId.updateById(); transactionalController.methodB(); int i = 1 / 0;}@Transactional(propagation= Propagation.REQUIRED)public void methodB(){ Test byId = testService.getById(1); byId.setParentId(1); byId.updateById(); int i = 1 / 0;} SUPPORTS A方法有事务,A方法报错,都回滚,,结果是:anthony和0 1234567891011121314151617@PostMapping("/test1")@Transactionalpublic void methodA(){ Test byId = testService.getById(1); byId.setUsername("anthony2"); byId.updateById(); transactionalController.methodB(); int i = 1 / 0;}@Transactional(propagation= Propagation.SUPPORTS)public void methodB(){ Test byId = testService.getById(1); byId.setParentId(1); byId.updateById();} A没有事务,,A方法报错,都没有回滚,结果是:anthony2和1 12345678910111213141516@PostMapping("/test1")public void methodA(){ Test byId = testService.getById(1); byId.setUsername("anthony2"); byId.updateById(); transactionalController.methodB(); int i = 1 / 0;}@Transactional(propagation= Propagation.SUPPORTS)public void methodB(){ Test byId = testService.getById(1); byId.setParentId(1); byId.updateById();} A没有事务,,A,B方法都报错,都没有回滚,结果是:anthony2和1 1234567891011121314151617@PostMapping("/test1")public void methodA(){ Test byId = testService.getById(1); byId.setUsername("anthony2"); byId.updateById(); transactionalController.methodB(); int i = 1 / 0;}@Transactional(propagation= Propagation.SUPPORTS)public void methodB(){ Test byId = testService.getById(1); byId.setParentId(1); byId.updateById(); int i = 1 / 0;} MANDATORY A有事务,A报错,都回滚,结果是:anthony和0 1234567891011121314151617@PostMapping("/test1")@Transactionalpublic void methodA(){ Test byId = testService.getById(1); byId.setUsername("anthony2"); byId.updateById(); transactionalController.methodB(); int i = 1 / 0;}@Transactional(propagation= Propagation.MANDATORY)public void methodB(){ Test byId = testService.getById(1); byId.setParentId(1); byId.updateById();} A没有事务,运行报错了 12345678910111213141516@PostMapping("/test1")public void methodA(){ Test byId = testService.getById(1); byId.setUsername("anthony2"); byId.updateById(); transactionalController.methodB(); int i = 1 / 0;}@Transactional(propagation= Propagation.MANDATORY)public void methodB(){ Test byId = testService.getById(1); byId.setParentId(1); byId.updateById();} REQUIRES_NEW 测试的时候,不要操作同一条数据,容易超时….. A开始事务,B也开始事务,B报错了,B回滚,A插入成功 1234567891011121314151617@PostMapping("/test1")@Transactionalpublic void methodA(){ Test byId = testService.getById(1); byId.setUsername("anthony2"); byId.updateById(); transactionalController.methodB(); int i = 1 / 0;}@Transactional(propagation= Propagation.REQUIRES_NEW)public void methodB(){ Test byId = testService.getById(2); byId.setParentId(1); byId.updateById();} 这样没有复现出问题 12345678910111213141516@PostMapping("/test1")@Transactionalpublic void methodA(){ Test byId = testService.getById(1); byId.setUsername("anthony2"); byId.updateById(); transactionalController.methodB();}@Transactional(propagation= Propagation.REQUIRES_NEW)public void methodB(){ Test byId = testService.getById(2); byId.setParentId(1); byId.updateById(); int i = 1 / 0;} A没有事务,B有事务 A报错,没有回滚,B插入数据成功 外围方法异常,不影响内部调用的方法 12345678910111213141516@PostMapping("/test1")public void methodA(){ Test byId = testService.getById(1); byId.setUsername("anthony2"); byId.updateById(); transactionalController.methodB(); int i = 1 / 0;}@Transactional(propagation= Propagation.REQUIRES_NEW)public void methodB(){ Test byId = testService.getById(2); byId.setParentId(1); byId.updateById();} A没有事务,B有事务 A插入数据成功,B回滚 内部调用的方法,不影响外围的方法成功插入 123456789101112131415@PostMapping("/test1")public void methodA(){ Test byId = testService.getById(1); byId.setUsername("anthony2"); byId.updateById(); transactionalController.methodB();}@Transactional(propagation= Propagation.REQUIRES_NEW)public void methodB(){ Test byId = testService.getById(2); byId.setParentId(1); byId.updateById(); int i = 1 / 0;} NOT_SUPPORTED A有事务,B也有事务,A回滚了,B报错了,没有回滚 12345678910111213141516171819@PostMapping("/test1")@Transactionalpublic void methodA(){ Test byId = testService.getById(1); byId.setUsername("anthony2"); byId.updateById(); transactionalController.methodB();}@Transactional(propagation= Propagation.NOT_SUPPORTED)public void methodB(){ Test byId = testService.getById(2); byId.setParentId(1); byId.updateById(); int i = 1 / 0;} A有事务,B也有事务,A回滚了,B没有回滚 1234567891011121314151617181920@PostMapping("/test1")@Transactionalpublic void methodA(){ Test byId = testService.getById(1); byId.setUsername("anthony2"); byId.updateById(); transactionalController.methodB(); int i = 1 / 0;}@Transactional(propagation= Propagation.NOT_SUPPORTED)public void methodB(){ Test byId = testService.getById(2); byId.setParentId(1); byId.updateById(); int i = 1 / 0;} NEVER 直接报错 123456789101112131415@PostMapping("/test1")@Transactionalpublic void methodA(){ Test byId = testService.getById(1); byId.setUsername("anthony2"); byId.updateById(); transactionalController.methodB();}@Transactional(propagation= Propagation.NEVER)public void methodB(){ Test byId = testService.getById(2); byId.setParentId(1); byId.updateById();} NESTED 全部提交成功 123456789101112131415@PostMapping("/test1")@Transactionalpublic void methodA(){ Test byId = testService.getById(1); byId.setUsername("anthony2"); byId.updateById(); transactionalController.methodB();}@Transactional(propagation= Propagation.NESTED)public void methodB(){ Test byId = testService.getById(2); byId.setParentId(1); byId.updateById();} 全部失败 12345678910111213141516@PostMapping("/test1")@Transactionalpublic void methodA(){ Test byId = testService.getById(1); byId.setUsername("anthony2"); byId.updateById(); transactionalController.methodB();}@Transactional(propagation= Propagation.NESTED)public void methodB(){ Test byId = testService.getById(2); byId.setParentId(1); byId.updateById(); int i = 1 / 0;} A没有事务,B有事务 A执行成功,B回滚成功 123456789101112131415@PostMapping("/test1")public void methodA(){ Test byId = testService.getById(1); byId.setUsername("anthony2"); byId.updateById(); transactionalController.methodB();}@Transactional(propagation= Propagation.NESTED)public void methodB(){ Test byId = testService.getById(2); byId.setParentId(1); byId.updateById(); int i = 1 / 0;} 拦截器和过滤器1、过滤器和拦截器触发时机不一样,先拦截器,后过滤器 2、拦截器可以获取IOC容器中的各个bean,而过滤器就不行,因为拦截器是spring提供并管理的,spring的功能可以被拦截器使用,在拦截器里注入一个service,可以调用业务逻辑。而过滤器是JavaEE标准,只需依赖servlet api ,不需要依赖spring。 3、过滤器的实现基于回调函数。而拦截器(代理模式)的实现基于反射 4、过滤器是依赖于Servlet容器,属于Servlet规范的一部分,而拦截器则是独立存在的,可以在任何情况下使用。 5、Filter的执行由Servlet容器回调完成,而拦截器通常通**过动态代理(反射)**的方式来执行。 6、Filter的生命周期由Servlet容器管理,而拦截器则可以通过IoC容器来管理,因此可以通过注入等方式来获取其他Bean的实例,因此使用会更方便。 7、过滤器只能在请求的前后使用,而拦截器可以详细到每个方法 Spring IOCSpring提供了两种容器:BeanFactory和ApplicationContext **BeanFactory:**基本的IoC容器,默认采用延迟初始化策略(lazy-load),即只有当客户端对象需要访问容器中某个bean对象的时候,才会对该bean对象进行初始化以及依赖注入操作。所以BeanFactory容器的特点是启动初期速度快,所需资源有限,适合于资源有限功能要求不严格的场景。 ApplicationContext: ApplicationContext在BeanFactory基础上构建,支持其他的高级特性,如国际化,事件发布等。相对于BeanFactory容器来说,ApplicationContext在启动的时候即完成资源的初始化,所以启动时间较长,适合于系统资源充足,需要更多功能的场景 Spring BeanJava 中Bean的定义: 类中所有的属性都必须封装,即:使用private声明;这个不太确定 封装的属性如果需要被外部所操作,则必须编写对应的setter、getter方法; 一个JavaBean中至少存在一个无参构造方法。 12345678910111213141516171819202122public class Staff{ private String name; private int age; public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public int getAge() { return age; }} 而Spring IoC容器就是管理bean的工厂。Spring中bean 是一个被实例化,组装,并通过 Spring IoC 容器所管理的对象。这些 bean 是由用容器提供的配置元数据创建的。Spring可以采用XML配置文件的方式来管理和配置Bean信息,如下: 12345678<?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="user" class="com.wgs.spring.bean.User"></bean></beans> <beans>是XML配置文件中根节点,下面可包含多个<bean>子节点。Spring的XML配置文件中的配置与<bean>元素是一一对应的。 属性 描述 id 注册到容器的对象都有一个唯一的id值,如id=”user” name bean的别名,name可以使用逗号、空格或冒号等分割指定多个name,而id就不可以 scope 作用域 constructor-arg 用来注入依赖关系 properties 用来注入依赖关系 autowiring mode 用来注入依赖关系 lazy-initialization mode 是否延迟加载 initialization method bean被创建的时候,初始化的的方法 destruction method 销毁指定的方法 Spring Bean 生命周期2.低昂registerBeanFactoryPostProcessor 完成扫描,运行之前,不会有我们自己的类,除了@componentScan这个注解的这个类,等完成之后,就会有我们自己的类 1:实例化一个ApplicationContext的对象;2:调用bean工厂后置处理器完成扫描;3:循环解析扫描出来的类信息;4:实例化一个BeanDefinition对象来存储解析出来的信息;5:把实例化好的beanDefinition对象put到beanDefinitionMap当中缓存起来,以便后面实例化bean;6:再次调用bean工厂后置处理器;7:当然spring还会干很多事情,比如国际化,比如注册BeanPostProcessor等等,如果我们只关心如何实例化一个bean的话那么这一步就是spring调用finishBeanFactoryInitialization方法来实例化单例的bean,实例化之前spring要做验证,需要遍历所有扫描出来的类,依次判断这个bean是否Lazy,是否prototype,是否abstract等等;8:如果验证完成spring在实例化一个bean之前需要推断构造方法,因为spring实例化对象是通过构造方法反射,故而需要知道用哪个构造方法;9:推断完构造方法之后spring调用构造方法反射实例化一个对象;注意我这里说的是对象、对象、对象;这个时候对象已经实例化出来了,但是并不是一个完整的bean,最简单的体现是这个时候实例化出来的对象属性是没有注入,所以不是一个完整的bean;10:spring处理合并后的beanDefinition(合并?是spring当中非常重要的一块内容,后面的文章我会分析);11:判断是否支持循环依赖,如果支持则提前把一个工厂存入singletonFactories——map;12:判断是否需要完成属性注入13:如果需要完成属性注入,则开始注入属性14:判断bean的类型回调Aware接口15:调用生命周期回调方法16:如果需要代理则完成代理17:put到单例池——bean完成——存在spring容器当中 Spring Bean 循环依赖https://juejin.im/post/6844904166351978504#h5 AnnotationConfigApplicationContext#AnnotationConfigApplicationContext 123456public AnnotationConfigApplicationContext(Class<?>... componentClasses) { this(); register(componentClasses); // 关键方法 refresh();} org.springframework.context.support.AbstractApplicationContext#refresh 1234567891011121314151617181920212223242526272829303132333435363738394041424344@Overridepublic void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. // 完成所有的扫描 initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. // 实例化所有没有延迟的单例类 finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } }} org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization 1234567protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // .... // Instantiate all remaining (non-lazy-init) singletons. // 实例化所有单例,非lazy beanFactory.preInstantiateSingletons();} org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons 12345678910111213141516171819202122232425262728293031323334353637383940414243@Overridepublic void preInstantiateSingletons() throws BeansException { if (logger.isTraceEnabled()) { logger.trace("Pre-instantiating singletons in " + this); } // Iterate over a copy to allow for init methods which in turn register new bean definitions. // While this may not be part of the regular factory bootstrap, it does otherwise work fine. List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); // Trigger initialization of all non-lazy singleton beans... for (String beanName : beanNames) { RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); // 验证,判断这个是是不是抽象的和是不是单例的和是不是延迟加载的 if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { if (isFactoryBean(beanName)) { Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); if (bean instanceof FactoryBean) { FactoryBean<?> factory = (FactoryBean<?>) bean; boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged( (PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit, getAccessControlContext()); } else { isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit()); } if (isEagerInit) { getBean(beanName); } } } else { // 验证一切都通过的类,开始实例化普通的bean,还不是spring bean getBean(beanName); } } } // ....} org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String) 1234@Overridepublic Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false);} org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean 这里面大部分都是验证,比如depenon,或者import 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161protected <T> T doGetBean( String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // 理解bean的名字是否非法 String beanName = transformedBeanName(name); Object bean; // Eagerly check singleton cache for manually registered singletons. // 这里的方法啊 Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { // Fail if we're already creating this bean instance: // We're assumably within a circular reference. // 判断这个类是不是在创建过程中,循环依赖的时候要用 if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } // Check if bean definition exists in this factory. BeanFactory parentBeanFactory = getParentBeanFactory(); if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { // 方法注入 // Not found -> check parent. String nameToLookup = originalBeanName(name); if (parentBeanFactory instanceof AbstractBeanFactory) { return ((AbstractBeanFactory) parentBeanFactory).doGetBean( nameToLookup, requiredType, args, typeCheckOnly); } else if (args != null) { // Delegation to parent with explicit args. return (T) parentBeanFactory.getBean(nameToLookup, args); } else if (requiredType != null) { // No args -> delegate to standard getBean method. return parentBeanFactory.getBean(nameToLookup, requiredType); } else { return (T) parentBeanFactory.getBean(nameToLookup); } } if (!typeCheckOnly) { markBeanAsCreated(beanName); } try { RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args); // Guarantee initialization of beans that the current bean depends on. String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } registerDependentBean(dep, beanName); try { getBean(dep); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex); } } } // Create bean instance. // 判断类是不是单例 if (mbd.isSingleton()) { // getSingleton(String,facotory) 这个方法里有正在创建中的标识设置 sharedInstance = getSingleton(beanName, () -> { try { // 完成了目标对象的创建 // 如果需要代理,还创建了代理 return createBean(beanName, mbd, args); } catch (BeansException ex) { // Explicitly remove instance from singleton cache: It might have been put there // eagerly by the creation process, to allow for circular reference resolution. // Also remove any beans that received a temporary reference to the bean. destroySingleton(beanName); throw ex; } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } else if (mbd.isPrototype()) { // It's a prototype -> create a new instance. Object prototypeInstance = null; try { beforePrototypeCreation(beanName); prototypeInstance = createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } else { String scopeName = mbd.getScope(); if (!StringUtils.hasLength(scopeName)) { throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'"); } Scope scope = this.scopes.get(scopeName); if (scope == null) { throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'"); } try { Object scopedInstance = scope.get(beanName, () -> { beforePrototypeCreation(beanName); try { return createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } }); bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider " + "defining a scoped proxy for this bean if you intend to refer to it from a singleton", ex); } } } catch (BeansException ex) { cleanupAfterBeanCreationFailure(beanName); throw ex; } } // Check if required type matches the type of the actual bean instance. if (requiredType != null && !requiredType.isInstance(bean)) { try { T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType); if (convertedBean == null) { throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } return convertedBean; } catch (TypeMismatchException ex) { if (logger.isTraceEnabled()) { logger.trace("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", ex); } throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } } return (T) bean;} org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String) 12345678910111213141516171819202122232425262728293031323334// 上个代码块第七行调用的@Override@Nullablepublic Object getSingleton(String beanName) { return getSingleton(beanName, true);}/** Cache of singleton objects: bean name to bean instance. *//** 缓存单例对象: bean name to bean instance. */private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);@Nullableprotected Object getSingleton(String beanName, boolean allowEarlyReference) { // 初始化的时候这里肯定是null,但是在初始化完成之后,再调用getBean就肯定不是null // isSingletonCurrentlyInCreation 这个方法很重要,说明对象是不是正在创建 // singletonFactories 也很重要 Object singletonObject = this.singletonObjects.get(beanName); // 判断循环依赖的时候 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject;} org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859@Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { if (logger.isTraceEnabled()) { logger.trace("Creating instance of bean '" + beanName + "'"); } RootBeanDefinition mbdToUse = mbd; // Make sure bean class is actually resolved at this point, and // clone the bean definition in case of a dynamically resolved Class // which cannot be stored in the shared merged bean definition. // 从beanDefinition对象中获取出来bean的类型 Class<?> resolvedClass = resolveBeanClass(mbd, beanName); if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) { mbdToUse = new RootBeanDefinition(mbd); mbdToUse.setBeanClass(resolvedClass); } // Prepare method overrides. try { mbdToUse.prepareMethodOverrides(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(), beanName, "Validation of method overrides failed", ex); } try { // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. // 第一次调用个后置处理器 Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; } } catch (Throwable ex) { throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "BeanPostProcessor before instantiation of bean failed", ex); } try { // 调用方法 Object beanInstance = doCreateBean(beanName, mbdToUse, args); if (logger.isTraceEnabled()) { logger.trace("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { // A previously detected exception with proper bean creation context already, // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry. throw ex; } catch (Throwable ex) { throw new BeanCreationException( mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex); } } org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // Instantiate the bean. BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { // 实例化对象,里面第二次调调用后置处理器 // 反射调用对象的构造方法 // 这里java对象就已经有了 instanceWrapper = createBeanInstance(beanName, mbd, args); } Object bean = instanceWrapper.getWrappedInstance(); Class<?> beanType = instanceWrapper.getWrappedClass(); if (beanType != NullBean.class) { mbd.resolvedTargetType = beanType; } // Allow post-processors to modify the merged bean definition. synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { try { // 第三次调用后置处理器 applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } mbd.postProcessed = true; } } // Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. // 判断是否需要循环依赖 boolean earlySingletonExposure = // 到这里了,也肯定是true (mbd.isSingleton() && // 默认值是true this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { // 第四次调用后置处理器,判断是否需要AOP addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // Initialize the bean instance. Object exposedObject = bean; try { // 填充属性,也就是我们说的自动注入 // 里面会完成第五次和第六次后置处理器的调用 // 看这里 populateBean(beanName, mbd, instanceWrapper); // 初始化spring // 里面会进行第七次和第八次后置处理的调用个 exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) { throw (BeanCreationException) ex; } else { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); } } if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); for (String dependentBean : dependentBeans) { if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } // 省略代码 } } } // Register bean as disposable. try { registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject;} org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { // Make sure bean class is actually resolved at this point. Class<?> beanClass = resolveBeanClass(mbd, beanName); if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Bean class isn't public, and non-public access not allowed: " + beanClass.getName()); } Supplier<?> instanceSupplier = mbd.getInstanceSupplier(); if (instanceSupplier != null) { return obtainFromSupplier(instanceSupplier, beanName); } if (mbd.getFactoryMethodName() != null) { return instantiateUsingFactoryMethod(beanName, mbd, args); } // Shortcut when re-creating the same bean... boolean resolved = false; boolean autowireNecessary = false; if (args == null) { synchronized (mbd.constructorArgumentLock) { if (mbd.resolvedConstructorOrFactoryMethod != null) { resolved = true; autowireNecessary = mbd.constructorArgumentsResolved; } } } if (resolved) { if (autowireNecessary) { return autowireConstructor(beanName, mbd, null, null); } else { return instantiateBean(beanName, mbd); } } // Candidate constructors for autowiring? // 第二次调用后置处理器构造方法,通过反射实例化对象,这时候构造方法里有打印,就会打印出日志 Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { return autowireConstructor(beanName, mbd, ctors, args); } // Preferred constructors for default construction? ctors = mbd.getPreferredConstructors(); if (ctors != null) { return autowireConstructor(beanName, mbd, ctors, null); } // No special handling: simply use no-arg constructor. return instantiateBean(beanName, mbd);} org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { if (bw == null) { if (mbd.hasPropertyValues()) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance"); } else { // Skip property population phase for null instance. return; } } // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the // state of the bean before properties are set. This can be used, for example, // to support styles of field injection. if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { return; } } } } PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null); int resolvedAutowireMode = mbd.getResolvedAutowireMode(); if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) { MutablePropertyValues newPvs = new MutablePropertyValues(pvs); // Add property values based on autowire by name if applicable. if (resolvedAutowireMode == AUTOWIRE_BY_NAME) { autowireByName(beanName, mbd, bw, newPvs); } // Add property values based on autowire by type if applicable. if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) { autowireByType(beanName, mbd, bw, newPvs); } pvs = newPvs; } boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors(); boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); PropertyDescriptor[] filteredPds = null; if (hasInstAwareBpps) { if (pvs == null) { pvs = mbd.getPropertyValues(); } for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { // 这里的ibp常用的有两种类型 // 1.@Resouce 使用的是CommonAnnotationBeanPostProcessor // 2.@Autowire 使用的是AutoWireAnnotationBeanPostProcessor InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; // 这里会调用属性的注入,也就是在这里,碰到循环依赖的时候,就会调用个 // org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { if (filteredPds == null) { filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); } pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { return; } } pvs = pvsToUse; } } } if (needsDepCheck) { if (filteredPds == null) { filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); } checkDependencies(beanName, mbd, filteredPds, pvs); } if (pvs != null) { applyPropertyValues(beanName, mbd, bw, pvs); }} org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>) 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(beanName, "Bean name must not be null"); synchronized (this.singletonObjects) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { if (this.singletonsCurrentlyInDestruction) { throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!)"); } if (logger.isDebugEnabled()) { logger.debug("Creating shared instance of singleton bean '" + beanName + "'"); } // 重点,如果没有获取到,就设置个标识,表示正在创建 beforeSingletonCreation(beanName); boolean newSingleton = false; boolean recordSuppressedExceptions = (this.suppressedExceptions == null); if (recordSuppressedExceptions) { this.suppressedExceptions = new LinkedHashSet<>(); } try { singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // Has the singleton object implicitly appeared in the meantime -> // if yes, proceed with it since the exception indicates that state. singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { throw ex; } } catch (BeanCreationException ex) { if (recordSuppressedExceptions) { for (Exception suppressedException : this.suppressedExceptions) { ex.addRelatedCause(suppressedException); } } throw ex; } finally { if (recordSuppressedExceptions) { this.suppressedExceptions = null; } afterSingletonCreation(beanName); } if (newSingleton) { addSingleton(beanName, singletonObject); } } return singletonObject; }} org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#beforeSingletonCreation 12345678910/** Names of beans that are currently in creation. */// 添加到这里来了之后就标识当前这个bean正在创建private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));protected void beforeSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); }} org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory 二级缓存 12345678910protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, "Singleton factory must not be null"); synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { this.singletonFactories.put(beanName, singletonFactory); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } }} 三个缓存12345678// singletonObjects:第一级缓存,里面存放的都是创建好的成品Bean。private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object(256);// earlySingletonObjects : 第二级缓存,里面存放的都是半成品的Beanprivate final Map<String, Object> earlySingletonObjects = new HashMap<String, Object(16);// singletonFactories :第三级缓存, 不同于前两个存的是 Bean对象引用,此缓存存的bean 工厂对象,也就存的是 专门创建Bean的一个工厂对象。此缓存用于解决循环依赖private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16); 两个缓存能解决不A引用创建后,提前暴露到半成品缓存中 依赖B,创建B ,B填充属性时发现依赖A, 先从成品缓存查找,没有,再从半成品缓存查找 取到A的早期引用。 1B顺利走完创建过程`, 将`B的早期引用从半成品缓存移动到成品缓存 B创建完成,A获取到B的引用,继续创建。 A创建完成,将A的早期引用从半成品缓存移动到成品缓存 为啥需要三个缓存上面两个缓存的地方,我们只是没有考虑代理的情况。 Bean在创建的最后阶段,会检查是否需要创建代理,如果创建了代理,那么最终返回的就是代理实例的引用。我们通过beanname获取到最终是代理实例的引用 也就是说:假设A最终会创建代理,提前暴露A的引用, B填充属性时填充的是A的原始对象引用。A最终放入成品库里是代理的引用。那么B中依然是A的早期引用。这种结果最终会与我们的期望的大相径庭了。 完整的流程关键点: A绑定到ObjectFactory 注册到工厂缓存singletonFactory中, B在填充A时,先查成品缓存有没有,再查半成品缓存有没有,最后看工厂缓存有没有单例工厂类,有A的ObjectFactory。调用getObject ,执行扩展逻辑,可能返回的代理引用,也可能返回原始引用。 成功获取到A的早期引用,将A放入到半成品缓存中,B填充A引用完毕。 代理问题, 循环依赖问题都解决了 Spring Bean 二次开发在实例化Bean之前,Spring会调用扩展的类,实现BeanFactoryPostProcessor,并且机上@component注解,如果没有实现,spring就不会调用 Spring AOPAOP是什么AOP的全称是Aspect Orient Programming,即面向切面编程。是对OOP(Object Orient Programming)的一种补充,战门用于处理一些具有横切性质的服务。常常用于日志输出、安全控制等。 上面说到是对OOP的一种补充,具体补充的是什么呢?考虑一种情况,如果我们需要在所有方法执行前打印一句日志,按照OOP的处理思想,我们需要在每个业务方法开始时加入一些语句,但是我们辛辛苦苦加完之后,如果又要求在这句日志打印后再打印一句,那是不是又要加一遍?这时候你一定会想到,在某个类中编写一个日志打印方法,该方法执行这些日志打印操作,然后在每个业务方法之前加入这句方法调用,这就是面向对象编程思想。但是如果要求我们在业务方法结束时再打印一些日志呢,是不是还要去每个业务方法结束时加一遍?这样始终不是办法,而且我们总是在改业务方法,在业务方法里面掺杂了太多的其他操作,侵入性太高。 这时候AOP就起到作用了,我们可以编写一个切面类(Aspect),在其中的方法中来编写横切逻辑(如打印日志),然后通过配置或者注解的方式来声明该横切逻辑起作用的位置。 实现技术AOP(这里的AOP指的是面向切面编程思想,而不是Spring AOP)主要的的实现技术主要有Spring AOP和AspectJ。 1、AspectJ的底层技术。 AspectJ的底层技术是静态代理,即用一种AspectJ支持的特定语言编写切面,通过一个命令来编译,生成一个新的代理类,该代理类增强了业务类,这是在编译时增强,相对于下面说的运行时增强,编译时增强的性能更好。 2、Spring AOP Spring AOP采用的是动态代理,在运行期间对业务方法进行增强,所以不会生成新类,对于动态代理技术,Spring AOP提供了对JDK动态代理的支持以及CGLib的支持。 JDK动态代理只能为接口创建动态代理实例,而不能对类创建动态代理。需要获得被目标类的接口信息(应用Java的反射技术),生成一个实现了代理接口的动态代理类(字节码),再通过反射机制获得动态代理类的构造函数,利用构造函数生成动态代理类的实例对象,在调用具体方法前调用invokeHandler方法来处理。 CGLib动态代理需要依赖asm包,把被代理对象类的class文件加载进来,修改其字节码生成子类。 但是Spring AOP基于注解配置的情况下,需要依赖于AspectJ包的标准注解,但是不需要额外的编译以及AspectJ的织入器,而基于XML配置不需要。 知识点PointCut你想要去切某个东西之前总得先知道要在哪里切入是吧,切点格式如下:execution(* com.nuofankj.springdemo.aop.*Service.*(..))格式使用了正常表达式来定义那个范围内的类、那些接口会被当成切点 Advice通知,所谓的Advice其实就是定义了Aop何时被调用,确实有种通知的感觉 Before 在方法被调用之前调用 After 在方法完成之后调用 After-returning 在方法成功执行之后调用 After-throwing 在方法抛出异常之后调用 Around 在被通知的方法调用之前和调用之后调用 JoinPointJoinPoint连接点,其实很好理解,上面又有通知、又有切点,那和具体业务的连接点又是什么呢?没错,其实就是对应业务的方法对象,因为我们在横切代码中是有可能需要用到具体方法中的具体数据的,而连接点便可以做到这一点。 Aspect就是我们关注点的模块化。这个关注点可能会横切多个对象和模块,事务管理是横切关注点的很好的例子。它是一个抽象的概念,从软件的角度来说是指在应用程序不同模块中的某一个领域或方面。又pointcut 和advice组成。 Weaving把切面应用到目标对象来创建新的 advised 对象的过程。 原理简单说说 AOP 的设计 每个 Bean 都会被 JDK 或者 Cglib 代理。取决于是否有接口。 每个 Bean 会有多个“方法拦截器”。注意:拦截器分为两层,外层由 Spring 内核控制流程,内层拦截器是用户设置,也就是 AOP。 当代理方法被调用时,先经过外层拦截器,外层拦截器根据方法的各种信息判断该方法应该执行哪些“内层拦截器”。内层拦截器的设计就是职责连的设计。 流程代理的创建(按步骤): 首先,需要创建代理工厂,代理工厂需要 3 个重要的信息:拦截器数组,目标对象接口数组,目标对象。 创建代理工厂时,默认会在拦截器数组尾部再增加一个默认拦截器 —— 用于最终的调用目标方法。 当调用 getProxy 方法的时候,会根据接口数量大余 0 条件返回一个代理对象(JDK or Cglib)。 注意:创建代理对象时,同时会创建一个外层拦截器,这个拦截器就是 Spring 内核的拦截器。用于控制整个 AOP 的流程。 代理的调用 当对代理对象进行调用时,就会触发外层拦截器。 外层拦截器根据代理配置信息,创建内层拦截器链。创建的过程中,会根据表达式判断当前拦截是否匹配这个拦截器。而这个拦截器链设计模式就是职责链模式。 当整个链条执行到最后时,就会触发创建代理时那个尾部的默认拦截器,从而调用目标方法。最后返回。 SpringMCC临时用的https://zhuanlan.zhihu.com/p/62562499 设置属性123456789// 1. 设置属性// Make web application context availablerequest.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());// Make locale resolver availablerequest.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);// Make theme resolver availablerequest.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); 根据 Request 请求的 URL 得到对应的 handler 执行链,其实就是拦截器和 Controller 代理对象12// 2. 找 handler 返回执行链HandlerExecutionChain mappedHandler = getHandler(request); 得到 handler 的适配器123// This will throw an exception if no adapter is found// 3. 返回 handler 的适配器HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); 循环执行 handler 的 pre 拦截器12345678// 4. 循环执行 handler 的 pre 拦截器for (int i = 0; i < mappedHandler.getInterceptors().length; i++) { HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i]; // pre 拦截器 if (!interceptor.preHandle(request, response, mappedHandler.getHandler())) { return; }} 执行真正的 handler,并返回 ModelAndView(Handler 是个代理对象,可能会执行 AOP )12// 5. 执行真正的 handler,并返回 ModelAndView(Handler 是个代理对象,可能会执行 AOP )ModelAndView mv = ha.handle(request, response, mappedHandler.getHandler()); 循环执行 handler 的 post 拦截器123456789101112131415161718// 6. 循环执行 handler 的 post 拦截器for (int i = mappedHandler.getInterceptors().length - 1; i >=0 ; i--) { HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i]; // post 拦截器 interceptor.postHandle(request, response, mappedHandler.getHandler());}# 根据 ModelAndView 信息得到 View 实例 View view = null;if (mv.isReference()) { // We need to resolve this view name // 7. 根据 ModelAndView 信息得到 View 实例 view = this.viewResolver.resolveViewName(mv.getViewName(), locale);}# 渲染 View 返回// 8. 渲染 View 返回view.render(mv.getModel(), request, response); 其实理解这些才是最重要的。 用户发送请求至前端控制器DispatcherServlet DispatcherServlet收到请求调用HandlerMapping处理器映射器。 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。 DispatcherServlet通过HandlerAdapter处理器适配器调用处理器 HandlerAdapter执行处理器(handler,也叫后端控制器)。 Controller执行完成返回ModelAndView HandlerAdapter将handler执行结果ModelAndView返回给DispatcherServlet DispatcherServlet将ModelAndView传给ViewReslover视图解析器 ViewReslover解析后返回具体View对象 DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)。 DispatcherServlet响应用户 Springboot 启动流程https://juejin.im/post/6844903669998026759 通过 SpringFactoriesLoader 加载 META-INF/spring.factories 文件,获取并创建 SpringApplicationRunListener 对象 然后由 SpringApplicationRunListener 来发出 starting 消息 创建参数,并配置当前 SpringBoot 应用将要使用的 Environment 完成之后,依然由 SpringApplicationRunListener 来发出 environmentPrepared 消息 创建 ApplicationContext 初始化 ApplicationContext,并设置 Environment,加载相关配置等 由 SpringApplicationRunListener 来发出 contextPrepared 消息,告知SpringBoot 应用使用的 ApplicationContext 已准备OK 将各种 beans 装载入 ApplicationContext,继续由 SpringApplicationRunListener 来发出 contextLoaded 消息,告知 SpringBoot 应用使用的 ApplicationContext 已装填OK refresh ApplicationContext,完成IoC容器可用的最后一步 由 SpringApplicationRunListener 来发出 started 消息 完成最终的程序的启动 由 SpringApplicationRunListener 来发出 running 消息,告知程序已运行起来了 静态变量注入1234application.properties中配置下面两个配置项ccb.ip.address=10.25.177.31ccb.ip.port=1600下面问题代码中读取不到application.properties配置文件中的配置 123456789101112131415161718@Componentpublic class BISFrontFileUtil { private static Logger logger = LoggerFactory.getLogger(BISFrontFileUtil.class); private static String CCBIPADDRESS; private static int CCBIPPORT; @Value("${ccb.ip.address}") public void setCCBIPADDRESS(String cCBIPADDRESS) { CCBIPADDRESS = cCBIPADDRESS; } @Value("${ccb.ip.port}") public void setCCBIPPORT(int cCBIPPORT) { CCBIPPORT = cCBIPPORT; }} 注意: 修正代码中的@Component不可丢掉了 set方法要是非静态的 SpringBoot的注解 @Configuration @Configuration配置并启动Spring容器@Configuration标注在类上,相当于把该类作为spring的xml配置文件中的,作用为:配置spring容器(应用上下文) 123456789import org.springframework.context.annotation.Configuration;@Configurationpublic class TestConfig { public TestConfig(){ System.out.println("testconfig collection init success"); }} 相当于 1234567891011121314<?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:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd" default-lazy-init="false"></beans> 主方法进行测试: 1234567891011121314151617181920import org.springframework.context.ApplicationContext;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class Main { public static void main(String[] args) { // @Configuration注解的spring容器加载方式,用AnnotationConfigApplicationContext替换ClassPathXmlApplicationContext ApplicationContext context = new AnnotationConfigApplicationContext(TestConfig.class); // 如果加载spring-context.xml文件: // ApplicationContext context = new // ClassPathXmlApplicationContext("spring-context.xml"); }}// 结果WARNING: All illegal access operations will be denied in a future releasetestconfig collection init successProcess finished with exit code 0 @Configuration启动容器+@Bean注册Bean@Bean标注在方法上(返回某个实例的方法),等价于spring的xml配置文件中的,作用为:注册bean对象 1234567891011121314151617181920212223242526272829303132333435@Configurationpublic class TestConfig { public TestConfig(){ System.out.println("testconfig collection init success"); } // @Bean注解注册bean,同时可以指定初始化和销毁方法 // @Bean(name="testBean",initMethod="start",destroyMethod="cleanup") //name属性相当于<bean>标签的id @Bean @Scope("prototype") public TestBean testBean() { return new TestBean(); }}class TestBean { private String username; private String url; private String password; public void sayHello() { System.out.println("TestBean sayHello..."); } public void start() { System.out.println("TestBean init..."); } public void cleanup() { System.out.println("TestBean destroy..."); }} 测试类 123456789101112131415161718192021public class Main { public static void main(String[] args) { // @Configuration注解的spring容器加载方式,用AnnotationConfigApplicationContext替换ClassPathXmlApplicationContext ApplicationContext context = new AnnotationConfigApplicationContext(TestConfig.class); System.out.println(context); // 如果加载spring-context.xml文件: // ApplicationContext context = new // ClassPathXmlApplicationContext("spring-context.xml"); //获取bean TestBean testBean = (TestBean) context.getBean("testBean"); testBean.sayHello(); }}// 结果结果:testconfig collection init successTestBean sayHello... @Bean注解在返回实例的方法上,如果未通过@Bean指定bean的名称,则默认与标注的方法名相同(第一个单词转小写) @Bean注解默认作用域为单例singleton作用域,可通过@Scope(“prototype”)设置为原型作用域 既然@Bean的作用是注册bean对象,那么完全可以使用@Component、@Controller、@Service、@Ripository等注解注册bean,当然需要配置@ComponentScan注解进行自动扫描 scope属性1). singleton属性值(掌握):默认值,单例2). prototype属性值(掌握):多例(原型作用域)3). request属性值(了解):创建对象,把对象放到request域里4). session属性值(了解):创建对象,把对象放到session域里5). globalSession属性值(了解):创建对象,把对象放到globalSession域里 @Bean下管理bean的生命周期1234567// 用上面的例子//@Bean注解注册bean,同时可以指定初始化和销毁方法@Bean(name="testBean",initMethod="start",destroyMethod="cleanUp")@Scope("prototype")public TestBean testBean() { return new TestBean();} 测试类 12345// 结果testconfig collection init successorg.springframework.context.annotation.AnnotationConfigApplicationContext@41975e01, started on Mon Jul 19 09:51:42 PST 2021TestBean init...TestBean sayHello... @Configuration启动容器+@Component注册Beanbean类 1234567891011121314151617181920//添加注册bean的注解@Componentpublic class TestBean { private String username; private String url; private String password; public void sayHello() { System.out.println("TestBean sayHello..."); } public void start() { System.out.println("TestBean init..."); } public void cleanup() { System.out.println("TestBean destroy..."); }} 配置类: 1234567891011121314151617@Configuration//添加自动扫描注解,basePackages为TestBean包路径@ComponentScan(basePackages = "com.example.demo.spring2")public class TestConfig { public TestConfig(){ System.out.println("testconfig collection init success"); } // @Bean注解注册bean,同时可以指定初始化和销毁方法// @Bean(name="testBean",initMethod="start",destroyMethod="cleanup")//// @Bean// @Scope("prototype")// public TestBean testBean() {// return new TestBean();// }} 测试类: 123456789101112131415161718public class Main { public static void main(String[] args) { // @Configuration注解的spring容器加载方式,用AnnotationConfigApplicationContext替换ClassPathXmlApplicationContext ApplicationContext context = new AnnotationConfigApplicationContext(TestConfig.class); // 如果加载spring-context.xml文件: // ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml"); //获取bean TestBean testBean1 = (TestBean) context.getBean("testBean"); testBean1.sayHello(); }}// 结果testconfig collection init successTestBean sayHello... AnnotationConfigApplicationContext 注册 AppContext 类的两种方法第一种: 123456789public static void main(String[] args) { // @Configuration注解的spring容器加载方式,用AnnotationConfigApplicationContext替换ClassPathXmlApplicationContext ApplicationContext context = new AnnotationConfigApplicationContext(TestConfig.class); //获取bean TestBean tb = (TestBean) context.getBean("testBean"); tb.sayHello();} 第二种: 1234567public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(); annotationConfigApplicationContext.register(TestConfig.class); annotationConfigApplicationContext.refresh(); }} @Configuration组合xml配置类 12345678@Configuration@ImportResource("classpath:configtest.xml")public class WebConfig { public WebConfig(){ System.out.println("WebConfig coolection init success"); }} 实体类 123456789101112131415161718192021222324252627282930public class TestBean2 { private String username; private String url; private String password; public void setUsername(String username) { this.username = username; } public void setUrl(String url) { this.url = url; } public void setPassword(String password) { this.password = password; } public void sayHello() { System.out.println("TestBean2 sayHello..."+username); } public void start() { System.out.println("TestBean2 init..."); } public void cleanUp() { System.out.println("TestBean2 destroy..."); }} spring的xml配置文件 123456789<?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="testBean2" class="com.example.demo.spring3.TestBean2"> <property name="username" value="ranjun"/> </bean></beans> 测试类 123456789101112131415161718public class TestMain2 { public static void main(String[] args) { // @Configuration注解的spring容器加载方式,用AnnotationConfigApplicationContext替换ClassPathXmlApplicationContext ApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class); // 如果加载spring-context.xml文件: // ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml"); // 获取bean TestBean2 tb = (TestBean2) context.getBean("testBean2"); tb.sayHello(); }}// 结果WebConfig coolection init successTestBean2 sayHello...ranjun @Configuration组合xml和其它注解实体类: 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849public class TestBean { private String username; private String url; private String password; public void sayHello() { System.out.println("TestBean sayHello..."); } public void start() { System.out.println("TestBean init..."); } public void cleanup() { System.out.println("TestBean destroy..."); }}public class TestBean2 { private String username; private String url; private String password; public void setUsername(String username) { this.username = username; } public void setUrl(String url) { this.url = url; } public void setPassword(String password) { this.password = password; } public void sayHello() { System.out.println("TestBean2 sayHello..."+username); } public void start() { System.out.println("TestBean2 init..."); } public void cleanUp() { System.out.println("TestBean2 destroy..."); }} 配置类 1234567891011121314151617181920212223@Configurationpublic class TestConfig { public TestConfig(){ System.out.println("testconfig collection init success"); } @Bean @Scope("prototype") public TestBean testBean() { return new TestBean(); }}@Configuration@ImportResource("classpath:configtest.xml")@Import(TestConfig.class)public class WebConfig { public WebConfig(){ System.out.println("WebConfig coolection init success"); }} 测试类: 1234567891011121314151617181920public class TestMain2 { public static void main(String[] args) { // @Configuration注解的spring容器加载方式,用AnnotationConfigApplicationContext替换ClassPathXmlApplicationContext ApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class); // 获取bean TestBean tb = (TestBean) context.getBean("testBean"); tb.sayHello(); TestBean2 tb2 = (TestBean2) context.getBean("testBean2"); tb2.sayHello(); }}// 结果WebConfig coolection init successtestconfig collection init successTestBean sayHello...TestBean2 sayHello...ranjun
评论