迷途小书童

读过几年书,尘世间一枚不起眼的小书童


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 搜索

xx后台开发面试总结

发表于 2020-11-21 | 分类于 面试

XX 后台开发工程师(接受Java转Go)

以下经历来源自个人好友谢晓峰

  • 一面 技术leader面 50分钟(20201019)

  1. 都是项目介绍,工作做了些啥 ?

  2. 索引的实现原理,为什么要用b树实现索引 ?

  3. 数据库相关原理 bufferpool redolog undolog changebuffer等等执行一条insert语句,数据库擎层面是怎么做的 ?

  4. 数据库主从原理 ?

  5. redis集群原理 rediscluster

  6. redis主从原理 sentinel机制 ?

  7. https原理是什么 ?

  8. 情景: 敏感词过滤 14w的敏感词要做么做 ?

  9. 网络安全相关 xss csrf sql注入等 ?

  10. linux命令 wc如何使用问题 ?

  11. 消息队列的选型对比kafka rabbitmq rocketmq ?

    只记得这么多问题了……

​ 面完后十分钟立刻通知二面,三少爷感觉回答问题都是游刃有余除了两个细节不太好, linux命令这块确实不熟悉,只是在知道命令层面,具体的参数写法都是需要baidu的那种,一般的top free df du iostat vmstat都是会的,也知道日志查看相关的命令,再细致点怎么统计就得靠度娘了。本次面试是xx会议语音面试

  • 二面 总监面 50分钟(20201022)

  1. 平时工作内容 最大挑战 ?
  2. 所在公司的职级 ?
  3. 年薪多少 ?
  4. 怎么学习,为什么要学习springcloud,讲讲springcloud的组件和核心原理 ?
  5. 这里工作强度等问题 ?
  6. 然后就是工作内容的询问等 ?

​ 本次面试是xx会议视频(我单方的视频是看不到面试官的)面试,这一点三少爷觉得并不太好,因为只有三少爷开了视频,嘿嘿,尽人事听天命吧,学习每天还是得继续的。

  • 三面 HR经理面试 60分钟(20201027)

​ 最难的一个面试,大概就是谈价和谈职级吧,所以说大厂经验才是谈资,像三少爷这种传统企业去互联网的真真儿没有什么话语权,怎么说呢,其实就是行业的问题,技术本身并不矛盾,缺乏的是行业经验,比如你ERP经验对于互联网来说并无任何光环加成,反过来也一样,每一个行业都有很多精英,多向身边比你优秀的人多多学习吧。

  • 四面 HR经理复面 20分钟(20201102)

​ 其实就是确定薪资和职级以及公司福利的介绍等等,基本就是确定offer走审批流程阶段了,其实这个阶段并不安全,没有邮件的offer都是会存在未知因素的,最终被顶掉的例子也是比比皆是,希望三少爷努力了这么久不会中彩吧!

  • 总结

​ 感受:历时一个多月,终于拿到offer,三少爷还是满怀喜悦的,毕竟努力得到了认可。整体面试下来总体感受还是不错的,面试进度也推进的很快,虽然最后offer在薪酬审批过程中被卡了很久,但总算是有惊无险。技术方面问的东西偏底层,需要平时大量的积累,关键是跟面试官聊的来很重要,业务方面考察平时解决问题的思路以及学习方法和学习深度等。后面可能就从Java转向Golang了,于少爷而言具有挑战但难度不大,毕竟少爷可是看了大量jdk源码的人呐,加油吧!

  • 复习学习计划

  1. 操作系统

  2. 计算机组成原理

  3. 计算机网络

  4. 数据结构与算法

  5. linux

  6. golang

    最后来一波鸡血吧,学习走起~

图p1

总结一下

发表于 2020-10-16 | 分类于 总结

后面该写一些什么

之前很长一段时间没有更新,其实也不是因为懒,主要是有很多东西需要沉淀和研究,该写的东西应该是都有记录,陆续再慢慢放上来,最主要是这个hexo的格式实在是太难调整了,在这里就打打鸡血,博客是要继续写的,好记性不如烂笔头,后续我这列了一个提纲,一步一步来写出自己的理解:

  • 1、并发编程
  • 2、jvm
  • 3、redis
  • 4、mysql
  • 5、框架分析(spring、springmvc、springboot、springcloud)
  • 6、rabbitmq
  • 7、zookeeper
  • 8、jdk源码分析
  • 9、分布式相关

接下来就开始吧!
图p1

springIOC---2、手工简单实现基于annotation的IOC框架

发表于 2020-01-29 | 分类于 spring相关框架原理

序言

