关于spring:Spring注册Bean系列方法5ImportImportBeanDefinitionRegistrar

原文网址:Spring注册Bean系列--办法5:@Import+ImportBeanDefinitionRegistrar_IT利刃出鞘的博客-CSDN博客 简介本文介绍Spring注册Bean的办法:@Import+ImportBeanDefinitionRegistrar。 注册Bean的办法我写了一个系列,见:Spring注册Bean(提供Bean)系列--办法大全_IT利刃出鞘的博客-CSDN博客 分享Java技术星球(自学精灵):https://learn.skyofit.com 办法概述实现ImportBeanDefinitionRegistrar接口<!----> 实现它的registerBeanDefinitions办法(返回须要导入的组件的全类名数组;)<!----> 启动类应用@Import导入实现类实例ImportBeanDefinitionRegistrar接口的实现类package com.knife.registrar;import com.knife.entity.MyBean;import org.springframework.beans.factory.support.BeanDefinitionRegistry;import org.springframework.beans.factory.support.RootBeanDefinition;import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;import org.springframework.core.type.AnnotationMetadata;public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { /** * AnnotationMetadata:以后类的注解信息 * BeanDefinitionRegistry:BeanDefinition注册类; * 把所有须要增加到容器中的bean;调用 * BeanDefinitionRegistry.registerBeanDefinition手工注册进来 */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // 能够判断对象是否曾经注册到容器 // boolean definition = registry.containsBeanDefinition("com.knife.example.entity.MyBean"); //指定Bean定义信息 RootBeanDefinition beanDefinition = new RootBeanDefinition(MyBean.class); //注册一个Bean,指定bean名 registry.registerBeanDefinition("myBean", beanDefinition); }}要注册的类(Bean)package com.knife.entity;public class MyBean { public String sayHello() { return "Hello World"; }}导入配置类法1:启动类package com.knife;import com.knife.registrar.MyImportBeanDefinitionRegistrar;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.annotation.Import;@SpringBootApplication@Import({MyImportBeanDefinitionRegistrar.class})public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); }}法2:@Configuration 标记的类其实,只有是注册到Spring容器中的类就能够,但个别用@Configuration。 ...

February 19, 2024 · 1 min · jiezi

关于spring:SpringBoot教程-SpringBoot中ApplicationEvent用法

前言咱们后面的文章中解说过RabbitMQ的用法,所谓MQ就是一种公布订阅模式的音讯模型。在Spring中其实自身也为咱们提供了一种公布订阅模式的事件处理形式,就是ApplicationEvent和 ApplicationListener,这是一种基于观察者模式实现事件监听性能。也已帮忙咱们实现业务逻辑的解耦,进步程序的扩展性和可维护性。 然而这里要留神ApplicationEvent和 MQ队列尽管实现的性能类似,然而MQ还是有其不可替代性的,最实质的区别就是MQ能够用于不同零碎之间的音讯公布,而SpringEvent这种模式只能在一个零碎中,也就是要求必须是同一个Spring容器。 好了接下来咱们就来演练一番。 在这个模型中,有两个重要的类,一个是事件,一个是监听。事件要继承ApplicationEvent类,监听要实现ApplicationListener接口。 一、开发ApplicationEvent事件事件其实就是咱们要发送的音讯体,这个个别要依据咱们的理论业务进行封装,须要什么类型的数据,就是用什么类型,须要哪些字段就增加哪些字段。咱们来给一个案例。 package com.lsqingfeng.springboot.applicationEvent; import lombok.Getter;import lombok.Setter;import org.springframework.context.ApplicationEvent; /** * @className: MyApplicationEvent * @description: 事件封装 * @author: sh.Liu * @date: 2022-03-23 14:41 */@Getter@Setter@ToStringpublic class MyApplicationEvent extends ApplicationEvent { private Integer age; private String name; /** * 须要重写构造方法 * @param source * @param name * @param age */ public MyApplicationEvent(Object source, String name, Integer age) { super(source); this.name = name; this.age = age; }}二、 开发监听器监听器就相当于咱们的MQ的消费者,当有工夫推送过去的时候,监听器的代码就能够执行。这里通过泛型来设置好咱们的事件类型。 ...

September 20, 2023 · 1 min · jiezi

关于spring:spring-03-AOP

0. 代理模式 (AOP的原理)代理模式 Rent: //形象角色:租房public interface Rent { public void rent();}Host: //实在角色: 房东,房东要出租房子public class Host implements Rent{ public void rent() { System.out.println("房屋出租"); }}Proxy: //代理角色:中介public class Proxy implements Rent { private Host host; public Proxy() { } public Proxy(Host host) { this.host = host; } //租房 public void rent(){ seeHouse(); host.rent(); fare(); } //看房 public void seeHouse(){ System.out.println("带房客看房"); } //收中介费 public void fare(){ System.out.println("收中介费"); }}Client: //客户类,个别客户都会去找代理!public class Client { public static void main(String[] args) { //房东要租房 Host host = new Host(); //中介帮忙房东 Proxy proxy = new Proxy(host); //你去找中介! proxy.rent(); }}剖析:在这个过程中,你间接接触的就是中介,就如同现实生活中的样子,你看不到房东,然而你仍旧租到了房东的房子通过代理,这就是所谓的代理模式。 ...

September 12, 2023 · 3 min · jiezi

关于spring:聊聊spring的TransactionalEventListener

序本文次要钻研一下spring的TransactionalEventListener TransactionalEventListenerorg/springframework/transaction/event/TransactionalEventListener.java /** * An {@link EventListener} that is invoked according to a {@link TransactionPhase}. * * <p>If the event is not published within an active transaction, the event is discarded * unless the {@link #fallbackExecution} flag is explicitly set. If a transaction is * running, the event is processed according to its {@code TransactionPhase}. * * <p>Adding {@link org.springframework.core.annotation.Order @Order} to your annotated * method allows you to prioritize that listener amongst other listeners running before * or after transaction completion. * * @author Stephane Nicoll * @author Sam Brannen * @since 4.2 */@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@EventListenerpublic @interface TransactionalEventListener { /** * Phase to bind the handling of an event to. * <p>The default phase is {@link TransactionPhase#AFTER_COMMIT}. * <p>If no transaction is in progress, the event is not processed at * all unless {@link #fallbackExecution} has been enabled explicitly. */ TransactionPhase phase() default TransactionPhase.AFTER_COMMIT; /** * Whether the event should be processed if no transaction is running. */ boolean fallbackExecution() default false; /** * Alias for {@link #classes}. */ @AliasFor(annotation = EventListener.class, attribute = "classes") Class<?>[] value() default {}; /** * The event classes that this listener handles. * <p>If this attribute is specified with a single value, the annotated * method may optionally accept a single parameter. However, if this * attribute is specified with multiple values, the annotated method * must <em>not</em> declare any parameters. */ @AliasFor(annotation = EventListener.class, attribute = "classes") Class<?>[] classes() default {}; /** * Spring Expression Language (SpEL) attribute used for making the event * handling conditional. * <p>The default is {@code ""}, meaning the event is always handled. * @see EventListener#condition */ String condition() default "";}TransactionalEventListener是EventListener的事务感知版本,默认的是TransactionPhase是AFTER_COMMITApplicationListenerMethodTransactionalAdapterorg/springframework/transaction/event/ApplicationListenerMethodTransactionalAdapter.java ...

September 11, 2023 · 5 min · jiezi

关于spring:聊聊TransactionSynchronization的invokeAfterCompletion

