claer

claer 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 该用户太懒什么也没留下

个人动态

claer 回答了问题 · 8月31日

解决请问现在那个使用那个跨前端技术会比较好。

image

关注 7 回答 9

claer 发布了文章 · 4月29日

Java注解

flag

解释注解的方式:反射!反射是jdk中提供的一种机制,通过读取编译后的运行文件,反向获取类信息的一种手段!可以通过反射获取类、方法、成员变量上的运行时注解内容!

元注解: 能注解到注解上的注解,能用在其他注解上的注解

JDK

从Java源码中提取的所有注解
注解版本说明
@WebParam表示方法的参数
@Oneway表示为只有输入消息而没有输出消息的 Web Service 单向操作
@WebResult表示方法的返回值
@HandlerChain使 Web Service 与外部定义的处理程序链相关联。
@WebService用于对接口,类进行注解,表示要发布的web服务
@WebMethod该注解用于用@WebService注解的类或接口的方法上,表示要发布的方法
@SOAPMessageHandlers
@SOAPMessageHandler
@SOAPBindingSOAP绑定类型:RPC、DOCUMENT
@InitParam
@Resource
@Resources
@PreDestroyServlet2.5被@PreDestroy修饰的方法会在服务器卸载Servlet之前运行,并且只会被服务器调用一次
@Generated
@PostConstructServlet2.5被@PostConstruct修饰的方法会在服务器加载Servle且构造函数之后执行,并且只会被服务器执行一次
@RespectBinding
@ResponseWrapper
@WebServiceRefs
@Action
@RequestWrapper
@WebServiceProvider
@WebServiceRef
@FaultAction
@WebFault
@ServiceMode
@WebEndpoint
@BindingType
@WebServiceClient
@Addressing
@MTOM
@WebServiceFeatureAnnotation
@XmlSeeAlso
@XmlElementDecl
@XmlIDREF
@XmlValue
@XmlAccessorType
@XmlMixed
@XmlID
@XmlRootElement
@XmlElementRef
@XmlEnum
@XmlElementWrapper
@XmlEnumValue
@XmlType
@XmlAccessorOrder
@XmlSchemaTypes
@XmlInlineBinaryData
@XmlSchema
@XmlAnyElement
@XmlSchemaType
@XmlRegistry
@XmlList
@XmlAttribute
@XmlAttachmentRef
@XmlNs
@XmlMimeType
@XmlElements
@XmlElement
@XmlElementRefs
@XmlTransient
@XmlAnyAttribute
@XmlJavaTypeAdapters
@XmlJavaTypeAdapter
@Interned
@NotNull
@Nullable
@MessageDumping
@FeatureConstructor
@FeatureListValidatorAnnotation
@InstanceResolverAnnotation
@SchemaValidation
@Serialization
@StreamingAttachment
@UsesJAXBContext
@MemberSubmissionAddressing
@XmlAccessorFactory
@OverrideAnnotationOf
@XmlIsSet
@XmlLocation
@WsgenProtocol
@InheritedAttributes
@DescriptorFields
@ParameterNames
@ManagedAttribute
@AMXMetadata
@InheritedAttribute
@IncludeSubclass
@ManagedData
@ManagedOperation
@Description
@NameValue
@ManagedObject
@DescriptorKey
@Taxonomy
@Reset
@ProbeParam
@ProbeProvider
@Probe
@ProbeListener
@EnvelopeStyle
@Property
@DatabindingMode
@Reference
@Immutable
@Ignore
@Logger
@Setter
@Constructor
@Optimistic
@SpecializedFunction
@ScriptClass
@Function
@Getter
@CallerSensitive
@Contended
@MXBean
@ProbeName
@ProviderName
@Attributes
@NameAttributes
@ProviderAttributes
@FunctionAttributes
@ModuleName
@ModuleAttributes
@ArgsAttributes
@FunctionName
@ConstructorProperties
@Transient
@Deprecatedjdk1.5内置注解:用于标志过时的类、方法和成员变量
@FunctionalInterfacejdk1.8函数式接口注解,用于定义函数式接口
@SafeVarargsjdk1.7参数安全类型注解,用于提示用户参数安全
@Overridejdk1.5内置注解:用于修饰重写的方法
@SuppressWarningsjdk1.5内置注解:用户忽略@Deprecated标志过的警告
@Repeatablejdk1.8表示注解的属性可以重复!@Repeatable通俗来讲,就是注解容器!
@Inheritedjdk1.5元注解:子类继承父类的注解(子类没有任何注解修饰)
@Retentionjdk1.5元注解:表示注解保留周期
@Documentedjdk1.5元注解:将注解写入文档
@Targetjdk1.5元注解:表示注解可以使用在什么地方
@Nativejdk1.8生成本机头文件的工具的提示,以确定是否需要头文件,如果需要,它应该包含哪些声明。
@Validate
@RequireContainer
@Require
@Exported
@SupportedOptions
@SupportedAnnotationTypes
@SupportedSourceVersion
@Trusted

Servlet

Tomcat 7.0及以上版本的server.api才有注解
注解版本说明
@WebServletServlet3.0用于声明servlet
@WebListenerServlet3.0用于声明过监听器,可通过控制filter的文件名来控制执行顺序
@WebFilterServlet3.0用于声明过滤器
@WebInitParamServlet3.0为 Servlet 或者过滤器指定初始化参数,配置@WebServlet或@WebFilter时使用
@MultipartConfigServlet3.0HttpServletRequest 提供的对上传文件的支持,该注解标注在 Servlet 上面
@HandlesTypes
@HttpConstraint
@HttpMethodConstraint
@ServletSecurity

Validation

JSR-303JAVA EE 6中的一项子规范,叫做Bean Validation,但是这只是一个接口,没有具体实现。

javax.validation.Validationhibernate-validatorSpring validtor
提供了JSR 303规范中所有内置constraint 的实现,除此之外还有一些附加的 constraint

注解支持Java类型备注
@AssertFalseBoolean, boolean验证元素值必须为flase
@AssertTrueBoolean, boolean验证元素值必须为true,否则抛异常
@CreditCardNumberCharSequence验证信用卡号码是否有效
@DecimalMaxObject验证数值是否小于等于指定值
@DecimalMinCharSequence验证数值是否大于等于指定值
@Digits(integer = 3, fraction = 2)Long, Integer, Double, Float验证注解的元素值的整数位数和小数位数上限
@EmailCharSequence验证元素必须是电子邮箱地址
@Futurejava.util.Date, java.util.Calendar验证日期为当前时间之后
@FutureOrPresentjava.util.Date, java.util.Calendar验证日期为当前时间或之后一个时间
@Length(min=,max=)CharSequence验证元素值包含在一个区间
@MaxCharSequence检验当前数值小于等于指定值
@MinBigDecimal, BigInteger, byte, short,int, long,Number.检验当前数值大于等于指定值
@NotBlankCharSequence验证元素值不为null且移除两边空格后长度大于0
@NotEmptyCharSequence,Collection,Map and Arrays验证元素值不为null且不为空(字符串长度不为0、集合大小不为0)
@NotNullObject验证元素值不能为 null
@NullObject验证元素值为null
@Pastjava.util.Date, java.util.Calendar验证日期为当前时间之前
@PastOrPresentjava.util.Date, java.util.Calendar验证日期为当前时间或之前
@Pattern(regex=,flag=)CharSequence验证元素必须符合指定的正则表达式
@Range(min=,max=,message=)CharSequence验证数值为指定值区间范围内
@Size(max=, min=)String,Collection,Map,arrays,CharSequence验证元素个数包含在一个区间
@UniqueElementsCollection校验集合中的元素必须保持唯一 否则异常
@URLCharSequence验证日期为当前时间之前
@ValidObject验证关联对象元素进行递归校验检查
@ScriptAssertCharSequence脚本表达式的计算结果为true
@SafeHtmlCharSequence可能包含不安全的html内容