……上一篇中我们探讨了基于xml配置文件的方式如何实现一个IOC容器,然而实际情况是这种方式在现在的开发中应用场景已经不多了,如果你们公司还在使用这种方式那说明你们公司的技术是真的还挺落后的,在敏捷开发至上的时代,我们往往使用的是另外一种方式—基于annotation来实现IOC管理,springboot中就已经很少去使用xml文件的方式了,基本上都是采用注解的方式,下面我们就来看看,基于annotation我们该如何去实现一个IOC容器。

如何基于注解实现一个IOC容器

……这里我也不多做关于spring的相关介绍,大家可自行去官网了解相关内容,我们直接进入正题,承接上一篇文章,今天就来讲解一下第2种方式,通过annotation的方式来实现IOC容器。

代码结构

……整体的代码结构如下图,下面就逐一解析
图p1

**代码结构**

入口类TestMain

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.hk.annospringioc;

public class TestMain {
public static void main(String[] args) {
MyAnnotationApplicationContext fac = new MyAnnotationApplicationContext("com.hk.annospringioc");
Boy bean1 = (Boy) fac.getBean("boy");
bean1.printAll();
Girl bean2 = (Girl) fac.getBean("girl");
bean2.printAll();
Person bean = (Person) fac.getBean("person");
Person beancopy = (Person) fac.getBean("person");
bean.printAll();
System.out.println(bean == beancopy ? "是同一个person" : "不是同一个person");
}
}

整个程序的main方法中我们new了一个MyAnnotationApplicationContext这样的一个对象,并传入了一个类似包路径的参数,这里我们管它叫包扫描根目录,springboot就有这个概念,默认在主函数同级目录,这个类也是整个注解IOC构建过程中的核心类,介绍该类之前,先说明两个接口
ApplicationContext用于IOC容器构建

1
2
3
4
5
package com.hk.springioc;

public interface ApplicationContext {
public void springIOC();
}

BeanFactory用于对象实例管理与获取

1
2
3
4
5
package com.hk.springioc;

public interface BeanFactory {
public Object getBean(String name);
}

MyAnnotationApplicationContext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
package com.hk.annospringioc;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class MyAnnotationApplicationContext implements BeanFactory,ApplicationContext {

private ConfigManager configManager;

private Map<String,AnnoBean> beanContainer;

private Map<String,Object> instanceContainer ;

public MyAnnotationApplicationContext(String path){
configManager = new ConfigManager();
instanceContainer = new HashMap<String,Object>();
beanContainer = configManager.ScanPackage(path);
springIOC();
}



//依賴注入的關鍵
@Override
public void springIOC() {
Set<Entry<String, AnnoBean>> entrySet = beanContainer.entrySet();
for(Entry<String, AnnoBean> entry : entrySet){
// String name = entry.getKey();
AnnoBean bean = entry.getValue();
Object instance = createBean(bean);
// instanceContainer.put(name, instance);
}
}

private Object createBean(AnnoBean bean) {
if(instanceContainer.get(bean.getName())!=null){
return instanceContainer.get(bean.getName());
}
String className = bean.getClassName();
List<Property> props = bean.getProps();
Object newInstance = null;
try {
Class<?> forName = Class.forName(className);
newInstance = forName.newInstance();
for(Property pro : props){
String name = pro.getName();
Field field = forName.getDeclaredField(name);
field.setAccessible(true);
if(pro.getRef()!=null){
String ref = pro.getRef();
Object existBean = getBean(ref);
if(existBean==null){
Object createBean = createBean(beanContainer.get(ref));
field.set(newInstance,createBean);
}else{
field.set(newInstance,existBean);
}
}else{
Class<?> type = field.getType();
if(type==int.class){
field.set(newInstance,Integer.parseInt(pro.getValue()));
}else{
field.set(newInstance,pro.getValue());
}

}
}
if(bean.getSingleton().equals("1")){
instanceContainer.put(bean.getName(), newInstance);
}

} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
return newInstance;
}



@Override
public Object getBean(String name) {
return instanceContainer.get(name);
}

}

解释一下这个类主要做了什么:
1、new了一个ConfigManager对象
2、构建了一个HashMap类型的实例化容器instanceContainer
3、用第一步的ConfigManager对象对包扫描根目录下面的所有类进行扫描,获取一个Bean容器,这里的Bean就是annotation注解的相关bean信息,后面会有一个类进行封装
4、对Bean容器进行解析,构建最终的IOC实例容器,也就是第二步中的instanceContainer
下面就是我们相关自定义注解以及实体类中的注解表达方式

VO组装已经相关类和属相的注解信息

实体组装结构如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125

封装Person信息,依赖注入Boy和Girl
package com.hk.annospringioc;


@MyComponent(value="person")
public class Person {

@MyAutoWired(value = "boy")
private Boy boy;
@MyAutoWired(value = "girl")
private Girl girl;

public void printAll(){
boy.printAll();
girl.printAll();
}
}


类封装girl信息
package com.hk.annospringioc;