序本文次要钻研一下TransactionSynchronization的invokeAfterCompletion afterCompletionorg/springframework/transaction/support/TransactionSynchronization.java public interface TransactionSynchronization extends Flushable { /** Completion status in case of proper commit. */ int STATUS_COMMITTED = 0; /** Completion status in case of proper rollback. */ int STATUS_ROLLED_BACK = 1; /** Completion status in case of heuristic mixed completion or system errors. */ int STATUS_UNKNOWN = 2; //...... /** * Invoked after transaction commit. Can perform further operations right * <i>after</i> the main transaction has <i>successfully</i> committed. * <p>Can e.g. commit further operations that are supposed to follow on a successful * commit of the main transaction, like confirmation messages or emails. * <p><b>NOTE:</b> The transaction will have been committed already, but the * transactional resources might still be active and accessible. As a consequence, * any data access code triggered at this point will still "participate" in the * original transaction, allowing to perform some cleanup (with no commit following * anymore!), unless it explicitly declares that it needs to run in a separate * transaction. Hence: <b>Use {@code PROPAGATION_REQUIRES_NEW} for any * transactional operation that is called from here.</b> * @throws RuntimeException in case of errors; will be <b>propagated to the caller</b> * (note: do not throw TransactionException subclasses here!) */ default void afterCommit() { } /** * Invoked after transaction commit/rollback. * Can perform resource cleanup <i>after</i> transaction completion. * <p><b>NOTE:</b> The transaction will have been committed or rolled back already, * but the transactional resources might still be active and accessible. As a * consequence, any data access code triggered at this point will still "participate" * in the original transaction, allowing to perform some cleanup (with no commit * following anymore!), unless it explicitly declares that it needs to run in a * separate transaction. Hence: <b>Use {@code PROPAGATION_REQUIRES_NEW} * for any transactional operation that is called from here.</b> * @param status completion status according to the {@code STATUS_*} constants * @throws RuntimeException in case of errors; will be <b>logged but not propagated</b> * (note: do not throw TransactionException subclasses here!) * @see #STATUS_COMMITTED * @see #STATUS_ROLLED_BACK * @see #STATUS_UNKNOWN * @see #beforeCompletion */ default void afterCompletion(int status) { }}afterCompletion办法有入参status,示意事务完结时候的状态,0示意事务已提交,1示意事务已回滚,2示意事务未知;与afterCommit的一个最重要的区别是afterCompletion的异样会被捕捉,不像afterCommit会抛给调用方invokeAfterCompletionorg/springframework/transaction/support/TransactionSynchronizationUtils.java ...

September 10, 2023 · 3 min · jiezi

关于spring:聊聊spring的TransactionSynchronizationAdapter

序本文次要钻研一下spring的TransactionSynchronizationAdapter 示例代码public void insert(TechBook techBook){ bookMapper.insert(techBook); // send after tx commit but is async TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { @Override public void afterCommit() { System.out.println("send email after transaction commit..."); } } ); System.out.println("service end"); }应用TransactionSynchronizationManager.registerSynchronization注册了一个TransactionSynchronizationAdapter,在其afterCommit办法也就是事务提交胜利之后执行一些额定逻辑TransactionSynchronizationAdapterorg/springframework/transaction/support/TransactionSynchronizationAdapter.java public abstract class TransactionSynchronizationAdapter implements TransactionSynchronization, Ordered { @Override public int getOrder() { return Ordered.LOWEST_PRECEDENCE; } @Override public void suspend() { } @Override public void resume() { } @Override public void flush() { } @Override public void beforeCommit(boolean readOnly) { } @Override public void beforeCompletion() { } @Override public void afterCommit() { } @Override public void afterCompletion(int status) { }}TransactionSynchronizationAdapter是个抽象类,申明实现TransactionSynchronization及Ordered接口TransactionSynchronizationorg/springframework/transaction/support/TransactionSynchronization.java ...

September 9, 2023 · 3 min · jiezi

关于spring:七种-BeanDefinition各显其能

聚沙成塔!人不知;鬼不觉 Spring 源码曾经间断更了两个月啦,视频也录制了不少了,对 Spring 源码剖析感兴趣的小伙伴戳这里哦Spring源码应该怎么学?~ 明天咱们持续来看 Spring 源码中一个十分重要的概念:BeanDefinition。 1.BeanDefinition在 Spring 容器中,咱们宽泛应用的是一个一个的 Bean,BeanDefinition 从名字上就能够看出是对于 Bean 的定义。 事实上就是这样,咱们在 XML 文件中配置的 Bean 的各种属性,亦或者用注解定义进去的 Bean 的各种属性,在真正生成 Bean 间接,咱们须要先对这些设置的属性进行解析,解析的后果须要有一个对象来承载,很显著,这个对象就是 BeanDefinition。 无论是通过 XML 中定义的 Bean 属性还是通过 Java 代码定义的 Bean 属性,都会先加载到 BeanDefinition 上,而后通过 BeanDefinition 来生成一个 Bean,从这个角度来说,BeanDefinition 和 Bean 的关系有点相似于类和对象的关系,BeanDefinition 是模板,Bean 是模板具体化之后的产物。 要了解 BeanDefinition,咱们从 BeanDefinition 的继承关系开始看起。 BeanDefinition 是一个接口,继承自 BeanMetadataElement 和 AttributeAccessor 接口。 BeanMetadataElement:该接口只有一个办法 getSource,该办法返回 Bean 的起源。AttributeAccessor:该接口次要标准了问任意对象元数据的办法。咱们来看下 AttributeAccessor: public interface AttributeAccessor { void setAttribute(String name, @Nullable Object value); @Nullable Object getAttribute(String name); @Nullable Object removeAttribute(String name); boolean hasAttribute(String name); String[] attributeNames();}这里定义了元数据的拜访接口,具体的实现则是 AttributeAccessorSupport,这些数据采纳 LinkedHashMap 进行存储。 ...

September 8, 2023 · 5 min · jiezi

关于spring:Spring-Bean-别名处理原理分析

明天来和小伙伴们聊一聊 Spring 中对于 Bean 别名的解决逻辑。 1. Alias别名,顾名思义就是给一个 Bean 去两个甚至多个名字。整体上来说,在 Spring 中,有两种不同的别名定义形式: 定义 Bean 的 name 属性,name 属性在真正的处理过程中,实际上就是依照别名来解决的。通过 alias 标签定义专门的别名,通过 alias 定义进去的别名和 name 属性定义的别名最终都是合并在一起解决的,所以这两种定义别名的形式最终是必由之路。那么定义的别名是保留在哪里呢? 大家晓得,Bean 解析进去之后被保留在容器中,别名其实也是一样的,容器中存在一个 aliasMap 专门用来保留 Bean 的别名,保留的格局是 alias->name,例如有一个 Bean 的名字是 user,别名是 userAlias,那么保留在 aliasMap 中就是 userAlias->user。 举个简略例子: <bean class="org.javaboy.demo.User" id="user" name="user4,user5,user6"/><alias name="user" alias="user2"/><alias name="user2" alias="user3"/>在下面这段定义中,user2、user3、user4、user5、user6 都是别名。 2. AliasRegistry2.1 AliasRegistrySpring 中为别名的解决提供了 AliasRegistry 接口,这个接口中提供了别名解决的次要办法: public interface AliasRegistry { void registerAlias(String name, String alias); void removeAlias(String alias); boolean isAlias(String name); String[] getAliases(String name);}registerAlias:这个办法用来增加别名,外围逻辑就是向 aliasMap 中增加数据。removeAlias:这个办法用来从 aliasMap 中移除一个别名。isAlias:判断给定的 name 是否是一个别名。getAliases:依据给定的名字去获取所有的别名。办法就这四个,看一下这个接口的实现类有哪些。 ...

September 5, 2023 · 4 min · jiezi

关于spring:一道经典面试题Configuration-和-Component-有何区别

对于 @Configuration 注解有一个特地经典的面试题: @Configuration 和 @Component 有什么区别?无论小伙伴们之前是否背过相干的面试题,明天这篇文章学完之后置信大家对这个问题都会有更深一层的了解,废话不多少,咱们开始剖析。 1. 情景展示@Configuration 和 @Component 到底有何区别呢?我先通过如下一个案例,在不剖析源码的状况下,小伙伴们先来直观感受一下这两个之间的区别。 @Configurationpublic class JavaConfig01 {}@Componentpublic class JavaConfig02 {}首先,别离向 Spring 容器中注入两个 Bean,JavaConfig01 和 JavaConfig02,其中,JavaConfig01 上增加的是 @Configuration 注解而 JavaConfig02 上增加的则是 @Component 注解。 当初,在 XML 文件中配置包扫描: <context:component-scan base-package="org.javaboy.demo.p6"/>最初,加载 XML 配置文件,初始化容器: public class Demo { public static void main(String[] args) { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans_demo.xml"); JavaConfig01 config01 = ctx.getBean(JavaConfig01.class); JavaConfig02 config02 = ctx.getBean(JavaConfig02.class); System.out.println("config01.getClass() = " + config01.getClass()); System.out.println("config02.getClass() = " + config02.getClass()); }}最终打印进去后果如下: ...

August 29, 2023 · 6 min · jiezi

关于spring:聊聊spring项目中如何动态刷新bean

前言前阵子和敌人聊天,他手头上有个spring单体我的项目,每次数据库配置变更,他都要重启我的项目,让配置失效。他就想说有没有什么方法,不重启我的项目,又能够让配置失效。过后我就跟他说,能够用配置核心,他的意思是因为是保护类我的项目,不想再额定引入一个配置核心,减少运维老本。后边跟他探讨了一个计划,能够实现一个监听配置文件变动的程序,当监听到文件变动,进行相应的变更操作。具体流程如下在这些步骤,比拟麻烦就是如何动静刷新bean,因为敌人是spring我的项目,明天就来聊下在spring我的项目中如何实现bean的动静刷新 实现思路理解spring的敌人,应该晓得spring的单例bean是缓存在singletonObjects这个map外面,所以能够通过变更singletonObjects来实现bean的刷新。咱们能够通过调用removeSingleton和addSingleton这两个办法来实现,然而这种实现形式的毛病就是会扭转bean的生命周期,会导致原来的一些加强性能生效,比方AOP。但spring作为一个极其优良的框架,他提供了让咱们本人治理bean的扩大点。这个扩大点就是通过指定scope,来达到本人治理bean的成果 实现步骤1、自定义scopepublic class RefreshBeanScope implements Scope { private final Map<String,Object> beanMap = new ConcurrentHashMap<>(256); @Override public Object get(String name, ObjectFactory<?> objectFactory) { if(beanMap.containsKey(name)){ return beanMap.get(name); } Object bean = objectFactory.getObject(); beanMap.put(name,bean); return bean; } @Override public Object remove(String name) { return beanMap.remove(name); } @Override public void registerDestructionCallback(String name, Runnable callback) { } @Override public Object resolveContextualObject(String key) { return null; } @Override public String getConversationId() { return null; }}2、自定义scope注册public class RefreshBeanScopeDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { beanFactory.registerScope(SCOPE_NAME,new RefreshBeanScope()); }}3、自定义scope注解(可选)@Target({ ElementType.TYPE, ElementType.METHOD })@Retention(RetentionPolicy.RUNTIME)@Scope("refreshBean")@Documentedpublic @interface RefreshBeanScope { /** * @see Scope#proxyMode() * @return proxy mode */ ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;}4、编写自定义scope bean刷新逻辑@RequiredArgsConstructorpublic class RefreshBeanScopeHolder implements ApplicationContextAware { private final DefaultListableBeanFactory beanFactory; private ApplicationContext applicationContext; public List<String> refreshBean(){ String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames(); List<String> refreshBeanDefinitionNames = new ArrayList<>(); for (String beanDefinitionName : beanDefinitionNames) { BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanDefinitionName); if(SCOPE_NAME.equals(beanDefinition.getScope())){ beanFactory.destroyScopedBean(beanDefinitionName); beanFactory.getBean(beanDefinitionName); refreshBeanDefinitionNames.add(beanDefinitionName); applicationContext.publishEvent(new RefreshBeanEvent(beanDefinitionName)); } } return Collections.unmodifiableList(refreshBeanDefinitionNames); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; }}以上步骤就是实现自定义scope治理bean的过程,上面咱们以一个配置变更实现bean刷新例子,来演示以上步骤 ...

August 29, 2023 · 4 min · jiezi

关于spring:Introduction-Advice

1、Advice与MethodInterceptorAdvice是被某个切面在特定的连接点采取的操作。Spring 提供了多种Advice,能够不便扩大。Advice的类型包含around(盘绕advice)、before(前置advice) 和 after(后置advice)。在Spring中将Advice建模为一个Interceptor。下图是Spring 中罕用的Advice的继承图。 ![](/img/bVc9oW8)图中的Advice接口时整个Advice体系的根接口,Advice接口的全门路的类名是:org.aopalliance.aop.Advice。这个类是Aop联盟定义的类。 Advice的子类带有Aspectj结尾,这些类是包装Aspectj注解的advice办法。 Spring反对仅反对办法级别的链接点,因而Spring的Advice最终都会通过MethodInterceptor 来执行invoke办法。 Object invoke(@Nonnull MethodInvocation invocation) throws Throwable;那么可能会有疑难,图中的AspectJMethodBeforeAdvice 没有实现MethodInterceptor,那么是怎么实现Advice操作的呢?Advise和Pointcut一起组成了Advisor,Spring在创立代理对象的时候会查找所有实用对象办法的Advisor,而后通过Advisor创立出MethodInterceptor,具体代码能够参考:AdvisorAdapter及其实现。 明天咱们要介绍的是Introduction Advice。 Introduction Advice(引入告诉)Spring 看待introduction advice 作为一种非凡的拦挡告诉.引入(Introduction)须要一个IntroductionAdvisor 和一个实现接口IntroductionInterceptor的类。 public interface IntroductionInterceptor extends MethodInterceptor { boolean implementsInterface(Class intf);}继承自MethodInterceptor接口的invoke()办法须要实现引入性能。也就是说,如果调用的办法切实一个引入接口办法,引入拦截器负责解决办法调用,而不须要调用连接点的proceed();Introduction advice 不能和任何pointcut应用,因为它仅实用于类,而不是办法级别。只能用IntroductionAdvisor 来应用introduction advice 。 public interface IntroductionAdvisor extends Advisor, IntroductionInfo { ClassFilter getClassFilter(); void validateInterfaces() throws IllegalArgumentException;}public interface IntroductionInfo { Class<?>[] getInterfaces();}getInterfaces()办法返回被这个Advisor对象引入的接口。validateInterfaces()办法被外部用来查看引入的接口是否被配置的IntroductionInterceptor来实现。 来看一个Spring test套件的例子,假如咱们想将以下接口引入到一个或者很多个对象中: public interface Lockable { void lock(); void unlock(); boolean locked();}这个例子解释了一个混入的实现。咱们想要将一个无论任何类型的类转换为Lockable,并能调用lock()和unlock()办法。如果咱们调用lock()办法,咱们想要实现所有的setter办法会抛出LockedException.因而,咱们能够通过一个切面来提供在对一个对象毫不理解的状况下来实现不可变的性能。 首先,咱们须要实现一个IntroductionInterceptor来实现这个艰巨工作。咱们能够通过扩大org.springframework.aop.support.DelegatingIntroductionInterceptor 便当类。当然咱们也能够间接实现IntroductionInterceptor,然而应用DelegatingIntroductionInterceptor在大多数状况下是最好的抉择。 ...

August 25, 2023 · 1 min · jiezi

关于spring:Spring-中-Primary-注解的原理是什么

1. 问题剖析当咱们应用 Spring 的时候,有时候会遇到上面这种状况。 假如我有 A、B 两个类,在 A 中注入 B,如下: @Componentpublic class A { @Autowired B b;}至于 B,则在配置类中存在多个实例: @Configuration@ComponentScanpublic class JavaConfig { @Bean("b1") B b1() { return new B(); } @Bean("b2") B b2() { return new B(); }}这样的我的项目启动之后,必然会抛出如下异样: 当然,对于这样的问题,置信有教训的同学都晓得该怎么解决: 能够应用 @Resource 注解,应用该注解时指定具体的 Bean 名称即可。在 @Autowired 注解之上,再多加一个 @Qualifier("b1") 注解,通过该注解去指定要加载的 Bean 名称。@Componentpublic class A { @Autowired @Qualifier("b1") B b;}在多个 B 对象的某一个之上,增加 @Primary 注解,示意当存在反复的 B 对象时,优先应用哪一个。@Configuration@ComponentScanpublic class JavaConfig { @Bean("b1") @Primary B b1() { return new B(); } @Bean("b2") B b2() { return new B(); }}除了这三个,还有没有其余方法呢?必须有!!!在 Spring 中 @Qualifier 注解还能这么用? 一文中,松哥还和大家扩大了 @Qualifier 注解的其余用法,感兴趣的小伙伴不要错过哦。 ...

August 21, 2023 · 4 min · jiezi

关于spring:SpringIOCBean

Scope对于Spring容器来说,当咱们把一个Bean标记为@Component后,它就会主动为咱们创立一个单例(Singleton),即容器初始化时创立Bean,容器敞开前销毁Bean。在容器运行期间,咱们调用getBean(Class)获取到的Bean总是同一个实例。 还有一种Bean,咱们每次调用getBean(Class),容器都返回一个新的实例,这种Bean称为Prototype(原型),它的生命周期显然和Singleton不同。申明一个Prototype的Bean时,须要增加一个额定的@Scope注解: @Component@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) // @Scope("prototype")public class MailSession { ...}注入List有些时候,咱们会有一系列接口雷同,不同实现类的Bean。例如,注册用户时,咱们要对email、password和name这3个变量进行验证。为了便于扩大,咱们先定义验证接口: public interface Validator { void validate(String email, String password, String name);}而后,别离应用3个Validator对用户参数进行验证: @Componentpublic class EmailValidator implements Validator { public void validate(String email, String password, String name) { if (!email.matches("^[a-z0-9]+\\@[a-z0-9]+\\.[a-z]{2,10}$")) { throw new IllegalArgumentException("invalid email: " + email); } }}@Componentpublic class PasswordValidator implements Validator { public void validate(String email, String password, String name) { if (!password.matches("^.{6,20}$")) { throw new IllegalArgumentException("invalid password"); } }}@Componentpublic class NameValidator implements Validator { public void validate(String email, String password, String name) { if (name == null || name.isBlank() || name.length() > 20) { throw new IllegalArgumentException("invalid name: " + name); } }}最初,咱们通过一个Validators作为入口进行验证: ...

August 20, 2023 · 2 min · jiezi

关于spring:Spring-BeanDefinition-也分父子关系

在 Spring 框架中,BeanDefinition 是一个外围概念,用于定义和配置 bean 的元数据,尽管在理论利用中,咱们个别并不会或者很少间接定义 BeanDefinition,然而,咱们在 XML 文件中所作的配置,以及利用 Java 代码做的各种 Spring 配置,都会被解析为 BeanDefinition,而后才会做进一步的解决。BeanDefinition 容许开发人员以一种申明性的形式定义和组织 bean,这里有很多属性,明天松哥单纯的来和小伙伴们聊一聊它的 parentName 属性,parentName 属性在 BeanDefinition 中扮演着重要的角色,用于建设 bean 之间的父子关系。 之前松哥有一篇文章和小伙伴们聊了 BeanFactory 之间的父子关系(Spring 中的父子容器是咋回事?),大家留神和明天的内容进行辨别,明天咱们聊的是 BeanDefinition 之间的父子关系。BeanDefinition 的 parentName 属性的次要性能是容许咱们在创立一个 bean 的同时,可能继承另一个曾经定义好的 bean。通过指定 parentName 属性,咱们能够重用已有 bean 的配置,并在此基础上进行批改或扩大。 先不废话了,我先来举两个例子,小伙伴们先感受一下 BeanDefinition 的作用。 1. 实际假如我有如下两个类,首先是一个动物的基类,如下: public class Animal { private String name; private Integer age; //省略 getter/setter}而后有一个 Dog 类,如下: public class Dog { private String name; private Integer age; private String color; //省略 getter/setter}小伙伴们留神,这里的 Dog 类并没有继承自 Animal 类,然而有两个跟 Animal 同名的属性。之所以这样设计是心愿小伙伴们了解 BeanDefinition 中的 parentName 属性和 Java 中的继承并无关系,尽管大部分状况下咱们用到 parentName 的时候,Java 中相干的类都是继承关系。当初,有一些通用的属性我想在 Animal 中进行配置,Dog 中特有的属性则在 Dog 中进行配置,咱们来看下通过 XML 和 Java 别离该如何配置。 ...

August 16, 2023 · 4 min · jiezi

关于spring:Spring开发

IOC容器在学习Spring框架时,咱们遇到的第一个也是最外围的概念就是容器。 什么是容器?容器是一种为某种特定组件的运行提供必要反对的一个软件环境。例如,Tomcat就是一个Servlet容器,它能够为Servlet的运行提供运行环境。相似Docker这样的软件也是一个容器,它提供了必要的Linux环境以便运行一个特定的Linux过程。 通常来说,应用容器运行组件,除了提供一个组件运行环境之外,容器还提供了许多底层服务。例如,Servlet容器底层实现了TCP连贯,解析HTTP协定等非常复杂的服务,如果没有容器来提供这些服务,咱们就无奈编写像Servlet这样代码简略,功能强大的组件。晚期的JavaEE服务器提供的EJB容器最重要的性能就是通过申明式事务服务,使得EJB组件的开发人员不用本人编写简短的事务处理代码,所以极大地简化了事务处理。 Spring的外围就是提供了一个IoC容器,它能够治理所有轻量级的JavaBean组件,提供的底层服务包含组件的生命周期治理、配置和组装服务、AOP反对,以及建设在AOP根底上的申明式事务服务等。 本章咱们探讨的IoC容器,次要介绍Spring容器如何对组件进行生命周期治理和配置组装服务。 IOC原理Spring提供的容器又称为IoC容器,什么是IoC? IoC全称Inversion of Control,直译为管制反转。那么何谓IoC?在了解IoC之前,咱们先看看通常的Java组件是如何合作的。 咱们假设一个在线书店,通过BookService获取书籍: public class BookService { private HikariConfig config = new HikariConfig(); private DataSource dataSource = new HikariDataSource(config); public Book getBook(long bookId) { try (Connection conn = dataSource.getConnection()) { ... return book; } }}为了从数据库查问书籍,BookService持有一个DataSource。为了实例化一个HikariDataSource,又不得不实例化一个HikariConfig。 当初,咱们持续编写UserService获取用户: public class UserService { private HikariConfig config = new HikariConfig(); private DataSource dataSource = new HikariDataSource(config); public User getUser(long userId) { try (Connection conn = dataSource.getConnection()) { ... return user; } }}因为UserService也须要拜访数据库,因而,咱们不得不也实例化一个HikariDataSource。 ...

August 15, 2023 · 1 min · jiezi

关于spring:Spring-中的父子容器是咋回事

@[toc]置信有小伙伴也据说过,在 SSM 我的项目中,Spring 容器是父容器,SpringMVC 是子容器,子容器能够拜访父容器的 Bean,然而父容器不能拜访子容器的 Bean。 更近一步,有小伙伴可能也理解过,不必父子容器,单纯就用一个 SpringMVC 容器仿佛也能够,我的项目也能运行。 那么当初问题来了:既然单纯一个 SpringMVC 容器就能使我的项目跑起来,那咱们为什么还要用父子容器?父子容器的劣势是什么? 带着这个问题,明天松哥来和小伙伴们聊一聊父子容器。 1. 父子容器首先,其实父子这种设计很常见,松哥记得在之前的 Spring Security 的系列文章中,Spring Security 中的 AuthenticationManager 其实也是相似的设计,预计那里就是借鉴了 Spring 中的父子容器设计。 当应用了父子容器之后,如果去父容器中查找 Bean,那么就单纯的在父容器中查找 Bean;如果是去子容器中查找 Bean,那么就会先在子容器中查找,找到了就返回,没找到则持续去父容器中查找,直到找到为止(把父容器都找完了还是没有的话,那就只能抛异样进去了)。 2. 为什么须要父子容器2.1 问题出现为什么须要父子容器?老老实实应用一个容器不行吗? 既然 Spring 容器中有父子容器,那么这个玩意就必然有其应用场景。 松哥举一个简略的例子。 假如我有一个多模块我的项目,其中有商家模块和客户模块,商家模块和客户模块中都有角色治理 RoleService,我的项目构造如下图: ├── admin│ ├── pom.xml│ └── src│ ├── main│ │ ├── java│ │ └── resources├── consumer│ ├── pom.xml│ └── src│ ├── main│ │ ├── java│ │ │ └── org│ │ │ └── javaboy│ │ │ └── consumer│ │ │ └── RoleService.java│ │ └── resources│ │ └── consumer_beans.xml├── merchant│ ├── pom.xml│ └── src│ ├── main│ │ ├── java│ │ │ └── org│ │ │ └── javaboy│ │ │ └── merchant│ │ │ └── RoleService.java│ │ └── resources│ │ └── merchant_beans.xml└── pom.xml当初 consumer 和 merchant 中都有一个 RoleService 类,而后在各自的配置文件中,都将该类注册到 Spring 容器中。 ...

July 12, 2023 · 4 min · jiezi

关于spring:YUM本地安装Docker

查看可用的软件版本#下载清华的镜像源文件wget -O /etc/yum.repos.d/docker-ce.repo https://download.docker.com/linux/centos/docker-ce.reposudo sed -i 's+download.docker.com+mirrors.tuna.tsinghua.edu.cn/docker-ce+' /etc/yum.repos.d/docker-ce.repoyum updatesudo yum list docker-ce --showduplicates|sort -rLoading mirror speeds from cached hostfileLoaded plugins: fastestmirrordocker-ce.x86_64 3:19.03.8-3.el7 docker-ce-stabledocker-ce.x86_64 3:19.03.7-3.el7 docker-ce-stabledocker-ce.x86_64 3:19.03.6-3.el7 docker-ce-stabledocker-ce.x86_64 3:19.03.5-3.el7 docker-ce-stabledocker-ce.x86_64 3:19.03.4-3.el7 docker-ce-stabledocker-ce.x86_64 3:19.03.3-3.el7 docker-ce-stabledocker-ce.x86_64 3:19.03.2-3.el7 docker-ce-stabledocker-ce.x86_64 3:19.03.1-3.el7 docker-ce-stable....下载到指定文件夹sudo yum install --downloadonly --downloaddir=/tmp/docker-19.03 docker-ce-19.03.8-3.el7 docker-ce-cli-19.03.8-3.el7 Dependencies Resolved==================================================================================================================================================================================== Package Arch Version Repository Size====================================================================================================================================================================================Installing: docker-ce x86_64 3:19.03.8-3.el7 docker 25 MInstalling for dependencies: container-selinux noarch 2:2.107-3.el7 extras 39 k containerd.io x86_64 1.2.13-3.1.el7 docker 23 M docker-ce-cli x86_64 1:19.03.8-3.el7 docker 40 MTransaction Summary====================================================================================================================================================================================Install 1 Package (+3 Dependent packages)Total download size: 87 MInstalled size: 363 MBackground downloading packages, then exiting:(1/4): container-selinux-2.107-3.el7.noarch.rpm | 39 kB 00:00:00(2/4): containerd.io-1.2.13-3.1.el7.x86_64.rpm | 23 MB 00:00:00(3/4): docker-ce-19.03.8-3.el7.x86_64.rpm | 25 MB 00:00:00(4/4): docker-ce-cli-19.03.8-3.el7.x86_64.rpm | 40 MB 00:00:00------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Total 118 MB/s | 87 MB 00:00:00exiting because "Download Only" specified解压后复制到指标服务器之后进入文件夹装置yum install *.rpm启动dockersystemctl start docker测试docker是否装置胜利docker ps

July 11, 2023 · 1 min · jiezi

关于spring:Spring5-中更优雅的第三方-Bean-注入

小伙伴们晓得,当咱们应用 Spring 容器的时候,如果遇到一些非凡的 Bean,一般来说能够通过如下三种形式进行配置: 动态工厂办法实例工厂办法FactoryBean不过从 Spring5 开始,在 AbstractBeandefinition 类中多了一个属性,对于非凡的 Bean 咱们有了更多的抉择: /** * Specify a callback for creating an instance of the bean, * as an alternative to a declaratively specified factory method. * <p>If such a callback is set, it will override any other constructor * or factory method metadata. However, bean property population and * potential annotation-driven injection will still apply as usual. * @since 5.0 * @see #setConstructorArgumentValues(ConstructorArgumentValues) * @see #setPropertyValues(MutablePropertyValues) */public void setInstanceSupplier(@Nullable Supplier<?> instanceSupplier) { this.instanceSupplier = instanceSupplier;}/** * Return a callback for creating an instance of the bean, if any. * @since 5.0 */@Nullablepublic Supplier<?> getInstanceSupplier() { return this.instanceSupplier;}接下来松哥就来和大家简略聊一聊这个话题。 ...

July 11, 2023 · 3 min · jiezi

关于spring:SpringIoc容器之Aware-京东云技术团队

1 前言Aware是Spring提供的一个标记超接口,批示bean有资格通过回调款式的办法由Spring容器告诉特定的框架对象,以获取到容器中特有对象的实例的办法之一。理论的办法签名由各个子接口确定,但通常只蕴含一个承受单个参数的void返回办法。 2 Spring中9个Aware内置实现|--Aware |--BeanNameAware |--BeanClassLoaderAware |--BeanFactoryAware |--EnvironmentAware |--EmbeddedValueResolverAware |--ResourceLoaderAware |--ApplicationEventPublisherAware |--MessageSourceAware |--ApplicationContextAware9个内置实现又分两类,前三个为间接调用,后6个通过ApplicationContextAwareProcessor后置处理器,间接回调 2.1 BeanNameAwarepublic interface BeanNameAware extends Aware { /** *设置创立此bean的bean工厂中的bean的名称。 *在一般bean属性填充之后但在 *初始化之前回调,如{@link InitializingBean#afterPropertiesSet()} *或自定义初始化办法。 * @param name工厂中bean的名称。 *留神,此名称是工厂中应用的理论bean名称,这可能 *与最后指定的名称不同:特地是对于外部bean * names,理论的bean名称能够通过增加 *“#…”后缀。应用{@link BeanFactoryUtils#originalBeanName(String)} *办法提取原始bean名称(不带后缀),如果需要的话。 * / void setBeanName(String name);}实现BeanNameAware接口须要实现setBeanName()办法,这个办法只是简略的返回咱们以后的beanName,这个接口外表上的作用就是让实现这个接口的bean晓得本人在spring容器里的名字,而且官网的意思是这个接口更多的应用在spring的框架代码中,理论开发环境应该不倡议应用,因为spring认为bean的名字与bean的分割并不是很深,(确实,抛开spring API而言,咱们如果获取了该bean的名字,其实意义不是很大,咱们没有获取该bean的class,只有该bean的名字,咱们也无从下手,相同,因为bean的名称在spring容器中可能是该bean的惟一标识,也就是说再beanDefinitionMap中,key值就是这个name,spring能够依据这个key值获取该bean的所有个性)所以spring说这个不是非必要的依赖。 2.2 BeanClassLoaderAwarepublic interface BeanClassLoaderAware extends Aware { /** *提供bean {@link ClassLoader}类加载器的回调 *一个bean实例在属性的填充之后但在初始化回调之前调用 * {@link InitializingBean * {@link InitializingBean#afterPropertiesSet()} *办法或自定义初始化办法。 * @param类加载器领有的类加载器;可能是{@code null}在例如,必须应用默认的{@code ClassLoader} * 获取的{@code ClassLoader} * {@link org.springframework.util.ClassUtils#getDefaultClassLoader()} * / void setBeanClassLoader(ClassLoader classLoader);}在bean属性填充之后初始化之前,提供类加制器的回调。让受管Bean自身晓得它是由哪一类装载器负责装载的。 ...

July 6, 2023 · 3 min · jiezi

关于spring:BeanFactoryPostProcessor-和-BeanPostProcessor-有什么区别

钻研 Spring 源码的小伙伴可能会发现,Spring 源码中有很多名称特地相近的 Bean,我就不挨个举例了,明天我是想和小伙伴们聊一聊 Spring 中 BeanFactoryPostProcessor 和 BeanPostProcessor 两个处理器的区别。 我将从以下几个方面来和小伙伴们分享。 1. 区别这两个接口说白了都是 Spring 在初始化 Bean 时对外裸露的扩大点,因为 Spring 框架提供的性能不肯定可能满足咱们所有的需要,有的时候咱们须要对其进行扩大,那么这两个接口就是用来做扩大性能的。 其实不必看源码,单纯从字面上看,大家应该也能了解个差不多: BeanFactoryPostProcessor 是针对 BeanFactory 的处理器。BeanPostProcessor 则是针对 Bean 的处理器。咱们先来看下 BeanFactoryPostProcessor 接口: @FunctionalInterfacepublic interface BeanFactoryPostProcessor { void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;}能够看到,这里的参数实际上就是一个 BeanFactory,在这个中央,咱们能够对 BeanFactory 进行批改,从新进行定制。例如能够批改一个 Bean 的作用域,能够批改属性值等。 再来看看 BeanPostProcessor 接口: public interface BeanPostProcessor { @Nullable default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Nullable default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; }}能够看到,这里是两个办法,这两个办法都有一个 bean 对象,阐明这里被触发的时候,Spring 曾经将 Bean 初始化了,而后才会触发这里的两个办法,咱们能够在这里对曾经到手的 Bean 进行额定的解决。其中: ...

July 5, 2023 · 4 min · jiezi

关于spring:Spring-Loaded代码热更新实践和原理分析-京东云技术团队

1、引言开发者在编码效率和疾速迭代中的痛点场景包含: 批改代码后,须要频繁重启利用,导致开发效率低下;实时调试时,不能立刻看到代码批改的后果;大型项目中,重启的工夫老本较高。针对这些问题,本文将深入探讨如何利用Spring Loaded热更新技术进步开发效率,缩小编译和重启工夫。剖析Spring Loaded的热更新原理,以及理论利用过程中所需的操作和注意事项。 2、框架简介Spring Loaded is a JVM agent for reloading class file changes whilst a JVM is running. It transforms classes at loadtime to make them amenable to later reloading. Unlike 'hot code replace' which only allows simple changes once a JVM is running (e.g. changes to method bodies), Spring Loaded allows you to add/modify/delete methods/fields/constructors. The annotations on types/methods/fields/constructors can also be modified and it is possible to add/remove/change values in enum types.Spring Loaded 是一个 JVM 代理,能够在 JVM 运行时从新加载类文件的更改。它会在加载时转换类,以便稍后从新加载。与“热代码替换”只容许在 JVM 运行时进行简略更改(例如更改办法体)不同,Spring Loaded 容许您增加/批改/删除办法/字段/构造函数。还能够批改类型/办法/字段/构造函数上的注解,并且能够增加/删除/更改枚举类型中的值。 ...

July 5, 2023 · 1 min · jiezi

关于spring:Spring容器获取Bean的9种方式-京东云技术团队

1 前言随着SpringBoot的遍及,Spring的应用也越来越广,在某些场景下,咱们无奈通过注解或配置的模式间接获取到某个Bean。比方,在某一些工具类、设计模式实现中须要应用到Spring容器治理的Bean,此时就须要间接获取到对应的Bean。 本文为大家整顿汇总了常见的获取Bean的形式,并提供一些优劣剖析,不便大家在应用到时有更好的抉择。同时,也会为大家适当的遍及和拓展一些相干常识。 2 Spring的IoC容器在Spring中,Bean的实例化、定位、配置应用程序中的对象及建设对象间的依赖关系,都是在IoC容器中进行的。因而,要在Spring中获取Bean,实质上就是从IoC容器当中获取Bean。 在Spring中,BeanFactory是IoC容器的理论代表者,该接口提供了IoC容器最基本功能。同时,Spring还提供了另外一种类型的容器:ApplicationContext容器。 ApplicationContext容器包含BeanFactory容器的所有性能(BeanFactory的子接口),提供了更多面向利用的性能,它提供了国际化反对和框架事件体系,更易于创立理论利用。 个别状况,咱们称BeanFactory为IoC容器,称ApplicationContext为利用上下文。但有时为了不便,也将ApplicationContext称为Spring容器。 通常不倡议应用BeanFactory,但BeanFactory 依然能够用于轻量级的应用程序,如挪动设施或基于applet的应用程序,其中它的数据量和速度是显著。 2.1 BeanFactory与ApplicationContext的区别BeanFactory是Spring框架的基础设施,面向Spring自身。ApplicationContext则面向应用Spring框架的开发者,简直所有的利用场景都能够间接应用ApplicationContext,而非底层的BeanFactory。 另外,ApplicationContext的初始化和BeanFactory有一个重大的区别: BeanFactory在初始化容器时,并未实例化Bean,直到第一次拜访某个Bean时才实例指标Bean。这样,咱们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次应用调用getBean办法才会抛出异样。 而ApplicationContext则在初始化利用上下文时就实例化所有单实例的Bean,绝对应的,ApplicationContext的初始化工夫会比BeanFactory长一些。 理解了上述的根本理论知识之后,咱们就能够尝试从IoC容器当中获取Bean对象了。 3 Bean获取形式3.1 形式一:通过BeanFactory获取通过BeanFactory来获取Bean。基于xml配置文件的时代,能够通过如下形式取得BeanFactory,再通过BeanFactory来取得对应的Bean。 BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));User user = (User) beanFactory.getBean("user");有肯定编程年龄的程序员,应该对此还有一些印象。这种写法预计也只会呈现在古老的我的项目当中。鉴于xml模式配置文件曾经被基于注解模式所代替,同时XmlBeanFactory也被标注为废除。此种形式不举荐应用。 其实,不举荐的理由还有一个,在下面曾经提到,尽量不要应用BeanFactory,而应该应用ApplicationContext。 3.2 形式二 :通过BeanFactoryAware获取在下面的形式中,XmlBeanFactory曾经被废除,但能够通过其余形式来取得BeanFactory,而后再从BeanFactory中取得指定的Bean。获取BeanFactory实例最简略的形式就是实现BeanFactoryAware接口。 BeanFactoryAware接口源码: public interface BeanFactoryAware extends Aware { /** * 初始化回调办法,Spring会主动将BeanFactory注入进去,接管之后即可应用BeanFactory */ void setBeanFactory(BeanFactory beanFactory) throws BeansException;}BeanFactoryAware属于 org.springframework.beans.factory.Aware根标记接口,应用setter注入来在应用程序上下文启动期间获取对象。Aware接口是回调,监听器和观察者设计模式的混合,它示意Bean有资格通过回调形式被Spring容器告诉。 示例如下: @Componentpublic class BeanFactoryHelper implements BeanFactoryAware { private static BeanFactory beanFactory; /** * 重写 BeanFactoryAware 接口的办法 * @param beanFactory :参数赋值给本地属性之后即可应用 BeanFactory * @throws BeansException BeansException */ @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { BeanFactoryHelper.beanFactory = beanFactory; } /** * 依据名称获取容器中的对象实例 * @param beanName :注入的实例必须曾经存在容器中,否则抛异样:NoSuchBeanDefinitionException * @return Object */ public static Object getBean(String beanName) { return beanFactory.getBean(beanName); } /** * 依据 class 获取容器中的对象实例 * @param requiredType :被注入的必须曾经存在容器中,否则抛异样:NoSuchBeanDefinitionException * @param <T> Class * @return 对象 */ public static <T> T getBean(Class<T> requiredType) { return beanFactory.getBean(requiredType); } /** * 判断 spring 容器中是否蕴含指定名称的对象 * @param beanName bean名称 * @return 是否存在 */ public static boolean containsBean(String beanName) { return beanFactory.containsBean(beanName); } //其它需要皆可参考 BeanFactory 接口和它的实现类}在上述工具类中,便是基于BeanFactoryAware的个性,取得了BeanFactory,而后再通过BeanFactory来取得指定的Bean。 ...

July 4, 2023 · 3 min · jiezi

关于spring:这问题巧了SpringMVC-不同参数处理机制引发的思考-京东云技术团队

这个问题十分乏味,不是SpringMVC 的问题,是理论开发中混合应用了两种申请形式裸露进去的。 问题场景功能模块中,提供两个 Http 服务。一个是列表查问(application/json 申请),一个是列表导出(表单申请)。运行环境发现个问题:MVC model 新增加的属性,相似的 Http 申请,一个有值,一个没有 代码如下: /** * application/json 申请。 这种状况 param.field2 有值 ✔ * @param param RequestResponseBodyMethodProcessr 解决 HttpServletRequest 参数 */@PostMapping(value = "query")public ResponseResult<Page<SomeData>> queryByCondition(@RequestBody SomeParam param){ // 业务逻辑...}/** * application/x-www-form-urlencoded 申请 这种状况 param.field2 没有有赋值 ❌ * @param param ServletModelAttributeMethodProcessor 解决 HttpServletRequest 参数 */@PostMapping(value = "export")public void exportExcel(SomeParam param) { // 业务逻辑...}public class SomeParam { // 这个是原有的,有 get set 办法 private String field1; // 这个是新增的,没有get set 办法 (这是一个偶合、意外)。 问题就出在这里。 private String field2;}❓ 依据代码剖析,那应该是 SpringMVC 针对这两种参数解决的机制不同。 针对上述的参数解决,能够参考: RequestResponseBodyMethodProcessor、 ServletModelAttributeMethodProcessor ...

June 26, 2023 · 2 min · jiezi

关于spring:后端面经SpringSpring-中-bean-的生命周期

1.bean简介bean是一个对象,是由Spring中的IoC创立、实例化的对象。个别的java对象,应用的时候创立,不须要就开释内存进行销毁,而bean的生命周期更加简单作用域 singleton:默认单例模式,惟一的bean实例。prototype:每次申请都会创立一个新的bean实例。request:每次HTTP申请都会创立一个新的bean实例,该作用域仅在以后http request内无效。session:每次HTTP申请都会创立一个新的bean实例,该作用域仅在以后HTTP session内无效。global-session:全局session作用域,仅仅在基于portlet的web利用中才有意义,Spring5曾经没有了。2. 生命周期实例化/创立 依附反射进行实例化,而不须要像个别的java对象一样手动创立;属性填充 应用依赖注入进行属性填充;初始化 实现初始化之后,bean能够应用;销毁 容器敞开或者进行服务的时候,销毁该对象;类比 人的毕生:出世->学习->成长->死亡bean的毕生:实例化->属性填充->初始化->销毁3. 参考资料阿里云开发社区-《聊透 Spring bean 的生命周期》 链接粗疏全面讲述了 bean 的生命周期,辅以代码了解。掘金社区-《Spring Bean 生命周期,如同人的毕生》 链接用形象的社会景象类比bean对象的生命周期,易于了解,并且用一个PersonBean作为示例,给出生命周期对应的代码。SegmentFault- 《一文读懂 Spring Bean 的生命周期》 链接具体介绍了bean对象的概念,以及bean的生命周期

June 23, 2023 · 1 min · jiezi

关于spring:Spring源码核心剖析-京东云技术团队

前言SpringAOP作为Spring最外围的能力之一,其重要性显而易见。而后须要晓得的是AOP并不只是Spring特有的性能,而是一种思维,一种通用的性能。而SpringAOP只是在AOP的根底上将能力集成到SpringIOC中,使其作为bean的一种,从而咱们可能很不便的进行应用。 一、SpringAOP的应用形式1.1 应用场景当咱们在日常业务开发中,例如有些功能模块是通用的(日志、权限等),或者咱们须要在某些性能前后去做一些加强,例如在某些办法执行后发送一条mq音讯等。 如果咱们将这些通用模块代码与业务代码放在一块,那么每个业务代码都要写这些通用模块,保护老本与耦合状况都非常重大。 因而,咱们能够将此模块形象进去,就有了”切面“的概念。 1.2 罕用形式AOP的应用形式绝对比较简单,首先咱们须要实现业务代码 @Servicepublic class AopDemo implements AopInterface{ public Student start(String name) { System.out.println("执行业务逻辑代码....."); return new Student(name); }}业务逻辑比较简单,接管一个name参数。 接下来咱们须要创立其对应的切面 //将该切面退出spring容器@Service//申明该类为一个切面@Aspectclass AopAspect { //申明要进行代理的办法 @Pointcut("execution(* com.example.demo.aop.AopInterface.start(..))") public void startAspect() { } //在办法执行之前的逻辑 @Before(value = "startAspect()") public void beforeAspect() { System.out.println("业务逻辑前代码....."); } //在办法执行之后的逻辑 @After(value = "startAspect()") public void afterAspect() { System.out.println("业务逻辑后代码....."); } //围绕办法前后的逻辑 @Around("startAspect()") public Object aroundAspect(ProceedingJoinPoint point) throws Throwable { Object[] requestParams = point.getArgs(); String name = requestParams[0].toString(); System.out.println("传入参数:" + name); requestParams[0] = "bob"; return point.proceed(requestParams); }}能够看到,首先须要咱们指明要代理的对象及办法,而后依据须要抉择不同的注解即可实现代理对象。 ...

June 21, 2023 · 5 min · jiezi

关于spring:Spring-FrameWork从入门到NB-classpath扫描和组件托管

通过@Configuration注解指定包扫描门路后,注解形式能够齐全代替xml配置的形式实现Bean的注入, @Configuration & @ComponentScan@Configuration注解作用在class上,指定以后类为配置类: @Configuration@ComponentScan(value={"springTest"})@PropertySource("classpath:application.properties")public class MyConfiguration { @Bean public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); }}@ComponentScan指定包扫描门路,Spring IoC容器将主动扫描该门路,加载门路下的基于@Component的组件注解(包含@Component、@Service、@Controller、@Repository)的类,作为Bean退出到Spring IoC容器中。 定制化包扫描能够通过@ComponentScan注解的includeFilters和excludeFilters过滤器定制化Spring的扫描行为: @Configuration@ComponentScan(basePackages = "org.example", includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"), excludeFilters = @Filter(Repository.class))public class AppConfig { // ...}Spring反对的过滤器类型: 也就是说,能够通过includeFilters或excludeFilters指定某一个class文件、aspectj类型、正则表达式、或者实现org.springframework.core.type.TypeFilter指定某一个或某一类组件加载或者不加载到Spring IoC容器中。 Spring IoC的通过注解主动扫描加载Bean的形式还是非常灵活的。如果再联合SpringBoot的一系列@Conditional注解,主动拆卸的能力又上了一个台阶,给开发带来的灵活性就更大了。 组件注解Spring提供了包含@Component,@Service,@Controller和@Repository等属性注解,等同于xml配置文件的: <bean id="..." ...> 这类注解加到class上,只有该class文件处于Spring的包扫描门路下,就会在Spring启动过程中被主动注入到Spring IoC容器中。 其中,@Component是根底的组件注解,@Service示意module层的服务组件,@Controller示意是controller层的管制组件,@Repository示意是dao层的数据组件。 其实@Component,@Service,@Controller和@Repository注解底层都是基于@Component实现的,@Service、@Controller和@Repository很大意义上只是一种应用标准,除了@Repository注解有特定的异样解决机制之外,其实注入Spring的时候并没有太大的区别。 @Bean注解@Bean是办法注解,能够和@Component以及@Configuration配合应用。被@Bean注解的办法的返回对象会作为bean退出Spring IoC容器。所以加了@Bean办法的类就相当于一个工厂类,@Bean办法相当于工厂办法,负责生成Spring Bean对象。 @Componentpublic class FactoryMethodComponent { private static int i; @Bean @Qualifier("public") public TestBean publicInstance() { return new TestBean("publicInstance"); } // use of a custom qualifier and autowiring of method parameters @Bean protected TestBean protectedInstance( @Qualifier("public") TestBean spouse, @Value("#{privateInstance.age}") String country) { TestBean tb = new TestBean("protectedInstance", 1); tb.setSpouse(spouse); tb.setCountry(country); return tb; } @Bean private TestBean privateInstance() { return new TestBean("privateInstance", i++); } @Bean @RequestScope public TestBean requestScopedInstance() { return new TestBean("requestScopedInstance", 3); }}以上代码会生成一个name为prototypeInstance、type为TestBean的原型对象,注入到Spring IoC容器中。 ...

June 19, 2023 · 2 min · jiezi

关于spring:一种实现Spring动态数据源切换的方法-京东云技术团队

1 指标不在现有查问代码逻辑上做任何改变,实现dao维度的数据源切换(即表维度) 2 应用场景节约bdp的集群资源。接入新的宽表时,通常uat验证后就会进行集群开释资源,在对应的查问服务器uat环境时须要查问的是生产库的表数据(uat库表因为bdp实时工作进行,没有数据落入),只进行服务器配置文件的改变而无需进行代码的批改变更,即可按需切换查问的数据源。 2.1 实时工作对应的集群资源[]() 2.2 实时工作产生的数据进行存储的两套环境[]() 2.3 数据应用零碎的两套环境(查问展现数据)[]() 即须要在zhongyouex-bigdata-uat中查问生产库的数据。 3 实现过程3.1 实现重点org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource spring提供的这个类是本次实现的外围,可能让咱们实现运行时多数据源的动静切换,然而数据源是须要当时配置好的,无奈动静的减少数据源。Spring提供的Aop拦挡执行的mapper,进行切换判断并进行切换。注:另外还有一个就是ThreadLocal类,用于保留每个线程正在应用的数据源。 3.2 AbstractRoutingDataSource解析public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean{ @Nullable private Map<Object, Object> targetDataSources; @Nullable private Object defaultTargetDataSource; @Override public Connection getConnection() throws SQLException { return determineTargetDataSource().getConnection(); } protected DataSource determineTargetDataSource() { Assert.notNull(this.resolvedDataSources, "DataSource router not initialized"); Object lookupKey = determineCurrentLookupKey(); DataSource dataSource = this.resolvedDataSources.get(lookupKey); if (dataSource == null && (this.lenientFallback || lookupKey == null)) { dataSource = this.resolvedDefaultDataSource; } if (dataSource == null) { throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]"); } return dataSource; } @Override public void afterPropertiesSet() { if (this.targetDataSources == null) { throw new IllegalArgumentException("Property 'targetDataSources' is required"); } this.resolvedDataSources = new HashMap<>(this.targetDataSources.size()); this.targetDataSources.forEach((key, value) -> { Object lookupKey = resolveSpecifiedLookupKey(key); DataSource dataSource = resolveSpecifiedDataSource(value); this.resolvedDataSources.put(lookupKey, dataSource); }); if (this.defaultTargetDataSource != null) { this.resolvedDefaultDataSource = resolveSpecifiedDataSource(this.defaultTargetDataSource); } }从下面源码能够看出它继承了AbstractDataSource,而AbstractDataSource是javax.sql.DataSource的实现类,领有getConnection()办法。获取连贯的getConnection()办法中,重点是determineTargetDataSource()办法,它的返回值就是你所要用的数据源dataSource的key值,有了这个key值,resolvedDataSource(这是个map,由配置文件中设置好后存入targetDataSources的,通过targetDataSources遍历存入该map)就从中取出对应的DataSource,如果找不到,就用配置默认的数据源。 ...

June 19, 2023 · 2 min · jiezi

关于spring:Spring-FrameWork从入门到NB-Lazyinitialized-Beans

懒加载Bean(Lazy-initialized Beans)的概念其实很简略,就是通过设置,Spring IoC容器在初始化的过程中不对单例Bean进行实例化,而默认状况下会。 默认状况下Spring IoC容器初始化的过程中会创立所有的单例Bean,“创立”的步骤包含实例化、以及实例化之后的属性填充,以及其余可能的前置后置操作(各种beanpostprocessor),都会被调用。 个别状况下这种提前实例化也是必要的,因为提前实例化能够在系统启动的过程中执行,能够提前裸露实例创立以及依赖注入的谬误。而懒加载则不同,这类谬误须要在Bean被调用的时候能力裸露,而Bean被调用很可能是在系统启动之后的某一时刻,很可能是在系统升级上线的几天之后了。 懒加载能够通过xml配置文件指定: <bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/><bean name="not.lazy" class="com.something.AnotherBean"/>也能够通过@Lazy注解指定。 咱们对后面的用例简略批改验证一下懒加载。首先批改DependencyB,减少无参结构器,打印一句话: public DependencyB(){ System.out.println("This is DependencyB constructor running..."); }同样,DependencyA也减少结构器: public DependencyA(){ System.out.println("This is DependencyA constructor running..."); }而后批改启动类,只初始化Spring,不获取Bean: public class App { public static void main(String[] args) { ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfiguration.class); System.out.println("I am Ok...");}执行启动类: This is DependencyB constructor running...This is DependencyA constructor running...I am Ok...尽管没有从Spring IoC容器获取Bean,然而能够看到DependencyB和DependencyA都被实例化了。 上面首先批改DependencyA,懒加载: @Service@Primary@Scope("prototype")@Lazypublic class DependencyA implements IDependencyA { @Override public void test(){ System.out.println("I am DependencyA test..."); } public DependencyA(){ System.out.println("This is DependencyA constructor running..."); }}执行启动类: ...

June 16, 2023 · 1 min · jiezi

关于spring:Spring-循环依赖那些事儿含Spring详细流程图

背景1 循环依赖异样信息利用工夫工夫久利用多人同时并行开发利用保障迭代进度经常出现启动时呈现循环依赖异样 Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'taskPunchEvent': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'playContentService': Bean with name 'playContentService' has been injected into other beans [toVoConvertor] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example. at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:325) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1404) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:592) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:277) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1255) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1175) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:595) ... 40 moreCaused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'playContentService': Bean with name 'playContentService' has been injected into other beans [toVoConvertor] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:622) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.resolveBeanByName(AbstractAutowireCapableBeanFactory.java:452) at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:527) at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:497) at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:637) at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:180) at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90) at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:322) ... 51 more2 依赖关系先不关注其余不标准问题,看景象 ...

June 15, 2023 · 9 min · jiezi

关于spring:聊聊如何利用服务定位器模式按需返回我们需要的服务实例

前言什么是服务定位器模式服务定位器是一个理解如何提供各种利用所需的服务(或组件)的对象。在服务定位器中,每个服务(或组件)都只有一个独自的实例,并通过ID 惟一地标识。 用这个 ID 就能从服务定位器中失去这个服务(或组件)。 何时能够思考应用服务定位器模式服务定位器模式的目标是按需返回服务实例,当依赖是按需的或须要在运行时查找时,咱们能够应用服务定位器模式将客户端与具体实现解耦。 服务定位器蕴含的组件客户端:在运行时须要服务的消费者。服务定位器:服务定位器负责将服务按需返回给客户端。它形象了服务的查找或创立。初始上下文:它创立、注册和缓存服务。这是查找和创立的终点。服务工厂: 服务工厂为服务提供生命周期治理,反对创立、查找或删除服务。服务:客户所需服务的具体实现。服务定位器执行流程 上面咱们就以一个模仿发送短信的例子,来体验一把服务定位器模式。因spring曾经提供了服务定位器,本示例就以spring提供的服务定位器为例 前置常识spring 服务定位器spring的服务定位器次要是通过ServiceLocatorFactoryBean实现。它实现 FactoryBean接口,并封装了服务定位器模式的所有设计组件,为客户端提供了一个洁净的 API 以按需获取对象 spring服务定位器实现流程 示例1、定义一个实体类,这个实体类后边插件绑定具体短信服务会用到@Data@AllArgsConstructor@NoArgsConstructor@Builderpublic class SmsRequest implements Serializable { private Map<String,Object> metaDatas; private String to; private String message; private SmsType smsType;}2、定义短信发送服务接口public interface SmsProvider { SmsResponse sendSms(SmsRequest smsRequest);}3、定义短信服务定位器工厂,用来选取具体的短信服务public interface SmsFactory { SmsProvider getProvider(SmsType smsType);}4、定义短信发送具体实现类@Componentpublic class AliyunSmsProvider implements SmsProvider { @Override public SmsResponse sendSms(SmsRequest smsRequest) { System.out.println("来自阿里云短信:" + smsRequest); return SmsResponse.builder() .code("200").message("发送胜利") .success(true).result("阿里云短信的回执").build(); }}注: 该具体服务必须是spring的bean 5、配置ServiceLocatorFactoryBean @Bean @ConditionalOnMissingBean public FactoryBean smsFactory(){ ServiceLocatorFactoryBean serviceLocatorFactoryBean = new ServiceLocatorFactoryBean(); serviceLocatorFactoryBean.setServiceLocatorInterface(SmsFactory.class); // spring beanName映射,自定义名称映射关系, Properties properties = new Properties(); properties.setProperty(SmsType.ALIYUN.toString(),"aliyunSmsProvider"); properties.setProperty(SmsType.TENCENT.toString(),"tencentSmsProvider"); serviceLocatorFactoryBean.setServiceMappings(properties); return serviceLocatorFactoryBean; }注: 短信服务定位器工厂,实质是通过beanName来找到具体的短信服务,如下示例 ...

June 13, 2023 · 2 min · jiezi

关于spring:Spring-Boot-启动注解分析

@[toc]尽管咱们在日常开发中,Spring Boot 应用十分多,算是目前 Java 开发畛域一个标配了,然而小伙伴们认真想想本人的面试经验,和 Spring Boot 相干的面试题都有哪些?个人感觉应该是比拟少的,Spring Boot 实质上还是已经 SSM 那一套,只是通过各种 starter 简化了配置而已,其余都是截然不同的,所以 Spring Boot 中很多面试题还是得回归到 Spring 中去解答!当然这并不是说 Spring Boot 中没什么可问的,Spring Boot 中其实也有一个十分经典的面试题,那就是 Spring Boot 中的自动化配置是怎么实现的?明天松哥就来和各位小伙伴聊一下这个问题。 其实松哥之前和小伙伴们聊过相干的问题,不过都是零散的,没有零碎梳理过,之前也率领小伙伴们自定义过一个 starter,置信各位小伙伴对于 starter 的原理也有肯定理解,所以明天这篇文章一些过于细节的内容我就不赘述了,大家能够翻看之前的文章。 1. @SpringBootApplication要说 Spring Boot 的自动化配置,那必须从我的项目的启动类 @SpringBootApplication 说起,这是整个 Spring Boot 宇宙的终点,咱们先来看下这个注解: @Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })public @interface SpringBootApplication {}能够看到,@SpringBootApplication 注解组合了多个常见注解的性能,其中: 前四个是元注解,这里咱们不做探讨。第五个 @SpringBootConfiguration 是一个反对配置类的注解,这里咱们也不做探讨。第六个 @EnableAutoConfiguration 这个注解就示意开启自动化配置,这是咱们明天要聊得重点。第七个 @ComponentScan 是一个包扫描注解,为什么 Spring Boot 我的项目中的 Bean 只有放对地位就会被主动扫描到,和这个注解无关。别看这里注解多,其实真正由 Spring Boot 提供的注解一共就两个,别离是 @SpringBootConfiguration 和 @EnableAutoConfiguration 两个,其余注解在 Spring Boot 呈现之前就曾经存在多年了。 ...

June 2, 2023 · 3 min · jiezi

关于spring:Spring5327源码构建