JAX-RS

JerseyCxF框架实现了JSR311/JSR339标准

jersey常用注解

Annotation作用说明
@GET查询请求相当于数据库的查询数据操作
@PUT更新请求相当于数据库的更新数据操作
@POST插入请求相当于数据库的插入数据操作
@DELETE删除请求相当于数据的删除数据操作
@Pathuri路径定义资源的访问路径,client通过这个路径访问资源。比如:@Path("user")
@Produces指定返回MIME格式资源按照那种数据格式返回,可取的值有:MediaType.APPLICATION_XXX。比如:@Produces(MediaType.APPLICATION_XML)
@Consumes接受指定的MIME格式只有符合这个参数设置的请求再能访问到这个资源。比如@Consumes("application/x-www-form-urlencoded")
@PathParamuri路径参数写在方法的参数中,获得请求路径参数。比如:@PathParam("username") String userName
@QueryParamuri路径请求参数写在方法的参数中,获得请求路径附带的参数。比如:@QueryParam("desc") String desc
@DefaultValue设置@QueryParam参数的默认值如果@QueryParam没有接收到值,就使用默认值。比如:@DefaultValue("description") @QueryParam("desc") String desc
@FormParamform传递的参数接受form传递过来的参数。比如:@FormParam("name") String userName
@BeanParam通过Bean的形式传递参数接受client传递的bean类型的参数,同时这个bean可以在属性上配置@FormParam用以解决client的属性名称和bean的属性名称不一致的问题。比如:@BeanParam User user
@Context获得一些系统环境信息通过@Context可以获得以下信息:UriInfo、ServletConfig、ServletContext、HttpServletRequest、HttpServletResponse和HttpHeaders等
@XmlRootElement将bean转换为xml如果要将bean以xml或json的格式返回,必须要这个注解。比如:@XmlRootElementpublic class User{...}
@XmlElements
@XmlElement

spring

// 手动回滚事务
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

声明bean的注解

  • @Component 组件,没有明确的角色
  • @Service 在业务逻辑层使用(service层)
  • @Repository 在数据访问层使用(dao层)
  • @Controller 在展现层使用,控制器的声明(C)

注入bean的注解

都可以注解在set方法和属性上,推荐注解在属性上(一目了然,少写代码)。
  • @Autowired 由Spring提供
  • @Inject 由JSR-330提供
  • @Resource 由JSR-250提供

java配置类相关注解

  • @Configuration 声明当前类为配置类,相当于xml形式的Spring配置(类上)
  • @Bean 注解在方法上,声明当前方法的返回值为一个bean,替代xml中的方式(方法上)
  • @Configuration 声明当前类为配置类,其中内部组合了@Component注解,表明这个类是一个bean(类上)
  • @ComponentScan 用于对Component进行扫描,相当于xml中的(类上)
  • @WishlyConfiguration@Configuration@ComponentScan的组合注解,可以替代这两个注解

切面(AOP)相关注解

Spring支持AspectJ的注解式切面编程。
  • @Aspect 声明一个切面(类上)
使用@After@Before@Around定义建言(advice),可直接将拦截规则(切点)作为参数。
  • @After 在方法执行之后执行(方法上)
  • @Before 在方法执行之前执行(方法上)
  • @Around 在方法执行之前与之后执行(方法上)
  • @PointCut 声明切点
在java配置类中使用@EnableAspectJAutoProxy注解开启Spring对AspectJ代理的支持(类上)
AOP配置元素描述
<aop:advisor>定义AOP通知器
<aop:after>定义AOP后置通知(不管该方法是否执行成功)
<aop:after-returning>在方法成功执行后调用通知
<aop:after-throwing>在方法抛出异常后调用通知
<aop:around>定义AOP环绕通知
<aop:aspect>定义切面
<aop:aspect-autoproxy>定义@AspectJ注解驱动的切面
<aop:before>定义AOP前置通知
<aop:config>顶层的AOP配置元素,大多数的<aop:*>包含在<aop:config>元素内
<aop:declare-parent>为被通知的对象引入额外的接口,并透明的实现
<aop:pointcut>定义切点

@Bean的属性支持

  • @Scope 设置Spring容器如何新建Bean实例(方法上,得有@Bean) ,其设置类型包括:

    • Singleton (单例,一个Spring容器中只有一个bean实例,默认模式),
    • Protetype (每次调用新建一个bean),
    • Request (web项目中,给每个http request新建一个bean),
    • Session (web项目中,给每个http session新建一个bean),
    • GlobalSession(给每一个 global http session新建一个Bean实例)
  • @StepScope 在Spring Batch中还有涉及
  • @PostConstruct 由JSR-250提供,在构造函数执行完之后执行,等价于xml配置文件中bean的initMethod
  • @PreDestory 由JSR-250提供,在Bean销毁之前执行,等价于xml配置文件中bean的destroyMethod

@Value注解

@Value 为属性注入值(属性上),支持如下方式的注入
  • @Value("Michael Jackson") String name; 注入普通字符
  • @Value("#{systemProperties['os.name']}") String osName; 注入操作系统属性
  • @Value("#{ T(java.lang.Math).random() * 100 }") String randomNumber; 注入表达式结果
  • @Value("#{domeClass.name}") String name; 注入其它bean属性
  • @Value("classpath:com/hgs/hello/test.txt") String Resource file; 注入文件资源
  • @Value("http://www.cznovel.com") Resource url; 注入网站资源
  • @Value("${book.name}") String bookName; 注入配置文件

注入配置使用方法

编写配置文件(test.properties)
book.name= test
@PropertySource 加载配置文件(类上)
@PropertySource("classpath:/test.propertie")
③ 还需配置一个PropertySourcesPlaceholderConfigurer的bean。

环境切换

  • @Profile 通过设定Environment的ActiveProfiles来设定当前context需要使用的配置环境。(类或方法上)
  • @Conditional Spring4中可以使用此注解定义条件话的bean,通过实现Condition接口,并重写matches方法,从而决定该bean是否被实例化。(方法上)

异步相关

@EnableAsync 配置类中,通过此注解开启对异步任务的支持,叙事性AsyncConfigurer接口(类上)

@Async 在实际执行的bean方法使用该注解来申明其是一个异步任务(方法上或类上所有的方法都将异步,需要@EnableAsync开启异步任务)

定时任务相关

  • @EnableScheduling 在配置类上使用,开启计划任务的支持(类上)
  • @Scheduled 来申明这是一个任务,包括cron,fixDelay,fixRate等类型(方法上,需先开启计划任务的支持)

@Enable*注解说明

这些注解主要用来开启对xxx的支持。
  • @EnableAspectJAutoProxy 开启对AspectJ自动代理的支持
  • @EnableAsync 开启异步方法的支持
  • @EnableScheduling 开启计划任务的支持
  • @EnableWebMvc 开启Web MVC的配置支持
  • @EnableConfigurationProperties 开启对@ConfigurationProperties注解配置Bean的支持
  • @EnableJpaRepositories 开启对SpringData JPA Repository的支持
  • @EnableTransactionManagement 开启注解式事务的支持
  • @EnableTransactionManagement 开启注解式事务的支持
  • @EnableCaching 开启注解式的缓存支持

测试相关注解

  • @RunWith 运行器,Spring中通常用于对JUnit的支持
@RunWith(SpringJUnit4ClassRunner.class)
  • @ContextConfiguration 用来加载配置ApplicationContext,其中classes属性用来加载配置类
@ContextConfiguration(classes={TestConfig.class})