@MyComponent("girl")
public class Girl {

@MyValue("lily")
private String name;
@MyValue("17")
private int age;

public void printAll(){
System.out.println(name+":"+age);
}
}


封装boy的信息
package com.hk.annospringioc;

@MyComponent("boy")
public class Boy {

@MyValue("lilei")
private String name;

@MyValue("12")
private int age;

public void printAll(){
System.out.println(name+":"+age);
}
}


封装在类级别打上了注解的bean信息
package com.hk.annospringioc;

import java.util.List;

public class AnnoBean {
private String name;
private String className;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getSingleton() {
return singleton;
}
public void setSingleton(String singleton) {
this.singleton = singleton;
}
public List<Property> getProps() {
return props;
}
public void setProps(List<Property> props) {
this.props = props;
}
private String singleton = "1";
private List<Property> props;
}


封装在属性级别打上了注解的property的bean信息
package com.hk.annospringioc;

public class Property {
private String name;
private String value;
private String ref;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}

public String getRef() {
return ref;
}

public void setRef(String ref) {
this.ref = ref;
}

}

ConfigManager

……这里我们只需要对包扫描根路径下的所有类进行扫描,通过类以及属性的注解扫描组装好对应bean容器即可,其实跟xml配置文件的整个流程是一样一样的,只是在实际实现上面,我们不用去解析xml文件,而是通过reflect对每一个类进行扫描解析,扫描到对应的注解就做对应的操作,仅此而已,大家稍看代码自己体会一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package com.hk.annospringioc;

import java.io.File;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