首先第一步先上官网文档,能够大略看一眼,步骤很简略,整体总来说就两步。当初开始入手搞 下载源码,个别是从git上,如果上不去就从我的网盘下吧。特地揭示 下载zip包时要选对版本,看一下如何选的在这个地位看图下载zip包,放到在本人要放的地位解压关上3. 这个只针对于5.3.27的所以gradle也必须要指定7.5.1版本,搞好配置,不会的话能够百度一下,很简略gradle指定版本 属性distributionUrl外面有gradle版本 distributionBase=GRADLE_USER_HOMEdistributionPath=wrapper/distsdistributionUrl=<https://services.gradle.org/distributions/gradle-7.5.1-bin.zip>zipStoreBase=GRADLE\_USER\_HOMEzipStorePath=wrapper/dists4. 后面两步筹备好后,关上命令行界面,win+R 走到spring-framework-5.3.27目录底下 5. 在命令行中执行命令依照官网文档来就行 win不须要./ linux 须要 gradlew build6. 而后你就发现你报错了,谬误如下贼烦来!!!搞搞问题在这,刚刚下了zip解压的,这个又是git我的项目,所以没有add,你想想就晓得了,解决一下 git init .git add .git statusgit commit --am 'fix: Git command error'没坑你哦,挨个执行完了就能行电脑忒慢的话能够等等,我午睡一会儿写(我电脑也很慢,没法截图进行不上来了) 弄完后就用idea关上这个我的项目,持续搞 7. 在idea中配置好gradle8. 批改这两个文件增加阿里的仓库配置,墙外的链接拜访太慢,没工具的也连不上 maven{ url 'https://maven.aliyun.com/repository/central'}maven{ url 'https://maven.aliyun.com/repository/public' }maven{ url 'https://maven.aliyun.com/repository/google'}maven{ url 'https://maven.aliyun.com/repository/gradle-plugin'}maven{ url 'https://maven.aliyun.com/repository/spring'}maven{ url 'https://maven.aliyun.com/repository/spring-plugin'}maven{ url 'https://maven.aliyun.com/mvn/guide'}maven{ url 'https://maven.aliyun.com/repository/apache-snapshots'}9. 改一下代码 避免编译出错 if (method.canAccess(null) && !KCallablesJvm.isAccessible(function)) { KCallablesJvm.setAccessible(function, true); }在这一步的时候,有可能你会呈现 点击build后发现还有这个谬误当然如果你们把maven仓库地址全都复制过来,应该遇不到这个问题,这个问题的起因是如果是拜访这个谬误外面的地址,会发现须要输出账号密码,咱们加了阿里仓库的全家桶,这个当前就不会呈现这个问题了。如果还呈现就重启一下idea,清一下缓存试试 而后从侧边中的gradle中点击spring底下的Tasks-->other-->compileTestJava如下图最初通过idea的奋力奔跑呈现了下图这个样子就阐明胜利了接下来就能够本人建一个模块连忙试试吧

June 2, 2023 · 1 min · jiezi

关于spring:聊聊如何利用spring插件来实现策略模式

前言偶尔的机会发现spring有个spring-plugin,官网对它的介绍是 Spring Plugin provides a more pragmatic approach to plugin development by providing the core flexibility of having plugin implementations extending a core system's functionality but of course not delivering core OSGi features like dynamic class loading or runtime installation and deployment of plugins. Although Spring Plugin thus is not nearly as powerful as OSGi, it serves a poor man's requirements to build a modular extensible application.粗心就是Spring插件提供了一种更实用的插件开发方法,它提供了插件实现扩大外围零碎性能的外围灵活性,但当然不提供外围OSGi性能,如动静类加载或运行时装置和部署插件。只管Spring插件因而不如OSGi弱小,但它满足了富人构建模块化可扩大应用程序的需要。 本文就来聊下如何应用spring插件来实现策略模式 应用spring-plugin插件实现策略模式步骤1、在我的项目中的pom引入spring-plugin <dependency> <groupId>org.springframework.plugin</groupId> <artifactId>spring-plugin-core</artifactId> <version>2.0.0.RELEASE<version> </dependency>注: springboot 2.2以下版本默认曾经集成spring-plugin-core,因而无需指定版本号。不过集成的版本号比拟低,而且局部办法与高版本不兼容 ...

May 23, 2023 · 1 min · jiezi

关于spring:Spring-国际化简介

ResourceBundleJava 提供了ResourceBundle类,能够实现特定区域(语言环境)的资源加载。以便咱们在编码时能够应用一个ResourceBundle而不必思考特定的语言环境信息,程序主动通过以后的用户的语言环境信息来加载特定资源。ResourceBundle的应用比较简单,如上面的代码。 public class ResourceBundleDemo { public static void main(String[] args) throws UnsupportedEncodingException { ResourceBundle rb = ResourceBundle.getBundle("test"); System.out.println(rb.getString("key1")); rb = ResourceBundle.getBundle("test", Locale.GERMAN); System.out.println(rb.getString("key1")); }}ResourceBundle通过了getBundle 工厂办法创立的实例默认被缓存。如果ResourceBundle被缓存屡次调用getBundle 办法返回同一个实例。getBundle 的客户端能够革除缓存、治理缓存的缓存工夫。参考:clearCache办法、ResourceBundle.Control.getTimeToLive和ResourceBundle.Control.needsReload。 Java平台提供了ResourceBundle两个子类ListResourceBundle 和PropertyResourceBundle。 ResourceBundle工厂办法在加载资源的过程中须要ResourceBundle.Control提供必须要的信息。ResourceBundle.Control 提供包含:bundle名字、创立Bundle、资源寻找等信息。默认的ResourceBundle.Control 能够被笼罩。默认的ResourceBundle.Control实现了两种格局资源的加载,class 格局和properties 格局。例如上面的代码: ResourceBundle rb = ResourceBundle.getBundle(“MyResource”); 那么会加载MyResource.class(及相干语言区域后缀的class),如果class找不到,会加载MyResource.properties(及相干语言区域后缀的properties)的相干文件(通过PropertyResourceBundle)。 MessageFomatMessageFormat 提供了以与语言无关形式生成连贯音讯的形式。应用此办法结构向终端用户显示的音讯。MessageFormat 自身不实现特定于语言环境的行为。特定于语言环境的行为是由所提供的模式和用于已插入参数的子格局来定义的。MessageFormat 简略实例如下代码: int planet = 7; String event = "a disturbance in the Force"; String result = MessageFormat.format( "At {1,time} on {1,date}, there was {2} on planet {0,number,integer}.", planet, new Date(), event);MessageFormat具体应用以及pattern模式能够参考javadoc。MessageFormat能够和ResourceBundle配合做到个不同语言环境的用户显示。 ...

May 11, 2023 · 2 min · jiezi

关于spring:springboot升级过程中踩坑定位分析记录-京东云技术团队

作者:京东批发 李文龙 1.背景“ 俗话说:为了修复一个小bug而引入了一个更大bug ”因所负责的零碎应用的spring框架版本5.1.5.RELEASE在线上出过一个偶发的小事变,最初定位为spring-context中的一个bug导致的。 为了修复此bug进行了spring版本的降级,最终定的版本为收银台团队应用的版本5.2.12.RELEASE,对应的springboot版本为2.2.12.RELEASE。 抉择这个版本的起因是: 1.有团队通过了长时间的线上验证 2.修复了5.1.5.RELEASE对应的bug 2.降级上线降级相干版本后在预发环境进行了验证,暂未遇到对于框架的问题。本认为平安降级实现,在上线过程中发现在APP中无法访问,此时还未挂载流量。 日志中剖析是某些参数未解析到,后在nginx日志中查到相干申请,应用postman模仿申请能够失常应用。 3.剖析验证定位起因1.长期修复在代码统一的状况下,惟一的可能就只能是线上与预发配置不同,经比照剖析得出是某个过滤器的程序在线上未配置,依照预发的配置后可失常应用。咱们暂且称批改的这两个过滤器为M和A, 其中默认状况下执行程序为M->A,程序批改为A->M后失常,其两者作用大抵为: M : 通用过滤器,解析url中的参数至parameterMap中,并初始化读取了body中的inputstream进行了byte数组的缓存,用于解决反复读取流问题 A: 特定处理器,先是查问parameter中的参数,而后逻辑解决后再设置一些非凡参数。2.为何须要改过滤器程序经查未降级前过滤器的程序与降级后过滤器程序统一,为何降级spring框架后须要批改配置。此时猜想可能是spring在降级过程中批改了一部分代码, 但未有脉络,只能先调转方向剖析为什么postman和浏览器中的swagger能够失常应用 3.剖析nginx日志前端申请与postman申请的nginx日志进行了剖析得出了起因,比照日志如下: postman : POST /shop/bpaas/floor?client&clientVersion&ip=111.202.149.19&gfid=getShopMainFloor&body= 前端 : POST /shop/bpaas/floor HTTP/1.0" 200 634 "-" "api" "0.94" 0.008 0.007 client&clientVersion&ip=111.202.149.17&gfid=getShopMainFloor&body=通过以上比照发现尽管postman应用了post申请,但数据还是搁置在url中,在通过零碎的一个内置过滤器M时将url中的参数解析到了parameterMap中,后续过滤器能够应用 request.getParameter获取到,留神此办法是解决问题的要害,此时还未意识到。 4.降级前后框架是否有大的批改因降级的版本是降级了一个小版本号,所以不好比照降级的buglist,只能缓缓进行剖析,后在剖析过滤器时发现降级spring后过滤器个数由11个缩小到了10个,缩小了那一个为: org.springframework.web.filter.HiddenHttpMethodFilter此过虑器的作用是在浏览器不反对PUT、DELETE、PATCH等method时,能够在form表单中应用暗藏的_method参数反对这几种method。如同跟参数解析没有任何关系, 持续剖析降级版本中 (由2.1.3.RELEASE->2.2.12.RELEASE)是否批改了此过滤器的一些内容,后在2.2.0.M5的release notes中发现HiddenHttpMethodFilter相干的: “ Disable auto-configuration of HiddenHttpMethodFilter by default ” github上对应的版本release notes: https://github.com/spring-projects/spring-boot/releases/tag/v2.2.0.M5也就是说降级后HiddenHttpMethodFilter默认配置由enable批改为了disable,如果再批改回去是不是能够修复参数解析的问题呢? 5.增加过滤器enable配置因bug修复列表中有对应的issues,所以找到了此过滤器对应的配置: -Dspring.mvc.hiddenmethod.filter.enabled=true增加后能够失常应用,证实是此过滤器中在某种条件下不可短少。 6.未降级spring版本时disable验证在确认未降级版本的spring反对此参数的状况下,增加了以上参数,将默认的启动批改成了禁用,教训证:在不代码批改的状况下,无此过滤器时参数无奈解析。证实了上步的猜想。 7.深刻源码剖析此时须要剖析HiddenHttpMethodFilter过滤器中是否有非凡操作,源码如下: protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { HttpServletRequest requestToUse = request; if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) { String paramValue = request.getParameter(this.methodParam); if (StringUtils.hasLength(paramValue)) { String method = paramValue.toUpperCase(Locale.ENGLISH); if (ALLOWED_METHODS.contains(method)) { requestToUse = new HttpMethodRequestWrapper(request, method); } } } filterChain.doFilter(requestToUse, response); }剖析以上源码能够发现,有且只有一种可能,就是request.getParameter可能是解决问题的是要害。 ...

April 28, 2023 · 1 min · jiezi

关于spring:一天吃透SpringMVC面试八股文

说说你对 SpringMVC 的了解SpringMVC是一种基于 Java 的实现MVC设计模型的申请驱动类型的轻量级Web框架,属于Spring框架的一个模块。 它通过一套注解,让一个简略的Java类成为解决申请的控制器,而无须实现任何接口。同时它还反对RESTful编程格调的申请。 什么是MVC模式?MVC的全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,是一种软件设计榜样。它是用一种业务逻辑、数据与界面显示拆散的办法来组织代码,将泛滥的业务逻辑汇集到一个部件外面,在须要改良和个性化定制界面及用户交互的同时,不须要从新编写业务逻辑,达到缩小编码的工夫。 View,视图是指用户看到并与之交互的界面。比方由html元素组成的网页界面,或者软件的客户端界面。MVC的益处之一在于它能为利用程序处理很多不同的视图。在视图中其实没有真正的解决产生,它只是作为一种输入数据并容许用户操纵的形式。 model,模型是指模型表示业务规定。在MVC的三个部件中,模型领有最多的解决工作。被模型返回的数据是中立的,模型与数据格式无关,这样一个模型能为多个视图提供数据,因为利用于模型的代码只需写一次就能够被多个视图重用,所以缩小了代码的重复性。 controller,控制器是指控制器承受用户的输出并调用模型和视图去实现用户的需要,控制器自身不输入任何货色和做任何解决。它只是接管申请并决定调用哪个模型构件去解决申请,而后再确定用哪个视图来显示返回的数据。 SpringMVC 有哪些长处?与 Spring 集成应用十分不便,生态好。配置简略,疾速上手。反对 RESTful 格调。反对各种视图技术,反对各种申请资源映射策略。本文曾经收录到Github仓库,该仓库蕴含计算机根底、Java根底、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等外围知识点,欢送star~ Github地址 如果拜访不了Github,能够拜访gitee地址。 gitee地址 Spring MVC和Struts的区别Spring MVC是基于办法开发,Struts2是基于类开发的。 Spring MVC会将用户申请的URL门路信息与Controller的某个办法进行映射,所有申请参数会注入到对应办法的形参上,生成Handler对象,对象中只有一个办法;Struts每解决一次申请都会实例一个Action,Action类的所有办法应用的申请参数都是Action类中的成员变量,随着办法增多,整个Action也会变得凌乱。Spring MVC反对单例开发模式,Struts只能应用多例 Struts因为只能通过类的成员变量接管参数,故只能应用多例。Struts2 的外围是基于一个Filter即StrutsPreparedAndExcuteFilter,Spring MVC的外围是基于一个Servlet即DispatcherServlet(前端控制器)。Struts处理速度略微比Spring MVC慢,Struts应用了Struts标签,加载数据较慢。Spring MVC的工作原理Spring MVC的工作原理如下: DispatcherServlet 接管用户的申请找到用于解决request的 handler 和 Interceptors,结构成 HandlerExecutionChain 执行链找到 handler 绝对应的 HandlerAdapter执行所有注册拦截器的preHandler办法调用 HandlerAdapter 的 handle() 办法解决申请,返回 ModelAndView倒序执行所有注册拦截器的postHandler办法申请视图解析和视图渲染 Spring MVC的次要组件?前端控制器(DispatcherServlet):接管用户申请,给用户返回后果。处理器映射器(HandlerMapping):依据申请的url门路,通过注解或者xml配置,寻找匹配的Handler。处理器适配器(HandlerAdapter):Handler 的适配器,调用 handler 的办法解决申请。处理器(Handler):执行相干的申请解决逻辑,并返回相应的数据和视图信息,将其封装到ModelAndView对象中。视图解析器(ViewResolver):将逻辑视图名解析成真正的视图View。视图(View):接口类,实现类可反对不同的View类型(JSP、FreeMarker、Excel等)。Spring MVC的罕用注解由有哪些?@Controller:用于标识此类的实例是一个控制器。@RequestMapping:映射Web申请(拜访门路和参数)。@ResponseBody:注解返回数据而不是返回页面@RequestBody:注解实现接管 http 申请的 json 数据,将 json 数据转换为 java 对象。@PathVariable:取得URL中门路变量中的值@RestController:@Controller+@ResponseBody@ExceptionHandler标识一个办法为全局异样解决的办法。@Controller 注解有什么用?@Controller 注解标记一个类为 Spring Web MVC 控制器。Spring MVC 会将扫描到该注解的类,而后扫描这个类上面带有 @RequestMapping 注解的办法,依据注解信息,为这个办法生成一个对应的处理器对象,在下面的 HandlerMapping 和 HandlerAdapter组件中讲到过。 ...

April 22, 2023 · 2 min · jiezi

关于spring:吃透SpringMVC面试八股文

说说你对 SpringMVC 的了解SpringMVC是一种基于 Java 的实现MVC设计模型的申请驱动类型的轻量级Web框架,属于Spring框架的一个模块。 它通过一套注解,让一个简略的Java类成为解决申请的控制器,而无须实现任何接口。同时它还反对RESTful编程格调的申请。 什么是MVC模式?MVC的全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,是一种软件设计榜样。它是用一种业务逻辑、数据与界面显示拆散的办法来组织代码,将泛滥的业务逻辑汇集到一个部件外面,在须要改良和个性化定制界面及用户交互的同时,不须要从新编写业务逻辑,达到缩小编码的工夫。 View,视图是指用户看到并与之交互的界面。比方由html元素组成的网页界面,或者软件的客户端界面。MVC的益处之一在于它能为利用程序处理很多不同的视图。在视图中其实没有真正的解决产生,它只是作为一种输入数据并容许用户操纵的形式。 model,模型是指模型表示业务规定。在MVC的三个部件中,模型领有最多的解决工作。被模型返回的数据是中立的,模型与数据格式无关,这样一个模型能为多个视图提供数据,因为利用于模型的代码只需写一次就能够被多个视图重用,所以缩小了代码的重复性。 controller,控制器是指控制器承受用户的输出并调用模型和视图去实现用户的需要,控制器自身不输入任何货色和做任何解决。它只是接管申请并决定调用哪个模型构件去解决申请,而后再确定用哪个视图来显示返回的数据。 SpringMVC 有哪些长处?与 Spring 集成应用十分不便,生态好。配置简略,疾速上手。反对 RESTful 格调。反对各种视图技术,反对各种申请资源映射策略。本文曾经收录到Github仓库,该仓库蕴含计算机根底、Java根底、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等外围知识点,欢送star~ Github地址 如果拜访不了Github,能够拜访gitee地址。 gitee地址 Spring MVC和Struts的区别Spring MVC是基于办法开发,Struts2是基于类开发的。 Spring MVC会将用户申请的URL门路信息与Controller的某个办法进行映射,所有申请参数会注入到对应办法的形参上,生成Handler对象,对象中只有一个办法;Struts每解决一次申请都会实例一个Action,Action类的所有办法应用的申请参数都是Action类中的成员变量,随着办法增多,整个Action也会变得凌乱。Spring MVC反对单例开发模式,Struts只能应用多例 Struts因为只能通过类的成员变量接管参数,故只能应用多例。Struts2 的外围是基于一个Filter即StrutsPreparedAndExcuteFilter,Spring MVC的外围是基于一个Servlet即DispatcherServlet(前端控制器)。Struts处理速度略微比Spring MVC慢,Struts应用了Struts标签,加载数据较慢。Spring MVC的工作原理Spring MVC的工作原理如下: DispatcherServlet 接管用户的申请找到用于解决request的 handler 和 Interceptors,结构成 HandlerExecutionChain 执行链找到 handler 绝对应的 HandlerAdapter执行所有注册拦截器的preHandler办法调用 HandlerAdapter 的 handle() 办法解决申请,返回 ModelAndView倒序执行所有注册拦截器的postHandler办法申请视图解析和视图渲染 Spring MVC的次要组件?前端控制器(DispatcherServlet):接管用户申请,给用户返回后果。处理器映射器(HandlerMapping):依据申请的url门路,通过注解或者xml配置,寻找匹配的Handler。处理器适配器(HandlerAdapter):Handler 的适配器,调用 handler 的办法解决申请。处理器(Handler):执行相干的申请解决逻辑,并返回相应的数据和视图信息,将其封装到ModelAndView对象中。视图解析器(ViewResolver):将逻辑视图名解析成真正的视图View。视图(View):接口类,实现类可反对不同的View类型(JSP、FreeMarker、Excel等)。Spring MVC的罕用注解由有哪些?@Controller:用于标识此类的实例是一个控制器。@RequestMapping:映射Web申请(拜访门路和参数)。@ResponseBody:注解返回数据而不是返回页面@RequestBody:注解实现接管 http 申请的 json 数据,将 json 数据转换为 java 对象。@PathVariable:取得URL中门路变量中的值@RestController:@Controller+@ResponseBody@ExceptionHandler标识一个办法为全局异样解决的办法。@Controller 注解有什么用?@Controller 注解标记一个类为 Spring Web MVC 控制器。Spring MVC 会将扫描到该注解的类,而后扫描这个类上面带有 @RequestMapping 注解的办法,依据注解信息,为这个办法生成一个对应的处理器对象,在下面的 HandlerMapping 和 HandlerAdapter组件中讲到过。 ...

April 20, 2023 · 2 min · jiezi

关于spring:Spring自定义参数解析器设计

作者:京东批发 王鹏超 1.什么是参数解析器@RequstBody、@RequstParam 这些注解是不是很相熟? 咱们在开发Controller接口时常常会用到此类参数注解,那这些注解的作用是什么?咱们真的理解吗? 简略来说,这些注解就是帮咱们将前端传递的参数间接解析成间接能够在代码逻辑中应用的javaBean,例如@RequstBody接管json参数,转换成java对象,如下所示: 前台传参参数格局{ "userId": 1, "userName": "Alex"}application/json失常代码书写如下: @RequestMapping(value = "/getUserInfo")public String getUserInfo(@RequestBody UserInfo userInfo){ //*** return userInfo.getName();}但如果是服务接管参数的形式扭转了,如下代码,参数就不能胜利接管了,这个是为什么呢? @RequestMapping(value = "/getUserInfo")public String getUserInfo(@RequestBody String userName, @RequestBody Integer userId){ //*** return userName;}如果下面的代码略微改变一下注解的应用并且前台更改一下传参格局,就能够失常解析了。 前台传参参数格局http://*?userName=Alex&userId=1无@RequestMapping(value = "/getUserInfo")public String getUserInfo(@RequestParam String userName, @RequestParam Integer userId){ //*** return userName;}这些这里就不得不引出这些注解背地都对应的内容—Spring提供的参数解析器,这些参数解析器帮忙咱们解析前台传递过去的参数,绑定到咱们定义的Controller入参上,不通类型格局的传递参数,须要不同的参数解析器,有时候一些非凡的参数格局,甚至须要咱们自定义一个参数解析器。 不论是在SpringBoot还是在Spring MVC中,一个HTTP申请会被DispatcherServlet类接管(实质是一个Servlet,继承自HttpServlet)。Spring负责从HttpServlet中获取并解析申请,将申请uri匹配到Controller类办法,并解析参数并执行办法,最初解决返回值并渲染视图。 参数解析器的作用就是将http申请提交的参数转化为咱们controller处理单元的入参。原始的Servlet获取参数的形式如下,须要手动从HttpServletRequest中获取所需信息。 @WebServlet(urlPatterns="/getResource")public class resourceServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) { /**获取参数开始*/ String resourceId = req.getParameter("resourceId"); String resourceType = req.getHeader("resourceType"); /**获取参数完结*/ resp.setContentType("text/html;charset=utf-8"); PrintWriter out = resp.getWriter(); out.println("resourceId " + resourceId + " resourceType " + resourceType); }}Spring为了帮忙开发者解放生产力,提供了一些特定格局(header中content-type对应的类型)入参的参数解析器,咱们在接口参数上只有加上特定的注解(当然不加注解也有默认解析器),就能够间接获取到想要的参数,不须要咱们本人去HttpServletRequest中手动获取原始入参,如下所示: ...

April 14, 2023 · 4 min · jiezi

关于spring:连接池-HikariPool-二-获取及关闭连接

获取数据库连贯是通过DataSource发动的,如果利用应用HikariPool作为连接池的话,须要配置DataSource为HikariDataSource,利用通过调用HikariDataSource的getConnection办法获取数据库连贯。 敞开数据库连贯是间接调用获取到的数据库连贯对象(Connection对象)的close办法实现的。 明天要钻研的课题:获取及敞开数据库连贯,获取数据库连贯其实就是从HikariDataSource的getConnection办法动手,直到getConnection办法获取到数据库连贯对象。而后再钻研数据库连贯对象的close办法。 获取数据库连贯间接看HikariDataSource的getConnection办法: public Connection getConnection() throws SQLException { if (isClosed()) { throw new SQLException("HikariDataSource " + this + " has been closed."); } if (fastPathPool != null) { return fastPathPool.getConnection(); } // See http://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java HikariPool result = pool; if (result == null) { synchronized (this) { result = pool; if (result == null) { validate(); LOGGER.info("{} - Starting...", getPoolName()); try { pool = result = new HikariPool(this); this.seal(); } catch (PoolInitializationException pie) { if (pie.getCause() instanceof SQLException) { throw (SQLException) pie.getCause(); } else { throw pie; } } LOGGER.info("{} - Start completed.", getPoolName()); } } }fastPathPool在HikariDataSource实例化的时候被初始化为HikariPool对象。如果fastPathPool在getConnection办法调用的时候尚未初始化,就在getConnection办法内实现初始化。之后再调用HikariPool对象的getConnection办法。 ...

April 11, 2023 · 3 min · jiezi

关于spring:Spring-探索丨既生Resource何生Autowired

提到Spring依赖注入,大家最先想到应该是@Resource和@Autowired,很多文章只是解说了性能上的区别,对于Spring为什么要反对两个这么相似的注解却未提到,属于知其然而不知其所以然。不知大家在应用这两个注解的时候有没有想过,@Resource又反对名字又反对类型,还要@Autowired干嘛,难道是Spring官网没事做了? 真的是没事做了吗?读了本文你将会理解到: @Resource和@Autowired起源Spring官网为什么会反对这两个性能如此类似的注解?为什么@Autowired属性注入的时候Idea会曝出黄色的正告?@Resource和@Autowired举荐用法起源既然要弄清楚,就要先理解他们的身世。 @Resource 于 2006年5月11日随着JSR 250 公布 ,官网解释是: Resource 正文标记了应用程序须要的资源。该注解能够利用于应用程序组件类,或组件类的字段或办法。当注解利用于字段或办法时,容器将在组件初始化时将所申请资源的实例注入到应用程序组件中。如果正文利用于组件类,则正文申明应用程序将在运行时查找的资源。能够看到它相似一个定义,而由其余的组件或框架自在实现。 @Autowired 于 2007年11月19日随着Spring2.5公布,同时官网也对@Resource进行了反对。@Autowired的官网解释是: 将构造函数、字段、设置办法或配置办法标记为由 Spring 的依赖注入工具主动拆卸。能够看到,@Autowired 是 Spring的亲儿子,而@Resource是Spring对它定义的一种实现,它们的性能如此类似。那么为什么要反对了@Resource,又要本人搞个@Autowired呢? 对此专门查了一下Spring2.5的官网文档,文档中有一段这么说到: However, Spring 2.5 dramatically changes the landscape. As described above, the autowiring choices have now been extended with support for the JSR-250 @Resource annotation to enable autowiring of named resources on a per-method or per-field basis. However, the @Resource annotation alone does have some limitations. Spring 2.5 therefore introduces an @Autowired annotation to further increase the level of control.大略的意思是说,Spring2.5 反对注解主动拆卸啦, 现曾经反对JSR-250 @Resource 基于每个办法或每个字段的命名资源的主动拆卸,然而只有@Resource是不行的,咱们还推出了“粒度”更大的@Autowired,来笼罩更多场景了。 ...

April 7, 2023 · 2 min · jiezi

关于spring:黑马程序员SSM框架教程

问:你学到了什么基于SpringBoot实现根底SSM框架整合把握第三方技术与SpringBoot整合思维 SpringCore Containter 是管对象的 什么是三层架构所谓的三层开发就是将零碎的整个业务利用划分为 表示层,业务逻辑层,数据拜访层,这样有利于零碎的开发、保护、部署和扩大。分层是为了实现 “高内聚,低耦合”,采纳 “分而治之” 的思维,把问题划分开来各个解决,易于控制、易于延展、易于分配资源。进行软件开发设计,肯定要懂得 分而治之 分而治之 分而治之 表示层:负责间接跟用户进行交互,个别也就是指零碎的界面,用于数据录入,数据显示等。意味着只做与外观显示相干的工作,不属于他的工作不必做。业务逻辑层:用于做一些无效的验证工作,以更好地保障程序运行的健壮性。如实现数据增加、批改和查问等;不容许指定的文本框中输出空字符串,数据格式是否正确及数据类型验证;用户的权限合法性判断等。通过以上诸多判断以决定是否将操作持续向后传递,尽量保障程序的失常运行。数据拜访层:顾名思义,就是专门跟数据库进行交互,执行数据的增加、删除、批改和显示等。须要强调的是,所有的数据对象只在这一层被援用,除数据层之外的任何中央都不应该呈现这样的援用。作者:TASK_RUNNING链接:https://www.jianshu.com/p/34736fcaaa9a起源:简书著作权归作者所有。商业转载请分割作者取得受权,非商业转载请注明出处。

April 4, 2023 · 1 min · jiezi

关于spring:Autowired注解自动装配之bean选择分析

抛出问题spring启动中NoUniqueBeanDefinitionException异样是开发人员常常碰到的异样之一。呈现NoUniqueBeanDefinitionException 个别的做法有:1、应用Qualifier 注解明确bean 2、指定一个bean为primary bean来解决。 然而在理论中碰到了一个意外,如下代码:public class User { Long id; String name; public User(Long id, String name) { this.id = id; this.name = name; }}public class VipUser extends User{ public VipUser(Long id, String name) { super(id, name); }}@Beanpublic User user(){ return new User(1L,"张三");}@Beanpublic VipUser vipUser(){ return new VipUser(2L, "李四");}public class DependecyDescribleTest { @Autowired User user; public static void main(String[] args) { ........ }}启动后,User类型的bean 注册了两个,一个是name为user的User对象,一个是name为vipUser的VipUser对象。 在启动类中主动拆卸 User user。我的项目中没有@Primary进行注解,也没有应用@Qualifier ,依照刻板印象,那么应该会抛出NoUniqueBeanDefinitionException,理论状况是,理论状况是运行失常。 同理将User user 改为User vipUser也运行失常, 而改为User user1 则抛出异样NoUniqueBeanDefinitionException。这阐明在 @Autowired进行拆卸时,能依据字段名称就行拆卸。 ...

April 3, 2023 · 3 min · jiezi

关于spring:Spring中常用注解整理

@Import注解@Import注解出自Spring3.0版本,此时spring3.0还属于注解形式开发的过渡期,当然相似于@Bean、@Configuration注解也是在spring3.0版本诞生的。@Import注解于<import>标签的作用是雷同的,在spring配置文件中,导入另一个配置文件。 @Import注解作用@import注解绝对于标签import还减少了bean对象的注入性能,上面是三种初始化对象的形式形式: 1. @Import(UserService.class)这种形式最为简略,作用是将UserService类进行初始化,并注册到IOC容器中2. 实现ImportSelector接口public class MyImportSelector implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{Logger.class.getName(), Cache.class.getName()};}这种形式是重写父类接口,作用是返回须要注册的Bean对象的全类名3. 实现ImportBeanDefinitionRegistrar接口,这种形式能够重写父类接口办法,间接应用BeanDefinitionRegistry,将咱们的Bean对象注入到IOC容器中public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Logger.class); registry.registerBeanDefinition("logger666", rootBeanDefinition); RootBeanDefinition cache = new RootBeanDefinition(Cache.class); registry.registerBeanDefinition("cache666", cache); } }}

March 31, 2023 · 1 min · jiezi

关于spring:你的收藏不能少的Spring笔记阿里十年架构师手写Spring笔记

前言:什么是Spring? Spring就是一个轻量级的Java框架,一个让咱们不必放心底层代码的工具。Spring是一个容器,这个容器蕴含两个外围的局部:管制反转(IOC)和面向切面(AOP)。 Spring 由哪些模块组成? Core moduleBean moduleContext moduleExpression Language moduleJDBC moduleORM moduleOXM moduleJava Messaging Service(JMS) moduleTransaction moduleWeb moduleWeb-Servlet moduleWeb-Struts moduleWeb-Portlet module毋庸置疑,Spring 早已成为 Java 后端开发事实上的行业标准,有数的公司抉择 Spring 作为根底的开发框架,大部分Java 后端程序员在日常工作中也会接触到 Spring。 这是一份阿里工作十年的架构师写进去的Spring学习文档,,将为你关上Spring的大门!须要这份Spring文档资料的【间接点击此处】即可获取~ 一、Spring 介绍与入门什么是 SpringSpring我的项目Spring的替代品获取Spring 框架理解Spring 打包小结 二、在Spring中引入 IoC 和 DI管制反转和依赖注入管制反转的类型Spring中的管制反转Spring中的依赖注入配置 ApplicationContext解析依赖项主动拆卸 bean设置 bean 继承小结 三、详述 Spring 配置和 Spring BootSpring 对应用程序可移植性的影响治理 bean 生命周期挂钩到 bean 的创立应用@Bean 申明一个初始办法挂钩到 bean 的销毁应用@bean 申明销毁办法理解解析的程序让 Spring 感知 bean应用 FactoryBean间接拜访 FactoryBean应用factory-bean和factory-method属性JavaBean PropertyEditor更多的 Spring ApplicationContext配置拜访资源应用 Java 类进行配置配置文件应用Java配置来配置Spring配置文件Envioronment 和 PropertySource 形象应用JSR-330注解进行配置应用Groovy进行配置Spring Boot小结 ...

March 31, 2023 · 1 min · jiezi

关于spring:Spring竟然可以创建重复名称的bean一次项目中存在多个bean名称重复问题的排查