SpringMVC注解

  • @EnableWebMvc 在配置类中开启Web MVC的配置支持,如一些ViewResolver或者MessageConverter等,若无此句,重写WebMvcConfigurerAdapter方法(用于对SpringMVC的配置)。
  • @Controller 声明该类为SpringMVC中的Controller
  • @RequestMapping 用于映射Web请求,包括访问路径和参数(类或方法上)
  • @ResponseBody 支持将返回值放在response内,而不是一个页面,通常用户返回json数据(返回值旁或方法上)
  • @RequestBody 允许request的参数在request体中,而不是在直接连接在地址后面。(放在参数前)
  • @PathVariable 用于接收路径参数,比如@RequestMapping(“/hello/{name}”)申明的路径,将注解放在参数中前,即可获取该值,通常作为Restful的接口实现方法。
  • @RestController 该注解为一个组合注解,相当于@Controller@ResponseBody的组合,注解在类上,意味着,该Controller的所有方法都默认加上了@ResponseBody
  • @ControllerAdvice 通过该注解,我们可以将对于控制器的全局配置放置在同一个位置,注解了@Controller的类的方法可使用@ExceptionHandler@InitBinder@ModelAttribute注解到方法上,这对所有注解了 @RequestMapping的控制器内的方法有效。
  • @ExceptionHandler 用于全局处理控制器里的异常
  • @InitBinder 用来设置WebDataBinder,WebDataBinder用来自动绑定前台请求参数到Model中。
  • @ModelAttribute 本来的作用是绑定键值对到Model里,在@ControllerAdvice中是让全局的@RequestMapping都能获得在此处设置的键值对。

Swagger2

注解名称注解属性作用域属性作用
@Apitags说明该类的作用
value说明该类的作用
@ApiOperation()value方法描述方法作用
notes方法提示内容
tags方法分组
@ApiParam()name方法参数参数名
value方法参数参数说明
required方法参数是否必填
@ApiModel()value对象名
description描述
@ApiModelProperty()value方法字段说明
name方法属性名
dataType方法属性类型
required方法是否必填
example方法举例
hidden方法隐藏
@ApiImplicitParam()value方法参数说明
name方法参数名
dataType方法数据类型
paramType方法参数类型
example方法举例
@ApiResponse()response方法返回类
code方法返回码
message方法返回信息
examples方法例子
注解属性备注
@Apivalue字符串可用在class头上,class描述
description字符串
@Api(value = "xxx", description = "xxx")
@ApiOperationvalue字符串可用在方法头上.参数的描述容器
notes字符串
@ApiOperation(value = "xxx", notes = "xxx")
@ApiImplicitParams{}@ApiImplicitParam数组可用在方法头上.参数的描述容器
@ApiImplicitParams({@ApiImplicitParam1,@ApiImplicitParam2,...})
@ApiImplicitParamname字符串 与参数命名对应可用在@ApiImplicitParams里
value字符串参数中文描述
required布尔值true/false
dataType字符串参数类型
paramType字符串参数请求方式:query/path
query:对应@RequestParam?传递
path: 对应@PathVariable{}path传递
defaultValue字符串在api测试中默认值
用例参见项目中的设置
@ApiResponses{}@ApiResponse数组可用在方法头上.参数的描述容器
@ApiResponses({@ApiResponse1,@ApiResponse2,...})
@ApiResponsecode整形可用在@ApiResponses里
message字符串错误描述
@ApiResponse(code = 200, message = "Successful")
查看原文

赞 0 收藏 0 评论 0

claer 发布了文章 · 4月29日

最全IDEA插件

博客地址:https://www.bajins.com/IDE/IDEA使用和插件.html#插件

Free Mybatis plugin

mybatis-lite

mybatis-plus

MyBatis Log Plugin

直接将Mybatis执行的sql脚本显示出来,无需处理,可以直接复制出来执行

.ignore

生成各种ignore文件,一键创建git ignore文件的模板,免得自己去写

Add to gitignore

在项目视图中右键单击文件/目录并将其添加到.gitignore

Maven Helper

一键查看maven依赖,查看冲突的依赖,一键进行exclude依赖

GsonFormat

google出的一键根据json文本生成java类,非常方便

RoboPOJOGenerator

从JSON生成Java和Kotlin POJO文件:GSON,FastJSON,AutoValue(GSON),Logan Square,Jackson,空注释模板。

Json2Pojo

允许使用GSON注释和访问器从JSON轻松创建Java POJO。

JSON-P to JSON generator

使用JSON-P在Java文件中自动创建toJson()方法

Toolkit

JavaBean复制为Json字符串,Json字符串格式化,Json字符串转换为JavaBean,Json压缩

JsonToKotlinClass

从JSON字符串生成Kotlin

bean2json

把bean对象转json

Java Bean To Json Tool

一个简单的Json工具将Java Bean转换为Json或Json-Schema

BoB Beans Builder

使用标准的getter,hashcode和equals以及生成器生成模式化的类。它非常适合生成的JSON序列化类或数据传输对象。

Code generator

提供操作以从自定义模板生成代码

codehelper.generator

生成器,可以更轻松,更高效地生成代码。

GenerateAllSetter

一键调用一个对象的所有set方法并且赋予默认值 在对象字段多的时候非常方便

POJO Generator

从数据库表生成JPA实体POJO

Protobuf Support

Google Protobuf对JetBrains产品的支持,Java序列化库,原型编译器,代码生成器

GoogleProtobufTool

protobuf生成java文件的IDEA插件.支持生成一个或者多个文件.

Lombok plugin

支持lombok的各种注解,从此不用写getter setter这些 可以把注解还原为原本的java代码,除此之外还有其他更多注解以减少代码

Translation

最好用的翻译插件,功能很强大,界面很漂亮

probie

这款插件可以让英语没那么好的开发者在定义类、方法、参数的时候更为合理,增加代码可读性。同时,也可以帮助我们快速的翻译英文注释来理解源码内容!

CodeGlance

代码编辑区迷你缩放插件,可以进行代码的全局预览。

CamelCase

命名风格转换插件,可以在 kebab-case,SNAKE_CASE,PascalCase,camelCase,snake_case 和 空格风格之间切换。
快捷键苹果为 + + U,windows下为Shift + Alt + U

String Manipulation

强大的字符串转换工具,驼峰、大小写、连接符等

FindBugs-IDEA

检测代码中可能的bug及不规范的位置,检测的模式相比p3c更多,写完代码后检测下 避免低级bug,强烈建议用一下,一不小心就发现很多老代码的bug

p3c

阿里巴巴出品的java代码规范插件,可以扫描整个项目 找到不规范的地方 并且大部分可以自动修复

Alibaba Java Coding Guidelines

阿里巴巴代码规范检查插件,当然规范可以参考《阿里巴巴Java开发手册》

CheckStyle-IDEA

代码规范检查

MetricsReloaded

代码复杂度检查

Statistic

代码统计

VisualVM Launcher

运行java程序的时候启动visualvm,方便查看jvm的情况 比如堆内存大小的分配,某个对象占用了多大的内存,jvm调优必备工具

Rainbow Brackets

彩虹颜色的括号,看着很舒服 敲代码效率变高

Markdown Navigator

带有GFM 的Markdown插件和匹配的预览样式。

IDEA Mind Map

思维导图支持多种格式文件的导入和导出,同步更新时便于合并「以文本格式保存,支持 markdown 的语法,方便解决冲突」

ASM Bytecode Outline

查看 Class 类的字节码

stackoverflow

这个插件对于查找问题特别方便,定位异常,点击右键快速跳转到stackoverflow网站,每次至少为你节约了10秒。

Codota

代码智能提示

Presentation Assistant

快捷键展示