/**
*
* @ClassName: ConfigManager
* @Description: TODO(这里用一句话描述这个类的作用)
* @author A18ccms a18ccms_gmail_com
* @date 2019年4月9日 下午5:33:23
*
*/
public class ConfigManager {

public Map<String,AnnoBean> ScanPackage(String basePackage){
List<String> list = new ArrayList<String>();
Map<String,AnnoBean> map = new HashMap<String,AnnoBean>();
final URL url = this.getClass().getClassLoader().getResource(basePackage.replaceAll("\\.", "/"));
final File basePackageFile = new File(url.getPath());
File[] listFiles = basePackageFile.listFiles();
for(File file : listFiles){
if(file.isFile()){
list.add(basePackage+"."+file.getName().split("\\.")[0]);
}
}

for(String strr : list){
String className = strr;
try {
Class<?> forName = Class.forName(className);
if(forName.isAnnotationPresent(MyComponent.class)){
MyComponent annotation = forName.getAnnotation(MyComponent.class);
String value = annotation.value();
AnnoBean bean = new AnnoBean();
bean.setName(value);
bean.setClassName(className);
List<Property> propList = new ArrayList<Property>();
Field[] fields = forName.getDeclaredFields();
for(Field field : fields){
if(field.isAnnotationPresent(MyAutoWired.class)){
Property prop = new Property();
MyAutoWired annotation2 = field.getAnnotation(MyAutoWired.class);
prop.setName(field.getName());
prop.setRef(annotation2.value());
propList.add(prop);
}
if(field.isAnnotationPresent(MyValue.class)){
Property prop = new Property();
MyValue annotation2 = field.getAnnotation(MyValue.class);
prop.setName(field.getName());
prop.setValue(annotation2.value());
propList.add(prop);
}
}
bean.setProps(propList);
map.put(value,bean);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}

}
return map;
}

}

写到这里还没有完,现在重点来了,有没有发现,上面这些类的类名或者属性上面多了三个这个东西,@MyAutoWired,@MyComponent,@MyValue,这就是我们的自定义注解,下面就看看这三个东西如何编写。

自定义注解编写

……自定义注解其实就是告诉框架,需要识别的注解是哪些,当构建bean容器的时候,框架初始化时见到对应的注解去做对应的操作,MyComponent是类级别注解,MyAutowired、MyValue是属性级别注解,只能在对应区域进行使用!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

package com.hk.annospringioc;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.dom4j.Element;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface MyAutoWired {
String value();
}


package com.hk.annospringioc;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.dom4j.Element;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyComponent {
String value();
}


package com.hk.annospringioc;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.dom4j.Element;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface MyValue {
String value();
}

好了,到此整个基于注解的ioc实现方式代码就写完了,下面我们来运行一下看看结果

最后运行TestMain

查看最终运行结果: 同样IOC容器内的实例也是单例的
图p2

**运行结果**

总结

……ok,写到这里我们就简单的实现了一个基于注解方式的IOC容器,原理其实也很简单,只需要你了解reflect以及java基础你都不用去了解xml相关的内容,本篇文章重点只是向大家介绍一个最基本的实现原理,理解这个原理对于以后的开发会有极大的帮助,所以现在你有稍微了解到我们在使用springboot开发过程中那些@Controller、@Service、@Component、@Autowired注解的含义了吗?下一篇文章我们就基于本篇的原理来自己实现一个简单的springmvc框架…谢谢!

springIOC---1、手工简单实现基于xml的IOC框架

发表于 2020-01-28 | 分类于 spring相关框架原理

如何自己实现一个IOC容器

……这里我并不想多做关于spring的相关介绍,大家可自行去官网了解相关内容,我们直接进入正题,假如让你自己去做开发,并且spring开始收取高额的费用了,这里只是假设,你该如何去开发?思考一分钟……稍微有点框架基础的人应该都会说自己开发一个IOC框架进行容器管理不就得了,确实,容器管理是本文的重点,今天就来讲解一下第一种方式,通过xml文件的方式来实现IOC容器。

代码结构

……整体的代码结构如下图,下面就逐一解析
图p1

**代码结构**

入口类TestMain

1
2
3
4
5
6
7
8
9
10
11
12
package com.hk.springioc;

public class TestMain {
public static void main(String[] args) {
MyXmlClassPathApplicationContext fac = new MyXmlClassPathApplicationContext("myspring.xml");
Person bean1 = (Person) fac.getBean("person");
Person bean2 = (Person) fac.getBean("person");
bean1.printAll();
bean2.printAll();
System.out.print(bean1 == bean2 ? "同一个对象" : "非同一个对象");
}
}

整个程序的main方法中我们new了一个MyXmlClassPathApplicationContext这样的一个对象,这也是整个稍后IOC构建过程中的核心类,介绍该类之前,先说明两个接口
ApplicationContext用于IOC容器构建

1
2
3
4
5
package com.hk.springioc;

public interface ApplicationContext {
public void springIOC();
}

BeanFactory用于对象实例管理与获取

1
2
3
4
5
package com.hk.springioc;

public interface BeanFactory {
public Object getBean(String name);
}

MyXmlClassPathApplicationContext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
package com.hk.springioc;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class MyXmlClassPathApplicationContext implements BeanFactory,ApplicationContext {

private ConfigManager configManager;

private Map<String,Bean> beanContainer;

private Map<String,Object> instanceContainer ;

public MyXmlClassPathApplicationContext(String path){
configManager = new ConfigManager();
instanceContainer = new HashMap<String,Object>();
beanContainer = configManager.parseXML(path);
springIOC();
}



//依賴注入的關鍵
@Override
public void springIOC() {
Set<Entry<String, Bean>> entrySet = beanContainer.entrySet();
for(Entry<String, Bean> entry : entrySet){
// String name = entry.getKey();
Bean bean = entry.getValue();
Object instance = createBean(bean);
// instanceContainer.put(name, instance);
}
}

private Object createBean(Bean bean) {
if(instanceContainer.get(bean.getName())!=null){
return instanceContainer.get(bean.getName());
}
String className = bean.getClassName();
List<Property> props = bean.getProps();
Object newInstance = null;
try {
Class<?> forName = Class.forName(className);
newInstance = forName.newInstance();
for(Property pro : props){
String name = pro.getName();
Field field = forName.getDeclaredField(name);
field.setAccessible(true);
if(pro.getRef()!=null){
String ref = pro.getRef();
Object existBean = getBean(ref);
if(existBean==null){
Object createBean = createBean(beanContainer.get(ref));
field.set(newInstance,createBean);
}else{
field.set(newInstance,existBean);
}
}else{
Class<?> type = field.getType();
if(type==int.class){
field.set(newInstance,Integer.parseInt(pro.getValue()));
}else{
field.set(newInstance,pro.getValue());
}

}
}
if(bean.getSingleton().equals("1")){
instanceContainer.put(bean.getName(), newInstance);
}

} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
return newInstance;
}



@Override
public Object getBean(String name) {
return instanceContainer.get(name);
}

}

解释一下这个类主要做了什么:
1、new了一个ConfigManager对象
2、构建了一个HashMap类型的实例化容器instanceContainer
3、用第一步的ConfigManager对象解析XML文件,获取一个Bean容器,这里的Bean就是XML文件中配置的相关bean信息,后面会有一个类进行封装
4、对Bean容器进行解析,构建最终的IOC实例容器,也就是第二步中的instanceContainer
下面就是xml文件以及对应封装类Bean

myspring.xml

配置文件结构如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean name="person" class="com.hk.springioc.Person">
<property name="boy" ref="boy" />
<property name="girl" ref="girl" />
</bean>

<bean name="boy" class="com.hk.springioc.Boy">
<property name="age" value="2" />
<property name="name" value="lilei" />
</bean>

<bean name="girl" class="com.hk.springioc.Girl">
<property name="age" value="1" />
<property name="name" value="lily" />
</bean>
</beans>

封装Person信息,依赖注入Boy和Girl
package com.hk.springioc;

public class Person {
private Boy boy;
private Girl girl;
public Boy getBoy() {
return boy;
}
public void setBoy(Boy boy) {
this.boy = boy;
}
public Girl getGirl() {
return girl;
}
public void setGirl(Girl girl) {
this.girl = girl;
}

public Person(){

}
public void printAll(){
System.out.println(boy.getName()+":"+boy.getAge());
System.out.println(girl.getName()+":"+girl.getAge());
}
}

类封装girl信息
package com.hk.springioc;

public class Girl {
private String name;
private int age;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public Girl(){

}
public void printAll(){
System.out.println(name+":"+age);
}
}

封装boy的信息
package com.hk.springioc;

public class Boy {

private String name;
private int age;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public Boy(){

}
public void printAll(){
System.out.println(name+":"+age);
}
}

类封装xml中的bean标签信息
package com.hk.springioc;

import java.util.List;

public class Bean {
private String name;
private String className;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getSingleton() {
return singleton;
}
public void setSingleton(String singleton) {
this.singleton = singleton;
}
public List<Property> getProps() {
return props;
}
public void setProps(List<Property> props) {
this.props = props;
}
private String singleton = "1";
private List<Property> props;
}

类封装xml文件中的property信息

package com.hk.springioc;

public class Property {
private String name;
private String value;
private String ref;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}

public String getRef() {
return ref;
}

public void setRef(String ref) {
this.ref = ref;
}

}