作者:京东科技 韩国凯一、我的项目中存在了名称反复的bean家喻户晓,在Spring中时不可能创立两个名称雷同的bean的,否则会在启动时报错: 然而我却在咱们的spring我的项目中发现了两个雷同名称的bean,并且我的项目也能够失常启动,对应的bean也能够失常应用。 因为我的项目起因中会用到多个redis集群,所以有配置了多个redis环境,并且在id上做了辨别。 然而在配置redis环境的时候,两个环境bean的id却是雷同的。 <bean id="cacheClusterConfigProvider" class="com.xxx.rediscluster.provider.CacheClusterConfigProvider"> <property name="providers"> <list> //创立了一个名为 ccProvider 的bean <bean id="ccProvider" class="com.xxx.rediscluster.provider.CCProvider"> <!--# 替换为以后环境的R2M 3C配置核心地址(详见上方R2M 3C服务地址)--> <property name="address" value="${r2m.zkConnection}"/> <!--# 替换为R2M集群名--> <property name="appName" value="${r2m.appName}"/> <!--# 替换为以后环境的客户端对应配置核心token口令(参考上方token获取形式)--> <property name="token" value="${r2m.token}"/> <!--# 替换为集群认证明码--> <property name="password" value="${r2m.password}"/> </bean> </list> </property></bean><bean id="tjCacheClusterConfigProvider" class="com.xxx.rediscluster.provider.CacheClusterConfigProvider"> <property name="providers"> <list> //这里居然也是 ccProvider <bean id="ccProvider" class="com.xxx.rediscluster.provider.CCProvider"> <!--# 替换为以后环境的R2M 3C配置核心地址(详见上方R2M 3C服务地址)--> <property name="address" value="${r2m.tj.zkConnection}"/> <!--# 替换为R2M集群名--> <property name="appName" value="${r2m.tj.appName}"/> <!--# 替换为以后环境的客户端对应配置核心token口令(参考上方token获取形式)--> <property name="token" value="${r2m.tj.token}"/> <!--# 替换为集群认证明码--> <property name="password" value="${r2m.tj.password}"/> </bean> </list> </property></bean>大家也都晓得,<bean>标签能够申明一个bean,是必定会被spring解析并且应用的,那么为什么在这外面两个雷同的bean名称却不会报错呢? 能够看到咱们创立的bean是失常的,并且从性能上来说也是能够应用的。 二、问题的排查过程2.1 尝试间接找到创立反复bean地位首先debug尝试找到创立反复bean时的相干信息,看看有没有什么思路 而后重启我的项目,抉择debug模式,然而在运行之后IDEA提醒断点被跳过了 查阅了一些材料跟形式都不起作用,遂放弃此思路。 2.2 从创立其父bean开始寻找思路放弃了上述思路后想到,能够凭借之前学习的spring源码从代码层面去排查此问题 将断点设置到创立reids bean处 果然,断点在这里是能进来的 那么咱们的思路就很简略了。 在spring中,拆卸属性的步骤产生在:populateBean(beanName, mbd, instanceWrapper)的过程中,如果发现其属性也是一个bean,那么会先获取bean,如果不存在则会先创立其属性bean,而后创立实现之后将属性bean赋值给要拆卸的bean。 ...

March 28, 2023 · 2 min · jiezi

关于spring:Spring源码核心剖析

作者:京东科技 韩国凯 前言SpringAOP作为Spring最外围的能力之一,其重要性显而易见。而后须要晓得的是AOP并不只是Spring特有的性能,而是一种思维,一种通用的性能。而SpringAOP只是在AOP的根底上将能力集成到SpringIOC中,使其作为bean的一种,从而咱们可能很不便的进行应用。 一、SpringAOP的应用形式1.1 应用场景当咱们在日常业务开发中,例如有些功能模块是通用的(日志、权限等),或者咱们须要在某些性能前后去做一些加强,例如在某些办法执行后发送一条mq音讯等。 如果咱们将这些通用模块代码与业务代码放在一块,那么每个业务代码都要写这些通用模块,保护老本与耦合状况都非常重大。 因而,咱们能够将此模块形象进去,就有了”切面“的概念。 1.2 罕用形式AOP的应用形式绝对比较简单,首先咱们须要实现业务代码 @Servicepublic class AopDemo implements AopInterface{ public Student start(String name) { System.out.println("执行业务逻辑代码....."); return new Student(name); }}业务逻辑比较简单,接管一个name参数。 接下来咱们须要创立其对应的切面 //将该切面退出spring容器@Service//申明该类为一个切面@Aspectclass AopAspect { //申明要进行代理的办法 @Pointcut("execution(* com.example.demo.aop.AopInterface.start(..))") public void startAspect() { } //在办法执行之前的逻辑 @Before(value = "startAspect()") public void beforeAspect() { System.out.println("业务逻辑前代码....."); } //在办法执行之后的逻辑 @After(value = "startAspect()") public void afterAspect() { System.out.println("业务逻辑后代码....."); } //围绕办法前后的逻辑 @Around("startAspect()") public Object aroundAspect(ProceedingJoinPoint point) throws Throwable { Object[] requestParams = point.getArgs(); String name = requestParams[0].toString(); System.out.println("传入参数:" + name); requestParams[0] = "bob"; return point.proceed(requestParams); }}能够看到,首先须要咱们指明要代理的对象及办法,而后依据须要抉择不同的注解即可实现代理对象。 ...

March 27, 2023 · 5 min · jiezi

关于spring:三天吃透Spring面试八股文

本文曾经收录到Github仓库,该仓库蕴含计算机根底、Java根底、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等外围知识点,欢送star~ Github地址 Spring的长处通过管制反转和依赖注入实现松耦合。反对面向切面的编程,并且把利用业务逻辑和零碎服务离开。通过切面和模板缩小样板式代码。申明式事务的反对。能够从枯燥繁冗的事务管理代码中解脱进去,通过申明式形式灵便地进行事务的治理,进步开发效率和品质。不便集成各种优良框架。外部提供了对各种优良框架的间接反对(如:Hessian、Quartz、MyBatis等)。不便程序的测试。Spring反对Junit4,增加注解便能够测试Spring程序。Spring 用到了哪些设计模式?1、简略工厂模式:BeanFactory就是简略工厂模式的体现,依据传入一个惟一标识来取得 Bean 对象。 @Overridepublic Object getBean(String name) throws BeansException { assertBeanFactoryActive(); return getBeanFactory().getBean(name);}2、工厂办法模式:FactoryBean就是典型的工厂办法模式。spring在应用getBean()调用取得该bean时,会主动调用该bean的getObject()办法。每个 Bean 都会对应一个 FactoryBean,如 SqlSessionFactory 对应 SqlSessionFactoryBean。 3、单例模式:一个类仅有一个实例,提供一个拜访它的全局拜访点。Spring 创立 Bean 实例默认是单例的。 4、适配器模式:SpringMVC中的适配器HandlerAdatper。因为利用会有多个Controller实现,如果须要间接调用Controller办法,那么须要先判断是由哪一个Controller解决申请,而后调用相应的办法。当减少新的 Controller,须要批改原来的逻辑,违反了开闭准则(对批改敞开,对扩大凋谢)。 为此,Spring提供了一个适配器接口,每一种 Controller 对应一种 HandlerAdapter 实现类,当申请过去,SpringMVC会调用getHandler()获取相应的Controller,而后获取该Controller对应的 HandlerAdapter,最初调用HandlerAdapter的handle()办法解决申请,实际上调用的是Controller的handleRequest()。每次增加新的 Controller 时,只须要减少一个适配器类就能够,无需批改原有的逻辑。 罕用的处理器适配器:SimpleControllerHandlerAdapter,HttpRequestHandlerAdapter,AnnotationMethodHandlerAdapter。 // Determine handler for the current request.mappedHandler = getHandler(processedRequest);HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// Actually invoke the handler.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());public class HttpRequestHandlerAdapter implements HandlerAdapter { @Override public boolean supports(Object handler) {//handler是被适配的对象,这里应用的是对象的适配器模式 return (handler instanceof HttpRequestHandler); } @Override @Nullable public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { ((HttpRequestHandler) handler).handleRequest(request, response); return null; }}5、代理模式:spring 的 aop 应用了动静代理,有两种形式JdkDynamicAopProxy和Cglib2AopProxy。 ...

March 25, 2023 · 6 min · jiezi

关于spring:spring-jpa关于线程池异步执行导致detached-entity-passed-to-persist问题排查和解决

我这边有个批量插入用户OpenUser和利用OpenApp关联关系数据的操作,因为耗时较长时间,所以筹备用线程池异步执行操作,然而却遇到了一个jpa的detached entity passed to persist问题,我这边的操作是批量保留一个OpenAppUser关联关系表,所以须要先取得对应OpenUser和OpenApp的援用,再设置到关联对象OpenAppUser里,而后在保留,我这边是先通过userRepository.findById(userId)获取到OpenUser,而后openAppUser.setOpenUser(openUser),在执行appUserRepository.save(openAppUser);时产生了如题目上的谬误,说是OpenUser对象处于游离态,无奈保留。 通过排查,我这边是因为OpenAppUser类里设置了@ManyToOne(cascade = CascadeType.ALL)级联OpenUser,所以在保留OpenAppUser的时候会级联操作OpenUser,原本在没有开线程异步的状况下,因为OpenUser之前通过findById查出来了,所以在jpa的PersistenceContext里是有该OpenUser的脱管对象的,这时候就不会报错,而在线程异步的状况下context里确没有该脱管对象了(这里阐明一下,为啥不开线程有,开了线程没有?)因为spring-boot默认jpa.open-in-view=true,会应用ThreadLocal在以后线程里保留EntityManager上下文信息,所以在整个controller里都是应用的同一个context PersistenceContext持久性上下文有两种类型: 事务范畴的持久性上下文;当咱们在事务中执行任何操作时,EntityManager 会查看持久性上下文。 如果存在,则将应用它。否则,它将创立一个持久性上下文扩大范畴的持久性上下文;扩大持久性上下文能够逾越多个事务。咱们能够在没有事务的状况下长久化实体,但不能在没有事务的状况下刷新它。在@PersistenceContext注解里type能够指定范畴:PersistenceContextType.TRANSACTION;PersistenceContextType.EXTENDED 而当咱们用线程池异步的时候,拿不到之前的EntityManager的配置信息,而spring jpa repository默认的办法上都会自带一个事务,所以在执行完userRepository.findById(userId)获取到OpenUser之后,会commit,而commit操作会clear掉EntityManager里保留的脱管对象OpenUser,等到appUserRepository.save(openAppUser);保留的时候,因为援用的OpenUser曾经没有在PersistenceContext上下文里了,不是脱管对象了(具体能够看EntityState entityState = getEntityState( entity, entityName, entityEntry, source );外面的实现,有几种判断条件,是不是脱管对象,有没有id、version等等属性),就会报detached entity passed to persist这个异样 所以依据理论状况,咱们只有参考open-in-view=true产生对应的OpenEntityManagerInViewInterceptor拦截器革新一下本人线程里的PersistenceContext上下文失效范畴,就能够解决该异样了

March 24, 2023 · 1 min · jiezi

关于spring:Spring-AOP编程思想注解开发实现

概念AOP(Aspect Oriented Programming)面向切面编程,是一种编程思维。能够在不轰动原始设计的根底上为办法进行性能上的加强。 1、代理:Spring下的AoP中,实现的外围实质是利用代理模式来实现的2、连接点:在Spring下的AoP中,能够了解为任意办法的执行3、切入点:匹配连接点的式子,是对具备共性功能的办法的形容(能够是一个办法,也能够是多个办法)4、告诉:若干个办法的共性功能在切入点处执行,最终体现为一个办法5、切面:形容告诉与切入点之间的关系 写一个简略的程序来感受一下。 应用AoP之前1、入口类 public class ApplicationDemo { public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); BookDao bookDao = ctx.getBean(BookDao.class); bookDao.borrowBook(); bookDao.returnBook(); }}2、SpringConfig类 @Configuration@ComponentScan("pers.happyfan")public class SpringConfig {}3、BookDao接口 public interface BookDao { void borrowBook(); void returnBook();}4、BookDaoImpl实现类 @Servicepublic class BookDaoImpl implements BookDao { @Override public void borrowBook() { System.out.println("书籍A被借阅"); } @Override public void returnBook() { System.out.println("书籍A已偿还"); }}执行 应用AoP1、在POM文件中导入对应的坐标 <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>6.0.4</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.7</version> </dependency> </dependencies>2、创立AopProxy类,在这个类中设立切入点、告诉与切面 ...

March 20, 2023 · 1 min · jiezi

关于spring:SpringCloud-GateWay通过过滤器GatewayFilter修改请求或响应内容

Spring Cloud Gateway在有些场景中须要获取request body内容进行参数校验或参数批改,咱们通过在GatewayFilter中获取申请内容来获取和批改申请体,上面咱们就基于ServerWebExchange来实现: ServerWebExchange命名为服务网络交换器,寄存着重要的申请-响应属性、申请实例和响应实例等等,有点像Context的角色,其中有两个重要的接口办法: // 获取ServerHttpRequest对象 ServerHttpRequest getRequest(); // 获取ServerHttpResponse对象 ServerHttpResponse getResponse();创立一个GatewayFilter,必须实现Ordered接口,返回一个小于-1的order值,这是因为NettyWriteResponseFilter的order值为-1,咱们须要笼罩返回响应体的逻辑,自定义的GlobalFilter必须比NettyWriteResponseFilter优先执行。 public class RequestGatewayFilter implements GatewayFilter, Ordered {@Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); HttpHeaders headers = request.getHeaders(); // 解决参数 MediaType contentType = headers.getContentType(); if (exchange.getRequest().getMethod().equals(HttpMethod.POST)) { //Content-type为“application/json” if (MediaType.APPLICATION_JSON.isCompatibleWith(contentType)) { return readBody(exchange, chain); } //Content-type为“application/x-www-form-urlencoded” else if(MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(contentType)){ GatewayContext gatewayContext = new GatewayContext(); gatewayContext.setRequestHeaders(headers); gatewayContext.getAllRequestData().addAll(request.getQueryParams()); exchange.getAttributes().put(GatewayContext.CACHE_GATEWAY_CONTEXT,gatewayContext); return readFormData(exchange, chain, gatewayContext); }//Content-type为“multipart/form-data”else if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType)) { GatewayContext gatewayContext = new GatewayContext(); gatewayContext.setRequestHeaders(headers); gatewayContext.getAllRequestData().addAll(request.getQueryParams()); exchange.getAttributes().put(GatewayContext.CACHE_GATEWAY_CONTEXT,gatewayContext); return readMultipartData(exchange, chain, gatewayContext); } } else { return readGetData(exchange, chain); } return chain.filter(exchange); } @Override public int getOrder() { return -2; }}解决content-type为application/json的办法: ...

March 13, 2023 · 4 min · jiezi

关于spring:网易二面CPU狂飙900该怎么处理

大家好,我是不才陈某~ [星球]()一位小伙伴面试了 网易,遇到了一个 性能类的面试题:CPU飙升900%,该怎么解决? 惋惜的是,以上的问题,这个小伙没有答复现实。 最终,导致他网易之路,终止在二面,十分惋惜 关注公众号:码猿技术专栏,回复关键词:1111 获取阿里外部Java性能调优手册!首先,阐明一下问题:CPU飙升200% 以上是生产容易产生的场景场景:1:MySQL过程飙升900%大家在应用MySQL过程,想必都有遇到过CPU忽然过高,或者达到200%以上的状况。 数据库执行查问或数据批改操作时,零碎须要耗费大量的CPU资源保护从存储系统、内存数据中的一致性。 并发量大并且大量SQL性能低的状况下,比方字段是没有建设索引,则会导致疾速CPU飙升,如果还开启了慢日志记录,会导致性能更加好转。生产上有MYSQL 飙升900% 的顽劣状况。 场景2:Java过程飙升900%一般来说Java 过程不做大量 CPU 运算,失常状况下,CPU 应该在 100~200% 之间, 然而,一旦高并发场景,要么走到了死循环,要么就是在做大量的 GC, 容易呈现这种 CPU 飙升的状况,CPU飙升900%,是齐全有可能的。 其余场景:其余的相似过程飙升900%的场景比方Redis、Nginx等等。 陈某提醒:大家介绍场景的时候,就说本人次要波及了两个场景, Java过程飙升900%、MySQL过程飙升900%两种场景,其实,这两个场景就足够讲半天了, 其余的,应用躲避技巧躲避一下就行。 场景一:MySQL过程CPU飙升到900%,怎么解决?定位过程: 应用top 命令察看,确定是mysqld导致还是其余起因。如果是mysqld导致的,show processlist,查看session状况,确定是不是有耗费资源的sql在运行。找出耗费高的 sql,看看执行打算是否精确, index 是否缺失,或者切实是数据量太大造成。处理过程: kill 掉这些线程(同时察看 cpu 使用率是否降落), 一般来说,必定要 kill 掉这些线程(同时察看 cpu 使用率是否降落),等进行相应的调整(比如说加索引、改 sql、改内存参数)之后,再从新跑这些 SQL。进行相应的调整(比如说加索引、改 sql、改内存参数) index 是否缺失,如果是,则 建设索引。也有可能是每个 sql 耗费资源并不多,然而忽然之间,有大量的 session 连进来导致 cpu 飙升,这种状况就须要跟利用一起来剖析为何连接数会激增,再做出相应的调整,比如说限度连接数等 优化的过程,往往不是一步实现的,而是一步一步,执行一项优化措辞,再察看,再优化。场景1的实在案例:MySQL数据库优化的实在案例陈某提醒:以下案例,来自互联网。大家参考一下,筹备一个本人的案例。 本问题亲身经历过。 之前开发共事编写的SQL语句,就导致过线上CPU过高,MySQL的CPU使用率达到900%+,通过优化最初升高到70%~80%。上面说说集体在这个过程中的排查思路。 首先,咱们要对问题定位而不是自觉的开启什么 慢日志,在并发量大并且大量SQL性能低的状况下,开启慢日志无心是将MySQL推向解体的边缘。 过后遇到这个状况,剖析了以后的数据量、索引状况、缓存应用状况。目测数据量不大,也就几百万条而已。接下来就去定位索引、缓存问题。 通过询问,发现很多查问都是走MySQL,没有用到缓存。既然没有用到缓存,则是大量申请全副查问MySQL导致。通过上面的命令查看:show processlist;发现相似很多雷同的SQL语句,始终处于query状态中。 select id form user where user_code = 'xxxxx';初步剖析可能是 user_code 字段没有索引导致。接着查问user表的索引状况: ...

March 13, 2023 · 2 min · jiezi

关于spring:Redis实现延时任务

1. 生成订单30分钟未领取,则主动勾销 2. 30分钟未回复,则完结会话 对上述的工作,咱们给一个业余的名字来形容,那就是延时工作 一、延时工作是什么延时工作不同于个别的定时工作,延时工作是在某事件触发后的将来某个时刻执行,没有反复的执行周期。二、延时工作和定时工作的区别是什么定时工作有明确的触发工夫,延时工作没有定时工作有执行周期,而延时工作在某事件触发后一段时间内执行,没有执行周期定时工作个别执行的是批处理多个工作,而延时工作个别是单任务解决 三、技术比照本文次要解说Redis的Zset实现延时工作,其余计划只做介绍1.数据库轮询通过定时组件的去扫描数据库,通过工夫来判断是否有超时的订单,而后进行update或delete等操作 长处:简单易行毛病: 对服务器内存耗费大工夫距离小,数据库损耗极大数据内存态,不牢靠如果工作量过大,对数据库造成的压力很大 。频繁查询数据库带来性能影响2.JDK的提早队列利用JDK自带的DelayQueue来实现,这是一个无界阻塞队列,该队列只有在提早期满的时候能力从中获取元素,放入DelayQueue中,是必须实现Delayed接口的。 长处:实现简略,效率高,工作触发时间延迟低。毛病: 服务器重启后,数据全副隐没,怕宕机因为内存条件限度的起因,比方下单未付款的订单数太多,那么很容易就呈现OOM异样数据内存态,不牢靠3.工夫轮算法工夫轮TimingWheel是一种高效、低提早的调度数据结构,底层采纳数组实现存储工作列表的环形队列,示意图如下:工夫轮 工夫轮算法能够类比于时钟,如上图箭头(指针)按某一个方向按固定频率轮动,每一次跳动称为一个 tick。这样能够看出定时轮由个3个重要的属数,ticksPerWheel(一轮的tick数),tickDuration(一个tick的持续时间)以及 timeUnit(工夫单位),例如当ticksPerWheel=60,tickDuration=1,timeUnit=秒,这就和事实中的始终的秒针走动齐全相似了。如果以后指针指在1下面,我有一个工作须要4秒当前执行,那么这个执行的线程回调或者音讯将会被放在5上。那如果须要在20秒之后执行怎么办,因为这个环形构造槽数只到8,如果要20秒,指针须要多转2圈。地位是在2圈之后的5下面(20 % 8 + 1) 长处:效率高,工作触发时间延迟工夫比delayQueue低毛病: 服务器重启后,数据全副隐没,怕宕机容易就呈现OOM异样数据内存态,不牢靠4.应用音讯队列应用RabbitMQ死信队列依赖于RabbitMQ的两个个性:TTL和DLX。     TTL:Time To Live,音讯存活工夫,包含两个维度:队列音讯存活工夫和音讯自身的存活工夫。     DLX:Dead Letter Exchange,死信交换器。 长处:异步交互能够削峰,高效,能够利用rabbitmq的分布式个性轻易的进行横向扩大,音讯反对长久化减少了可靠性。毛病:1.自身的易用度要依赖于rabbitMq的运维.因为要援用rabbitMq,所以复杂度和老本变高2.RabbitMq是一个消息中间件;提早队列只是其中一个小性能,如果团队技术栈中原本就是应用RabbitMq那还好,如果不是,那为了应用提早队列而去部署一套RabbitMq老本有点大;5.Redis的Zset实现延时工作为什么采纳Redis的ZSet实现提早工作? zset数据类型的去重有序(分数排序)特点进行提早。例如:工夫戳作为score进行排序5.1 思路剖析我的项目启动时启用一条线程,线程用于距离肯定工夫去查问redis的待执行工作。其工作jobId为业务id,值为要执行的工夫。查问到执行的工作时,将其从redis的信息中进行删除。(删除胜利才执行延时工作,否则不执行,这样能够防止分布式系统延时工作屡次执行。)删除redis中的记录之后,执行工作。将执行jobId也就是业务id对应的工作。理论场景中,还会波及延时工作批改,删除等,这些场景能够指定标记,批改标识即可,当然也能够在业务逻辑中做补充条件的判断。5.2 Redis中Zset的简略介绍及应用Redis 有序汇合是 string 类型元素的汇合,且不容许反复的成员。每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为汇合中的成员进行从小到大的排序。有序汇合的成员是惟一的,但分数(score)却能够反复。常用命令 ZADD命令 : 将一个或多个成员元素及其分数值退出到有序集当中,或者更新已存在成员的分数ZCARD命令 : 获取有序汇合的成员数ZRANGEBYSCORE: 通过分数返回有序汇合指定区间内的成员ZREM : 移除有序汇合中的一个或多个成员java中操作简略介绍 1.add(K key, V value, double score) 增加元素到变量中同时指定元素的分值。 redisTemplate.opsForZSet().add("zSetValue","A",1); 2.rangeByScore(K key, double min, double max) 依据设置的score获取区间值。 zSetValue = redisTemplate.opsForZSet().rangeByScore("zSetValue",1,2); 3.rangeByScore(K key, double min, double max,long offset, long count) 依据设置的score获取区间值从给定下标和给定长度获取最终值。 zSetValue = redisTemplate.opsForZSet().rangeByScore("zSetValue",1,5,1,3); 4.rangeWithScores(K key, long start, long end) 获取RedisZSetCommands.Tuples的区间值。 Set<ZSetOperations.TypedTuple<Object>> typedTupleSet = redisTemplate.opsForZSet().rangeWithScores("typedTupleSet",1,3); Iterator<ZSetOperations.TypedTuple<Object>> iterator = typedTupleSet.iterator(); while (iterator.hasNext()){ ZSetOperations.TypedTuple<Object> typedTuple = iterator.next(); Object value = typedTuple.getValue(); double score = typedTuple.getScore(); } 5.删除成员 redisTemplate.opsForZSet().remove("myZset","a","b");以下代码能够间接应用-基于Spring Boot我的项目 ...

March 8, 2023 · 3 min · jiezi

关于spring:mybatisplus分页插件失效探究