Presentation Assistant

显示调用的操作的名称和键盘快捷键。

Key promoter X

很多人不愿意切换 IDE 就是因为快捷键的习惯问题。刚好这个插件可以提醒快捷键,有代入感,提醒的多了你就会了。
对于新手建议安装熟悉一下快捷键。

Key promoter

快捷键提示插件,会统计你鼠标点击某个功能的次数,提示你应该用什么快捷键,帮助记忆快捷键,等熟悉了之后可以关闭掉这个插件。

IDE Features Trainer

可以在IDE内部以交互方式学习基本快捷方式和基本功能。

JavaDoc

快速生成 java 注释的插件有很多,评分比较高的就是 JavaDoc ,注意作者为 Sergey Timofiychuk 。通过快捷 就可以生成注释。mac 的快捷键需要自己去设置, windows 快捷键如下:
  • 要为活动元素生成 javadocs,请按 shift + alt + G。
  • 要为当前 java 文件中的所有元素生成 javadocs,请按 shift + ctrl + alt + G。
  • 删除当前/选定元素上的 javadocs 请按 shift + alt + Z。
  • 删除当前类所有元素上的 javadocs:请按 shift + ctrl + alt + Z。

Git Commit Template

Git格式化模版,你可以按照实际情况格式化你的提交信息

Git Flow Integration

Git Flow 的图形界面操作

Eclipse Code Formatter

使用 Eclipse 的代码格式化风格,在一个团队中如果公司有规定格式化风格,这个可以使用。

Jindent-Source Code Formatter

自定义类、方法、doc、变量注释模板

Properties to YAML Converter

把 Properties 的配置格式改为 YAML 格式

mongo4idea

mongo客户端

iedis

redis客户端

Cloud Toolkit

帮助开发者更高效地开发、测试、诊断并部署应用。通过插件,可以将本地应用一键部署到任意服务器

RestfulToolkit

一套RESTful服务开发辅助工具集

RESTMan

RESTful接口调试工具, 支持json和xml格式报文,借助原生编辑器实现语法高亮和语法检查。

查看原文

赞 0 收藏 0 评论 0

claer 发布了文章 · 4月29日

Ehcache3配置使用

Ehcache3配置方式与Ehcache2的配置方式区别很大!
缓存管理器只有全局状态下其他地方才能根据名称拿到缓存,也就是只有保证在同一个缓存管理器下才能拿到指定缓存

两种配置方式可同时使用

xml配置方式

<config xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
        xmlns='http://www.ehcache.org/v3'
        xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core.xsd">

    <!--  http://www.ehcache.org/documentation/3.8/xml.html  -->

    <!--指定缓存持久化目录-->
    <persistence directory="${java.io.tmpdir}/ehcache-data"/>

    <!-- 1、声明一个名为foo的Cache-->
    <cache alias="foo">
        <!--2、foo的键值对被声明为字符串类型,如果没有指明,默认是Object类型。-->
        <key-type>java.lang.String</key-type>
        <value-type>java.lang.String</value-type>
        <resources>
            <!--3、foo被声明在堆上保存多达2,000个条目。-->
            <heap unit="entries">2000</heap>
            <!--4、在它开始被驱逐之前,还有多达500 MB的外堆内存-->
            <offheap unit="MB">500</offheap>
        </resources>
    </cache>

    <!-- <cache-template>可以让你创建一个抽象的<cache>配置文件,该配置文件可以进一步的被扩展。-->
    <cache-template name="myDefaults">
        <key-type>java.lang.Long</key-type>
        <value-type>java.lang.String</value-type>
        <!--  org.ehcache.config.ResourceUnit中的属性,声明在堆上保存多达2,000个条目 -->
        <heap unit="entries">200</heap>
    </cache-template>
    <!-- bar是这样的cache,它使用名为myDefaults的<cache-template>,并将其主键覆盖到更广泛的类型-->
    <cache alias="bar" uses-template="myDefaults">
        <key-type>java.lang.Number</key-type>
        <expiry>
            <!-- 通过ttl指定了cache过期时间,java.wechatutil.concurrent.TimeUnit中的属性,这里指定为10秒 -->
            <ttl unit="seconds">10</ttl>
        </expiry>
    </cache>
    <!-- simpleCache是另一个cache,它使用myDefaults配置文件作为其唯一的CacheConfiguration-->
    <cache alias="simpleCache" uses-template="myDefaults"/>


    <cache-template name="stringTemplate">
        <key-type>java.lang.String</key-type>
        <value-type>java.lang.Object</value-type>
        <heap unit="MB">200</heap>
    </cache-template>
    <cache alias="stringCache" uses-template="stringTemplate">
        <value-type>java.lang.String</value-type>
        <expiry>
            <!-- 通过ttl指定了cache过期时间,java.wechatutil.concurrent.TimeUnit中的属性,这里指定为10分钟 -->
            <ttl unit="minutes">10</ttl>
        </expiry>
    </cache>
</config>

SpringBoot配置

# 新版本使用jcache,老版本使用ehcache
spring.cache.jcache.config=classpath:ehcache3.xml

代码配置方式

  • 资源池生成器配置持久化
ResourcePoolsBuilder resourcePoolsBuilder = ResourcePoolsBuilder.newResourcePoolsBuilder()
    // 堆内缓存大小
    .heap(heapCacheSize, MemoryUnit.KB)
    // 堆外缓存大小
    .offheap(offHeapCacheSize, MemoryUnit.MB)
    // 文件缓存大小
    .disk(diskCacheSize, MemoryUnit.MB);
  • 生成配置
CacheConfigurationBuilder.newCacheConfigurationBuilder(keyType, valueType, resourcePoolsBuilder)
    // 缓存超时时间
    .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(withExpiry))
    // 统计对象大小时对象图遍历深度
    .withSizeOfMaxObjectGraph(heapMaxObjectGraph)
    //可缓存的最大对象大小
    .withSizeOfMaxObjectSize(heapMaxObjectSize, MemoryUnit.MB)
    // 添加监听器
    .add(CacheEventListenerConfigurationBuilder.newEventListenerConfiguration(
            new EhCacheEventListener(), EventType.EXPIRED).unordered().asynchronous())
    //最后调用build()返回一个完整的实例,但是该实例并未初始化。
    .build();
  • 根据配置创建缓存管理器
CacheManagerBuilder.newCacheManagerBuilder().withCache(cacheName, config).build(true);
查看原文

赞 1 收藏 1 评论 0

claer 回答了问题 · 4月26日

解决java8 lambda foreach return变量报错

AtomicReference<String> content = new AtomicReference<>("链接已过期,请在公众号《小C技术栈》中回复:”idea共享码“,重新获取");
jetBrainsKey.forEach(entry -> {
    if (entry.getValue().equals(id)) {
        Article article = new Article();
        article.setTitle("idea激活码");
        List<Article> articles = articleService.selectSelective(article);
        Article article1 = articles.get((int) (Math.random() * articles.size()));
        content.set(article1.getContent());
    }
});

关注 6 回答 5

claer 发布了文章 · 4月3日

SpringBoot项目目录结构

一、代码层结构

根目录:com.bajins

  • 启动类BajinsApplication.java推荐放在根目录com.bajins包下
  • 数据实体类domain

    • jpa项目: com.bajins.domain
    • mybatis项目: com.bajins.entity
  • 数据接口访问层Dao

    • jpa项目: com.bajins.repository
    • mybatis项目: com.bajins.mapper
  • 数据服务接口层Service:com.bajins.service
  • 数据服务接口实现层Service Implements:com.bajins.service.impl
  • 前端控制器层Controller:com.bajins.controller
  • 工具类库utils:com.bajins.utils
  • 配置类config:com.bajins.config
  • 数据传输对象dto:com.bajins.dto