ConfigManager

我们可以使用dom4j或者SAXReader对xml文件进行解析,构建需要的Bean容器,这里需要重点说明一点!!!我们的xml配置文件中没有特殊说明的bean都是singleton,也只有单例的对象才需要IOC容器进行管理,非单例的对象IOC容器是不会管理的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package com.hk.springioc;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

/**
*
* @ClassName: ConfigManager
* @Description: TODO(这里用一句话描述这个类的作用)
* @author A18ccms a18ccms_gmail_com
* @date 2019年4月9日 下午5:33:23
* 解析xml組裝Bean容器
*
*/
public class ConfigManager {

public Map<String,Bean> parseXML(String path){
Map<String,Bean> map = new HashMap<String,Bean>();
InputStream is = this.getClass().getResourceAsStream(path);
SAXReader reader = new SAXReader();
try {
Document doc = reader.read(is);
List<Element> selectNodes = doc.selectNodes("//bean");
for(Element ele : selectNodes){
String nameValue = ele.attributeValue("name");
String classValue = ele.attributeValue("class");
String singtonValue = ele.attributeValue("singleton");
Bean bean = new Bean();
bean.setName(nameValue);
bean.setClassName(classValue);
if(singtonValue!=null){
bean.setSingleton(singtonValue);
}
List<Property> list = new ArrayList<Property>();
List<Element> pros = ele.elements("property");
for(Element pro : pros){
Property prop = new Property();
prop.setName(pro.attributeValue("name"));
prop.setValue(pro.attributeValue("value"));
prop.setRef(pro.attributeValue("ref"));
list.add(prop);
}
bean.setProps(list);
map.put(bean.getName(), bean);
}
} catch (DocumentException e) {
e.printStackTrace();
}


return map;
}

}

最后运行TestMain

查看最终运行结果:
图p2

**运行结果**

总结

……好了,写到这里我们就简单的实现了一个基于xml配置文件方式的IOC容器,原理其实很简单,只需要你了解reflect以及java基础,难的在于实现,spring框架通过各种设计模式实现了方便大家使用的最终发版,这里我不多做讲解,因为本人也在研究和学习当中,设计模式也是本人的缺陷,本篇文章重点只是向大家介绍一个最基本的实现原理,理解这个原理对于以后的开发会有很大的帮助,下一篇文章中我们再来讨论一下基于annotation的IOC容器构建…谢谢

springboot添加actuator

发表于 2019-11-25 | 分类于 springboot

springboot如何添加actuator

一、正常添加

首先你需要引入相关jar包

1
2
3
dependencies {
compile('org.springframework.boot:spring-boot-starter-actuator')
}

接下来需要在yml添加如下配置,大致意思就是开放节点检查并且暴露出所有的endpoint以http的方式

1
2
3
4
5
6
7
8
management:
endpoint:
health:
show-details: always
endpoints:
web:
exposure:
include: "*"

其实到这一步就完成了配置了,你就可以在浏览器通过http:\ip:port\actuator进行访问了,但是这个东东还是个挺隐私的东西,我们不能让任何人通过url就能进行访问和操作,所以需要你对这个东西进行权限验证,
这里我们使用springsecurity,添加如下代码:

1
2
3
4
5
6
7
8
9
10
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin().and().authorizeRequests()
.antMatchers("/actuator/*").authenticated()
.and().csrf().disable();
}
}