网上基本上都是举荐配置如下: @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return mybatisPlusInterceptor; }然而,仅仅这么做,就能达到咱们的预期吗?其终局就是分页插件没有成果,起因是为什么呢?图1图2 通过比照下面两张图能够发现,图一DefaultSqlSession.selectList()底层调用Plugin.invoke();图二DefaultSqlSession.selectList()底层调用CachingExecutor.query()。其中,图一是分页插件失效的调用链,图二是分页插件生效的调用链。 也就是说,分页插件生效的起因是,mybatis-plusPlugin类没有为分页插件拦截器生成Executor代理。具体应该怎么做呢?像上面这样,在构建SqlSessionFactory时,须要在MybatisSqlSessionFactoryBean显示设置Plugin。 @Bean(name = "defaultSqlSessionFactory") public SqlSessionFactory defaultSqlSessionFactory(){ MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean(); bean.setDataSource(dataSource); //设置拦截器 bean.setPlugins(mybatisPlusInterceptor); SqlSessionFactory sqlSessionFactory = bean.getObject(); //设置主动提交 sqlSessionFactory.openSession(true); return sqlSessionFactory;}那么,为分页插件生成代理类是在什么机会生成呢?先颁布答案: //设置主动提交sqlSessionFactory.openSession(true);调用链如下: 图3 咱再看细节:DefaultSqlSessionFactory.openSessionFromDataSource()详情: private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { final Environment environment = configuration.getEnvironment(); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); //这步很要害,创立执行者实例 final Executor executor = configuration.newExecutor(tx, execType); return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { closeTransaction(tx); // may have fetched a connection so lets call close() throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }Configuration.newExecutor()详情: ...

March 6, 2023 · 2 min · jiezi

关于spring:吹爆Alibaba自研的Spring全能笔记建议人手一份

Spring 这个技术栈,想必大家都晓得,这玩意面试必考工作必用,对于每位 Java 程序员来说都是至关重要的;俗话说 Java 开发不学习 Spring 就像士兵上战场不带枪,我认为这个比喻很形象。Spring 简直占据了 Java web 的半壁江山,其重要性显而易见,它跟它前面诞生的一系列解决方案被咱们亲切的称为“Spring 全家桶”。 在当下毫不夸大地说,若是不会 Spring 就去面试,通过的概率根本为零。Spring 系列框架在面试中是必考的,如果连根底的都答复不上来,那就只能间接被 pass。明天分享一份Spring生态全家桶笔记,其中不仅涵盖了当下支流框架,同时还整合了一线互联网大厂罕用技术与中间件等等内容! 须要残缺的文档的敌人,点赞+转发之后【点击此处】即可收费获取!第一大部分:前置常识 第二大部分:根底利用局部 第三局部:高级局部

March 3, 2023 · 1 min · jiezi

关于spring:手写模拟Spring底层原理Bean的创建与获取

作者:京东物流 张鼎元 1 引言大家好,置信大家对Spring的底层原理都有肯定的理解,这里咱们会针对Spring底层原理,在海量的Spring源代码中进行抽丝剥茧手动实现一个Spring繁难版本,来促成咱们对Spring架构有个更深的了解,对Spring的罕用性能进行手写模仿实现。 2 启动Spring针对Bean的创立和获取性能,咱们来进行性能的实 首先咱们创立JdApplicationContext类做为Spring启动类,实现bean的加载和获取性能。 UserService和OrderService类作为Bean的实现类,通过JdApplicationContext类中的getBean办法获取到后面两个类的实现。 App为启动测试类AppConfig为启动配置类注:上面的代码会顺着内容解说逐渐实现 首先创立App类做为入口,测试Spring性能。通过初始化JdApplicationContext类,动静加载bean实例。 通过getBean办法获取bean实例。 创立JdApplicationContext类,提供获取Bean实例办法,通过构造函数动静初始化bean实例。 3 扫描类门路并缓存BeanDefinition数据在JdApplicationContext类初始化的时候,通过AppConfig配置类获取类的扫描门路,在扫描门路下,找到须要创立Bean的类,通过标注Component注解的类辨认须要创立的Bean。 通过Component注解辨认出的类,进行封装成BeanDefinition. 再缓存到beanDefinitionMap内存中。 上述的代码中,咱们发现创立BeanDefinition类时,封装了class类,beanName,scope三个次要属性。用于创立bean的时候,提供class类进行初始化和属性的注入,创立单例类或原型类提供数据根据。 4 初始化Bean和依赖注入接下来,在下面的扫描操作实现后,所有待初始化的bean数据存储beanDefinitionMap中。咱们只须要遍历beanDefinitionMap数据进行一一初始化和属性的注入。 上述代码中,对bean进行初始化时候,从beanDefinition中获取要初始化的class,通过反射机构进行无参初始化。 初始化实现后,再对有Autowired注解的属性进行依赖注入,Autowired注解没有传递value值时默认取属性名称作为beanName,通过getBean办法获取bean实例。 getBean办法会通过beanName,从beanDefinitionMap中获得beanDefinition数据。通过beanDefinition确认该bean为单例类原型类 如果为原型类,间接调用createBean办法进行bean初始化。 如果为单例类,首先从singletonBeanMap缓存中获取bean实例。如果未获取到,调用createBean办法获取bean实例,同时将已创立bean实例缓存到singletonBeanMap缓存中。 此时,在上述的性能中,依赖注入繁难版本已实现。同时咱们留神到UserService和OrderService可能会产生循环依赖的问题,在这里如何解决呢? 问题代码如下 : 上图就是循环依赖问题代码导致的异样。反复创立bean进入死循环。 在初始化bean和属性注入之间,咱们能够减少二级缓存作为突破口,解决死循环问题。 userService初始化后,须要注入orderService,通过getBean办法获取,因为orderService没有在singletonBeanMap缓存中,也须要初始化并注入userService属性, 同时userService还在初始化过程中,不能缓存到singletonBeanMap缓存中。造成彼此循环期待属性的注入。为解决此问题,咱们只须要设立初始化过程中缓存到creatingBeanMap中,在userService初始化过后,未进行属性注入前缓存到creatingBeanMap中,userService须要的orderService属性在创立bean实例过程中,优先从creatingBeanMap缓存中失去userService实例,来实现bean实例的创立过程。orderService实现bean实例创立后,userService也相应的实现实例创立。 5 实现InitializingBean接口在createBean过程中,咱们能够对外提供初始化扩大接口InitializingBean接口。只有实现该接口,咱们就能够针对bean的初始化进行扩大性能实现。 ![] 6 实现BeanPostProcessor接口模仿AOP首先创立BeanPostProcessor接口,作为所有bean实例的对外扩大接口创立BeanPostProcessor接口实现类,模仿AOP性能,指定userService类进行切面。 在扫描类的时候,将已实现BeanPostProcessor接口类缓存到beanPostProcessorList中。 通过下面的扫描,beanPostProcessorList已缓存所有的BeanPostProcessor实现类。在createBean的时候,对已创立的bean实例进行预处理扩大。 通过上述代码的实现成果如下: 源代码: https://3.cn/109Aj-Zok 7 总结在上述的解说中,咱们对Spring底层原理进行简略的实现,通过对类的扫描,注解标识的判断,beanDefinition的定义和缓存。通过反射和代理进行bean实例的创立和扩大。置信大家也看进去在实现过程中,有很多中央须要改良,还能够持续扩大Spring很多其它性能。例如扩大beanDefinition的注册,引入Bean工厂,提早加载等。

March 3, 2023 · 1 min · jiezi

关于spring:Spring-AOT应用实践

环境筹备操作系统: MacOS Monterey 12.5.1 CPU: Intel I7 装置java17从Oracle下载java17对应版本,并装置在Mac零碎中设置环境变量便于疾速切换shell的环境。以以后用户的zsh为例,以后用户home下的.zshrc文件中减少内容# 指定java17的home目录export JAVA_17_HOME='/Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home'# 疾速将JAVA的HOME指定为java17的home目录,零碎上装置多jdk版本时好用alias java17="export JAVA_HOME=$JAVA_17_HOME"# 设置maven别名,在应用maven命令时先设置以后shell的java环境alias mvn17='java17;mvn '在IDE中开发代码间接指定目录设置我的项目的JDK版本为java17即可,倡议应用最新版本的IDEA 装置Graalvm下载对应零碎对应JDK版本的Graalvm,下载页面地址: https://github.com/graalvm/graalvm-ce-builds/releases/tag/vm-22.3.1设置Graalvm的home目录 # 将Graalvm的home门路增加到零碎变量中export GRAALVM_HOME='/Users/{userName}/{path}/graalvm-ce-java17-amd64/Contents/Home'# 将graalvm的bin目录增加到零碎path中,能够间接应用bin下的命令,不再须要残缺的门路export PATH=$GRAALVM_HOME/bin:$PATH装置native-image $gu install native-image# 或者应用命令全门路$GRAALVM_HOME/bin/gu install native-image最简示例代码我的项目的代码目录如下: POM.xml文件<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <!-- 继承自Springboot的parent,因而内置了native的profile及plugin等信息 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.0.3</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>cn.followtry.app</groupId> <artifactId>spring-image-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>spring-image-demo</name> <description>测试Spring的native image</description> <properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-prometheus</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <!-- 构建原生镜像的插件 --> <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> <configuration> <imageName>followtry-image</imageName> <buildArgs> <!-- 开发时可应用,放慢构建速度,部署时须要去掉 --> <buildArg>-Ob</buildArg> </buildArgs> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>jAVA代码利用启动入口类: ...

March 2, 2023 · 2 min · jiezi

关于spring:Spring-WebSocket简析

博文原始连贯 前言Websocket是一种在TCP连贯上进行全双工网络通信的协定,之所以在有了Http协定之后仍旧诞生了Websocket的起因在于Http的缺点:通信只能由客户端发动。在Websocket呈现之前,为了在Web C/S架构上实现服务端向客户端推送音讯,大都是靠Ajax进行轮询来“实现”推送,而因须要不停地关上连贯以及Http较长的头部信息,在一直申请的过程中这将产生资源的节约。Websocket因而应运而生。 因为目前可搜得的材料各式各样天壤之别(因为有的是Java原生API而有的是基于STOMP的利用示例),本文将着重介绍Spring环境下(SpringBoot为例)的Websocket利用。 Websocket基本概念基本概念原理这里就不细讲了,一查一大把,举荐大佬的这篇博客(偏代码实际一些)以及知乎的这篇高赞答复(偏寓教于乐一些)。须要重点阐明的是: Websocket作为规范的通信协议在Web C/S应用时,须要浏览器和Web服务容器的反对Websocket依附Http来实现握手,握手实现后才是齐全走Websocket协定资源描述符的前缀为ws,加密通道传输则为wss,例如ws://example.com:80/some/pathWebsocket没有规定发送内容的格局,反对文本、二进制Http握手握手比拟重要因而独自拿出来说一下,握手的具体细节能够参考下面举荐的文章以及百度,这里细说下为什么要应用Http来进行握手而不是齐全独立采纳自有协定,集体认为这一点次要起因有: Websocket次要还是作为Http的一种补充,与Http紧密结合是荒诞不经的,并且可能较好地融入Http生态提供了良好的兼容性解决,能够通过Http来获取兼容性反对反馈以及应用Http来在不反对websocket的客户端上模仿兼容WebsocketSockJSSockJS是一个JavaScript库,次要用于应答浏览器缺失websocket反对的状况。它提供了连贯的、跨浏览器的JavaScript API,它首先尝试应用原生Websocket,在失败时可能应用各种浏览器特定的传输协定来模仿Websocket的行为。 Java标准Java公布提供了Websocket的规范API接口JSR-356,作为Java EE7规范的一部分。大部分规范的Java web容器都曾经实现了对Websocket的反对,同时也是兼容这个标准接口的,例如Tomcat 7.0.47+, Jetty 9.1+, GlassFish 4.1+, WebLogic 12.1.3+, Undertow 1.0+ (WildFly 8.0+)等。 何时应用Websocket这里次要参考Spring文档中的叙述: Websocket尽管能够使网页变得动静以及更加有交互性,然而在很多状况下Ajax联合Http Streaming或者长轮询能够提供简略高效的解决方案。例如新闻、邮件、社交订阅等须要动静更新,然而在这些情景下每隔几分钟更新一次是齐全没有问题的。而另一方面,合作、游戏以及金融利用则须要更加靠近实时更新。留神提早自身并不是决定性因素,如果信息量绝对较少(例如监控网络故障),Http Streaming或轮询同样也能够高效地解决。低提早、高频率以及高信息量的组合状况下,Websocket才是最佳抉择。 STOMP协定这里把STOMP协定提到后面一点的地位,也能够抉择先看上面的内容,到介绍STOMP利用时再看这部分。 STOMP是一个简略的互操作协定,它被设计为罕用消息传递模式的最小子集,定义了一种基于文本的简略异步音讯协定,它最后是为脚本语言(如 Ruby、 Python 和 Perl)创立的,用于连贯企业音讯代理。STOMP曾经宽泛应用了好几年,并且失去了很多客户端(如stomp.js、Gozirra、stomp.py、stompngo等)、音讯代理端(如ActiveMQ、RabbitMQ等)工具库的反对,目前最新的协定版本为1.2。 STOMP是一种基于'Frame'的协定,Frame基于Http建模,每个Frame由一个命令(Command)、一组头部(Headers)和可选的注释(Body)组成,如下是一个STOMP frame的根本构造示例: COMMANDheader1:value1header2:value2Body^@能够看到STOMP自身的构造是十分简单明了的。STOMP同样有客户端和服务端的概念,服务端被认为是能够接管和发送音讯的一组目的地;而客户端则是用户代理,能够进行两种操作:发送音讯(SEND)、发送订阅(SUBSCRIBE),为此,STOMP的命令有如下几种。 客户端命令: CONNECT:用于初始化信息流或TCP连贯,是客户端第一个须要发送的命令SEND:示意向目的地发送音讯,必须要蕴含一个名为destination的头部SUBSCRIBE:用于注册监听一个目的地,必须蕴含一个名为destination的头部BEGIN:用于启动事务,必须蕴含一个名为transaction的头部COMMIT:用于提交事务,必须蕴含一个名为transaction的头部ABORT:用于回滚事务,必须蕴含一个名为transaction的头部DISCONNECT:告知服务端敞开连贯服务端命令: CONNECTED:服务器响应客户的段的CONNECT申请,示意连贯胜利MESSAGE:用于将订阅的音讯发送给客户端,头部destination的值应与SEND frame中的雷同,且必须蕴含一个名为message-id的头部用于惟一标识这个音讯RECIPT:收据,示意服务器胜利解决了一个客户端要求返回收据的音讯,必须蕴含头部message-id表明是哪个音讯的收据ERROR:出现异常时,服务端可能会发送该命令,通常在发送ERROR后将敞开连贯能够说STOMP次要就是提供了发送音讯、订阅音讯的语义,同时还可能反对事务的解决。 Spring利用集成与其余提供绝对繁多的最佳实际的Spring整合不同,在Spring中利用Websocket能够有多种形式,且天壤之别,这也造成了初步查阅材料时的困惑,为何两篇文章中的示例会看起来齐全不一样。 这里咱们选用Springboot(2.x)以及其默认应用的tomcat来作为根底进行各种模式的示例演示阐明,其中几个示例没有前端,能够应用在线测试工具来试验。应用Maven搭建我的项目,在最简单的利用模式下,POM的依赖将蕴含如下内容: <dependencies> <!-- websocket根底,次要引入了spring-websocket和spring-messaging --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> <!-- web根底 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- security --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- message security --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-messaging</artifactId> </dependency> <!-- lombok,可选 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!-- test,可选 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <!-- 以下为前端库的反对 --> <dependency> <groupId>org.webjars</groupId> <artifactId>webjars-locator-core</artifactId> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>sockjs-client</artifactId> <version>1.0.2</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>stomp-websocket</artifactId> <version>2.3.3</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>bootstrap</artifactId> <version>3.3.7</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>3.1.1-1</version> </dependency></dependencies>原生Java API这种状况只利用Spring的根底能力以及辅助工具类,间接以规范的java标准接口(javax.websocket包下的内容)来编写websocket利用,在依赖上仅须要spring-boot-starter-websocket。让程序可能解决websocket申请仅须要编写两个额定的类即可。 ...

March 1, 2023 · 5 min · jiezi

关于spring:Spring-Framework-异步任务原理

Spring Framework 异步工作原理原因Spring Framework 外部提供了除IOC和AOP之外的很多有意思的性能,深入研究其技术原理,可在我的项目技术选型上提供更多的思路,和技术把控。Spring Framewrok 代码由大佬精心打磨,能够从性能实现上学习和模拟。另外Spring Framework是Java我的项目开发的根底,其余技术框架都会基于或者与Spring 兼容,钻研其中的技术原理,可疾速把握其余技术框架的技术原理,比方 Spring boot \ Spring cloud... 简述Spring Framework 提供了代码异步执行的性能。在须要异步执行的办法下面增加 @Async 注解,两外在@Configuration 类上减少 @EnableAsync 注解。代码执行到须要异步执行的办法上,就能够跨线程运行,达到异步执行的目标。 前提Spring Framework 的异步工作性能是基于Spring AOP实现,Spring Aop 又是切面编程的一种,须要对切面编程和 Spring Aop 中的根底概念有所理解。 注释@EnableAsync通过浏览注解的源文件 org.springframework.scheduling.annotation.EnableAsync 中正文,可对异步工作性能应用与定制有一个根本理解。 搭配@Configuration 注解一起应用。反对 Spring's @Async、 EJB 3.1 @jakarta.ejb.Asynchronous、 自定义注解(在 annotation() 中指定) 三种注解。默认应用 org.springframework.core.task.SimpleAsyncTaskExecutor 线程池执行异步工作。能够通过自定义一个org.springframework.core.task.TaskExecutor Bean 或者 java.util.concurrent.Executor Bean 名字是 'taskExecutor'。异步办法的执行产生的异样不能传递给调用方,对于为解决的异样仅打印日志。通过实现 AsyncConfigurer 能够线程池和异样处理器进行定制。 @Configuration@EnableAsyncpublic class AppConfig implements AsyncConfigurer { // @Bean 不增加此注解也能失常应用,增加后该线程池能够被Spring 治理。 @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(7); executor.setMaxPoolSize(42); executor.setQueueCapacity(11); executor.setThreadNamePrefix("MyExecutor-"); // 应用@Bean 后,能够正文上面一行,线程池的初始化工作会有Spring负责。 executor.initialize(); return executor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new MyAsyncUncaughtExceptionHandler(); }}mode 属性管制 advice 如何利用。默认是 AdviceMode.PROXY,其余属性能够管制代理的行为,代理后的办法只能通过代理援用调用,调用本类中的代理办法不会失效。另外能够将 mode 属性 设置为 AdviceMode.ASPECTJ. 须要引入spring-aspectj jar,反对以后类办法调用。将正文删除后的代码: ...

March 1, 2023 · 3 min · jiezi

关于spring:Spring-Boot自动装配原理易懂

Spring Boot的主动拆卸原理(易懂) 纯熟应用Spring Boot那么主动拆卸原理的把握是必不可少的一、主动拆卸是什么Spring Boot的主动拆卸实际上是从META-INF/spring.factories文件中获取到对应的须要进行主动拆卸的类,并生成相应的Bean对象,而后将它们交给Spring容器进行治理二、启动类注解流程关系剖析@SpringBootApplicationpublic class EntryApplication { public static void main(String[] args) { SpringApplication.run(EntryApplication.class, args); }}1.首先展现@SpringBootConfiguration注解流程图graph TDA["@SpringBootApplication"]A-->B["@SpringBootConfiguration"]A-->C["@EnableAutoConfiguration"]A-->D["@ComponentScan"]C-->E["@AutoConfigurationPackage"]C-->F["@Import(AutoConfigurationImportSelector.class)"]E-->G["@Import(AutoConfigurationPackages.Registrar.class)"]2.代码块展现@SpringBootConfiguration注解流程启动类的@SpringBootAppliation注解是一个组合注解,由以下3个注解组成 @SpringBootConfiguration@EnableAutoConfiguration@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })public @interface SpringBootApplication {其中@EnableAutoConfiguration 注解又分为以下两种注解 @AutoConfigurationPackage@Import(AutoConfigurationPackages.Registrar.class) //给容器中导入一个组件public @interface AutoConfigurationPackage {}//利用Registrar给容器中导入一系列组件其中@AutoConfigurationPackage 注解则由以下注解形成 @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage {3.注解含意@SpringBootConfiguration : 标注在某个类上,示意这是一个Spring Boot的配置类;@ComponentScan : 配置扫描门路,用于加载应用注解格局定义的bean@EnableAutoConfiguration : 开启主动拆卸性能 @AutoConfigurationPackage $\color{#F0F}{指定了默认的包规定 }$就是将主程序类所在包及所有子包下的组件扫描到Spring容器中;@Import(AutoConfigurationImportSelector.class) : 通过 @Import 注解导入 AutoConfigurationImportSelector类,而后通过该类的 $\color{#F0F}{selectImports }$办法去读取MATE-INF/spring.factories文件中配置的组件的全类名,并依照肯定的规定过滤掉不符合要求的组件的全类名,将残余读取到的各个组件的全类名汇合返回给IOC容器并将这些组件注册为bean三、具体介绍1.@AutoConfigurationPackage默认包规定详解 2.@Import(AutoConfigurationImportSelector.class)进行主动配置详解1、利用getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件2、调用List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)获取到所有须要导入到容器中的配置类3、利用工厂加载 Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader);失去所有的组件4、从META-INF/spring.factories地位来加载一个文件。默认扫描咱们以后零碎外面所有META-INF/spring.factories地位的文件**,$\color{#F0F}{依照条件拆卸(@Conditional)最终会按需配置}$  ...

February 28, 2023 · 1 min · jiezi

关于spring:三天吃透Spring面试八股文最新整理

本文曾经收录到Github仓库,该仓库蕴含计算机根底、Java根底、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等外围知识点,欢送star~ Github地址:https://github.com/Tyson0314/... Spring的长处通过管制反转和依赖注入实现松耦合。反对面向切面的编程,并且把利用业务逻辑和零碎服务离开。通过切面和模板缩小样板式代码。申明式事务的反对。能够从枯燥繁冗的事务管理代码中解脱进去,通过申明式形式灵便地进行事务的治理,进步开发效率和品质。不便集成各种优良框架。外部提供了对各种优良框架的间接反对(如:Hessian、Quartz、MyBatis等)。不便程序的测试。Spring反对Junit4,增加注解便能够测试Spring程序。Spring 用到了哪些设计模式?1、简略工厂模式:BeanFactory就是简略工厂模式的体现,依据传入一个惟一标识来取得 Bean 对象。 @Overridepublic Object getBean(String name) throws BeansException { assertBeanFactoryActive(); return getBeanFactory().getBean(name);}2、工厂办法模式:FactoryBean就是典型的工厂办法模式。spring在应用getBean()调用取得该bean时,会主动调用该bean的getObject()办法。每个 Bean 都会对应一个 FactoryBean,如 SqlSessionFactory 对应 SqlSessionFactoryBean。 3、单例模式:一个类仅有一个实例,提供一个拜访它的全局拜访点。Spring 创立 Bean 实例默认是单例的。 4、适配器模式:SpringMVC中的适配器HandlerAdatper。因为利用会有多个Controller实现,如果须要间接调用Controller办法,那么须要先判断是由哪一个Controller解决申请,而后调用相应的办法。当减少新的 Controller,须要批改原来的逻辑,违反了开闭准则(对批改敞开,对扩大凋谢)。 为此,Spring提供了一个适配器接口,每一种 Controller 对应一种 HandlerAdapter 实现类,当申请过去,SpringMVC会调用getHandler()获取相应的Controller,而后获取该Controller对应的 HandlerAdapter,最初调用HandlerAdapter的handle()办法解决申请,实际上调用的是Controller的handleRequest()。每次增加新的 Controller 时,只须要减少一个适配器类就能够,无需批改原有的逻辑。 罕用的处理器适配器:SimpleControllerHandlerAdapter,HttpRequestHandlerAdapter,AnnotationMethodHandlerAdapter。 // Determine handler for the current request.mappedHandler = getHandler(processedRequest);HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// Actually invoke the handler.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());public class HttpRequestHandlerAdapter implements HandlerAdapter { @Override public boolean supports(Object handler) {//handler是被适配的对象,这里应用的是对象的适配器模式 return (handler instanceof HttpRequestHandler); } @Override @Nullable public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { ((HttpRequestHandler) handler).handleRequest(request, response); return null; }}5、代理模式:spring 的 aop 应用了动静代理,有两种形式JdkDynamicAopProxy和Cglib2AopProxy。 ...

February 21, 2023 · 6 min · jiezi

关于spring:OpenSergo-Spring-Cloud-Alibaba-带来的服务治理能力

Spring Cloud 利用为何须要服务治理随着微服务技术的倒退,微服务(MicroServices) 的概念早已深入人心,越来越多的公司开始应用微服务架构来开发业务利用。 如果采纳切当,微服务架构能够带来十分大的劣势。微服务架构的最大益处是它能够晋升开发效率和零碎整体的稳定性: 开发部署简略:单个微服务的性能能够更快地更改,因为能够独立部署、影响范畴更小,启动和调试单个微服务的工夫老本相比于单体利用也大大减少。横向扩大简略:依据业务的顶峰低谷周期疾速的横向扩大非常简单,因为单个微服务通常很小,能够随着零碎整体负载的变动更快地启动和进行。架构降级灵便:单个微服务的“外部架构”能够迅速降级,因为微服务之间涣散耦合,只面向定义好的通信接口进行编程。这使开发团队可能基于本身的技术背景和偏好灵便抉择,而不会间接影响其余应用程序、服务或团队。更好的容错性:微服务之间能够实现更好的故障隔离,单个服务内的内存泄露等故障不容易影响其余服务,相比单体利用一个组件故障会拖垮整个零碎。然而微服务在施行过程中,也很容易遇到一些难点。如果微服务治理得不失当,反而有可能事与愿违,不仅不能享受到微服务架构带来的益处,反而会因为微服务带来的零碎复杂性,造成开发、运维部署的复杂度减少,进而影响开发迭代的速度,甚至影响零碎的整体稳定性,比方: 服务之间应用近程调用进行通信,这比过程内的间接调用简单很多。因为通信链路的复杂性,可能会呈现很多不确定的问题,会呈现近程调用不可用或者提早较高的状况,开发人员须要可能解决这些偶发问题,防止影响业务。随着业务的倒退,微服务之间的拓扑图开始变得复杂,排查问题变得艰难,搭建残缺的开发测试环境老本也越来越大。当性能波及到多个微服务模块时,迭代时须要审慎地协调多个开发团队的迭代排期,能力保障迭代可能按时交付,达到麻利开发的成果。因而, 微服务落地的胜利与否,很大水平上取决于是否很好的对这些微服务进行治理,对于传统的 Spring Cloud 利用,并没有提供开箱即用的服务治理机制,用户只能通过本人去实现 Spring Cloud 中的各种扩大点来实现服务治理。与其让用户费神费劲去本人写代码实现扩大点,不如 Spring Cloud Alibaba 间接来代替用户做这些事件,用户只需导入 Spring Cloud Alibaba 中的 starter,即可疾速给本人的 Spring Cloud 利用增加对应的微服务治理能力。 Spring Cloud Alibaba 新版本服务治理能力预览最新公布的 Spring Cloud Alibaba 2.2.10-RC1 新版本,基于社区 2.2.x 分支进行的构建公布,其中 2.2.10-RC1 版本通过首次给 Spring Cloud 生态带来了微服务治理能力,并给 Spring Cloud Alibaba 利用带来了 Proxyless Mesh 的能力,让 Spring Cloud Alibaba 利用更好的拥抱服务网格。具体的新版本预览内容如下: 2.2.10-RC1 是在 Spring Cloud Hoxton.SR12、Spring Cloud 2.3.12.RELEASE 的根底上,反对 Istio 以及 OpenSergo 管制面下发的服务治理规定,对 Spring Cloud 生态中的 Ribbon 负载均衡器以及 Spring MVC, Spring WebFlux 的各种拦截器做了加强,实现了服务治理的局部能力,属于一个重大个性公布的新版本[1]。 ...

February 16, 2023 · 2 min · jiezi

关于spring:记录一个-SpringSecurity-和-xauthtoken-一直登录失败的排查过程

前言近期要跑一个对于微信登录的Demo(应用 Spring+Angular),为了省事,打算从已有的我的项目上复制粘贴。我进行了以下操作:第一步:初始化我的项目,粘贴基本功能,如简略的页面、实体、必要的服务和控制器等,如果呈现依赖,则视状况粘贴依赖或删掉代码第二步:粘贴登录性能,以后这个我的项目用的和之前学的曾经不一样了,应用的是 SpringSecurity、SpringSession 和x-auth-token,所以第一反馈就是粘贴所有的过滤器和拦截器,登录性能可能会失常运行第三步:实现 Demo 的微信登录的性能而后因为某种问题,在第二步呈现了明码登录失败的状况,具体表现在:①输出正确的用户名明码后,网页提醒登录失败 ②控制台network提醒401,没有响应的 body,console 提醒登录失败 ③ 后端的控制台没有任何输入 ④ 如果在 login 办法上打断点,这个断点并不会被触发 到这里没有发现有用信息,仿佛无从下手我找到原来失常的我的项目,在 login 办法上打断点发现:⑤输出正确明码的状况下,后端 C层 login 会被触发,否则不会于是推出:明码的正确性应该是过滤器来校验,而不是用 login 办法校验,login 只负责校验胜利后更新登录信息排查那么问题可能出在这些过滤器上,于是在后端尽可能打断点来排查既然鉴权性能个别是过滤器和拦截器来实现的,于是排查办法就是把所有解决申请的办法打上断点打完断点重启我的项目,就能够看到真正的执行程序,分为两个阶段一是后端启动时进行的一系列初始化,二是发动申请时的处理过程先来看启动时是如何初始化的(省去了微信相干的步骤):首先是加载主类javapublic static void main(String[] args) { SpringApplication.run(WebSoctetAndStompStudyApplication.class, args);}过滤器执行构造函数(不止一个过滤器)java public WechatAuthFilter(WxMaService wxMaService, UserRepository userRepository) { this.wxMaService = wxMaService;this.userRepository = userRepository;}校验工具执行构造函数java public SuperPasswordBCryptPasswordEncoder(OneTimePassword oneTimePassword) { super();this.oneTimePassword = oneTimePassword;}header 解决java /** 应用header认证来替换默认的cookie认证 */@Bean public HttpSessionStrategy httpSessionStrategy() { return new HeaderAndParamHttpSessionStrategy();}明码工具java @Bean PasswordEncoder passwordEncoder() { return this.passwordEncoder;}SpringSecurity 设置路由java /** ...

February 14, 2023 · 2 min · jiezi

关于spring:Spring-系列框架的中文文档

当初互联网上的 Spring 框架相干的中文文档基本上都是机器翻译,内容艰涩难懂且长年未更新。例如,像 spring-security 等这种概念繁多,体系简单的技术,对于老手来说,没有优质的技术文档基本上是举步维艰。所以,我花了不少工夫制作了一个高质量的,且定期维护更新的中文文档(页面洁净,简洁无广告),心愿能够帮忙到更多的 spring 用户。 文档地址:springdoc.cn 文档中的所有内容,均是应用 Deppl 进行翻译,而后人工对每一行内容进行校验。 集体精力有限,目前仅仅实现了如下框架文档的翻译工作。 Spring Boot 中文文档Spring Data 中文文档 Spring Data Jdbc 中文文档Spring Data Jpa 中文文档Spring Cloud Gateway 中文文档Spring Security 中文文档 Spring Authorization Server 中文文档对标 Spring 官网文档 ,其余系列框架的中文文档的翻译工作,我也会在空闲工夫缓缓进行!

February 7, 2023 · 1 min · jiezi

关于spring:Spring项目中用了这种模式经理对我刮目相看

大家好,我是不才陈某~ 不晓得大家在我的项目中有没有遇到过这样的场景,依据传入的类型,调用接口不同的实现类或者说服务,比方依据文件的类型应用 CSV解析器或者JSON解析器,在调用的客户端个别都是用if else去做判断,比方类型等于JSON,我就用JSON解析器,那如果新加一个类型的解析器,是不是调用的客户端还要批改呢?这显然太耦合了,本文就介绍一种办法,服务定位模式Service Locator Pattern来解决,它帮忙咱们打消紧耦合实现及其依赖性,并提出将服务与其具体类解耦。 举荐Java工程师技术指南:https://github.com/chenjiabin... 文件解析器的例子咱们通过一个例子来通知你如何应用Service Locator Pattern。 假如咱们有一个从各种起源获取数据的应用程序,咱们必须解析不同类型的文件,比方解析CSV文件和JSON文件。 1、定义一个类型的枚举 public enum ContentType { JSON, CSV}2、定义一个解析的接口 public interface Parser { List parse(Reader r);}3、依据不同的文件类型有不同的实现类 // 解析csv@Componentpublic class CSVParser implements Parser { @Override public List parse(Reader r) { .. }}// 解析json@Componentpublic class JSONParser implements Parser { @Override public List parse(Reader r) { .. }}4、最初写一个调用的客户端,通过switch case依据不同的类型调用不同的实现 @Servicepublic class Client { private Parser csvParser, jsonParser; @Autowired public Client(Parser csvParser, Parser jsonParser) { this.csvParser = csvParser; this.jsonParser = jsonParser; } public List getAll(ContentType contentType) { .. switch (contentType) { case CSV: return csvParser.parse(reader); case JSON: return jsonParser.parse(reader); .. } } ..}可能大部分人都是像下面一样的形式实现的,也能失常运行,那深刻思考下,存在什么问题吗? ...

January 31, 2023 · 2 min · jiezi

关于spring:基于Spring-Cache实现CaffeinejimDB多级缓存实战

作者: 京东批发 王震背景在晚期参加涅槃气氛标签中台我的项目中,前台要求接口性能999要求50ms以下,通过设计Caffeine、ehcache堆外缓存、jimDB三级缓存,利用内存、堆外、jimDB缓存不同的个性晋升接口性能, 内存缓存采纳Caffeine缓存,利用W-TinyLFU算法取得更高的内存命中率;同时利用堆外缓存升高内存缓存大小,缩小GC频率,同时也缩小了网络IO带来的性能耗费;利用JimDB晋升接口高可用、高并发;前期通过压测及性能调优999性能<20ms 过后因为我的项目工期缓和,三级缓存实现较为臃肿、业务侵入性强、可读性差,在近期场景化举荐我的项目中,为B端商家场景化资源投放举荐,思考到B端流量绝对C端流量较小,但需保障接口性能稳固。采纳SpringCache实现caffeine、jimDB多级缓存计划,实现了低侵入性、可扩大、高可用的缓存计划,极大晋升了零碎稳定性,保障接口性能小于100ms; Spring Cache实现多级缓存多级缓存实例MultilevelCache/** * 分级缓存 * 基于Caffeine + jimDB 实现二级缓存 * @author wangzhen520 * @date 2022/12/9 */public class MultilevelCache extends AbstractValueAdaptingCache { /** * 缓存名称 */ private String name; /** * 是否开启一级缓存 */ private boolean enableFirstCache = true; /** * 一级缓存 */ private Cache firstCache; /** * 二级缓存 */ private Cache secondCache; @Override protected Object lookup(Object key) { Object value; recordCount(getUmpKey(this.getName(), UMP_GET_CACHE, UMP_ALL)); if(enableFirstCache){ //查问一级缓存 value = getWrapperValue(getForFirstCache(key)); log.info("{}#lookup getForFirstCache key={} value={}", this.getClass().getSimpleName(), key, value); if(value != null){ return value; } } value = getWrapperValue(getForSecondCache(key)); log.info("{}#lookup getForSecondCache key={} value={}", this.getClass().getSimpleName(), key, value); //二级缓存不为空,则更新一级缓存 boolean putFirstCache = (Objects.nonNull(value) || isAllowNullValues()) && enableFirstCache; if(putFirstCache){ recordCount(getUmpKey(this.getName(), UMP_FIRST_CACHE, UMP_NO_HIT)); log.info("{}#lookup put firstCache key={} value={}", this.getClass().getSimpleName(), key, value); firstCache.put(key, value); } return value; } @Override public void put(Object key, Object value) { if(enableFirstCache){ checkFirstCache(); firstCache.put(key, value); } secondCache.put(key, value); } /** * 查问一级缓存 * @param key * @return */ private ValueWrapper getForFirstCache(Object key){ checkFirstCache(); ValueWrapper valueWrapper = firstCache.get(key); if(valueWrapper == null || Objects.isNull(valueWrapper.get())){ recordCount(getUmpKey(this.getName(), UMP_FIRST_CACHE, UMP_NO_HIT)); } return valueWrapper; } /** * 查问二级缓存 * @param key * @return */ private ValueWrapper getForSecondCache(Object key){ ValueWrapper valueWrapper = secondCache.get(key); if(valueWrapper == null || Objects.isNull(valueWrapper.get())){ recordCount(getUmpKey(this.getName(), UMP_SECOND_CACHE, UMP_NO_HIT)); } return valueWrapper; } private Object getWrapperValue(ValueWrapper valueWrapper){ return Optional.ofNullable(valueWrapper).map(ValueWrapper::get).orElse(null); }}多级缓存管理器形象/** * 多级缓存实现抽象类 * 一级缓存 * @see AbstractMultilevelCacheManager#getFirstCache(String) * 二级缓存 * @see AbstractMultilevelCacheManager#getSecondCache(String) * @author wangzhen520 * @date 2022/12/9 */public abstract class AbstractMultilevelCacheManager implements CacheManager { private final ConcurrentMap<String, MultilevelCache> cacheMap = new ConcurrentHashMap<>(16); /** * 是否动静生成 * @see MultilevelCache */ protected boolean dynamic = true; /** * 默认开启一级缓存 */ protected boolean enableFirstCache = true; /** * 是否容许空值 */ protected boolean allowNullValues = true; /** * ump监控前缀 不设置不开启监控 */ private String umpKeyPrefix; protected MultilevelCache createMultilevelCache(String name) { Assert.hasLength(name, "createMultilevelCache name is not null"); MultilevelCache multilevelCache = new MultilevelCache(allowNullValues); multilevelCache.setName(name); multilevelCache.setUmpKeyPrefix(this.umpKeyPrefix); multilevelCache.setEnableFirstCache(this.enableFirstCache); multilevelCache.setFirstCache(getFirstCache(name)); multilevelCache.setSecondCache(getSecondCache(name)); return multilevelCache; } @Override public Cache getCache(String name) { MultilevelCache cache = this.cacheMap.get(name); if (cache == null && dynamic) { synchronized (this.cacheMap) { cache = this.cacheMap.get(name); if (cache == null) { cache = createMultilevelCache(name); this.cacheMap.put(name, cache); } return cache; } } return cache; } @Override public Collection<String> getCacheNames() { return Collections.unmodifiableSet(this.cacheMap.keySet()); } /** * 一级缓存 * @param name * @return */ protected abstract Cache getFirstCache(String name); /** * 二级缓存 * @param name * @return */ protected abstract Cache getSecondCache(String name); public boolean isDynamic() { return dynamic; } public void setDynamic(boolean dynamic) { this.dynamic = dynamic; } public boolean isEnableFirstCache() { return enableFirstCache; } public void setEnableFirstCache(boolean enableFirstCache) { this.enableFirstCache = enableFirstCache; } public String getUmpKeyPrefix() { return umpKeyPrefix; } public void setUmpKeyPrefix(String umpKeyPrefix) { this.umpKeyPrefix = umpKeyPrefix; }}基于jimDB Caffiene缓存实现多级缓存管理器/** * 二级缓存实现 * caffeine + jimDB 二级缓存 * @author wangzhen520 * @date 2022/12/9 */public class CaffeineJimMultilevelCacheManager extends AbstractMultilevelCacheManager { private CaffeineCacheManager caffeineCacheManager; private JimCacheManager jimCacheManager; public CaffeineJimMultilevelCacheManager(CaffeineCacheManager caffeineCacheManager, JimCacheManager jimCacheManager) { this.caffeineCacheManager = caffeineCacheManager; this.jimCacheManager = jimCacheManager; caffeineCacheManager.setAllowNullValues(this.allowNullValues); } /** * 一级缓存实现 * 基于caffeine实现 * @see org.springframework.cache.caffeine.CaffeineCache * @param name * @return */ @Override protected Cache getFirstCache(String name) { if(!isEnableFirstCache()){ return null; } return caffeineCacheManager.getCache(name); } /** * 二级缓存基于jimDB实现 * @see com.jd.jim.cli.springcache.JimStringCache * @param name * @return */ @Override protected Cache getSecondCache(String name) { return jimCacheManager.getCache(name); }}缓存配置/** * @author wangzhen520 * @date 2022/12/9 */@Configuration@EnableCachingpublic class CacheConfiguration { /** * 基于caffeine + JimDB 多级缓存Manager * @param firstCacheManager * @param secondCacheManager * @return */ @Primary @Bean(name = "caffeineJimCacheManager") public CacheManager multilevelCacheManager(@Param("firstCacheManager") CaffeineCacheManager firstCacheManager, @Param("secondCacheManager") JimCacheManager secondCacheManager){ CaffeineJimMultilevelCacheManager cacheManager = new CaffeineJimMultilevelCacheManager(firstCacheManager, secondCacheManager); cacheManager.setUmpKeyPrefix(String.format("%s.%s", UmpConstants.Key.PREFIX, UmpConstants.SYSTEM_NAME)); cacheManager.setEnableFirstCache(true); cacheManager.setDynamic(true); return cacheManager; } /** * 一级缓存Manager * @return */ @Bean(name = "firstCacheManager") public CaffeineCacheManager firstCacheManager(){ CaffeineCacheManager firstCacheManager = new CaffeineCacheManager(); firstCacheManager.setCaffeine(Caffeine.newBuilder() .initialCapacity(firstCacheInitialCapacity) .maximumSize(firstCacheMaximumSize) .expireAfterWrite(Duration.ofSeconds(firstCacheDurationSeconds))); firstCacheManager.setAllowNullValues(true); return firstCacheManager; } /** * 初始化二级缓存Manager * @param jimClientLF * @return */ @Bean(name = "secondCacheManager") public JimCacheManager secondCacheManager(@Param("jimClientLF") Cluster jimClientLF){ JimDbCache jimDbCache = new JimDbCache<>(); jimDbCache.setJimClient(jimClientLF); jimDbCache.setKeyPrefix(MultilevelCacheConstants.SERVICE_RULE_MATCH_CACHE); jimDbCache.setEntryTimeout(secondCacheExpireSeconds); jimDbCache.setValueSerializer(new JsonStringSerializer(ServiceRuleMatchResult.class)); JimCacheManager secondCacheManager = new JimCacheManager(); secondCacheManager.setCaches(Arrays.asList(jimDbCache)); return secondCacheManager; }接口性能压测压测环境廊坊4C8G * 3压测后果1、50并发时,未开启缓存,压测5min,TP99: 67ms,TP999: 223ms,TPS:2072.39笔/秒,此时服务引擎cpu利用率40%左右;订购履约cpu利用率70%左右,磁盘使用率4min后被打满; ...

January 31, 2023 · 4 min · jiezi

关于spring:Spring-学习笔记InstantiationAwareBeanPostProcessor接口

InstantiationAwareBeanPostProcessor 接口继承了 BeanPostProcessor 接口。 BeanPostProcessor 次要是在 Bean 调用初始化办法前后进行拦挡。 而 InstantiationAwareBeanPostProcessor 接口在 BeanPostProcessor 根底上增加了对 bean 的创立前后,以及设置属性前进行拦挡。 它的办法如下: 办法执行程序备注postProcessBeforeInstantiation()在 Bean 创立前调用可用于创立代理类,如果返回的不是 null(也就是返回的是一个代理类) ,那么后续只会调用 postProcessAfterInitialization() 办法postProcessAfterInstantiation()在 Bean 创立后调用返回值会影响 postProcessProperties() 是否执行,其中返回 false 的话,是不会执行。postProcessProperties()在 Bean 设置属性前调用用于批改 bean 的属性,如果返回值不为空,那么会更改指定字段的值postProcessBeforeInitialization()在 Bean 调用初始化办法前调用容许去批改 bean 实例化后,没有调用初始化办法前状态的属性postProcessAfterInitialization()在 Bean 调用初始化办法后调用容许去批改 bean 调用初始化办法后状态的属性验证TestBean 类public class TestBean implements InitializingBean { protected Object field; public TestBean() { System.out.println("创立 TestBean 对象"); } public Object getField() { return field; } public void setField(Object field) { System.out.println("设置 field 属性"); this.field = field; } @Override public void afterPropertiesSet() throws Exception { System.out.println("调用初始化办法"); } @Override public String toString() { return "TestBean{" + "field=" + field + '}'; }}InstantiationAwareBeanPostProcessor 类/*** InstantiationAwareBeanPostProcessor 的实现类*/public class CustomInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor { @Override public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { if(isMatchClass(beanClass)){ System.out.println("调用 postProcessBeforeInstantiation 办法"); } return null; } @Override public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { if(isMatchClass(bean.getClass())){ System.out.println("调用 postProcessAfterInstantiation 办法"); } return true; } @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException { if(isMatchClass(bean.getClass())){ System.out.println("调用 postProcessProperties 办法"); } return pvs; } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if(isMatchClass(bean.getClass())){ System.out.println("调用 postProcessBeforeInitialization 办法"); } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if(isMatchClass(bean.getClass())){ System.out.println("调用 postProcessAfterInitialization 办法"); } return bean; } private boolean isMatchClass(Class<?> beanClass){ // ClassUtils 类为 org.springframework.util.ClassUtils return TestBean.class.equals(ClassUtils.getUserClass(beanClass)); }}测试类@SpringBootTestpublic class CustomProcessorTest { @Autowired private ApplicationContext applicationContext; @Test void test(){ TestBean testBean = applicationContext.getBean(TestBean.class); System.out.println(testBean); } /** * 创立 TestBean 和 CustomInstantiationAwareBeanPostProcessor 这2个 bean */ @TestConfiguration static class TestConfig{ @Bean public TestBean getTestBean(){ TestBean testBean = new TestBean(); testBean.setField("1"); return testBean; } @Bean public CustomInstantiationAwareBeanPostProcessor customInstantiationAwareBeanPostProcessor(){ return new CustomInstantiationAwareBeanPostProcessor(); } }}执行后果调用 postProcessBeforeInstantiation 办法创立 TestBean 对象设置 field 属性调用 postProcessAfterInstantiation 办法调用 postProcessProperties 办法调用 postProcessBeforeInitialization 办法调用初始化办法调用 postProcessAfterInitialization 办法TestBean{field=1}分析方法1. postProcessBeforeInstantiation()postProcessBeforeInstantiation() 办法是在 Bean 创立之前调用的,该办法容许咱们返回 Bean 的其余子类(咱们也能够用 cglib 返回一个代理对象)。 这个办法在 InstantiationAwareBeanPostProcessor 接口的默认实现是返回 null。 ...

January 31, 2023 · 6 min · jiezi

关于spring:如何动态注册或注销Scheduled-cron定时任务

实在业务场景Spring中,@Scheduled 能够通过读取配置或者硬编码的形式指定 cron表达式,来实现触发某个工夫的定时工作的性能但咱们须要不重启服务,来实现动静地批改定时工作的触发工夫,原有性能无奈实现。 @Scheduled原理@EnableScheduling注解引入SchedulingConfiguration配置类SchedulingConfiguration注册ScheduledAnnotationBeanPostProcessor Scheduled注解后处理器实例ScheduledAnnotationBeanPostProcessor#postProcessAfterInitializationScheduled注解后处理器扫描带org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor#postProcessAfterInitialization@Scheduled的办法 依据注解中定义的属性,构建并注册定时工作实现思路找到Scheduled注解后处理器 注册的局部,查看源码 a. 是否预留可扩大的局部(例如 局部存在逻辑转发,转发到其余接口实例或者其余形象办法) b. 是否能够继承 c. 要害办法是否为protected d. 应用反射(下下策)找到扩大办法,封装 注册/登记逻辑具体实现思路定时工作注册逻辑在ScheduledAnnotationBeanPostProcessor#processScheduled办法中,办法为protected,创立子类,能够拜访该办法。办法须要传入 Scheduled注解实例,创立代理,构建注解对象实例 a. 寻找现有框架逻辑 i. 寻找Annotation结尾的Utils办法、Factory办法 ii. hibernate 存在相似办法 `AnnotationDescriptor.Builder`能够构建`org.hibernate.validator.internal.util.annotation.AnnotationFactory#create` iii. Spring框架存在相似办法`org.springframework.core.annotation.AnnotationUtils#synthesizeAnnotation(java.util.Map<java.lang.String,java.lang.Object>, java.lang.Class<A>, java.lang.reflect.AnnotatedElement)`b. 拷贝框架逻辑(上策) c. 本人编写创立代理实例(下下策) 定时工作登记相似逻辑在ScheduledAnnotationBeanPostProcessor#postProcessBeforeDestruction a. scheduledTasks公有属性无法访问,只能应用反射具体代码逻辑import com.google.common.collect.Maps;import org.redisson.liveobject.misc.ClassUtils;import org.springframework.core.annotation.AnnotationUtils;import org.springframework.scheduling.annotation.Scheduled;import org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor;import org.springframework.scheduling.config.CronTask;import org.springframework.scheduling.config.ScheduledTask;import org.springframework.stereotype.Component;import java.lang.reflect.Method;import java.util.Map;import java.util.Set;import static java.util.Collections.emptySet;/** * @author fuhangbo */@Componentpublic class ConfigurableScheduler { private final InnerScheduledAnnotationBeanPostProcessor postProcessor; public ConfigurableScheduler(InnerScheduledAnnotationBeanPostProcessor postProcessor) { this.postProcessor = postProcessor; } public void registerScheduleTask(String cron, Method method, Object target) { Map<String, Object> attributes = Maps.newHashMap(); attributes.put("cron", cron); // 构建Scheduled 注解实例 Scheduled scheduled = AnnotationUtils.synthesizeAnnotation(attributes, Scheduled.class, null); postProcessor.registerScheduleTask(scheduled, method, target); } public void unregister(String cron, Object target) { postProcessor.unregister(target, cron); } @Component public static class InnerScheduledAnnotationBeanPostProcessor extends ScheduledAnnotationBeanPostProcessor { private final Map<Object, Set<ScheduledTask>> scheduledTasksMap; public InnerScheduledAnnotationBeanPostProcessor() { // ScheduledAnnotationBeanPostProcessor 不提供remove 某个工作的逻辑 // 实现登记定时工作 须要拜访scheduledTasks属性,然而ScheduledAnnotationBeanPostProcessor中为公有,因而只能应用反射或者scheduledTasks实例 scheduledTasksMap = ClassUtils.getField(this, "scheduledTasks"); } public void registerScheduleTask(Scheduled scheduled, Method method, Object bean) { // 调用父类 prtotected办法 super.processScheduled(scheduled, method, bean); } public void unregister(Object bean, String cron) { synchronized (scheduledTasksMap) { Set<ScheduledTask> tasks = getScheduledTasks(); for (ScheduledTask task : tasks) { if (task.getTask() instanceof CronTask // cron表达式雷同时,勾销并从scheduledTasks中的实例 && ((CronTask) task.getTask()).getExpression().equals(cron)) { task.cancel(); scheduledTasksMap.getOrDefault(bean, emptySet()).remove(task); } } } } }}最终成果// cron变更 登记public modifyScheduleTask(String newCron, String oldCron) { if (oldCron!= null && !oldCron.equals(newCron)) { Method xxxMethod = ClassUtils.getMethod(StatisticsSchedules.class, "xxxMethod"); configurableScheduler.unregister(oldCron, this); if (newCron != null) { configurableScheduler.registerScheduleTask(newCron, xxxMethod, this); } }}public void xxxMethod() { // 定时工作办法 // do something...}

January 16, 2023 · 2 min · jiezi

关于spring:Spring获取Bean的9种方式

前言随着SpringBoot的遍及,Spring的应用也越来越广,在某些场景下,咱们无奈通过注解或配置的模式间接获取到某个Bean。比方,在某一些工具类、设计模式实现中须要应用到Spring容器治理的Bean,此时就须要间接获取到对应的Bean。 本文为大家整顿汇总了常见的获取Bean的形式,并提供一些优劣剖析,不便大家在应用到时有更好的抉择。同时,也会为大家适当的遍及和拓展一些相干常识。 Spring的IoC容器在Spring中,Bean的实例化、定位、配置应用程序中的对象及建设对象间的依赖关系,都是在IoC容器中进行的。因而,要在Spring中获取Bean,实质上就是从IoC容器当中获取Bean。 在Spring中,BeanFactory是IoC容器的理论代表者,该接口提供了IoC容器最基本功能。同时,Spring还提供了另外一种类型的容器:ApplicationContext容器。 ApplicationContext容器包含BeanFactory容器的所有性能(BeanFactory的子接口),提供了更多面向利用的性能,它提供了国际化反对和框架事件体系,更易于创立理论利用。 个别状况,咱们称BeanFactory为IoC容器,称ApplicationContext为利用上下文。但有时为了不便,也将ApplicationContext称为Spring容器。 通常不倡议应用BeanFactory,但BeanFactory 依然能够用于轻量级的应用程序,如挪动设施或基于applet的应用程序,其中它的数据量和速度是显著。 BeanFactory与ApplicationContext的区别BeanFactory是Spring框架的基础设施,面向Spring自身。ApplicationContext则面向应用Spring框架的开发者,简直所有的利用场合都能够间接应用ApplicationContext,而非底层的BeanFactory。 另外,ApplicationContext的初始化和BeanFactory有一个重大的区别: BeanFactory在初始化容器时,并未实例化Bean,直到第一次拜访某个Bean时才实例指标Bean。这样,咱们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次应用调用getBean办法才会抛出异样。 而ApplicationContext则在初始化利用上下文时就实例化所有单实例的Bean,绝对应的,ApplicationContext的初始化工夫会比BeanFactory长一些。 理解了上述的根本理论知识之后,咱们就能够尝试从IoC容器当中获取Bean对象了。 形式一:通过BeanFactory获取通过BeanFactory来获取Bean。基于xml配置文件的时代,能够通过如下形式取得BeanFactory,再通过BeanFactory来取得对应的Bean。 BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));UserInfo userInfo = (UserInfo) beanFactory.getBean("userInfo");有肯定编程年龄的程序员,应该对此还有一些印象。这种写法预计也只会呈现在古老的我的项目当中。鉴于xml模式配置文件曾经被基于注解模式所代替,同时XmlBeanFactory也被标注为废除。此种形式不举荐应用。 其实,不举荐的理由还有一个,在下面曾经提到,尽量不要应用BeanFactory,而应该应用ApplicationContext。 形式二:通过BeanFactoryAware获取在下面的形式中,XmlBeanFactory曾经被废除,但能够通过其余形式来取得BeanFactory,而后再从BeanFactory中取得指定的Bean。获取BeanFactory实例最简略的形式就是实现BeanFactoryAware接口。 BeanFactoryAware接口源码: public interface BeanFactoryAware extends Aware { /** * 初始化回调办法,Spring会主动将BeanFactory注入进去,接管之后即可应用BeanFactory */ void setBeanFactory(BeanFactory beanFactory) throws BeansException;}BeanFactoryAware属于org.springframework.beans.factory.Aware根标记接口,应用setter注入来在应用程序上下文启动期间获取对象。Aware接口是回调,监听器和观察者设计模式的混合,它示意Bean有资格通过回调形式被Spring容器告诉。 这里提供一个残缺的工具类: @Componentpublic class BeanFactoryHelper implements BeanFactoryAware { private static BeanFactory beanFactory; /** * 重写 BeanFactoryAware 接口的办法 * @param beanFactory :参数赋值给本地属性之后即可应用 BeanFactory * @throws BeansException BeansException */ @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { BeanFactoryHelper.beanFactory = beanFactory; } /** * 依据名称获取容器中的对象实例 * @param beanName :注入的实例必须曾经存在容器中,否则抛异样:NoSuchBeanDefinitionException * @return Object */ public static Object getBean(String beanName) { return beanFactory.getBean(beanName); } /** * 依据 class 获取容器中的对象实例 * @param requiredType :被注入的必须曾经存在容器中,否则抛异样:NoSuchBeanDefinitionException * @param <T> Class * @return 对象 */ public static <T> T getBean(Class<T> requiredType) { return beanFactory.getBean(requiredType); } /** * 判断 spring 容器中是否蕴含指定名称的对象 * @param beanName bean名称 * @return 是否存在 */ public static boolean containsBean(String beanName) { return beanFactory.containsBean(beanName); } //其它需要皆可参考 BeanFactory 接口和它的实现类}在上述工具类中,便是基于BeanFactoryAware的个性,取得了BeanFactory,而后再通过BeanFactory来取得指定的Bean。 ...

January 13, 2023 · 4 min · jiezi

关于spring:一文搞懂Spring堪称Spring源码终结者

Spring的影响力想必无需与大家多说,如果你用spring,那么读读源码有助于对你最重要的工具的了解,好的框架源码也能够帮忙咱们了解什么是好代码。 刚加入工作那会,没想过来读源码,更没想过来改框架的源码;甚至感觉那些有事没事扯源码的人就是在装,只是为了进步他们的逼格而已,在工作中没什么太大的用;但当初我的想法扭转了,上面我就我本人的一些见解来与大家聊聊为什么要读Spring源码。 为什么要读Spring源码?说实话我读Spring源码一刚开始为了面试,起初为了解决工作中的问题,再起初就是集体爱好了。说的好听点是有匠人精力;说的婉转点是好奇(底层是怎么实现的);说的不自信点是对黑盒的货色我用的没底,怕用错;说的简略直白点是晋升自我价值,为了更高的薪资待遇(这里对真正的技术迷说声道歉)。正如后面所说的Spring的影响力大家引人注目,会读Spring源码无疑是一个硬核涨薪技能。要读Spring源码看书是个不错的抉择,自成体系,让咱们把握的知识点不至于太散。互联网雷锋(小编我)也是费尽心思为大家网罗到两份很nice的学习笔记供大家参考,须要的小伙伴【间接点击此处】即可获取。 第一份文档Spring源码高级笔记(因为篇幅限度就只以截图的模式展现进去了) 第—局部Spring 概述第二局部核心思想第三局部手写实现loC和AOP第四局部Spring lOC利用第五局部Spring IOC源码深度分析第六局部Spring AOP利用第七局部Spring AOP源码深度分析目录一览: 笔记局部内容: 核心思想: 手写实现loC和AOP: Spring AOP利用: Spring AOP源码深度分析: 第二份文档Spring源码解析 本文档从外围实现和企业应用两个方面,由浅入深、由易到难地对Spring源码开展了零碎的解说,包含Spring 的设计理念和整体架构、容器的根本实现、默认标签的解析、自定义标签的解析、bean的加载、容器的性能扩大、AOP、数据库连贯JDBC、整合MyBatis、事务、SpringMVC、近程服务、Spring 音讯服务等内容。因为篇幅限度就只展现目录了~第一局部 外围实现 第1章 Spring整体架构和环境搭建第2章 容器的根本实现第3章 默认标签的解析第4章 自定义标签的解析第5章 bean的加载第6章 容器的性能扩大第7章 AOP第二局部 企业应用 第8章 数据库连贯JDBC第9章 整合MyBatis第10章 事务第11章 SpringMVC第12章 近程服务第13章 Spring音讯目录一览: 学习视频举荐: 写在最初源码中咱们能够学到很多货色,学习他人高效的代码书写、学习他人对设计模式的纯熟应用、学习他人对整个架构的布局,等等。如果你还能找出其中的有余,那么祝贺你,你要飞升了!会应用诚然重要,但晓得为什么这么应用同样重要。从模拟中学习,从模拟中翻新。如果你在读Spring源码的过程中有什么纳闷跟不解,那么这两份文档跟这些学习视频肯定会对你有所帮忙,如果有须要的敌人【间接点击此处】即可收费获取~

January 12, 2023 · 1 min · jiezi

关于spring:Spring中的循环依赖

转载一篇解说Spring循环依赖较为清晰的文章。 https://blog.csdn.net/weixin_...

January 11, 2023 · 1 min · jiezi

关于spring:基于Mybatis的分页控制-PageHelper分页控制底层原理

分页是基于WEB的利用绕不开的话题,个别状况下基于Mybatis的java我的项目可选的分页计划包含: 开源我的项目PageHelper。基于Mybatis的RowBounds分页。Mybatis-plus分页。本人实现的分页计划。明天咱们次要剖析前两种分页计划,Mybatis-plus的分页放在下一篇文章中剖析。除非有非凡起因,个别状况下也不太倡议本人再去实现分页计划,因为无论是PageHelper还是Mybatis-plus的分页计划,绝大部分状况下也够用了,没有必要反复造轮子。 物理分页和逻辑分页一般来讲,分页针对的是执行数据库查问的时候,符合条件的数据有很多、然而前端页面一次不须要展现全副数据的利用场景。 在这一场景下,应用层获取当前页数据的计划天然就分为两种:一种是应用层向数据库获取所有满足条件的数据,而后在应用层内存中对后果集进行过滤、获取到当前页数据后返回给前端。另一种须要数据库的反对,应用层只向数据库申请当前页的数据、获取数据后间接返回给前端。 第一种形式就是咱们常说的逻辑分页,也能够叫内存分页。第二种形式就是物理分页。 两种形式的区别其实高深莫测,逻辑分页不止是对数据库有内存、性能的压力,而且对于网络传输、应用层内存都会存在性能压力。尤其是在满足条件的数据量特地大(比方10w条)、而当前页须要的数据量比拟小(个别状况下都会比拟小,比方10条)的状况下,应用层获取到的绝大部分数据都被抛弃了,所以对于数据库服务器内存、网络、应用服务器内存都是一种极大地节约。 而物理分页因为从数据库获取到的数据量比拟小,所以性能压力比拟小。 因而,正式我的项目即便后期可能判断未来数据量不会太大的状况下,也不倡议应用逻辑分页计划。 当然,物理分页须要数据库的反对,比方MySQL的limit,Oracle的rownum等等,目前大部分的支流数据库都能够提供相似的反对。 基于Mybatis的RowBounds的分页实现Mybatis内置提供了基于RowBounds的分页计划,只有咱们在mapper接口中提供RowBounds参数,Mybatis天然就能够帮咱们实现分页。 然而咱们必须要晓得,RowBounds是逻辑分页!所以咱们用学习理解的态度来钻研一下RowBounds分页计划的实现机制,我的项目中不倡议间接应用。 基于RowBounds的分页实现非常简单,只有在mapper接口中设置RowBounds参数即可,比方获取所有用户的接口: List<User> selectPagedAllUsers(RowBounds rowBounds);List<User> selectAllUsers();在mapper.xml文件中上述两个接口对应的sql语句能够齐全一样,selectPagedAllUsers是实现分页的接口,selectAllUsers是不分页的接口。 那么咱们应该怎么传递RowBounds呢?首先要理解一下RowBounds具体是个什么东东。 其实RowBounds的定义很简略,最重要的两个参数,offset其实就是起始地位,limit能够了解为每页行数。 private final int offset; private final int limit; public RowBounds(int offset, int limit) { this.offset = offset; this.limit = limit; }转换为咱们比拟相熟的概念,currentPage示意当前页,pageSize示意每页行数,则Dao层获取分页数据的办法能够这么写: public List<User> getPagedUser(int currentPage, int pageSize){ int offset=(currentPage - 1) * pageSize; RowBounds rowBounds=new RowBounds(offset,pageSize); return UserMapper.selectPagedAllUsers(rowBounds);}你能够找一个数据量比拟大的表试一下,性能是能够直观感触到的(慢)。 PageHelper的分页原理PageHelper是利用Mybatis拦截器实现分页的,他的基本原理是: 应用层在须要分页的查问执行前,设置分页参数。应用Mybatis的Executor拦截器拦挡所有的query申请。在拦截器中查看以后申请是否设置了分页参数,没有设置分页参数则执行原查问返回所有后果。如果以后查问设置了分页参数,则执行分页查问:依据数据库类型革新以后的查问sql语句,减少获取当前页数据的sql参数,比方对于mysql数据库,在sql语句中减少limit语句。执行革新后的分页查问,获取数据返回。PageHelper分页的实现#RowBounds形式Springboot我的项目利用PageHelper实现分页非常简单,pom.xml中引入依赖即可: <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.4.1</version></dependency>除了咱们后面提到过的,通过PageHelper.startpage()设置分页参数之外,PageHelper还有另外一个设置分页参数的办法就是通过RowBounds。 通过RowBounds进行分页参数的设置从而实现分页的形式这种状况下参数offset-as-page-num会失效,设置为true的话代表RwoBounds的offset会作为pageNum,limt会作为pageSize参数应用。 引入PageHelper之后,再跑上一节RowBounds的那个例子,就会发现分页最终是变成了通过PageHelper实现、而非Mybatis原生的RowBounds实现的。 如果加上咱们后面文章说过的打印sql语句、统计sql执行时长的Mybatis拦截器(必须确保打印sql语句的拦截器在PageHelper拦截器之前初始化),会发现打印进去的sql语句多了limit语句。而且,如果咱们是对数据量比拟大(比方10w条数据)的表执行分页查问的话(比方查问最初一页),会发现通过Mybatis原生RowBounds实现的分页查问要比PageHelper的RowBounds形式慢很多。 PageHelper#RowBounds形式的实现原理PageHelper的源码不算简单,跟踪一下就能够发现应用RowBounds传递分页参数的底层原理。 PageHelper分页拦截器执行过程中会调用到PageParameters的getPage办法: ...

January 8, 2023 · 2 min · jiezi

关于spring:SpringBoot系列之数据库初始化datasource配置方式

【DB系列】数据库初始化-datasource配置形式 | 一灰灰Blog在咱们的日常业务开发过程中,如果有db的相干操作,通常咱们是间接建设好对应的库表构造,并初始化对应的数据,即更常见的状况下是咱们在已有表构造根底之下,进行开发;然而当咱们是以我的项目模式工作时,更常见的做法是所有的库表构造变更、数据的初始、更新等都须要持有对应的sql变更,并保留在我的项目工程中,这也是应用liqubase的一个重要场景; 将下面的问题进行简略的翻译一下,就是如何实现在我的项目启动之后执行相应的sql,实现数据库表的初始化? 本文将作为初始化形式的第一篇:基于SpringBoot的配置形式实现的数据初始化 <!-- more --> I. 我的项目搭建1. 依赖首先搭建一个规范的SpringBoot我的项目工程,相干版本以及依赖如下 本我的项目借助SpringBoot 2.2.1.RELEASE + maven 3.5.3 + IDEA进行开发 <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency></dependencies>本文应用MySql数据库, 版本8.0.31 2. 配置留神实现初始化数据库表操作的外围配置就在上面,重点关注 配置文件: resources/application.yml # 默认的数据库名database: name: storyspring: datasource: url: jdbc:mysql://127.0.0.1:3306/${database.name}?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: initialization-mode: always platform: mysql separator: ; data: classpath:config-data.sql #data-username: root #data-password: schema: classpath:config-schema.sql # schema必须也存在,若只存在data,data中的sql也不会被执行# springboot 2.7+ 版本应用上面这个# sql:# init:# mode: always# data-location: classpath:config-data.sql# schema-location: classpath:init-schema.sqllogging: level: root: info org: springframework: jdbc: core: debug下面的配置中,相比拟于一般的数据库链接配置,多了几个配置项 ...

December 28, 2022 · 2 min · jiezi

关于spring:从源码层面深度剖析Spring循环依赖

作者:郭艳红 以下举例皆针对单例模式探讨 图解参考 https://www.processon.com/vie... 1、Spring 如何创立Bean?对于单例Bean来说,在Spring容器整个生命周期内,有且只有一个对象。 Spring 在创立 Bean 过程中,应用到了三级缓存,即 DefaultSingletonBeanRegistry.java 中定义的: /** Cache of singleton objects: bean name to bean instance. */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); /** Cache of singleton factories: bean name to ObjectFactory. */ private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); /** Cache of early singleton objects: bean name to bean instance. */ private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);以 com.gyh.general 包下的 OneBean 为例,debug springboot 启动过程,剖析spring是如何创立bean的。 ...

December 21, 2022 · 3 min · jiezi

关于spring:面试八股文七什么是IOCinversion-of-Control和DIDependency-Injection

一、失常依赖对象的创立以及问题1.我在A类中蕴含有B类对象、C类对象、D类对象,则失常应用时 public A(B,C,D){ this.b = b; this.c = c; this.d = d;}//创立A类对象B b = new B();C c = new C();D d = new D();A a = new A(b,c,d);2.失常依赖对象的问题①因为须要创立多个不同的对象,代码量大②依赖有调整的时候,代码的耦合度比拟高,不利于扩大 二、什么是IOC?1.IOC是一种思维,不是一种技术,他是为了解决失常依赖对象的创立问题而产生的,领导咱们如何设计出松耦合、更低劣的程序2.IOC指由Spring来管制对象的生命周期和对象间的关系,举个例子说,咱们找女朋友,咱们须要找到她的微信、打听她的兴趣爱好、找机会意识他们等。失常创建对象就是相似的过程,咱们必须本人设计每个环节,须要的对象,咱们须要本人去创立它。而IOC容器相似婚介所,咱们就能够将咱们的“要求”通知Spring容器,由它来提供给咱们一个mm,整个过程不须要咱们管制,咱们只须要跟她谈恋爱、结婚就能够了3.从下面咱们能够看出,管制反转了什么?反转了获取依赖对象的形式,失常咱们是靠“本人”,当初咱们是靠“第三方” 三、什么是DI?1.DI是IOC的另一种说法或者说实现,他是一种技术,而不是一种思维2.从下面咱们能够晓得,IOC思维是反转了获取依赖对象的形式,换句话说,取得依赖对象的过程由本身治理变成了由IOC容器被动注入,Martin Fowler在04年的时候给“管制反转”起了个其余的名字叫“依赖注入”,依赖注入指在IOC容器在运行时,动静地将某种依赖关系注入到对象中3.它的实现形式是应用反射,因为反射容许运行时动静的生成对象、应用对象的办法、扭转对象的属性

December 18, 2022 · 1 min · jiezi

关于spring:Spring-Bean参数校验Validator

Spring Bean参数校验Validator以下2种形式能够用于所有的 Spring bean 不仅仅是 Controller 控制器。 一、原始类型参数在控制器(或者其余Bean)上应用@Validated注解。 控制器类@RestController@RequestMapping("account")@Validatedpublic class AccountController { // ......}在参数前增加校验注解 参数校验@GetMapping("account")public Result<AccountVO> getAccount(@NotNull Long accountId) { // ......}二、表单对象参数参数 AddressForm,增加 @Valid 注解。 表单校验@PostMapping("/address/save")public Object saveAddress(@Valid AddressForm addressForm) { // ......}AddressForm 中增加注解:表单校验@Datapublic class AddressForm { private Integer id; @NotBlank private String name; @NotBlank(message = "{mobile.not.blank}") @Pattern(regexp = "^1[34578]\\d{9}$") private String mobile; /** * list 汇合须要增加 @Valid 注解 */ @Valid private List<Xxx> list; // ...... }留神: 示例中 {name.not.blank} 为国际化配置。国际化须要在配置文件下自定义 org/hibernate/validator/ValidationMessages.properties 配置文件。 ...

December 17, 2022 · 1 min · jiezi

关于spring:Spring-Security自定义用户认证过程2

我的实例是Spring Boot我的项目,轻易一个SpringBoot我的项目都能够,先不启用SpringSecurity。 创立一个demoController,加办法: @ResponseBody @RequestMapping("wtf") public String wtf(){ return "WTF"; }运行我的项目,浏览器测试一下: 而后,pom文件退出SpringSecurity的依赖后就启用SpringSecurity了。 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>重新启动我的项目,拜访wtf测试一下:阐明SpringSecurity起作用了,因为Spring Security的默认安全策略是对所有申请都必须通过平安验证,所以申请wtf须要首先通过身份认证能力拜访。这个时候他用的是默认的叫InMemoryUserDetailsManager的用户信息管理器来实现咱们上篇文章所说的用户管理工作的,他的默认用户为user,明码在启动的过程中打印在了控制台:用这个用户名、明码试一下: 能够拜访: 好的,咱们开始干活。 用户对象Userdetails首先须要有用户对象,用户对象须要有用户名、明码,SpringSecurity要求用户对象实现接口:org.springframework.security.core.userdetails。 代码如下,简略一点: @Componentpublic class User implements UserDetails { private String name; private String password; @Override public Collection<? extends GrantedAuthority> getAuthorities() { return null; } @Override public String getPassword() { return password; } @Override public String getUsername() { return name; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; }}UserDetailsService而后须要有咱们本人的UserDetailsService。 ...

December 17, 2022 · 2 min · jiezi

关于spring:面试八股文二ApplicationContext和BeanFactory的区别

一、论断BeanFactory是Spring中的外围组件,代表Bean工厂,能够生成Bean,保护Bean,而ApplicationContext继承了BeanFactory,也是一个Bean工厂,但它也继承了其余接口,因而它还具备获取环境变量、国际化、事件公布等性能 二、UML类图及对应其余继承接口性能1.UML类图 2.对应继承接口性能 类名性能EnvironmentCapable获取环境变量MessageSource国际化ApplicationEventPublisher事件公布ResourcePatternResolver解析所有合乎模式的资源

December 15, 2022 · 1 min · jiezi

关于spring:底层到底做了什么-从spring-的Async-看AOP调用过程

上文曾经讲过@EnableAsync的bean构建过程,这里持续合成@Async执行过程,以及spring 的AOP调用过程。 先上demo public class ApiGatewayApplication { public static void main(String[] args) { SpringApplication.run(ApiGatewayApplication.class, args); AsyncTest test = BeanUtils.getBean("asyncTest"); test.test();//1 }}@Configurationpublic class WorkPool { @Bean("WorkPool") public ThreadPoolTaskExecutor taskExecutor(){ ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); threadPoolTaskExecutor.initialize(); return threadPoolTaskExecutor; }}@Componentpublic class AsyncTest { @Async public void test() { System.out.printf("test"); }}1、这一步是上面的重点,底层是怎么调用。 private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable { @Nullable public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); Object retVal; if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) { Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = methodProxy.invoke(target, argsToUse);//2 } else { retVal = (new CglibAopProxy.CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy)).proceed();//3 } }2、如果没有AOP行为,间接调用原bean。3、如果有AOP行为,结构一个 Cglib的CglibMethodInvocation 办法回调对象。 ...

December 2, 2022 · 2 min · jiezi

关于spring:观察者模式在spring中的应用

作者:王子源 1 观察者模式简介1.1 定义指多个对象间存在一对多的依赖关系,当一个对象的状态产生扭转时,所有依赖于它的对象都失去告诉并被自动更新。这种模式有时又称作公布-订阅模式、模型-视图模式,它是对象行为型模式。 1.2 角色介绍在观察者模式中,有以下几个角色。 主题也叫被观察者(Subject): 定义被观察者必须实现的职责,它能动静的减少勾销观察者,它个别是抽象类或者是实现类,仅仅实现作为被观察者必须实现的职责:治理观察者并告诉观察者。观察者(Observer): 观察者承受到音讯后,即进行更新操作,对接管到的信息进行解决。 具体的被观察者(ConcreteSubject): 定义被观察者本人的业务逻辑,同时定义对哪些事件进行告诉。 具体的观察者(ConcreteObserver): 具体的观察者,每个观察者接管到音讯后的解决反馈是不同的,每个观察者都有本人的解决逻辑。 1.3 观察者模式的实用场景对象间存在一对多关系,一个对象的状态产生扭转会影响其余对象。当一个形象模型有两个方面,其中一个方面依赖于另一方面时,可将这二者封装在独立的对象中以使它们能够各自独立地扭转和复用。实现相似播送机制的性能,不须要晓得具体收听者,只需散发播送,零碎中感兴趣的对象会主动接管该播送。多层级嵌套应用,造成一种链式触发机制,使得事件具备跨域(逾越两种观察者类型)告诉。2 观察者模式在Spring中的利用2.1 spring的事件监听机制Spring事件机制是观察者模式的实现。ApplicationContext中事件处理是由ApplicationEvent类和ApplicationListener接口来提供的。如果一个Bean实现了ApplicationListener接口,并且曾经公布到容器中去,每次ApplicationContext公布一个ApplicationEvent事件,这个Bean就会接到告诉。ApplicationEvent 事件的公布须要显示触发,要么 Spring 触发,要么咱们编码触发。spring内置事件由spring触发。咱们先来看一下,如何自定义spring事件,并使其被监听和公布。 2.1.1 事件 事件,ApplicationEvent,该抽象类继承了EventObject,EventObject是JDK中的类,并倡议所有的事件都应该继承自EventObject。 public abstract class ApplicationEvent extends EventObject {private static final long serialVersionUID = 7099057708183571937L;private final long timestamp = System.currentTimeMillis();public ApplicationEvent(Object source) {super(source);}public final long getTimestamp() {return this.timestamp;}}2.1.2 监听器 ApplicationListener,是一个接口,该接口继承了EventListener接口。EventListener接口是JDK中的,倡议所有的事件监听器都应该继承EventListener。 @FunctionalInterfacepublic interface ApplicationListener<E extends ApplicationEvent> extends EventListener {void onApplicationEvent(E var1);}2.1.3 事件公布器 ApplicationEventPublisher,ApplicationContext继承了该接口,在ApplicationContext的形象实现类AbstractApplicationContext中做了实现上面咱们来看一下 org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object, org.springframework.core.ResolvableType) protected void publishEvent(Object event, @Nullable ResolvableType eventType) {Assert.notNull(event, "Event must not be null");ApplicationEvent applicationEvent;if (event instanceof ApplicationEvent) {applicationEvent = (ApplicationEvent) event;}else {applicationEvent = new PayloadApplicationEvent<>(this, event);if (eventType == null) {eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();}}if (this.earlyApplicationEvents != null) {this.earlyApplicationEvents.add(applicationEvent);}else {//获取以后注入的公布器,执行公布办法getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);}if (this.parent != null) {if (this.parent instanceof AbstractApplicationContext) {((AbstractApplicationContext) this.parent).publishEvent(event, eventType);}else {this.parent.publishEvent(event);}}}咱们能够看到,AbstractApplicationContext中publishEvent办法最终执行公布事件的是ApplicationEventMulticaster#multicastEvent办法,上面咱们再来一起看一下multicastEvent办法 ...

November 28, 2022 · 3 min · jiezi

关于spring:Spring中获取bean的八种方式你get了几种

1、在初始化时保留ApplicationContext对象实用于Spring框架的独立应用程序,须要程序通过配置文件初始化Spring。 applicationContext.xml配置: <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="test" class="com.sxtx.bean.Test"></bean></beans>代码: @Testpublic void test() { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); //ApplicationContext applicationContext = new FileSystemXmlApplicationContext("applicationContext.xml"); Test test= (Test) applicationContext.getBean("test"); System.out.println(test);}2、通过Spring提供的工具类获取ApplicationContext对象适宜于Spring框架的B/S零碎,通过ServletContext对象获取ApplicationContext对象。而后在通过它获取须要的类实例。以下两个工具形式的差异是,前者在获取失败时抛出异样。后者返回null。 ApplicationContext ac1 = WebApplicationContextUtils.getRequiredWebApplicationContext(ServletContext sc); ApplicationContext ac2 = WebApplicationContextUtils.getWebApplicationContext(ServletContext sc); ac1.getBean("beanId"); ac2.getBean("beanId"); 3、实现接口ApplicationContextAware(举荐)实现该接口的setApplicationContext(ApplicationContext context)办法,并保留ApplicationContext 对象。Spring初始化时,扫描到该类,就会通过该办法将ApplicationContext对象注入。而后在代码中就能够获取spring容器bean了。 例如: User bean = SpringUtils.getBean(“user”);@Componentpublic class SpringUtils implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { SpringUtils.applicationContext = applicationContext; } public static <T> T getBean(String beanName) { if(applicationContext.containsBean(beanName)){ return (T) applicationContext.getBean(beanName); }else{ return null; } } public static <T> Map<String, T> getBeansOfType(Class<T> baseType){ return applicationContext.getBeansOfType(baseType); }}4、继承自抽象类ApplicationObjectSupport调用父类的getApplicationContext()办法,获取Spring容器对象。 ...

November 25, 2022 · 2 min · jiezi

关于spring:Java对象拷贝原理剖析及最佳实践

作者:宁海翔 1 前言对象拷贝,是咱们在开发过程中,绕不开的过程,既存在于Po、Dto、Do、Vo各个体现层数据的转换,也存在于零碎交互如序列化、反序列化。 Java对象拷贝分为深拷贝和浅拷贝,目前罕用的属性拷贝工具,包含Apache的BeanUtils、Spring的BeanUtils、Cglib的BeanCopier、mapstruct都是浅拷贝。 1.1 深拷贝深拷贝:对根本数据类型进行值传递,对援用数据类型,创立一个新的对象,并复制其内容称为深拷贝。 深拷贝常见有以下四种实现形式: 构造函数Serializable序列化实现Cloneable接口JSON序列化 1.2 浅拷贝浅拷贝:对根本数据类型进行值传递,对援用数据类型进行援用传递般的拷贝称为浅拷贝。通过实现Cloneabe接口并重写Object类中的clone()办法能够实现浅克隆。 2 罕用对象拷贝工具原理分析及性能比照目前罕用的属性拷贝工具,包含Apache的BeanUtils、Spring的BeanUtils、Cglib的BeanCopier、mapstruct。 Apache BeanUtils:BeanUtils是Apache commons组件外面的成员,由Apache提供的一套开源 api,用于简化对javaBean的操作,可能对根本类型主动转换。Spring BeanUtils:BeanUtils是spring框架下自带的工具,在org.springframework.beans包下, spring我的项目能够间接应用。Cglib BeanCopier:cglib(Code Generation Library)是一个弱小的、高性能、高质量的代码生成类库,BeanCopier依靠于cglib的字节码加强能力,动静生成实现类,实现对象的拷贝。mapstruct:mapstruct 是一个 Java正文处理器,用于生成类型平安的 bean 映射类,在构建时,依据注解生成实现类,实现对象拷贝。2.1 原理剖析2.1.1 Apache BeanUtils 应用形式:BeanUtils.copyProperties(target, source); BeanUtils.copyProperties 对象拷贝的外围代码如下: // 1.获取源对象的属性形容PropertyDescriptor[] origDescriptors = this.getPropertyUtils().getPropertyDescriptors(orig);PropertyDescriptor[] temp = origDescriptors;int length = origDescriptors.length;String name;Object value;// 2.循环获取源对象每个属性,设置指标对象属性值for(int i = 0; i < length; ++i) {PropertyDescriptor origDescriptor = temp[i];name = origDescriptor.getName();// 3.校验源对象字段可读切指标对象该字段可写if (!"class".equals(name) && this.getPropertyUtils().isReadable(orig, name) && this.getPropertyUtils().isWriteable(dest, name)) {try {// 4.获取源对象字段值value = this.getPropertyUtils().getSimpleProperty(orig, name);// 5.拷贝属性this.copyProperty(dest, name, value);} catch (NoSuchMethodException var10) {}}}循环遍历源对象的每个属性,对于每个属性,拷贝流程为: ...

November 24, 2022 · 3 min · jiezi

关于spring:Resource和Autowire的区别

@Resource和@Autowired@Resource和@Autowired都能够用来拆卸bean,都能够用于字段或setter办法。 @Autowired默认按类型拆卸,默认状况下必须要求依赖对象必须存在,如果要容许null值,能够设置它的required属性为false。 @Resource默认按名称拆卸,当找不到与名称匹配的bean时才依照类型进行拆卸。名称能够通过name属性指定,如果没有指定name属性,当注解写在字段上时,默认取字段名,当注解写在setter办法上时,默认取属性名进行拆卸。 留神:如果name属性一旦指定,就只会依照名称进行拆卸。 @Autowire和@Qualifier配合应用成果和@Resource一样:@Autowired(required = false) @Qualifier("example")private Example example;@Resource(name = "example")private Example example;@Resource拆卸程序 1.如果同时指定name和type,则从容器中查找惟一匹配的bean拆卸,找不到则抛出异样 2.如果指定name属性,则从容器中查找名称匹配的bean拆卸,找不到则抛出异样 3.如果指定type属性,则从容器中查找类型惟一匹配的bean拆卸,找不到或者找到多个抛出异样 4.如果都不指定,则主动依照byName形式拆卸,如果没有匹配,则回退一个原始类型进行匹配,如果匹配则主动拆卸 简要比照表格 注解比照@Resource@Autowire注解起源JDKSpring拆卸形式优先按名称优先按类型属性name、typerequired

November 23, 2022 · 1 min · jiezi

关于spring:Spring-和-Spring-MVC经典面试题

1.为什么应用Spring ?1). 不便解耦,简化开发 通过Spring提供的IoC容器,能够将对象之间的依赖关系交由Spring进行管制,防止硬编码所造成的适度程序耦合。 2). AOP编程的反对 通过Spring提供的AOP性能,不便进行面向切面的编程,如性能监测、事务管理、日志记录等。 3). 申明式事务的反对 4). 不便集成各种优良框架 5). 升高Java EE API的应用难度 如对JDBC,JavaMail,近程调用等提供了简便封装。2. 什么是IoC,为什应用IoC ?IoC全称Iversion of Controller,管制反转。 这概念是说你不必创建对象,而只须要形容它如何被创立。你不在代码里间接组装你的组件和服务,然而要在配置文件里形容哪些组件须要哪些服务,之后一个容器(IOC容器)负责把他们组装起来。 它能领导咱们如何设计出松耦合、更低劣的程序。 3.什么是AOP,为什么应用AOP ?AOP全称:Aspect-Oriented Programming,面向切面编程。 AOP,面向切面编程,就是把可重用的性能提取进去,而后将这些通用性能在适合的时候织入到应用程序中,比方事务管理、权限管制、日志记录、性能统计等。 AOP并没有帮忙咱们解决任何新的问题,它只是提供了一种更好的方法,可能用更少的工作量来解决现有的一些问题,使得零碎更加强壮,可维护性更好。 4.什么是Spring的事务管理?事务就是对一系列的数据库操作(比方插入多条数据)进行对立的提交或回滚操作,如果插入胜利,那么一起胜利,如果两头有一条出现异常,那么回滚之前的所有操作。这样能够防止出现脏数据,避免数据库数据呈现问题。 开发中为了防止这种状况个别都会进行事务管理。 Spring的申明式事务通常是指在配置文件中对事务进行配置申明,其中包含了很多申明属性,它是通过Spring Proxy帮你做代理,本人不必额定的写代码,只有在Spring配置文件中申明即可;通常用在数据库的操作外面; 编程式事务就是指通过硬编码的形式做事务处理,这种解决形式须要写代码,事务中的逻辑能够本人定制;能够是数据库的东东,也能够是其余的操作。 Spring中也有本人的事务管理机制,个别是应用TransactionMananger进行治理,能够通过Spring的注入来实现此性能。 5.Spring框架反对以下五种bean的作用域?singleton: 默认值,bean在每个Spring ioc 容器中只有一个实例。 prototype:一个bean的定义能够有多个实例。 request:每次http申请都会创立一个bean,该作用域仅在基于web的Spring ApplicationContext情景下无效。 session:在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情景下无效。 global-session:在一个全局的HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情景下无效。 6.什么是Spring的MVC框架?Spring 装备构建Web 利用的全功能MVC框架。Spring能够很便捷地和其余MVC框架集成,如Struts,Spring 的MVC框架用管制反转把业务对象和管制逻辑清晰地隔离。它也容许以申明的形式把申请参数和业务对象绑定。 spring mvc是一个基于mvc的web框架。spring mvc是spring框架的一个模块,springmvc和spring无需通过两头整合层进行整合。 7.如何启用注解?<context:annotation-config/>如果应用 <context:component-scan base-package="com.tgb.web.controller.annotation"> </context:component-scan> 则下面内容能够省略 8.Spring MVC的申请流程?第一步:发动申请到前端控制器(DispatcherServlet) 第二步:前端控制器申请HandlerMapping查找Handler能够依据xml配置、注解进行查找 第三步:处理器映射器HandlerMapping向前端控制器返回Handler 第四步:前端控制器调用处理器适配器去执行Handler 第五步:处理器适配器去执行Handler 第六步:Handler执行实现给适配器返回ModelAndView 第七步:处理器适配器向前端控制器返回ModelAndView。ModelAndView是springmvc框架的一个底层对象,包含 Model和view 第八步:前端控制器申请视图解析器去进行视图解析,依据逻辑视图名解析成真正的视图(jsp) 第九步:视图解析器向前端控制器返回View ...

November 23, 2022 · 5 min · jiezi

关于spring:百度架构师手写万字Spring-Security实战笔记一篇就搞懂

Spring Security是一个弱小且高度可定制的平安框架,致力于为Java利用提供身份认证和受权。 尽管Spring Security有弱小的性能,但它同时也有很高的学习老本。它囊括了身份认证的各种利用场景以及Web平安的大量常识,仅官网参考手册就有数十万字,并且还省略了诸多实现细节。许多开发人员在面对这样的“硕大无朋”时无从动手,更因为对其不够理解而在理论我的项目中不敢轻易采纳。本书由浅入深、抽丝剥茧地解说了SpringSecurity的典型利用场景,另外,还剖析了局部外围源码,以及许多开发语言之外的平安常识。通过本书,读者不仅能够学习如何利用 Spring Security,还能够学习借鉴它的实现思路,以将这种实现思路利用到其余开发场景中。 本书共分为4个局部。 第1局部(第1章至第3章)次要介绍Spring Security的根本配置,包含默认配置、简略表单认证,以及基于数据库模型的认证与受权。 第2局部(第4章至第11章)次要介绍各种定制化的配置场景,分析Web我的项目可能遇到的平安问题,并解说如何应用Spring Security进行无效防护,局部章节还装备了具体的源码导读。 第3局部(第13章)将登录用户的数据起源从零碎内转移到社交平台,具体介绍了OAuth,并应用Spring Social整合Spring Security,实现QQ快捷登录,满足一般性的我的项目需要。 第4局部(第14章)率领读者意识Spring Security OAuth框架,并基于该框架残缺实现了OAuth客户端、OAuth受权服务器以及OAuth资源服务器三种角色。除此之外,还简略分析了SpringSecurityOAuth的局部外围源码,以帮忙读者更好地了解OAuth框架。 因为文章篇幅问题,以下将只展现部门重要内容,须要残缺文档的小伙伴【间接点击此处】即可获取!! 废话不多说,先来看看目录 第1局部(第1章至第3章) 第2局部(第4章至第11章) 第3局部(第13章) 第4局部(第14章)

November 23, 2022 · 1 min · jiezi

关于spring:Spring面试题总结的很全面

1、什么是Spring?Spring是一个开源的Java EE开发框架。Spring框架的外围性能能够利用在任何Java应用程序中,但对Java EE平台上的Web应用程序有更好的扩展性。Spring框架的指标是使得Java EE应用程序的开发更加简捷,通过应用POJO为根底的编程模型促成良好的编程格调。 2、Spring有哪些长处?轻量级:Spring在大小和透明性方面相对属于轻量级的,根底版本的Spring框架大概只有2MB。 管制反转(IOC):Spring应用管制反转技术实现了松耦合。依赖被注入到对象,而不是创立或寻找依赖对象。 面向切面编程(AOP): Spring反对面向切面编程,同时把利用的业务逻辑与零碎的服务拆散开来。 容器:Spring蕴含并管理应用程序对象的配置及生命周期。 MVC框架:Spring的web框架是一个设计低劣的web MVC框架,很好的取代了一些web框架。 事务管理:Spring对下至本地业务上至全局业务(JAT)提供了对立的事务管理接口。 异样解决:Spring提供一个不便的API将特定技术的异样(由JDBC, Hibernate, 或JDO抛出)转化为统一的、Unchecked异样。 3、Spring 事务实现形式编程式事务管理:这意味着你能够通过编程的形式治理事务,这种形式带来了很大的灵活性,但很难保护。 申明式事务管理:这种形式意味着你能够将事务管理和业务代码拆散。你只须要通过注解或者XML配置管理事务。 4、Spring框架的事务管理有哪些长处它为不同的事务API(如JTA, JDBC, Hibernate, JPA, 和JDO)提供了对立的编程模型。它为编程式事务管理提供了一个简略的API而非一系列简单的事务API(如JTA).它反对申明式事务管理。它能够和Spring 的多种数据拜访技术很好的交融。 5、spring事务定义的流传规定流传机制阐明PROPAGATION_REQUIRED如果以后没有事务,就创立一个事务,如果曾经存在事务,就退出到这个事务。以后流传机制也是spring默认流传机制PROPAGATION_REQUIRES_NEW新建事务,如果以后存在事务,就抛出异样PROPAGATION_SUPPORTS反对以后事务,如果以后没有事务,就以非事务形式执行PROPAGATION_NOT_SUPPORTED以非事务形式执行操作,如果以后存在事务,就把以后事务挂起PROPAGATION_MANDATORY应用以后的事务,如果以后没有事务,就抛出异样PROPAGATION_NEVER以非事务形式执行,如果以后存在事务,则抛出异样PROPAGATION_NESTED如果以后存在事务,则在嵌套的事务内执行。如果以后没有事务,则执行与PROPAGATION_REQUIRED相似的操作6、Spring 事务底层原理划分处理单元——IoC 因为spring解决的问题是对单个数据库进行部分事务处理的,具体的实现首先用spring中的IoC划分了事务处理单元。并且将对事务的各种配置放到了ioc容器中(设置事务管理器,设置事务的流传个性及隔离机制)。 AOP拦挡须要进行事务处理的类 Spring事务处理模块是通过AOP性能来实现申明式事务处理的,具体操作(比方事务履行的配置和读取,事务对象的形象),用TransactionProxyFactoryBean接口来应用AOP性能,生成proxy代理对象,通过TransactionInterceptor实现对代理办法的拦挡,将事务处理的性能编织到拦挡的办法中。读取ioc容器事务配置属性,转化为spring事务处理须要的外部数据结构(TransactionAttributeSourceAdvisor),转化为TransactionAttribute示意的数据对象。 对事务处理实现(事务的生成、提交、回滚、挂起) spring委托给具体的事务处理器实现。实现了一个形象和适配。适配的具体事务处理器:DataSource数据源反对、hibernate数据源事务处理反对、JDO数据源事务处理反对,JPA、JTA数据源事务处理反对。这些反对都是通过设计PlatformTransactionManager、AbstractPlatforTransaction一系列事务处理的反对。 为罕用数据源反对提供了一系列的TransactionManager。 联合 PlatformTransactionManager实现了TransactionInterception接口,让其与TransactionProxyFactoryBean联合起来,造成一个Spring申明式事务处理的设计体系。 7、Spring MVC 运行流程第一步:发动申请到前端控制器(DispatcherServlet) 第二步:前端控制器申请HandlerMapping查找 Handler( 能够依据xml配置、注解进行查找) 第三步:处理器映射器HandlerMapping向前端控制器返回Handler 第四步:前端控制器调用处理器适配器去执行Handler 第五步:处理器适配器去执行Handler 第六步:Handler执行实现给适配器返回ModelAndView 第七步:处理器适配器向前端控制器返回ModelAndView(ModelAndView是springmvc框架的一个底层对象,包含Model和view) 第八步:前端控制器申请视图解析器去进行视图解析(依据逻辑视图名解析成真正的视图(jsp)) 第九步:视图解析器向前端控制器返回View 第十步:前端控制器进行视图渲染( 视图渲染将模型数据(在ModelAndView对象中)填充到request域) 第十一步:前端控制器向用户响应后果 8、BeanFactory和ApplicationContext有什么区别?ApplicationContext提供了一种解决文档信息的办法,一种加载文件资源的形式(如图片),他们能够向监听他们的beans发送音讯。 另外,容器或者容器中beans的操作,这些必须以bean工厂的编程形式解决的操作能够在利用上下文中以申明的形式解决。利用上下文实现了MessageSource,该接口用于获取本地音讯,理论的实现是可选的。 相同点:两者都是通过xml配置文件加载bean,ApplicationContext和BeanFacotry相比,提供了更多的扩大性能。 不同点:BeanFactory是提早加载,如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次应用调用getBean办法才会抛出异样;而ApplicationContext则在初始化本身是测验,这样有利于查看所依赖属性是否注入;所以通常状况下咱们抉择应用ApplicationContext。 9、什么是Spring Beans?Spring Beans是形成Spring利用外围的Java对象。这些对象由Spring IOC容器实例化、组装、治理。这些对象通过容器中配置的元数据创立,例如,应用XML文件中定义的创立。 在Spring中创立的beans都是单例的beans。在bean标签中有一个属性为”singleton”,如果设为true,该bean是单例的,如果设为false,该bean是原型bean。Singleton属性默认设置为true。因而,spring框架中所有的bean都默认为单例bean。 10、说一下Spring中反对的bean作用域Spring框架反对如下五种不同的作用域: singleton:在Spring IOC容器中仅存在一个Bean实例,Bean以单实例的形式存在。 prototype:一个bean能够定义多个实例。 request:每次HTTP申请都会创立一个新的Bean。该作用域仅实用于WebApplicationContext环境。 session:一个HTTP Session定义一个Bean。该作用域仅实用于WebApplicationContext环境。 globalSession:同一个全局HTTP Session定义一个Bean。该作用域同样仅实用于WebApplicationContext环境。 bean默认的scope属性是"singleton"。 ...

November 23, 2022 · 1 min · jiezi

关于spring:Spring面试题

1. 什么是spring?Spring 是个java企业级利用的开源开发框架。Spring次要用来开发Java利用,然而有些扩大是针对构建J2EE平台的web利用。Spring 框架指标是简化Java企业级利用开发,并通过POJO为根底的编程模型促成良好的编程习惯。 2. 应用Spring框架的益处是什么?轻量:Spring 是轻量的,根本的版本大概2MB。 管制反转:Spring通过管制反转实现了涣散耦合,对象们给出它们的依赖,而不是创立或查找依赖的对象们。 面向切面的编程(AOP):Spring反对面向切面的编程,并且把利用业务逻辑和零碎服务离开。 容器:Spring 蕴含并治理利用中对象的生命周期和配置。 MVC框架:Spring的WEB框架是个精心设计的框架,是Web框架的一个很好的替代品。 事务管理:Spring 提供一个继续的事务管理接口,能够扩大到上至本地事务下至全局事务(JTA)。 异样解决:Spring 提供方便的API把具体技术相干的异样(比方由JDBC,Hibernate or JDO抛出的)转化为统一的unchecked 异样。 3. Spring由哪些模块组成?以下是Spring 框架的根本模块:Core moduleBean moduleContext moduleExpression Language moduleJDBC moduleORM moduleOXM moduleJava Messaging Service(JMS) moduleTransaction moduleWeb moduleWeb-Servlet moduleWeb-Struts moduleWeb-Portlet module 4. 外围容器(利用上下文) 模块。这是根本的Spring模块,提供spring 框架的根底性能,BeanFactory 是任何以spring为根底的利用的外围。Spring 框架建设在此模块之上,它使Spring成为一个容器。 5. BeanFactory – BeanFactory 实现举例。BeanFactory是工厂模式的一个实现,提供了管制反转性能,用来把利用的配置和依赖从真正的利用代码中拆散。最罕用的BeanFactory 实现是XmlBeanFactory 类。 6. XMLBeanFactory最罕用的就是org.springframework.beans.factory.xml.XmlBeanFactory ,它依据XML文件中的定义加载beans。该容器从XML 文件读取配置元数据并用它去创立一个齐全配置的零碎或利用。 7. 解释AOP模块AOP模块用于给咱们的Spring利用做面向切面的开发, 很多反对由AOP联盟提供,这样就确保了Spring和其余AOP框架的共通性。这个模块将元数据编程引入Spring。 8. 解释JDBC形象和DAO模块。通过应用JDBC形象和DAO模块,保障数据库代码的简洁,并能防止数据库资源谬误敞开导致的问题,它在各种不同的数据库的错误信息之上,提供了一个对立的异样拜访层。它还利用Spring的AOP 模块给Spring利用中的对象提供事务管理服务。 9. 解释对象/关系映射集成模块。Spring 通过提供ORM模块,反对咱们在间接JDBC之上应用一个对象/关系映射映射(ORM)工具,Spring 反对集成支流的ORM框 架,如Hiberate,JDO和 iBATIS SQL Maps。Spring的事务管理同样反对以上所有ORM框架及JDBC。 ...

November 23, 2022 · 3 min · jiezi

关于spring:基于SpringAOP的自定义分片工具

作者:陈昌浩 1 背景随着数据量的增长,发现零碎在与其余零碎交互时,批量接口会呈现超时景象,发现原批量接口在实现时,没有做分片解决,当数据过大时或超过其余零碎阈值时,就会呈现谬误。因为与其余零碎交互比拟多,一个一个接口做分片优化,改变量较大,所以思考通过AOP解决此问题。 2 Spring-AOPAOP (Aspect Orient Programming),直译过去就是 面向切面编程。AOP 是一种编程思维,是面向对象编程(OOP)的一种补充。面向对象编程将程序形象成各个档次的对象,而面向切面编程是将程序形象成各个切面。 Spring 中的 AOP 是通过动静代理实现的。 Spring AOP 不能拦挡对对象字段的批改,也不反对结构器连接点,咱们无奈在 Bean 创立时利用告诉。 3 性能实现自定义分片解决分三个局部:自定义注解(MethodPartAndRetryer)、重试器(RetryUtil)、切面实现(RetryAspectAop)。 3.1 MethodPartAndRetryer源码 @Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface MethodPartAndRetryer {/*** 失败重试次数* @return*/int times() default 3;/*** 失败距离执行工夫 300毫秒* @return*/long waitTime() default 300L;/*** 分片大小* @return*/int parts() default 200;}@interface阐明这个类是个注解。 @Target是这个注解的作用域  public enum ElementType {/** 类、接口(包含正文类型)或枚举申明 */TYPE,/** 字段申明(包含枚举常量) */FIELD,/** 办法申明 */METHOD,/** 正式的参数申明 */PARAMETER,/** 构造函数申明 */CONSTRUCTOR,/** 局部变量申明 */LOCAL_VARIABLE,/** 正文类型申明*/ANNOTATION_TYPE,/** 程序包申明 */PACKAGE,/**类型参数申明*/TYPE_PARAMETER,/**类型的应用*/TYPE_USE}@Retention注解的生命周期 public enum RetentionPolicy {/** 编译器解决完后不存储在class中*/SOURCE,/**正文将被编译器记录在类文件中,但不须要在运行时被VM保留。 这是默认值*/CLASS,/**编译器存储在class中,能够由虚拟机读取*/RUNTIME}times():接口调用失败时,重试的次数。waitTime():接口调用失败是,距离多长时间再次调用。int parts():进行分片时,每个分片的大小。3.2 RetryUtil ...

November 22, 2022 · 2 min · jiezi

关于spring:Spring-Boot-分离配置文件的-N-种方式

明天聊一个小伙伴在星球上的发问: 问题不难,解决方案也有很多,因而我决定撸一篇文章和大家认真说说这个问题。 1. 配置文件地位首先小伙伴们要明确,Spring Boot 默认加载的配置文件是 application.properties 或者 application.yaml,默认的加载地位一共有五个,五个地位能够分为两类: 从 classpath 下加载,这个又细分为两种: 间接读取 classpath 下的配置文件,对应到 Spring Boot 我的项目中,就是 resources 目录下的配置。读取 classpath:/config/ 目录下的文件,对应到 Spring Boot 我的项目中就是 resources/config 目录下的配置。这两种状况如下图: 从我的项目所在的当前目录下加载,这个又细分为三种状况: 从我的项目当前目录下加载配置文件。从我的项目当前目录下的 config 文件夹中加载配置文件。从我的项目当前目录下的 config 文件夹的子文件夹中加载(孙子文件夹不能够)。这三种状况如下图: config 目录下的配置文件能够被加载,config/a 目录下的配置文件也能够被加载,然而 config/a/b 目录下的配置文件不会被加载,因为不是间接子文件夹。配置文件能够放在这么多不同的地位,如果同一个属性在多个配置文件中都写了,那么前面加载的配置会笼罩掉后面的。例如在 classpath:application.yaml 中设置我的项目端口号是 8080,在 我的项目当前目录/config/a/application.yaml 中设置我的项目端口是 8081,那么最终的我的项目端口号就是 8081。 这是默认的文件地位。 如果你不想让本人的配置文件叫 application.properties 或者 application.yaml,那么也能够自定义配置文件名称,只须要在我的项目启动的时候指定配置文件名即可,例如我想设置我的配置文件名为 app.yaml,那么咱们能够在启动 jar 包的时候依照如下形式配置,此时零碎会主动去下面提到的五个地位查找对应的配置文件: java -jar boot_config_file-0.0.1-SNAPSHOT.jar --spring.config.name=app如果我的项目曾经打成 jar 包启动了,那么后面所说的目录中,后三个中的我的项目当前目录就是指 jar 包所在的目录。如果你不想去这五个地位查找,那么也能够在启动 jar 包的时候明确指定配置文件的地位和名称,如下: java -jar boot_config_file-0.0.1-SNAPSHOT.jar --spring.config.location=optional:classpath:/app.yaml留神,我在 classpath 后面加上了 optional: 示意如果这个配置文件不存在,则依照默认的形式启动,而不会报错说找不到这个配置文件。如果不加这个前缀,那么当零碎找不到指定的配置文件时,就会抛出 ConfigDataLocationNotFoundException 异样,进而导致利用启动失败。 ...

November 21, 2022 · 1 min · jiezi

关于spring:mybatis常用配置笔记

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration> <!--引入jdbc.properties文件,尔后能够应用${key}的形式来拜访value--> <properties resource="jdbc.properties"/> <!-- 设置别名的标签,为某个具体的类型来设置一个别名,在mybatis配置文件的范畴中,来示意一个具体的类型 mybatis外围配置文件必须按指定的程序配置 type=""须要起别名的类型 alias 示意别名的名字 --> <typeAliases> <!--<typeAlias type="com.huawei.mybatis.pojo.User" alias="uerMapperAliase"></typeAlias>--> <!--或者 若不设置alias,以后的别名领有类型的别名,即类名且不辨别大小写--> <!-- <typeAlias type="com.huawei.mybatis.pojo.User"></typeAlias>--> <!--以包的形式来写别名--> <!--通过包来设置类型别名,指定包下所有的类型领有默认的别名,即类名且不辨别大小写--> <package name="com.huawei.mybatis.pojo"/> </typeAliases> <!-- 配置连贯数据库的环境 default="" 设置默认应用环境的id --> <environments default="development"> <!-- environment 设置一个具体的连贯数据库的环境 id 设置环境的惟一标识不能反复 --> <environment id="development"> <!-- transactionManager 设置事务管理器 type 设置事务的治理形式 type="JDBC" 应用JDBC原生的事务管理形式 type="MANAGED" 示意被治理,例如Spring --> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <!-- dataSource 设置数据源 type="" 示意设置数据源的类型 type="POOLED" 示意应用数据库连接池 type="UNPOOLED" 示意不应用数据库连接池,每次连贯从新创立连接池 type="JNDI" 示意应用上下文的数据源 --> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jsbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> <environment id="test"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <!--引入mybatis的映射文件--> <mappers> <!--<mapper resource="mappers/UserMapper.xml"/>--> <!-- 通过包来引入映射文件,必须满足两个条件: 1.mapper接口和映射文件所在的包必须统一 2.mapper接口的名字和映射文件的名字必须统一 --> <package name="com.huawei.mybatis.mapper"/> </mappers></configuration> ...

November 8, 2022 · 4 min · jiezi

关于spring:SpringFactoryBean源码简略分析

在spring中,如果一个bean的创立过程很简单,咱们能够应用FactoryBean。比方像上面的状况,我定义一套webService框架。 public interface WebService { void service(); }public class DefaultWebService implements WebService { private String serviceName; public DefaultWebService(String serviceName) { this.serviceName = serviceName; } @Override public void service() { System.out.println(serviceName + ": current support service for you...."); }}public class WebServiceWrapper implements WebService { private WebService webService; public WebServiceWrapper(WebService webService) { this.webService = webService; } @Override public void service() { System.out.println("befor service, we need do something...."); webService.service(); }}WebService接口定义服务的规范,DefaultWebService是一个默认实现,WebServiceWrapper的次要作用是在理论服务之前,做一些查看,筹备等工作。因而当咱们应用WebService,实际上是心愿应用的是WebServiceWrapper。 当初webService框架实现好了,并打包进去了,当初我须要在我的项目中利用应用spring注解个性去应用。 那该怎么办呢?我能够实现spring提供的FactoryBean接口,就像上面这样: ...

November 8, 2022 · 7 min · jiezi

关于spring:手写-Spring写到简历上被怼

作者:小傅哥博客:https://bugstack.cn图书:https://u.jd.com/4LapTH4 积淀、分享、成长,让本人和别人都能有所播种!始终都有一个十分好的硬核我的项目在你我身边,简单的架构、优良的设计、强悍的实现。如果能跟着实现一遍,编码能力至多晋升3年! 这个我的项目就是 Spring 框架,你想过把它当成本人的我的项目手写一遍吗? 一、它是:技术高峰IOC、AOP、SPI,Spring 给你的不只是一个开发框架,还包含它的设计思维。它通过解耦 Bean 对象的实例化过程,治理 Bean 的生命周期,来保护你在程序开发中所需对象应用过程。让你不须要刀耕火种般 new 一个对象,也不须要如 EJB 一样轻便臃肿的开发保护,而像春风一样润物(万物皆可Bean)细无声的应用。因而能够说它齐全担得起 Java 技术设计的高峰。 除了使用以外,Spring 框架也是泛滥码农,最能最先接触到的一个源码级简单我的项目。任何初出新手村变质的码农,寻找苦找的锤炼我的项目,都不如学习 Spring 源码来的畅快。从架构设计的简单、从分治形象的使用、从设计模式的驾驭,Spring 框架都是顶级的,也是最能给你带来丰盛播种的。 但学习 Spring 源码难吗?难,难到编程个1-2年的研发,也不晓得从哪下手学习。看到大部分材料和书籍也都是从一个知识点间接透析到骨头。没有浏览源码教训的小白,基本没法如编写者感同身受般学习,云里雾里一样的看,过眼云烟一样的忘。 为啥会这样,因为 Spring 倒退太久了,源码太大了,骨干外围源码外的繁枝末节有太多太多。就像沙发左移套个套,套上盖罩,罩上铺块小布料。但除了沙发以外的套、罩、布料对初学源码的研发来说,并没有那么重要。我要的就是最后的沙发,最开始的木板,看看它的构造,闻闻它的滋味。 所以,如果你想真的把 Spring 这个源码级简单框架的设计和实现精华排汇喽,就应该像开发一个我的项目一样,跟着小傅哥写一遍。只有这个我的项目是你写的,你能力晓得哪些细节是如何解决的,那些设计是如何驾驭的。 二、简历:学以致用《手写Spring》实现后我的能力如何体现到简历上?给个案例。 如果面试挑战你写Spring为啥?:我写spring,不是为了再造一个轮子。是为了排汇更好的架构和设计思维,面试官你们公司有那个我的项目的架构级别能对标Spring吗,我能够学学。那么这样的技术使用到理论我的项目,既能够解决业务的耦合实现,晋升交付品质,又能够扩大插件,升高反复建设。难道不好吗? 体现在专业技能上,例如;深刻学习 Spring 外围流程模块,包含;IOC、AOP、依赖倒置等流程,把握Spring解决简单场景所使用的分治、形象和常识(设计模式、设计准则),在解决Spring场景问题时,能够从外围原理上给出计划。同时也具备基于 Spring 开发 SpringBoot Starter 技能,为简单我的项目缩小同类共性需要的开发,凝练通用的技术组件,缩小研发老本。深刻学习 MyBaits 外围流程模块,包含;会话、反射、代理、事务、插件等流程,熟练掌握 ORM 框架的设计思维、实现形式和利用价值。并能按需联合 MyBatis 的插件机制,开发属于企业本人所需的性能,包含;数据分页、数据库表路由、监控日志、数据安全等方面。体现在我的项目教训上,例如;—— 对校招和实习比拟有用把 Spring、MyBatis 当一个学习我的项目来形容,这是你在离校前,最可能接触到的一个残缺的、成型的、出名的,有企业应用的,框架。你就依照本人学习并开发了这样一个框架为指标来写我的项目,并形容出这个我的项目,你用了什么技术栈,解决了什么问题,学习到了哪些。体现在我的项目利用上,例如;对于 Spring、MyBatis 的我的项目,个别都是插件类开发,比方各类的 SpringBoot Starter,MyBatis 插件,都是基于框架的深刻整合类技术解决方案,体现在简历上,十分抓眼球。一看你就是有深度和自研能力的研发人员。—— 个别不让你造轮子,但须要你有造轮子的能力,这样企业中一些软件能够被你进行优化和批改。体现在解决问题是上,例如;在你的本人的业务我的项目中,渗入一些对于解决了原我的项目应用 Spring 时,对于感知 Aware 形式或者联合 FactoryBean 包装对象等,所遇到的问题,因为你学习过源码,所以十分清晰这样的流程,因而解决了一个问题。通用 MyBatis 也实用于这样的形容形式,包含;事务、查问次数、批查问、插件能监听到的四个类(ParameterHandler、ResultSetHandler、StatementHandler、Executor )你给了更好的抉择。三、教你:驾驭源码对于大部分应用 Spring 框架的研发人员来说,可能在遇到 Spring 框架的报错揭示, 以及须要基于 Spring 框架开发 SpringBoot Starter 等技术类组件时,都会尝试浏览 Spring 框架的源码。因为 Spring 框架的源码宏大、简单,也不像平时的业务流程代码开发一样 具备分层构造,并且其中还应用了大量的设计模式,所以浏览难度较大。研发人员很难厘清其中的调用链路和各个类之间的关系。 ...

November 8, 2022 · 1 min · jiezi

关于spring:Spring事务传播机制

对事务的学习总体来讲应该蕴含以下几个局部: 事务的概念:数据库层面的,简略理解什么是事务,以及隔离级别、事务的提交、回滚、保留点等基本概念。动静代理以及Spring AOP,是Spring框架实现事务管制的底层技术根底。Spring框架事务的实现形式。Spring框架事务实现的底层原理。能够循序渐进逐渐学习,也能够独自学习其中某一部分,然而只有全副彻底把握了,能力对事务有一个全局的理解。 一般来讲,程序员实现事务管制有两种抉择:编程式事务、申明式事务。从代码编写的角度讲,编程式事务太麻烦,当初用的很少了,20年前的程序员绝大部分用的都是编程式事务,十分麻烦,须要本人获取连贯、开启事务、提交或回滚事务、敞开连贯等。 申明式事务是基于AOP实现的,当初JAVA世界的绝大部分我的项目都是基于Spring框架实现的,Spring框架、尤其是Springboot框架对事务管理的反对十分敌对,应用非常简单。 Spring框架实现事务管制其实非常简单,次要的注解只有两个: @EnableTransactionManagement@Transactional@EnableTransactionManagement注解是负责在配置类中关上Spring的事务管理性能,@Transactional是具体负责开启及实现事务管制的。从利用层面来讲,@Transactional是学习的重点。 @Target({ElementType.TYPE, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Inherited@Documentedpublic @interface Transactional { @AliasFor("transactionManager") String value() default ""; @AliasFor("value") String transactionManager() default ""; String[] label() default {}; Propagation propagation() default Propagation.REQUIRED; Isolation isolation() default Isolation.DEFAULT; int timeout() default -1; String timeoutString() default ""; boolean readOnly() default false; Class<? extends Throwable>[] rollbackFor() default {}; String[] rollbackForClassName() default {}; Class<? extends Throwable>[] noRollbackFor() default {}; String[] noRollbackForClassName() default {};}@Transactional的重要属性包含: 事务流传机制Propagation隔离级别isolation期待超时工夫timeout回滚条件rollbackFor不回滚条件noRollbackFor其实Spring事务管理是重度依赖于数据库底层的反对的,尤其是相似隔离级别、期待超时工夫等概念,都是间接依赖于数据库底层去实现的,Spring事务管理其实什么都不须要干,只须要把配置好的属性传递给数据库连贯、交给数据库去实现即可。 而事务流传机制Propagation与其余个性不同,是spring框架事务管理性能的重头戏。事务流传机制负责管制不同事务产生的时候,下层事务与上层事务之间的关系。 Spring定义了以下7种事务流传机制: ...

November 5, 2022 · 2 min · jiezi

关于spring:Dubbo-268-移植指南openEuler-2003-LTS-SP1

介绍简要介绍Dubbo是阿里巴巴公司开源的一个高性能优良的服务框架,使得利用可通过高性能的RPC(近程过程调用)实现服务的输入和输出性能,能够和Spring框架无缝集成。简略地说,Dubbo是一个基于Spring的RPC框架,可能实现服务的近程调用、服务的治理。 倡议版本倡议应用Dubbo 2.6.8版本。 环境要求硬件要求硬件要求如表1所示。 我的项目阐明CPU鲲鹏920处理器网络可拜访外网存储无要求内存无要求操作系统要求操作系统要求如表2所示。 我的项目版本openEuler20.03 LTS-SP1 aarch64Kernel4.19.90配置编译环境配置编译环境# cat /etc/resolv.conf nameserver 114.114.114.114nameserver 8.8.8.8装置依赖包下载并装置依赖包 yum install java-1.8.0* tcl git gcc gcc-c++ make cmake libtool autoconf automake -y查看Java版本 [[email protected] ~]# java -versionopenjdk version "1.8.0_272"OpenJDK Runtime Environment Bisheng (build 1.8.0_272-b10)OpenJDK 64-Bit Server VM Bisheng (build 25.272-b10, mixed mode)装置Maven下载Maven安装包 wget https://archive.apache.org/dist/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.tar.gz解压安装包到指定目录 tar -zxvf apache-maven-3.6.3-bin.tar.gz -C /opt/配置Maven环境变量。 a.在“/etc/profile” 文件开端减少Maven门路 echo "MAVEN_HOME=/opt/apache-maven-3.6.3/" >> /etc/profileecho "export PATH=$MAVEN_HOME/bin:$PATH" >> /etc/profileb.使批改的环境变量失效。 source /etc/profile查看配置是否失效。 [[email protected] ~]# mvn -vApache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)Maven home: /opt/apache-maven-3.6.3Java version: 1.8.0_272, vendor: Bisheng, runtime: /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.272.b10-7.oe1.aarch64/jreDefault locale: en_US, platform encoding: UTF-8OS name: "linux", version: "4.19.90-2012.4.0.0053.oe1.aarch64", arch: "aarch64", family: "unix"批改Maven配置文件中的本地仓、近程仓、代理等。配置文件门路:“/opt/apache-maven-3.6.3/conf/settings.xml”。 ...

November 4, 2022 · 1 min · jiezi

关于spring:Spring-Boot-实现接口幂等性的-4-种方案

一、什么是幂等性幂等是一个数学与计算机学概念,在数学中某一元运算为幂等时,其作用在任一元素两次后会和其作用一次的后果雷同。在计算机中编程中,一个幂等操作的特点是其任意屡次执行所产生的影响均与一次执行的影响雷同。幂等函数或幂等办法是指能够应用雷同参数反复执行,并能取得雷同后果的函数。这些函数不会影响零碎状态,也不必放心反复执行会对系统造成扭转。 二、什么是接口幂等性在HTTP/1.1中,对幂等性进行了定义。它形容了一次和屡次申请某一个资源对于资源自身应该具备同样的后果(网络超时等问题除外),即第一次申请的时候对资源产生了副作用,然而当前的屡次申请都不会再对资源产生副作用。这里的副作用是不会对后果产生毁坏或者产生不可意料的后果。也就是说,其任意屡次执行对资源自身所产生的影响均与一次执行的影响雷同。 三、为什么须要实现幂等性在接口调用时个别状况下都能失常返回信息不会反复提交,不过在遇见以下状况时能够就会呈现问题,如: 前端反复提交表单: 在填写一些表格时候,用户填写实现提交,很多时候会因网络稳定没有及时对用户做出提交胜利响应,以致用户认为没有胜利提交,而后始终点提交按钮,这时就会产生反复提交表单申请。用户歹意进行刷单: 例如在实现用户投票这种性能时,如果用户针对一个用户进行反复提交投票,这样会导致接口接管到用户反复提交的投票信息,这样会使投票后果与事实重大不符。接口超时反复提交: 很多时候 HTTP 客户端工具都默认开启超时重试的机制,尤其是第三方调用接口时候,为了防止网络稳定超时等造成的申请失败,都会增加重试机制,导致一个申请提交屡次。音讯进行反复生产: 当应用 MQ 消息中间件时候,如果产生消息中间件呈现谬误未及时提交生产信息,导致产生反复生产。应用幂等性最大的劣势在于使接口保障任何幂等性操作,免去因重试等造成零碎产生的未知的问题。 四、引入幂等性后对系统的影响幂等性是为了简化客户端逻辑解决,能搁置反复提交等操作,但却减少了服务端的逻辑复杂性和老本,其次要是: 把并行执行的性能改为串行执行,升高了执行效率。减少了额定管制幂等的业务逻辑,复杂化了业务性能;所以在应用时候须要思考是否引入幂等性的必要性,依据理论业务场景具体分析,除了业务上的特殊要求外,个别状况下不须要引入的接口幂等性。 五、Restful API 接口的幂等性当初风行的 Restful 举荐的几种 HTTP 接口办法中,别离存在幂等行与不能保障幂等的办法,如下: √ 满足幂等x 不满足幂等可能满足也可能不满足幂等,依据理论业务逻辑无关办法类型是否幂等形容Get√Get 办法用于获取资源。其个别不会也不该当对系统资源进行扭转,所以是幂等的。Post×Post 办法个别用于创立新的资源。其每次执行都会新增数据,所以不是幂等的。Put-Put 办法个别用于批改资源。该操作则分状况来判断是不是满足幂等,更新操作中间接依据某个值进行更新,也能放弃幂等。不过执行累加操作的更新是非幂等。Delete-Delete 办法个别用于删除资源。该操作则分状况来判断是不是满足幂等,当依据惟一值进行删除时,删除同一个数据屡次执行成果一样。不过须要留神,带查问条件的删除则就不肯定满足幂等了。例如在依据条件删除一批数据后,这时候新减少了一条数据也满足条件,而后又执行了一次删除,那么将会导致新减少的这条满足条件数据也被删除。六、如何实现幂等性计划一:数据库惟一主键 计划形容 数据库惟一主键的实现次要是利用数据库中主键惟一束缚的个性,一般来说惟一主键比拟实用于“插入”时的幂等性,其能保障一张表中只能存在一条带该惟一主键的记录。应用数据库惟一主键实现幂等性时须要留神的是,该主键一般来说并不是应用数据库中自增主键,而是应用分布式 ID 充当主键(能够参考 Java 中分布式 ID 的设计方案 这篇文章),这样能力能保障在分布式环境下 ID 的全局唯一性。 实用操作: 插入操作删除操作应用限度: 须要生成全局惟一主键 ID;次要流程: 次要流程: ① 客户端执行创立申请,调用服务端接口。② 服务端执行业务逻辑,生成一个分布式 ID,将该 ID 充当待插入数据的主键,而后执数据插入操作,运行对应的 SQL 语句。③ 服务端将该条数据插入数据库中,如果插入胜利则示意没有反复调用接口。如果抛出主键反复异样,则示意数据库中曾经存在该条记录,返回错误信息到客户端。计划二:数据库乐观锁计划形容: 数据库乐观锁计划个别只能实用于执行“更新操作”的过程,咱们能够提前在对应的数据表中多增加一个字段,充当以后数据的版本标识。这样每次对该数据库该表的这条数据执行更新时,都会将该版本标识作为一个条件,值为上次待更新数据中的版本标识的值。 实用操作: 更新操作应用限度: 须要数据库对应业务表中增加额定字段;形容示例: 例如,存在如下的数据表中: idnameprice1小米手机10002苹果手机25003华为手机1600为了每次执行更新时避免反复更新,确定更新的肯定是要更新的内容,咱们通常都会增加一个 version 字段记录以后的记录版本,这样在更新时候将该值带上,那么只有执行更新操作就能确定肯定更新的是某个对应版本下的信息。 idnamepriceversion1小米手机1000102苹果手机2500213华为手机16005这样每次执行更新时候,都要指定要更新的版本号,如下操作就能精确更新 version=5 的信息: UPDATE my_table SET price=price+50,version=version+1 WHERE id=1 AND version=5下面 WHERE 前面跟着条件 id=1 AND version=5 被执行后,id=1 的 version 被更新为 6,所以如果反复执行该条 SQL 语句将不失效,因为 id=1 AND version=5 的数据曾经不存在,这样就能保住更新的幂等,屡次更新对后果不会产生影响。 ...

November 4, 2022 · 4 min · jiezi

关于spring:SpringAop实现入口点简略分析

我的项目中经常会应用到spring提供的aop技术,那么它的大略实现原理是什么?省略spring扫描bean段落,当spring扫描完所有的bean后,开始对这些bean进行实例化和初始化,这样bean就筹备好了。接下来先看创立bean办法 protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { .... // 让BeanPostProcessors有机会返回一个代理,而不是指标bean实例 Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; } // 否则就去创立这个bean Object beanInstance = doCreateBean(beanName,mbdToUse, args); ... return beanInstance; }在下面的办法中,咱们留神到resolveBeforeInstantiation(),当看到这里的时候,我认为我的项目中的aop技术就是在这里使用的,可是理论debug下来并不是。 假如咱们有上面的一段代码: @RestControllerpublic class TestController { @MethodLog @GetMapping(value = "test") public String test(String name) { System.out.println("i come here"); return "hello"; }}其中@MethodLog是一个注解用来标识AOP,次要目标是在具体方法调用的时候,打印办法的参数。spring在创立这个TestController bean的时候,为什么不在resolveBeforeInstantiation返回这个代理对象呢?这是因为对于TestController aop的目标是加强其性能,而不是随便返回一个TestController类就行了。因而必须要先对TestController进行实例化,而后对TestController进行包装。 上面看doCreateBean(): protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {// 实例化beaninstanceWrapper = createBeanInstance(beanName, mbd, args);Object bean = instanceWrapper.getWrappedInstance();// 填充beanpopulateBean(beanName, mbd, instanceWrapper);// 初始化beanexposedObject = initializeBean(beanName, exposedObject, mbd);return exposedObject;}省略了很多代码,次要剖析要害局部,首先spring会抉择结构器实例化一个具体的bean,而后利用@value, @Autowired等注解来进行注入。最初进行初始化。在实例化和填充阶段都不应该解决代理的逻辑。 ...

November 3, 2022 · 2 min · jiezi

关于spring:如何不通过Controller编码方式批量暴露内网接口

该文章为原创(转载请注明出处):如何不通过@Controller编码方式批量裸露内网接口 - 简书 (jianshu.com)实在业务场景心愿在原有根底上裸露内网接口,且不心愿应用nginx做转发例如api/xxx/lan/yyy定义为内网接口然而现有接口为service/xxx/yyy 服务调用接口 须要达成目标主动将原有的 service/xxx/yyy 裸露为 api/xxx/lan/yyy,且不影响原有性能例如:原有接口 @Controller@RequestMapping("/service")public class ServiceController { @PostMapping("/xxx/yyy") public Object yyy(@RequestBody Object body) { }}计划一(繁琐耗时,不好保护)手动形式调用 @Controller@RequestMapping("/api")public class ApiLanController { @Autowired private ServiceController serviceController; @PostMapping("/xxx/lan/yyy") public Object yyy(@RequestBody Object body) { serviceController.yyy(body); }}计划二主动依据原有接口,裸露新接口 package xxx;import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanPostProcessor;import org.springframework.boot.context.properties.bind.Bindable;import org.springframework.boot.context.properties.bind.Binder;import org.springframework.core.env.Environment;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.method.HandlerMethod;import org.springframework.web.servlet.mvc.method.RequestMappingInfo;import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;import java.util.HashMap;import java.util.Map;import java.util.Set;import static java.util.Collections.emptySet;class ForwardingRequestMapping implements BeanPostProcessor { private final Set<String> paths; public ForwardingRequestMapping(Environment environment) { paths = Binder.get(environment).bind("xxx.mapping-lan-forwarding", Bindable.setOf(String.class)).orElse(emptySet()); } private static RequestMappingInfo buildNewRequestMappingInfo(RequestMappingInfo info, RequestMappingInfo.BuilderConfiguration config) { RequestMappingInfo.Builder builder = RequestMappingInfo .paths(changePatterns(info.getPatternsCondition().getPatterns().toArray(new String[0]))) .methods(info.getMethodsCondition().getMethods().toArray(new RequestMethod[0])) .params(info.getParamsCondition().getExpressions().stream().map(Object::toString).toArray(String[]::new)) .headers(info.getHeadersCondition().getExpressions().stream().map(Object::toString).toArray(String[]::new)) .consumes(info.getConsumesCondition().getExpressions().stream().map(Object::toString).toArray(String[]::new)) .produces(info.getProducesCondition().getExpressions().stream().map(Object::toString).toArray(String[]::new)) .mappingName(info.getName()); builder.customCondition(info.getCustomCondition()); return builder.options(config).build(); } private static String[] changePatterns(String[] patterns) { String[] newPatterns = new String[patterns.length]; for (int i = 0, patternsLength = patterns.length; i < patternsLength; i++) { // serviceController service/xxx/yyy 裸露为 api/xxx/lan/yyy// newPatterns[i] = } return newPatterns; } public static RequestMappingInfo.BuilderConfiguration builderConfiguration(RequestMappingHandlerMapping mapping) { RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration(); config.setUrlPathHelper(mapping.getUrlPathHelper()); config.setPathMatcher(mapping.getPathMatcher()); config.setSuffixPatternMatch(mapping.useSuffixPatternMatch()); config.setTrailingSlashMatch(mapping.useTrailingSlashMatch()); config.setRegisteredSuffixPatternMatch(mapping.useRegisteredSuffixPatternMatch()); config.setContentNegotiationManager(mapping.getContentNegotiationManager()); return config; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof RequestMappingHandlerMapping) { RequestMappingHandlerMapping mapping = ((RequestMappingHandlerMapping) bean); Map<RequestMappingInfo, HandlerMethod> newMap = new HashMap<>(); RequestMappingInfo.BuilderConfiguration config = builderConfiguration(mapping); mapping.getHandlerMethods().forEach((info, handleMethod) -> { for (String pattern : info.getPatternsCondition().getPatterns()) { if (paths.contains(pattern)) { RequestMappingInfo newInfo = buildNewRequestMappingInfo(info, config); newMap.put(newInfo, handleMethod); } } }); // 注册新的映射规定 newMap.forEach((mappingInfo, handleMethod) -> mapping.registerMapping(mappingInfo, handleMethod.getBean(), handleMethod.getMethod())); } return bean; } }最终成果新增配置,主动将service/xxx/yyy依据规定裸露为新的api/xxx/lan/yyy ...

October 28, 2022 · 2 min · jiezi

关于spring:如何使Value注解支持类似ConfigurationProperties的功能Value支持对象类型

该文章为原创(转载请注明出处):如何使@Value注解反对相似@ConfigurationProperties的性能? - 简书 (jianshu.com)实在业务场景(不心愿配置类注册为Bean 或 不心愿申明@ConfigurationProperties)假如某一个jar包内封装了DataSourceProperties @Configuration@ConfigurationProperties( prefix = "my.datasource")@Datapublic class DataSourceProperties { private List<String> suffix; private List<DataSourceDetailProperties> db;}在jar包的Configuration中,某个@Bean的结构过程中援用了这个DataSourceProperties public JdbcTemplate buildJdbcTemplate(DataSourceProperties dataSourceProperties) {}在某个业务场景中,同时存在两个DataSourceProperties会造成一个问题,注入的时候会提醒有多个候选的bean然而没法去批改Jar包中的内容 本人反复写一个DataSourceProperties 不是很优雅 这时候引出了一个需要,DataSourceProperties不心愿注册为Bean,然而可能从配置文件读取构建对象 解决方案一应用org.springframework.boot.context.properties.bind.Binder 从配置文件构建配置对象 @Beanpublic JdbcTemplate buildJdbcTemplate(Environment environment) { Binder binder = Binder.get(environment); DataSourceProperties properties1 = binder.bind("my.datasource1", Bindable.of(DataSourceProperties.class)).get(), properties2 = binder.bind("my.datasource2", Bindable.of(DataSourceProperties.class)).get();}binder.bind("xxx", Bindable.of(type)).get()仿佛是反复的编码方式?解决方案二使@Value注解可能反对主动调用这段代码 binder.bind("xxx", Bindable.of(type)).get()例如 @Beanpublic JdbcTemplate buildJdbcTemplate(@Value("my.datasource1") DataSourceProperties properties1, @Value("my.datasource2") DataSourceProperties properties2) { }org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency最初会交由converter解决 Class<?> type = descriptor.getDependencyType();Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);if (value != null) { if (value instanceof String) { String strVal = resolveEmbeddedValue((String) value); BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null); value = evaluateBeanDefinitionString(strVal, bd); } TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter()); try { return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor()); } catch (UnsupportedOperationException ex) { // A custom TypeConverter which does not support TypeDescriptor resolution... return (descriptor.getField() != null ? converter.convertIfNecessary(value, type, descriptor.getField()) : converter.convertIfNecessary(value, type, descriptor.getMethodParameter())); }}我的项目启动时,增加String to Object的转换器,反对@Value 并且 "bind:"结尾(避免影响@Value原有性能) ...

October 22, 2022 · 2 min · jiezi

关于spring:请求类中的json字符串字段如何自动转换RequestBodyModelAttribute

该文章为原创(转载请注明出处):申请类中的json字符串字段如何主动转换(@RequestBody、@ModelAttribute) - 简书 (jianshu.com)应用场景前端传值:{ "userJson": "{\"phone\":\"123\",\"name\":\"xxx\"}"}为了反对前端的数据结构,后端做了斗争,没法应用对本人更不便更正当的构造?后端@RequestBody注解的类中的字段为 userJson 传输,须要在controller层手动解决@GetMapping("test.do")public RestResponse<Void> test(@RequestBody TestBody body) { body.setUser(JSON.parseObject(body.getUserJson(), User.class)); // service.handleUser(body);}@Datastatic class TestBody { private String userJson; private User user;}static class User { private String phone; private String name;}如何把逻辑提取到框架或者工具层面,实现主动拆卸,无感应用对象 ?1. 扩大Jackson的反序列化器,转换String到字段类型的对象import com.fasterxml.jackson.core.JsonParser;import com.fasterxml.jackson.databind.*;import com.fasterxml.jackson.databind.annotation.JsonDeserialize;import com.fasterxml.jackson.databind.deser.ContextualDeserializer; import java.io.IOException; /** * @author uhfun * @see JsonDeserialize#using() */public class JacksonStringToJavaObjectDeserializer extends JsonDeserializer<Object> implements ContextualDeserializer { private JavaType javaType; @Override public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { ObjectMapper mapper = (ObjectMapper) p.getCodec(); String value = p.getValueAsString(); return value == null ? null : mapper.readValue(value, javaType.getRawClass()); } @Override public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) { javaType = property.getType(); return this; }}2. 扩大FastJson的反序列化器,转换String到字段类型的对象import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.annotation.JSONField;import com.alibaba.fastjson.parser.DefaultJSONParser;import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer; import java.lang.reflect.Type; import static com.alibaba.fastjson.parser.JSONToken.LITERAL_STRING; /** * @author uhfun * @see JSONField#deserializeUsing() */public class FastJsonStringToJavaObjectDeserializer implements ObjectDeserializer { @Override public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) { String s = parser.parseObject(String.class); return JSON.parseObject(s, type); } @Override public int getFastMatchToken() { return LITERAL_STRING; }}如何应用扩大的反序列化器1. Jackson@Datastatic class TestBody { @JsonDeserialize(using = JacksonStringToJavaObjectDeserializer.class) private User userJson;}2. FastJson@Datastatic class TestBody { @JSONField(deserializeUsing = FastJsonStringToJavaObjectDeserializer.class) private User userJson;}@ModelAttribute如何反对主动绑定json字符串的字段 为 对象 ?@GetMapping("test.do")public RestResponse<Void> test(@ModelAttribute TestBody body) { body.setUser(JSON.parseObject(body.getUserJson(), User.class)); // service.handleUser(body);}@Datastatic class TestBody { private String userJson; private User user;}static class User { private String phone; private String name;}实现上面的切面,主动注册须要转换的转换器import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.annotation.JSONField;import com.fasterxml.jackson.databind.annotation.JsonDeserialize;import com.google.common.collect.Sets;import org.springframework.boot.autoconfigure.web.format.WebConversionService;import org.springframework.core.annotation.AnnotatedElementUtils;import org.springframework.core.annotation.AnnotationUtils;import org.springframework.core.convert.TypeDescriptor;import org.springframework.core.convert.converter.GenericConverter;import org.springframework.web.bind.WebDataBinder;import org.springframework.web.bind.annotation.ControllerAdvice;import org.springframework.web.bind.annotation.InitBinder;import org.springframework.web.bind.annotation.ModelAttribute;import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; import java.lang.reflect.Field;import java.util.Set; import static java.util.Collections.singleton; /** * 使@ModelAttribute反对绑定Json字符串为对象 * * @author uhfun * @see RequestMappingHandlerAdapter#initControllerAdviceCache() * 寻找@ControllerAdvice切面下@InitBinder注解的办法 * @see org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#INIT_BINDER_METHODS * @see org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#getDataBinderFactory(org.springframework.web.method.HandlerMethod) * 依据切面构建InitBinderMethod办法 * @see org.springframework.web.method.annotation.InitBinderDataBinderFactory#initBinder * 初始化binder时反射调用 * @see ModelAttribute */@ControllerAdvicepublic class StringToJsonObjectSupport { private static final Set<Class<?>> CONVERTER_INITIALIZED = Sets.newConcurrentHashSet(); @InitBinder public void initStringJsonObjectConverter(WebDataBinder dataBinder) { Object target = dataBinder.getTarget(); WebConversionService conversionService; if (dataBinder.getConversionService() instanceof WebConversionService) { conversionService = (WebConversionService) dataBinder.getConversionService(); if (target != null && !CONVERTER_INITIALIZED.contains(target.getClass())) { for (Field field : dataBinder.getTarget().getClass().getDeclaredFields()) { JSONField jsonField = AnnotationUtils.findAnnotation(field, JSONField.class); boolean jsonFieldAnnotated = jsonField != null && jsonField.deserialize(), deserializeAnnotated = jsonFieldAnnotated || AnnotatedElementUtils.hasAnnotation(field, JsonDeserialize.class); if (deserializeAnnotated) { Class<?> type = field.getType(); conversionService.addConverter(new JsonStringToObjectConverter(type)); } } CONVERTER_INITIALIZED.add(target.getClass()); } } else { throw new IllegalStateException("dataBinder的ConversionService不是WebConversionService类型实例"); } } static class JsonStringToObjectConverter implements GenericConverter { private final Class<?> targetType; public JsonStringToObjectConverter(Class<?> targetType) { this.targetType = targetType; } @Override public Set<ConvertiblePair> getConvertibleTypes() { return singleton(new ConvertiblePair(String.class, targetType)); } @Override public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { return JSON.parseObject((String) source, targetType.getType()); } }}@Datastatic class TestBody { @JsonDeserialize private User userJson;}该文章为原创(转载请注明出处):申请类中的json字符串字段如何主动转换(@RequestBody、@ModelAttribute) - 简书 (jianshu.com)

October 22, 2022 · 2 min · jiezi

关于spring:85springboot-EhCache-集群二rmi-多播方式

ehcache.xml: <?xml version="1.0" encoding="UTF-8"?><ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false"> <!--超过大小后长久化磁盘地位--> <diskStore path="java.io.tmpdir"/> <cacheManagerPeerProviderFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" properties="peerDiscovery=automatic, multicastGroupAddress=230.0.0.1, multicastGroupPort=4446, multicastPacketTimeToLive=32" /> <cacheManagerPeerListenerFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory" properties="port=40002" /> <!-- default cache --> <defaultCache maxElementsInMemory="1000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="false"/> <!--自定义缓存配置--> <cache name="dcsCache" maxElementsInMemory="1000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="false" memoryStoreEvictionPolicy="LRU"> <cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory" properties="replicateAsynchronously=true, replicatePuts=true, replicateUpdates=true, replicateUpdatesViaCopy=true, replicateRemovals=true"/> <bootstrapCacheLoaderFactory class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory" /> </cache></ehcache>其余代码没变,见 上一篇blog...

October 17, 2022 · 1 min · jiezi

关于spring:这些不知道别说你熟悉-Spring

大家好,这篇文章跟大家来聊下 Spring 中提供的罕用扩大点、Spring SPI 机制、以及 SpringBoot 主动拆卸原理,重点介绍下 Spring 基于这些扩大点怎么跟配置核心(Apollo、Nacos、Zookeeper、Consul)等做集成。 写在后面咱们大多数 Java 程序员的日常工作根本都是在做业务开发,俗称 crudboy。 作为 crudboy 的你有没有这些懊恼呢? 随着业务的迭代,新性能的退出,代码变得越来越臃肿,可维护性越来越低,缓缓变成了屎山遇到一些框架层的问题不晓得怎么解决面试被问到应用的框架、中间件原理、源码层货色,不晓得怎么答复写了 5 年代码了,感觉本人的技术没有现实的出息如果你有上述这些懊恼,我想看优良框架的源码会是一个很好的晋升形式。通过看源码,咱们能学到业界大佬们优良的设计理念、编码格调、设计模式的应用、高效数据结构算法的应用、魔鬼细节的奇妙利用等等。这些货色都是助力咱们成为一个优良工程师不可或缺的。 如果你打算要看源码了,优先举荐 Spring、Netty、Mybatis、JUC 包。 Spring 扩大咱们晓得 Spring 提供了很多的扩大点,第三方框架整合 Spring 其实大多也都是基于这些扩大点来做的。所以纯熟的把握 Spring 扩大能让咱们在浏览源码的时候能疾速的找到入口,而后断点调试,一步步深刻框架内核。 这些扩大包含但不限于以下接口: BeanFactoryPostProcessor:在 Bean 实例化之前对 BeanDefinition 进行批改 BeanPostProcessor:在 Bean 初始化前后对 Bean 进行一些批改包装加强,比方返回代理对象 Aware:一个标记接口,实现该接口及子接口的类会收到 Spring 的告诉回调,赋予某种 Spring 框架的能力,比方 ApplicationContextAware、EnvironmentAware 等 ApplicationContextInitializer:在上下文筹备阶段,容器刷新之前做一些初始化工作,比方咱们罕用的配置核心 client 根本都是继承该初始化器,在容器刷新前将配置从近程拉到本地,而后封装成 PropertySource 放到 Environment 中供应用 ApplicationListener:Spring 事件机制,监听特定的利用事件(ApplicationEvent),观察者模式的一种实现 FactoryBean:用来自定义 Bean 的创立逻辑(Mybatis、Feign 等等) ImportBeanDefinitionRegistrar:定义@EnableXXX 注解,在注解上 Import 了一个 ImportBeanDefinitionRegistrar,实现注册 BeanDefinition 到容器中 InitializingBean:在 Bean 初始化时会调用执行一些初始化逻辑 ...

October 10, 2022 · 4 min · jiezi