数据传输对象Data Transfer Object用于封装多个实体类domain之间的关系,不破坏原有的实体类结构
  • 视图包装对象vo:com.bajins.vo
视图包装对象View Object用于封装客户端请求的数据,防止部分数据泄露如:管理员ID,保证数据安全,不破坏 原有的实体类结构
  • 常量类constant:com.bajins.constant

二、资源目录结构

根目录:resources

  • 项目配置文件:resources/application.yml
  • 静态资源目录:resources/static/
用于存放html、css、js、图片等资源
  • 视图模板目录:resources/templates/
用于存放jsp、thymeleaf等模板文件
  • mybatis映射文件:resources/mappers/
  • mybatis配置文件:resources/spring-mybatis.xml

三、mybatis项目结构示例

.
│  mvnw
│  mvnw.cmd
│  pom.xml
│  README.md
│  
└─src
    ├─main
    │  ├─java
    │  │  └─com
    │  │      └─bajins
    │  │          └─api
    │  │              │  BajinsApiApplication.java
    │  │              │      
    │  │              ├─config
    │  │              │  │  QuartzJob.java
    │  │              │  │  Swagger2.java
    │  │              │  │  TaskExecutorConfig.java
    │  │              │  │  
    │  │              │  └─websocket
    │  │              │          WebSocketConfig.java
    │  │              │          WebSocketHandler.java
    │  │              │          WebSocketInterceptor.java
    │  │              │          
    │  │              ├─constants
    │  │              │      WeChatConstants.java
    │  │              │      
    │  │              ├─mapper
    │  │              │      WechatLoginLogMapper.java
    │  │              │      WechatMsgMapper.java
    │  │              │      WechatUserOpenidMapper.java
    │  │              │      
    │  │              ├─domain
    │  │              │      WechatLoginLog.java
    │  │              │      WechatMsg.java
    │  │              │      WechatUserOpenid.java
    │  │              │      
    │  │              ├─service
    │  │              │  │  UserSignatureService.java
    │  │              │  │  WechatService.java
    │  │              │  │  WxMsgService.java
    │  │              │  │  
    │  │              │  └─impl
    │  │              │          WechatServiceImpl.java
    │  │              │          WxMsgServiceImpl.java
    │  │              │          
    │  │              ├─utils
    │  │              │  │  EmailUtil.java
    │  │              │  │  EncryptUtil.java
    │  │              │  │  StringUtil.java
    │  │              │  │          
    │  │              │  └─wechat
    │  │              │          SHA1.java
    │  │              │          WXBizMsgCrypt.java
    │  │              │          XMLParse.java
    │  │              │          
    │  │              ├─vo
    │  │              │      TemplateLibraryVO.java
    │  │              │      TemplateMessageVO.java
    │  │              │      
    │  │              └─controller
    │  │                      WechatController.java
    │  │                      WxMsgController.java
    │  │                      
    │  └─resources
    │      │  application.properties
    │      │  ehcache3.xml
    │      │  logback-spring.xml
    │      │  
    │      ├─mappers
    │      │      WechatLoginLogMapper.xml
    │      │      WechatMsgMapper.xml
    │      │      WechatUserOpenidMapper.xml
    │      │      
    │      ├─static
    │      └─templates
    └─test
        └─java
            └─com
                └─bajins
                    └─api
                            BajinsApiApplicationTests.java
                            
查看原文

赞 0 收藏 0 评论 0

claer 回答了问题 · 3月25日

jetbrains学生账户更换邮箱后怎么重新激活?

关注 2 回答 1

claer 赞了文章 · 3月22日

java项目中的classpath到底是什么

java项目中的classpath到底是什么

在java项目中,你一定碰到过classpath,通常情况下,我们是用它来指定配置/资源文件的路径。在刚开始学习的时候,自己也糊里糊涂,但是现在,是时候弄清楚它到底是指什么了。

顾名思义,classpath就是class的path,也就是类文件(*.class的路径)。一谈到文件的路径,我们就很有必要了解一个java项目(通常也是web项目)它在真正运行时候,这个项目内部的目录、文件的结构;这样,我们才好分析、理解classpath。

开发时期的web项目结构

下面,我以一个ssm的项目为例,我先把开发时候的项目的目录结构图放出来。根据maven的约定,一般我们的项目结构就像下面这样。

clipboard.png

classpath用在哪里了?

而我们经常用到classpath的地方,就是在指定一些配置/资源文件的时候会使用到。比如说,我们在web.xml中指定springmvc的配置文件,如下图,我们使用:classpath:entry/dev/spring-mvc.xml;再比如,当我们把*Mapper.xml文件放在了main/java/../mapping/文件夹下时,在mybatis的配置文件中配置其位置,我们使用:

classpath*:**/mapper/mapping/*Mapper.xml

clipboard.png

clipboard.png

很显然,上面这2个classpath的配置,是为了告诉配置文件,去哪里寻找我们要指定的配置文件。要想弄清楚为什么是上面这样写的,我们就要来看看项目运行时(或者是发布后)的目录结构了。

web项目发布后的目录结构

我们使用IDEA对项目进行打包,一种是war包,一种是explorer的文件夹,war包解压后就是explorer了。我们来对解压后的目录结构进行分析。

clipboard.png

经过对比,我们要注意到,开发时期的项目里,src/main/下面的javaresources文件夹都被(编译)打包到了生产包的WEB-INF/classes/目录下;而原来WEB-INF下面的views和web.xml则仍然还是在WEB-INF下面。同时由maven引入的依赖都被放入到了WEB-INF/lib/下面。最后,编译后的class文件和资源文件都放在了classes目录下。

clipboard.png

classpath原来是这个

在编译打包后的项目中,根目录是META-INFWEB-INF 。这个时候,我们可以看到classes这个文件夹,它就是我们要找的classpath。

在第1个例子里,classpath:entry/dev/spring-mvc.xml 中,classpath就是指WEB-INF/classes/这个目录的路径。需要声明的一点是,使用classpath:这种前缀,就只能代表一个文件

在第2个例子里,classpath*:**/mapper/mapping/*Mapper.xml,使用classpath*:这种前缀,则可以代表多个匹配的文件**/mapper/mapping/*Mapper.xml,双星号**表示在任意目录下,也就是说在WEB-INF/classes/下任意层的目录,只要符合后面的文件路径,都会被作为资源文件找到。

查看原文

赞 75 收藏 45 评论 14

claer 收藏了文章 · 3月19日

Dubbo源码解析(五)注册中心——multicast

注册中心——multicast

目标:解释以为multicast实现的注册中心原理,理解单播、广播、多播区别,解读duubo-registry-multicast的源码

这是dubbo实现注册中心的第二种方式,也是dubbo的demo模块中用的注册中心实现方式。multicast其实是用到了MulticastSocket来实现的。

我这边稍微补充一点关于多点广播,也就是MulticastSocket的介绍。MulticastSocket类是继承了DatagramSocket类,DatagramSocket只允许把数据报发送给一个指定的目标地址,而MulticastSocket可以将数据报以广播的形式发送给多个客户端。它的思想是MulticastSocket会把一个数据报发送给一个特定的多点广播地址,这个多点广播地址是一组特殊的网络地址,当客户端需要发送或者接收广播信息时,只要加入该组就好。IP协议为多点广播提供了一批特殊的IP地址,地址范围是224.0.0.0至239.255.255.255。MulticastSocket类既可以将数据报发送到多点广播地址,也可以接收其他主机的广播信息。

以上是对multicast背景的简略介绍,接下来让我们具体的来看dubbo怎么把MulticastSocket运用到注册中心的实现中。

我们先来看看包下面有哪些类:

multicast目录

可以看到跟默认的注册中心的包结构非常类似。接下来我们就来解读一下这两个类。

(一)MulticastRegistry

该类继承了FailbackRegistry类,该类就是针对注册中心核心的功能注册、订阅、取消注册、取消订阅,查询注册列表进行展开,利用广播的方式去实现。

1.属性

// logging output
// 日志记录输出
private static final Logger logger = LoggerFactory.getLogger(MulticastRegistry.class);

// 默认的多点广播端口
private static final int DEFAULT_MULTICAST_PORT = 1234;

// 多点广播的地址
private final InetAddress mutilcastAddress;

// 多点广播
private final MulticastSocket mutilcastSocket;

// 多点广播端口
private final int mutilcastPort;

//收到的URL
private final ConcurrentMap<URL, Set<URL>> received = new ConcurrentHashMap<URL, Set<URL>>();

// 任务调度器
private final ScheduledExecutorService cleanExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("DubboMulticastRegistryCleanTimer", true));

// 定时清理执行器,一定时间清理过期的url
private final ScheduledFuture<?> cleanFuture;

// 清理的间隔时间
private final int cleanPeriod;

// 管理员权限
private volatile boolean admin = false;

看上面的属性,需要关注以下几个点:

  1. mutilcastSocket,该类是muticast注册中心实现的关键,这里补充一下单播、广播、以及多播的区别,因为下面会涉及到。单播是每次只有两个实体相互通信,发送端和接收端都是唯一确定的;广播目的地址为网络中的全体目标,而多播的目的地址是一组目标,加入该组的成员均是数据包的目的地。
  2. 关注任务调度器和清理计时器,该类封装了定时清理过期的服务的策略。

2.构造方法

public MulticastRegistry(URL url) {
    super(url);
    if (url.isAnyHost()) {
        throw new IllegalStateException("registry address == null");
    }
    if (!isMulticastAddress(url.getHost())) {
        throw new IllegalArgumentException("Invalid multicast address " + url.getHost() + ", scope: 224.0.0.0 - 239.255.255.255");
    }
    try {
        mutilcastAddress = InetAddress.getByName(url.getHost());
        // 如果url携带的配置中没有端口号,则使用默认端口号
        mutilcastPort = url.getPort() <= 0 ? DEFAULT_MULTICAST_PORT : url.getPort();
        mutilcastSocket = new MulticastSocket(mutilcastPort);
        // 禁用多播数据报的本地环回
        mutilcastSocket.setLoopbackMode(false);
        // 加入同一组广播
        mutilcastSocket.joinGroup(mutilcastAddress);
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                byte[] buf = new byte[2048];
                // 实例化数据报
                DatagramPacket recv = new DatagramPacket(buf, buf.length);
                while (!mutilcastSocket.isClosed()) {
                    try {
                        // 接收数据包
                        mutilcastSocket.receive(recv);
                        String msg = new String(recv.getData()).trim();
                        int i = msg.indexOf('\n');
                        if (i > 0) {
                            msg = msg.substring(0, i).trim();
                        }
                        // 接收消息请求,根据消息并相应操作,比如注册,订阅等
                        MulticastRegistry.this.receive(msg, (InetSocketAddress) recv.getSocketAddress());
                        Arrays.fill(buf, (byte) 0);
                    } catch (Throwable e) {
                        if (!mutilcastSocket.isClosed()) {
                            logger.error(e.getMessage(), e);
                        }
                    }
                }
            }
        }, "DubboMulticastRegistryReceiver");
        // 设置为守护进程
        thread.setDaemon(true);
        // 开启线程
        thread.start();
    } catch (IOException e) {
        throw new IllegalStateException(e.getMessage(), e);
    }
    // 优先从url中获取清理延迟配置,若没有,则默认为60s
    this.cleanPeriod = url.getParameter(Constants.SESSION_TIMEOUT_KEY, Constants.DEFAULT_SESSION_TIMEOUT);
    // 如果配置了需要清理
    if (url.getParameter("clean", true)) {
        // 开启计时器
        this.cleanFuture = cleanExecutor.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                try {
                    // 清理过期的服务
                    clean(); // Remove the expired
                } catch (Throwable t) { // Defensive fault tolerance
                    logger.error("Unexpected exception occur at clean expired provider, cause: " + t.getMessage(), t);
                }
            }
        }, cleanPeriod, cleanPeriod, TimeUnit.MILLISECONDS);
    } else {
        this.cleanFuture = null;
    }
}

这个构造器最关键的就是一个线程和一个定时清理任务。

  1. 线程中做的工作是根据接收到的消息来判定是什么请求,作出对应的操作,只要mutilcastSocket没有断开,就一直接收消息,内部的实现体现在receive方法中,下文会展开讲述。
  2. 定时清理任务是清理过期的注册的服务。通过两次socket的尝试来判定是否过期。clean方法下文会展开讲述

3.isMulticastAddress

private static boolean isMulticastAddress(String ip) {
    int i = ip.indexOf('.');
    if (i > 0) {
        String prefix = ip.substring(0, i);
        if (StringUtils.isInteger(prefix)) {
            int p = Integer.parseInt(prefix);
            return p >= 224 && p <= 239;
        }
    }
    return false;
}

该方法很简单,为也没写注释,就是判断是否为多点广播地址,地址范围是224.0.0.0至239.255.255.255。

4.clean

private void clean() {
    // 当url中携带的服务接口配置为是*时候,才可以执行清理
    if (admin) {
        for (Set<URL> providers : new HashSet<Set<URL>>(received.values())) {
            for (URL url : new HashSet<URL>(providers)) {
                // 判断是否过期
                if (isExpired(url)) {
                    if (logger.isWarnEnabled()) {
                        logger.warn("Clean expired provider " + url);
                    }
                    //取消注册
                    doUnregister(url);
                }
            }
        }
    }
}

该方法也比较简单,关机的是如何判断过期以及做的取消注册的操作。下面会展开讲解这几个方法。

5.isExpired

private boolean isExpired(URL url) {
    // 如果为非动态管理模式或者协议是consumer、route或者override,则没有过期
    if (!url.getParameter(Constants.DYNAMIC_KEY, true)
            || url.getPort() <= 0
            || Constants.CONSUMER_PROTOCOL.equals(url.getProtocol())
            || Constants.ROUTE_PROTOCOL.equals(url.getProtocol())
            || Constants.OVERRIDE_PROTOCOL.equals(url.getProtocol())) {
        return false;
    }
    Socket socket = null;
    try {
        // 利用url携带的主机地址和端口号实例化socket
        socket = new Socket(url.getHost(), url.getPort());
    } catch (Throwable e) {
        // 如果实例化失败,等待100ms重试第二次,如果还失败,则判定已过期
        try {
            // 等待100ms
            Thread.sleep(100);
        } catch (Throwable e2) {
        }
        Socket socket2 = null;
        try {
            socket2 = new Socket(url.getHost(), url.getPort());
        } catch (Throwable e2) {
            return true;
        } finally {
            if (socket2 != null) {
                try {
                    socket2.close();
                } catch (Throwable e2) {
                }
            }
        }
    } finally {
        if (socket != null) {
            try {
                socket.close();
            } catch (Throwable e) {
            }
        }
    }
    return false;
}

这个方法就是判断服务是否过期,有两次尝试socket的操作,如果尝试失败,则判断为过期。

6.receive

private void receive(String msg, InetSocketAddress remoteAddress) {
    if (logger.isInfoEnabled()) {
        logger.info("Receive multicast message: " + msg + " from " + remoteAddress);
    }
    // 如果这个消息是以register、unregister、subscribe开头的,则进行相应的操作
    if (msg.startsWith(Constants.REGISTER)) {
        URL url = URL.valueOf(msg.substring(Constants.REGISTER.length()).trim());
        // 注册服务
        registered(url);
    } else if (msg.startsWith(Constants.UNREGISTER)) {
        URL url = URL.valueOf(msg.substring(Constants.UNREGISTER.length()).trim());
        // 取消注册服务
        unregistered(url);
    } else if (msg.startsWith(Constants.SUBSCRIBE)) {
        URL url = URL.valueOf(msg.substring(Constants.SUBSCRIBE.length()).trim());
        // 获得以及注册的url集合
        Set<URL> urls = getRegistered();
        if (urls != null && !urls.isEmpty()) {
            for (URL u : urls) {
                // 判断是否合法
                if (UrlUtils.isMatch(url, u)) {
                    String host = remoteAddress != null && remoteAddress.getAddress() != null
                            ? remoteAddress.getAddress().getHostAddress() : url.getIp();
                    // 建议服务提供者和服务消费者在不同机器上运行,如果在同一机器上,需设置unicast=false
                    // 同一台机器中的多个进程不能单播单播,或者只有一个进程接收信息,发给消费者的单播消息可能被提供者抢占,两个消费者在同一台机器也一样,
                    // 只有multicast注册中心有此问题
                    if (url.getParameter("unicast", true) // Whether the consumer's machine has only one process
                            && !NetUtils.getLocalHost().equals(host)) { // Multiple processes in the same machine cannot be unicast with unicast or there will be only one process receiving information
                        unicast(Constants.REGISTER + " " + u.toFullString(), host);
                    } else {
                        broadcast(Constants.REGISTER + " " + u.toFullString());
                    }
                }
            }
        }
    }/* else if (msg.startsWith(UNSUBSCRIBE)) {
    }*/
}