有没有发现我们配置了地址授权,但是我们要输入什么授权信息了?所以你还需要在yml中添加如下代码

1
2
3
4
5
6
spring:
security:
user:
roles: ACTUATOR_ADMIN
name: actuator
password: AA@4321,

到这里我们就真正完成了actuator的添加,我们来测试一下,但我访问:
http:\ip:port\actuator\info时,浏览器直接就跳转了:
图p1

**图p1**
当然这是一种简单的情况,应用本来就没有做任何的权限或加密动作,添加起来也是很简单,下面就说一种小猴我项目中遇到的特殊情况

二、不正常情况添加

其实我说的这种不正常添加就是原本的项目你就已经添加了security,并且使用了加密检测。

1
2
3
4
@Bean
public BCryptPasswordEncoder bcryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}

现在我们就用上述的方式启动项目然后访问http:\ip:port\actuator\info看看是什么情况,没错你会发现正常会跳转到授权页面
图p2

**图p2**
但是你只要输入密码, 就会像下图一样显示这样的报错信息: 同时, 我们在控制台也能看到如下的警告: ![图p4](20191125-springboot-actuator/4.jpg)
**图p4**
很明显所有的问题都出在这个加密器上面,这时你只需要加入下面一行代码,所有的问题就可以得到解决:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Value("${spring.security.user.roles}")
private String roles;
@Value("${spring.security.user.name}")
private String name;
@Value("${spring.security.user.password}")
private String password;

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser(name)
.password(new BCryptPasswordEncoder().encode(password))
.roles(roles);
}

这行代码大致意思就是还是在配置文件yml里面配置相关信息,但是这些信息注册到授权时先用这个加密器把密码加密好,然后当你重启后再输入用户密码,就能看到以下页面了
图p5

**图p5**

Git---删除本地和远程分支

发表于 2019-11-25 | 分类于 Git

Git—删除本地和远程分支

1.列出本地和远程分支:

1
git branch -a

2.删除本地分支:

1
git branch -D BranchName

其中-D也可以是–delete,如:

1
git branch --delete BranchName

3.删除本地的远程分支:

1
git branch -r -D origin/BranchName

4.远程删除git服务器上的分支:

1
git push origin -d BranchName

其中-d也可以是–delete,如:

1
git push origin --delete BranchName

5.再使用命令查看本地和远程分支情况

1
git branch -a

Jenkins-1、jenkins环境搭建

发表于 2019-11-19 | 分类于 Jenkins

Jenkins环境搭建

1、jdk安装

1.1 安装jdk

此步骤省略,参考这篇文章

2、Jenkins安装

以下是jenkins安装的完整步骤:

2.1 下载jenkins

下载jenkin命令:

wget https://pkg.jenkins.io/redhat/jenkins-2.205-1.1.noarch.rpm

2.2 安装jenkins

安装执行:

rpm -ivh jenkins-2.83-1.1.noarch.rpm

2.3 修改jenkins用户和端口

修改用户和端口:

vi /etc/sysconfig/jenkins
JENKINS_USER=root
JENKINS_PORT=8888

2.4 启动服务:

执行启动命令:
systemctl start jenkins

本人第一次启动是失败的遇到如下问题:

[root@localhost ~]# systemctl start jenkins
Job for jenkins.service failed because the control process exited with error code. See systemctl status jenkins.service and journalctl -xe for details.

按照提示输入查看问题,发现是jdk问题:

[root@localhost bin]# systemctl status jenkins.service
Starting Jenkins bash: /software/jdk/jdk1.8.0_152/bin: 是一个目录

所以需要修改按如下修改一下

vi /etc/init.d/jenkins

在最下面添加你的jdk位置即可

candidates=
/etc/alternatives/java
/usr/lib/jvm/java-1.8.0/bin/java
/usr/lib/jvm/jre-1.8.0/bin/java
/usr/lib/jvm/java-1.7.0/bin/java
/usr/lib/jvm/jre-1.7.0/bin/java
/usr/bin/java
/software/jdk/jdk1.8.0_152/bin/java

重新执行如下命令:

systemctl daemon-reload
systemctl start jenkins

然后就启动成功了

2.5 访问服务:

地址:http:\服务器ip:上面配置的port 都是你之前配置的,配置步骤就省略了
http://10.170.198.239:8080/
yonyou/yonyou@600588
图p1

**图p1**
ok搭建完成。

2.6 maven插件安装和Git插件安装

在系统管理–>插件管理中安装插件
图p2

**图p2**

Quartz---3、定时任务开发手册1--基础使用篇

发表于 2019-11-17 | 分类于 Quartz

项目结构说明

项目结构图

图p1

**图p1**
整体的后台任务项目结构如上图所述,我们在开发过程中只需要关注job类的编写以及相关调用服务类的编写并且在前台配置好执行计划即可,至于任务的执行计划也就是后台任务日志会由job监听器来自动完成。我们只需要关注业务代码编写即可,这里我们希望按照一种定义好的规范进行自定义job的编写,如下:

job类代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/***
* 测试job类
*/
@Slf4j
@DisallowConcurrentExecution
public class Job extends QuartzJobBean {

@Autowired
TestService testService;


@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
// 获取参数
JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
// 业务逻辑 ...
testService.test();
}
}

job类需要继承QuartzJobBean,并且复写executeInternal方法,这里需要注意不要直接在该方法内写业务代码因为可能会导至事务完整性问题,实际的业务代码需要在TestService中进行编写,并将该服务类注入到自定的job中进行业务调用。@DisallowConcurrentExecution注解不可缺少。

Service类代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Service
public class TestService {


@Transactional(propagation = Propagation.REQUIRED)
public void test(){
System.out.println("test");
}


@Transactional(propagation = Propagation.REQUIRES_NEW)
public void test_NEW(){
System.out.println("test");
}

}

如上代码,需要@Service注解让你的服务类被IOC容器管理,并且在你的业务方法上面使用@Transactional注解,至于传播属性需要根据实际业务情况进行使用,如果不存在事务嵌套的情况,上述两种方式基本都可以解决大部分的问题,下一篇文章中我们会详细分析一下遇到事务嵌套情况该如何处理(这是一个比较复杂的问题,可能需要对aop有一定深度的理解,下一篇中我们详细探讨一下)。

事务完整性演示

介绍完基本的开发方法,来检测一下是否按照上述标准事务完整性是正确的。
1、job类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Slf4j
@DisallowConcurrentExecution
public class Job extends QuartzJobBean {

@Autowired
TestService testService;


@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
// 获取参数
JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
// 业务逻辑 ...
System.out.println("job start");
testService.test();
}
}

2、service类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Service
public class TestService {

@Autowired
JdbcTemplate jdbcTemplate;

@Transactional(propagation = Propagation.REQUIRED)
public void test(){
System.out.println("test");
jdbcTemplate.execute("insert into quartz_test(code,name) values(1,'111') ");
int j = 1/0;//注意这里
jdbcTemplate.execute("insert into quartz_test(code,name) values(2,'222') ");
}


@Transactional(propagation = Propagation.REQUIRES_NEW)
public void test_NEW(){
System.out.println("test");
}

}

3、注册任务项目启动后每隔15秒执行一次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Component
@Order(value = 1)
public class AgentApplicationRun implements CommandLineRunner {

@Autowired
QuartzService quartzService;

@Override
public void run(String... strings) throws Exception {
HashMap<String,Object> map = new HashMap<>();
map.put("name",3);
quartzService.deleteJob("job3", "icbc");
quartzService.addJob(Job.class, "job3", "icbc", "15 * * * * ?","job任务3", map, new Date(),new Date(new Date().getTime()+1000000));

}
}

4、执行结果
4.1 服务代码中int j = 1/0没有注释掉
图p2

**图p2**
上图我们看到任务执行,并且如期抛出了/0异常,我们再检查数据库中的确一条数据也没有

图p3

**图p3**

4.1 下面我们把服务代码中int j = 1/0注释掉,再看执行结果
图p4

**图p4**
上图我们看到任务执行,并且没有抛出异常,我们再检查数据库中的确两条数据都进去了

图p5

**图p5**

定制任务计划说明

最后需要很强调一点,由于quartz有一个复杂的misfired机制,本人暂时也没有摸的很清楚,希望在前台定制执行计划的时候选择任务开始时间时尽量选择在当前时间之后,也就是你点击保存按钮之后的时间最佳,否则就有可能出现第一次执行任务出现misfired情况,轮到第二次执行任务时会立即执行两次或两次以上,这都是由于quartz内部不同的trigger类型采用了不同的misfired机制导致的。

Quartz---2、项目Quartz定时任务使用

发表于 2019-11-15 | 分类于 Quartz

界面说明

注意如果项目启动报错:
需要执行以下语句安装必须包

1
npm install --save react-cron-builder

项目目前只采用一个任务组:ICBC

1、界面展示

图p1

**图p1**
后台任务管理界面在系统设置里面,界面上能监控任务的实时状态

功能说明

目前后台任务节点包含以下功能

1、查询全部任务

图p3

**图p2**

2、查询运行中任务

可以查询正在处于运行的任务

3、新建Simple任务

图p3

**图p3**

4、新建Cron任务

同上,只是jobtime换成的Cron表达式,关于Cron表达式,可以参考上一篇
Cron表达式介绍!

5、修改任务

可以动态的修改任务的执行计划等

6、暂停任务

可以停止正在运行中的任务

7、继续任务

可以重启启动已经暂停的任务

8、立即运行一次任务

内存copy一份job进行独立运行

9、删除任务

手动删除不需要的job

开发说明

目前整个台任务采取独立jar包的方式,支持集群部署(集群服务器时间必须保持一致),随机负载均衡策略。
开发具体的自定job如下图:

图p4

**图p4**

图p5

**图p5**

自定义job类按照如图方式必须继承QuartzJobBean,并且重写executeInternal方法,在方法中添加业务代码即可,注意自定义job类上的注解要保留,目前业务代码的事务还在测试中,非数据库交互定时任务可以正常使用

Quartz---1、Cron表达式

发表于 2019-11-15 | 分类于 Quartz

CronTrigger

借用网上的一篇Cron表达式说明
CronTriggers往往比SimpleTrigger更有用,如果您需要基于日历的概念,而非SimpleTrigger完全指定的时间间隔,复发的发射工作的时间表。
CronTrigger,你可以指定触发的时间表如“每星期五中午”,或“每个工作日9:30时”,甚至“每5分钟一班9:00和10:00逢星期一上午,星期三星期五“。
即便如此,SimpleTrigger一样,CronTrigger拥有的startTime指定的时间表时生效,指定的时间表时,应停止(可选)结束时间。

Cron Expressions
cron的表达式被用来配置CronTrigger实例。 cron的表达式是字符串,实际上是由七子表达式,描述个别细节的时间表。这些子表达式是分开的空白,代表:

  1. Seconds
  2. Minutes
  3. Hours
  4. Day-of-Month
  5. Month
  6. Day-of-Week
  7. Year (可选字段)
    例 “0 0 12 ? * WED” 在每星期三下午12:00 执行,
    个别子表达式可以包含范围, 例如,在前面的例子里(“WED”)可以替换成 “MON-FRI”, “MON, WED, FRI”甚至”MON-WED,SAT”.

“*” 代表整个时间段.

每一个字段都有一套可以指定有效值,如

Seconds (秒) :可以用数字0-59 表示,

Minutes(分) :可以用数字0-59 表示,

Hours(时) :可以用数字0-23表示,

Day-of-Month(天) :可以用数字1-31 中的任一一个值,但要注意一些特别的月份

Month(月) :可以用0-11 或用字符串 “JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV and DEC” 表示

Day-of-Week(每周):可以用数字1-7表示(1 = 星期日)或用字符口串“SUN, MON, TUE, WED, THU, FRI and SAT”表示

“/”:为特别单位,表示为“每”如“0/15”表示每隔15分钟执行一次,“0”表示为从“0”分开始, “3/20”表示表示每隔20分钟执行一次,“3”表示从第3分钟开始执行

“?”:表示每月的某一天,或第周的某一天

“L”:用于每月,或每周,表示为每月的最后一天,或每个月的最后星期几如“6L”表示“每月的最后一个星期五”

“W”:表示为最近工作日,如“15W”放在每月(day-of-month)字段上表示为“到本月15日最近的工作日”

““#”:是用来指定“的”每月第n个工作日,例 在每周(day-of-week)这个字段中内容为”6#3” or “FRI#3” 则表示“每月第三个星期五”

1)Cron表达式的格式:秒 分 时 日 月 周 年(可选)。

字段名                 允许的值                        允许的特殊字符  
秒                      0-59                       , - * /  
分                      0-59                       , - * /  
小时                    0-23                        , - * /  
日                      1-31                       , - * ? / L W C  
月                      1-12 or JAN-DEC           , - * /  
周几                    1-7 or SUN-SAT           , - * ? / L C #  
年 (可选字段)         empty, 1970-2099                 , - * /



 “?”字符:表示不确定的值

 “,”字符:指定数个值

 “-”字符:指定一个值的范围

 “/”字符:指定一个值的增加幅度。n/m表示从n开始,每次增加m

 “L”字符:用在日表示一个月中的最后一天,用在周表示该月最后一个星期X

 “W”字符:指定离给定日期最近的工作日(周一到周五)

 “#”字符:表示该月第几个周X。6#3表示该月第3个周五

2)Cron表达式范例:

每隔5秒执行一次:*/5 * * * * ?

每隔1分钟执行一次:0 */1 * * * ?

每天23点执行一次:0 0 23 * * ?

每天凌晨1点执行一次:0 0 1 * * ?

每月1号凌晨1点执行一次:0 0 1 1 * ?

每月最后一天23点执行一次:0 0 23 L * ?

每周星期天凌晨1点实行一次:0 0 1 ? * L

在26分、29分、33分执行一次:0 26,29,33 * * * ?

每天的0点、13点、18点、21点都执行一次:0 0 0,13,18,21 * * ?
1…345
史蒂芬猴

史蒂芬猴

dora is my precious

49 日志
27 分类
38 标签
GitHub
© 2023 史蒂芬猴
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4
访客数 人 总访问量 次