可以很清楚的看到,根据接收到的消息开头的数据来判断需要做什么类型的操作,重点在于订阅,可以选择单播订阅还是广播订阅,这个取决于url携带的配置是什么。

7.broadcast

private void broadcast(String msg) {
    if (logger.isInfoEnabled()) {
        logger.info("Send broadcast message: " + msg + " to " + mutilcastAddress + ":" + mutilcastPort);
    }
    try {
        byte[] data = (msg + "\n").getBytes();
        // 实例化数据报,重点是目的地址是mutilcastAddress
        DatagramPacket hi = new DatagramPacket(data, data.length, mutilcastAddress, mutilcastPort);
        // 发送数据报
        mutilcastSocket.send(hi);
    } catch (Exception e) {
        throw new IllegalStateException(e.getMessage(), e);
    }
}

这是广播的实现方法,重点是数据报的目的地址是mutilcastAddress。代表着一组地址

8.unicast

private void unicast(String msg, String host) {
    if (logger.isInfoEnabled()) {
        logger.info("Send unicast message: " + msg + " to " + host + ":" + mutilcastPort);
    }
    try {
        byte[] data = (msg + "\n").getBytes();
        // 实例化数据报,重点是目的地址是只是单个地址
        DatagramPacket hi = new DatagramPacket(data, data.length, InetAddress.getByName(host), mutilcastPort);
        // 发送数据报
        mutilcastSocket.send(hi);
    } catch (Exception e) {
        throw new IllegalStateException(e.getMessage(), e);
    }
}

这是单播的实现,跟广播的区别就只是目的地址不一样,单播的目的地址就只是一个地址,而广播的是一组地址。

9.doRegister && doUnregister && doSubscribe && doUnsubscribe

@Override
protected void doRegister(URL url) {
    broadcast(Constants.REGISTER + " " + url.toFullString());
}
@Override
protected void doUnregister(URL url) {
    broadcast(Constants.UNREGISTER + " " + url.toFullString());
}
@Override
protected void doSubscribe(URL url, NotifyListener listener) {
    // 当url中携带的服务接口配置为是*时候,才可以执行清理,类似管理员权限
    if (Constants.ANY_VALUE.equals(url.getServiceInterface())) {
        admin = true;
    }
    broadcast(Constants.SUBSCRIBE + " " + url.toFullString());
    // 对监听器进行同步锁
    synchronized (listener) {
        try {
            listener.wait(url.getParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT));
        } catch (InterruptedException e) {
        }
    }
}
@Override
protected void doUnsubscribe(URL url, NotifyListener listener) {
    if (!Constants.ANY_VALUE.equals(url.getServiceInterface())
            && url.getParameter(Constants.REGISTER_KEY, true)) {
        unregister(url);
    }
    broadcast(Constants.UNSUBSCRIBE + " " + url.toFullString());
}

这几个方法就是实现了父类FailbackRegistry的抽象方法。都是调用了broadcast方法。

10.destroy

@Override
public void destroy() {
    super.destroy();
    try {
        // 取消清理任务
        if (cleanFuture != null) {
            cleanFuture.cancel(true);
        }
    } catch (Throwable t) {
        logger.warn(t.getMessage(), t);
    }
    try {
        // 把该地址从组内移除
        mutilcastSocket.leaveGroup(mutilcastAddress);
        // 关闭mutilcastSocket
        mutilcastSocket.close();
    } catch (Throwable t) {
        logger.warn(t.getMessage(), t);
    }
    // 关闭线程池
    ExecutorUtil.gracefulShutdown(cleanExecutor, cleanPeriod);
}

该方法的逻辑跟dubbo注册中心的destroy方法类似,就多了把该地址从组内移除的操作。gracefulShutdown方法我在《dubbo源码解析(四)注册中心——dubbo》中已经讲到。

11.register

@Override
public void register(URL url) {
    super.register(url);
    registered(url);
}
protected void registered(URL url) {
    // 遍历订阅的监听器集合
    for (Map.Entry<URL, Set<NotifyListener>> entry : getSubscribed().entrySet()) {
        URL key = entry.getKey();
        // 判断是否合法
        if (UrlUtils.isMatch(key, url)) {
            // 通过消费者url获得接收到的服务url集合
            Set<URL> urls = received.get(key);
            if (urls == null) {
                received.putIfAbsent(key, new ConcurrentHashSet<URL>());
                urls = received.get(key);
            }
            // 加入服务url
            urls.add(url);
            List<URL> list = toList(urls);
            for (NotifyListener listener : entry.getValue()) {
                // 把服务url的变化通知监听器
                notify(key, listener, list);
                synchronized (listener) {
                    listener.notify();
                }
            }
        }
    }
}

可以看到该类重写了父类的register方法,不过逻辑没有过多的变化,就是把需要注册的url放入缓存中,如果通知监听器url的变化。

12.unregister

@Override
public void unregister(URL url) {
    super.unregister(url);
    unregistered(url);
}
protected void unregistered(URL url) {
    // 遍历订阅的监听器集合
    for (Map.Entry<URL, Set<NotifyListener>> entry : getSubscribed().entrySet()) {
        URL key = entry.getKey();
        if (UrlUtils.isMatch(key, url)) {
            Set<URL> urls = received.get(key);
            // 缓存中移除
            if (urls != null) {
                urls.remove(url);
            }
            if (urls == null || urls.isEmpty()){
                if (urls == null){
                    urls = new ConcurrentHashSet<URL>();
                }
                // 设置携带empty协议的url
                URL empty = url.setProtocol(Constants.EMPTY_PROTOCOL);
                urls.add(empty);
            }
            List<URL> list = toList(urls);
            // 通知监听器 服务url变化
            for (NotifyListener listener : entry.getValue()) {
                notify(key, listener, list);
            }
        }
    }
}

这个逻辑也比较清晰,把需要取消注册的服务url从缓存中移除,然后如果没有接收的服务url了,就加入一个携带empty协议的url,然后通知监听器服务变化。

13.lookup

@Override
public List<URL> lookup(URL url) {
    List<URL> urls = new ArrayList<URL>();
    // 通过消费者url获得订阅的服务的监听器
    Map<String, List<URL>> notifiedUrls = getNotified().get(url);
    // 获得注册的服务url集合
    if (notifiedUrls != null && notifiedUrls.size() > 0) {
        for (List<URL> values : notifiedUrls.values()) {
            urls.addAll(values);
        }
    }
    // 如果为空,则从内存缓存properties获得相关value,并且返回为注册的服务
    if (urls.isEmpty()) {
        List<URL> cacheUrls = getCacheUrls(url);
        if (cacheUrls != null && !cacheUrls.isEmpty()) {
            urls.addAll(cacheUrls);
        }
    }
    // 如果还是为空则从缓存registered中获得已注册 服务URL 集合
    if (urls.isEmpty()) {
        for (URL u : getRegistered()) {
            if (UrlUtils.isMatch(url, u)) {
                urls.add(u);
            }
        }
    }
    // 如果url携带的配置服务接口为*,也就是所有服务,则从缓存subscribed获得已注册 服务URL 集合
    if (Constants.ANY_VALUE.equals(url.getServiceInterface())) {
        for (URL u : getSubscribed().keySet()) {
            if (UrlUtils.isMatch(url, u)) {
                urls.add(u);
            }
        }
    }
    return urls;
}

该方法是返回注册的服务url列表,可以看到有很多种获得的方法这些缓存都保存在AbstractRegistry类中,相关的介绍可以查看《dubbo源码解析(三)注册中心——开篇》

14.subscribe && unsubscribe

@Override
public void subscribe(URL url, NotifyListener listener) {
    super.subscribe(url, listener);
    subscribed(url, listener);
}

@Override
public void unsubscribe(URL url, NotifyListener listener) {
    super.unsubscribe(url, listener);
    received.remove(url);
}
protected void subscribed(URL url, NotifyListener listener) {
    // 查询注册列表
    List<URL> urls = lookup(url);
    // 通知url
    notify(url, listener, urls);
}

这两个重写了父类的方法,分别是订阅和取消订阅。逻辑很简单。

(二)MulticastRegistryFactory

该类继承了AbstractRegistryFactory类,实现了AbstractRegistryFactory抽象出来的createRegistry方法,看一下原代码:

public class MulticastRegistryFactory extends AbstractRegistryFactory {

    @Override
    public Registry createRegistry(URL url) {
        return new MulticastRegistry(url);
    }

}

可以看到就是实例化了MulticastRegistry而已,所有这里就不解释了。

后记

该部分相关的源码解析地址:https://github.com/CrazyHZM/i...

该文章讲解了dubbo利用multicast来实现注册中心,其中关键的是需要弄明白MulticastSocket以及单播、广播、多播的概念,其他的逻辑并不复杂。如果我在哪一部分写的不够到位或者写错了,欢迎给我提意见,我的私人微信号码:HUA799695226。

查看原文

claer 发布了文章 · 3月18日

使用可用源从CentOS6升级到CentOS7

参考:
https://tlanyan.me/upgrade-ce...
https://blog.51cto.com/moerji...
https://blog.csdn.net/hjnth/a...

说明

非必要情况,请使用重新安装系统的方式升级,原因如下:

  1. 并非所有的系统都能顺利从6升级到7,安装的软件越少,升级成功的可能性越大;
  2. 只支持6.5及以上系统升级到不高于7.2系统;
  3. 升级的耗时完全不比重新安装少,绝大多数情况下会耗费更长的时间和更多精力;
  4. 升级完成后处理各种依赖是一个非常头大的问题。

升级

配置升级工具upgradetool源

cat>/etc/yum.repos.d/upgradetool.repo<<EOF
[upg]
name=CentOS-$releasever - Upgrade Tool
baseurl=https://buildlogs.centos.org/centos/6/upg/x86_64/
gpgcheck=1
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6
EOF

或者

cat <<EOF >/etc/yum.repos.d/upgrade.repo
[upgrade]
name=upgrade
baseurl=https://buildlogs.centos.org/centos/6/upg/x86_64/
enable=1
gpgcheck=0
EOF

安装升级工具

为防止报错preupg: error: [Errno 2] No such file or directory: '/root/preupgrade/result.html',重新装旧版本的openscp
yum erase openscap -y &&\
yum install -y http://buildlogs.centos.org/centos/6/upg/x86_64/Packages/openscap-1.0.8-1.0.1.el6.centos.x86_64.rpm &&\
sudo yum install -y preupgrade-assistant-contents redhat-upgrade-tool preupgrade-assistant

升级前可行性分析

查看支持的升级

preupg -l

执行升级分析

preupg -s CentOS6_7
没有提示出错,可以继续下一步。否则请检查!!!要全部PASS才可以。

上面这个命令生成的报告需要看看,主要是关于升级的风险的
个人经验就是升级前尽量将非官方的rpm安装的软件都删掉
安装的第三方的rpm包越少,升级的风险越小

导入CentOS7的key

多个源镜像

官方源:

rpm --import http://mirror.centos.org/centos/RPM-GPG-KEY-CentOS-7
rpm --import http://vault.centos.org/centos/7.2.1511/os/x86_64/RPM-GPG-KEY-CentOS-7

阿里源:

rpm --import https://mirrors.aliyun.com/centos/7/os/x86_64/RPM-GPG-KEY-CentOS-7

升级系统

多个源镜像

官方源:

redhat-upgrade-tool-cli --force --network 7 --instrepo=http://mirror.centos.org/centos/7/os/x86_64
redhat-upgrade-tool-cli --force --network 7 --instrepo=http://vault.centos.org/centos/7.2.1511/os/x86_64/

阿里源:

redhat-upgrade-tool --force --network 7.0 --instrepo=http://mirrors.aliyun.com/centos/7/os/x86_64/

新华大学源:

centos-upgrade-tool-cli --force --network 7 --instrepo=https://mirrors.tuna.tsinghua.edu.cn/centos-vault/7.2.1511/os/x86_64/
如果遇到报错Error: database disk image is malformed,清除缓存,再次重试
yum clean dbcache

重启服务器

reboot 

收尾工作

机器起来后,登上服务器,需要做一些擦屁股的工作,比如,看还有没有CentOS6的软件残余,用命令:
rpm -qa | grep -i el6;
有的话要么想办法删掉,要么想办法将其升级到el7的相应的软件包。

升级完成后遇到的问题

ssh、yum不可用问题:

vi /root/start.sh 输入以下内容:
#!/bin/bash
ln -s /usr/lib64/libsasl2.so.3.0.0 /usr/lib64/libsasl2.so.2
ln -s /usr/lib64/libpcre.so.1.2.0 /usr/lib64/libpcre.so.0
service sshd restart
rm -rf /etc/rc.d/rc.local 
mv /etc/rc.d/rc.local.bak /etc/rc.d/rc.local #恢复原始文件
rm -rf /root/start.sh #删除自身
执行以下命令
chmod +x start.sh &&\
chmod +x /etc/rc.d/rc.local &&\
# 创建备份
cp /etc/rc.d/rc.local /etc/rc.d/rc.local.bak &&\
# 添加脚本为开机自启动
echo 'bash /root/start.sh' >>/etc/rc.d/rc.local
重启后看下ssh是否可以正常连接
reboot

ps工具不可用问题:

yum upgrade -y &&\
yum downgrade grep &&\
yum upgrade python &&\
yum update
查看原文

赞 0 收藏 0 评论 0

认证与成就

  • 获得 3 次点赞
  • 获得 4 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 4 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2018-05-10
个人主页被 162 人浏览